Python中的OpenSSH SFTP包装器。
项目描述
# PySFTPserver
用Python编写的OpenSSH SFTP包装器。
## 特点
* 用户登录后,可以[自动在虚拟chroot环境中jail用户](#authorized_keys_magic)。
* 可以[自动转发SFTP请求到另一个服务器](#usage)。
* 与Python 2和Python 3兼容。
* 完全可扩展和可定制(以下为示例)。
* 完全符合[SFTP RFC](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt)。
## 安装
只需用pip安装PySFTPserver
```bash
$ pip install pysftpserver # 使用--user标志仅为您安装
```
**注意**:如果您想使用[自动转发存储](#usage),您必须明确指定paramiko依赖项
```bash
$ pip install pysftpserver[pysftpproxy]
```
否则,您始终可以克隆此存储库并手动启动`setup.py`
```bash
$ git clone https://github.com/unbit/pysftpserver.git
$ cd pysftpserver
$ python setup.py install
```
## 使用
我们提供了一些完全工作的示例
**pysftpjail**:一种SFTP存储,将用户限制在虚拟chroot环境中。
**pysftpproxy**:一种作为代理的SFTP存储,将每个请求转发到另一个SFTP服务器。
安装后,您将在您的 `$PATH` 中找到我们的两个存储,因此您可以通过使用适当的命令行可执行文件/参数来简单地启动它们。
```
$ pysftpjail -h
用法:pysftpjail [-h] [--logfile LOGFILE] [--umask UMASK] chroot
一个OpenSSH SFTP服务器包装器,将用户限制在chroot目录中。
位置参数
chroot chroot监狱的路径
可选参数
-h, --help 显示此帮助信息并退出
--logfile LOGFILE, -l LOGFILE
日志文件的路径
--umask UMASK, -u UMASK
设置SFTP服务器的umask
```
```
$ pysftpproxy -h
用法:pysftpproxy [-h] [-l LOGFILE] [-k private-key-path] [-p PORT] [-a]
[-c ssh config path] [-n known_hosts path] [-d]
用户[:密码]@主机名
一个OpenSSH SFTP服务器代理,将每个请求转发到远程服务器。
位置参数
用户[:密码]@主机名
远程服务器的ssh-url([用户[:密码]@]主机名)。主机名也可以指定为ssh_config的主机名。所有缺失的信息都将从那里收集
。
。
。
可选参数
-h, --help 显示此帮助信息并退出
-l LOGFILE, --logfile LOGFILE
日志文件的路径
-k private-key-path, --key private-key-path
私钥身份路径(默认为 ~/.ssh/id_rsa)
-p PORT, --port PORT SSH远程端口(默认为22)
-a, --ssh-agent 启用ssh-agent支持
-c ssh config path, --ssh-config ssh config path
ssh配置文件的路径(默认为 ~/.ssh/config)
。
-n known_hosts path, --known-hosts known_hosts path
已知主机的路径
-d, --disable-known-hosts
禁用known_hosts指纹检查(安全警告!)
。
```
###授权密钥魔法
使用 `pysftpjail`,您可以在用户连接到SFTP服务器后立即将其限制在虚拟chroot中。
您可以通过在SSH `authorized_keys` 文件中的用户条目前添加 `pysftpjail` 命令来实现此操作,例如:
```
command="pysftpjail path_to_your_jail" ssh-rsa AAAAB3[...等等]
```
您可能还想添加以下选项:
```
no-port-forwarding,no-x11-forwarding,no-agent-forwarding
```
最终结果是:
```
command="pysftpjail path_to_your_jail",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[...等等]
```
显然,您也可以使用 `pysftpproxy` 做同样的事情。
##自定义
我们提供了两个完整的SFTP存储示例:简单和限制。
无论如何,您可以子类化我们的 [通用抽象存储](pysftpserver/abstractstorage.py) 并将其适配到您的需求。
欢迎任何贡献,就像往常一样。 :+1
###实际自定义:MongoDB / GridFS存储
[MongoDB](http://www.mongodb.org/) 是一个开源的NOSQL文档数据库。
[GridFS](http://docs.mongodb.org/manual/core/gridfs/) 是一个用于在MongoDB数据库中存储和检索任意文件的规范。
以下示例将展示如何构建一个处理MongoDB / GridFS数据库中文件的存储。
####先决条件
我假设您已经在某处运行了一个MongoDB数据库,并且您正在使用一个 [virtualenv](https://virtualenv.readthedocs.org/en/latest/virtualenv.html)。
让我们使用以下命令安装MongoDB Python驱动程序 `pymongo`:
```bash
$ pip install pymongo
```
现在克隆此项目的存储库并使用开发模式安装基本包。
```bash
$ git clone https://github.com/unbit/pysftpserver.git
$ cd pysftpserver
$ python setup.py develop
```
**信息**:开发模式将允许我们修改包的源并全局使用它,而无需重新安装。
现在您已准备好创建存储。
####新的存储类
让我们创建一个新的存储(保存为 `pysftpserver/mongostorage.py`),该存储是 [抽象存储](pysftpserver/abstractstorage.py) 类的子类。
```python
"""MongoDB GridFS SFTP存储."""
from pysftpserver.abstractstorage import SFTPAbstractServerStorage
from pysftpserver.pysftpexceptions import SFTPNotFound
import pymongo
import gridfs
class SFTPServerMongoStorage(SFTPAbstractServerStorage)
"""MongoDB GridFS SFTP存储类."""
def __init__(self, home, remote, port, db_name)
"""家,温馨的家。
注意:您应该将您的家设置为一个合理的值。
指导客户端连接到您的MongoDB。
"""
self.home = "/"
client = pymongo.MongoClient(remote, port)
db = client[db_name]
self.gridfs = gridfs.GridFS(db)
def open(self, filename, flags, mode)
"""返回文件句柄。
filename = filename.decode() # 需要在Python 3中使用
if self.gridfs.exists(filename=filename)
return self.gridfs.find({'filename': filename})[0]
raise SFTPNotFound
def read(self, handle, off, size)
"""从句柄中读取size。偏移量被忽略。
return handle.read(size)
def close(self, handle)
"""关闭文件句柄。
handle.close()
"""
警告
此实现不完整,许多必需的方法都缺失。
"""
```
如你所见,一切都相当简单明了。
在`init`方法中,我们初始化MongoDB客户端,选择要使用的数据库,然后初始化GridFS。
然后,在`open`方法中,我们检查文件是否存在并返回其句柄;在`read`和`close`方法中,我们简单地转发调用到GridFS。
####测试新的存储
我强烈建议您测试您创建的新存储。
以下是一个示例(保存为`pysftpserver/tests/test_server_mongo.py`)
```python
import unittest
import os
from shutil import rmtree
import pymongo
import gridfs
from pysftpserver.server import *
from pysftpserver.mongostorage import SFTPServerMongoStorage
from pysftpserver.tests.utils import *
"""要运行这些测试,您必须在某处运行一个MongoDB实例。”
REMOTE = "localhost"
PORT = 1727
DB_NAME = "mydb"
class Test(unittest.TestCase)
@classmethod
def setUpClass(cls)
client = pymongo.MongoClient(REMOTE, PORT)
db = client[DB_NAME]
cls.gridfs = gridfs.GridFS(db)
def setUp(self)
os.chdir(t_path())
self.home = 'home'
if not os.path.isdir(self.home)
os.mkdir(self.home)
self.server = SFTPServer(
SFTPServerMongoStorage(REMOTE, PORT, DB_NAME),
logfile=t_path('log'),
raise_on_error=True
)
def tearDown(self)
os.chdir(t_path())
rmtree(self.home)
def test_read(self)
s = b"This is a test file."
f_name = "test" # put expects a non byte string!
b_f_name = b"test"
f = self.gridfs.put(s, filename=f_name)
self.server.input_queue = sftpcmd(
SSH2_FXP_OPEN,
sftpstring(b_f_name),
sftpint(SSH2_FXF_CREAT),
sftpint(0)
)
self.server.process()
handle = get_sftphandle(self.server.output_queue)
self.server.output_queue = b'' # reset the output queue
self.server.input_queue = sftpcmd(
SSH2_FXP_READ,
sftpstring(handle),
sftpint64(0),
sftpint(len(s)),
)
self.server.process()
data = get_sftpdata(self.server.output_queue)
self.assertEqual(s, data)
self.server.output_queue = b'' # reset output queue
self.server.input_queue = sftpcmd(
SSH2_FXP_CLOSE,
sftpstring(handle)
)
self.server.process()
# 清理!
self.gridfs.delete(f)
@classmethod
def tearDownClass(cls)
os.unlink(t_path("log")) # comment me to see the log!
rmtree(t_path("home"), ignore_errors=True)
```
####最终结果
最后,您可以使用创建的存储轻松创建一个二进制文件来启动服务器。
将其保存为`bin/pysftpmongo`。
```python
#!/usr/bin/env python
"""pysftpmongo可执行文件。”
import argparse
from pysftpserver.server import SFTPServer
from pysftpserver.mongostorage import SFTPServerMongoStorage
def main()
parser = argparse.ArgumentParser(
description='一个使用MongoDB/GridFS存储的OpenSSH SFTP服务器包装器。
)
parser.add_argument('remote', type=str,
help='MongoDB实例的远程地址')
parser.add_argument('port', type=int,
help='MongoDB实例的远程端口')
parser.add_argument('db_name', type=str,
help='要使用的DB的名称')
parser.add_argument('--logfile', '-l', dest='logfile',
help='日志文件的路径')
args = parser.parse_args()
SFTPServer(
storage=SFTPServerMongoStorage(
args.remote,
args.port,
args.db_name
),
logfile=args.logfile
).run()
if __name__ == '__main__'
main()
```
现在,使用 `chmod` 命令修改二进制文件权限并检查它是否可以顺利启动
```bash
$ chmod +x bin/pysftpmongo
$ bin/pysftpmongo "localhost" 1727 "mydb"
```
最后,您应该编辑 `setup.py` 文件的 `scripts` 字段以包含您的新二进制文件。
现在,运行 `python setup.py install` 将其安装到您的 `$PATH` 中,以便将来方便使用:例如,在 [使用 authorized_keys 文件时](#authorized_keys_magic)。
最终结果的预览(在 `authorized_keys` 文件中)
```
command="pysftpmongo REMOTE_TO_YOUR_DB REMOTE_PORT DB_NAME",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[... 以及如此等等]
```
就是这样!
####本例中使用的代码
本例中使用的所有代码都可以在该存储库的 `examples/mongodb_gridfs` 目录中找到。
##FileZilla 兼容性
FileZilla 需要每次调用 `SSH2_FXP_NAME` 响应(例如每次调用 `readdir` 时)返回的 `longname` 是与 `ls -l` 命令输出相同格式的字符串。
因此,如果您想保持与 FileZilla 的兼容性,请确保在从您的存储返回的统计字典中包含一个适当的 `longname` 字段,就像我们在这里做的那样 [这里](pysftpserver/storage.py#L78)。
##测试
您可以使用 [nose](https://nose.readthedocs.org/en/latest/) 进行测试。
从项目目录中,只需运行
```bash
$ nosetests
$ python setup.py test # 或者
```
用Python编写的OpenSSH SFTP包装器。
## 特点
* 用户登录后,可以[自动在虚拟chroot环境中jail用户](#authorized_keys_magic)。
* 可以[自动转发SFTP请求到另一个服务器](#usage)。
* 与Python 2和Python 3兼容。
* 完全可扩展和可定制(以下为示例)。
* 完全符合[SFTP RFC](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt)。
## 安装
只需用pip安装PySFTPserver
```bash
$ pip install pysftpserver # 使用--user标志仅为您安装
```
**注意**:如果您想使用[自动转发存储](#usage),您必须明确指定paramiko依赖项
```bash
$ pip install pysftpserver[pysftpproxy]
```
否则,您始终可以克隆此存储库并手动启动`setup.py`
```bash
$ git clone https://github.com/unbit/pysftpserver.git
$ cd pysftpserver
$ python setup.py install
```
## 使用
我们提供了一些完全工作的示例
**pysftpjail**:一种SFTP存储,将用户限制在虚拟chroot环境中。
**pysftpproxy**:一种作为代理的SFTP存储,将每个请求转发到另一个SFTP服务器。
安装后,您将在您的 `$PATH` 中找到我们的两个存储,因此您可以通过使用适当的命令行可执行文件/参数来简单地启动它们。
```
$ pysftpjail -h
用法:pysftpjail [-h] [--logfile LOGFILE] [--umask UMASK] chroot
一个OpenSSH SFTP服务器包装器,将用户限制在chroot目录中。
位置参数
chroot chroot监狱的路径
可选参数
-h, --help 显示此帮助信息并退出
--logfile LOGFILE, -l LOGFILE
日志文件的路径
--umask UMASK, -u UMASK
设置SFTP服务器的umask
```
```
$ pysftpproxy -h
用法:pysftpproxy [-h] [-l LOGFILE] [-k private-key-path] [-p PORT] [-a]
[-c ssh config path] [-n known_hosts path] [-d]
用户[:密码]@主机名
一个OpenSSH SFTP服务器代理,将每个请求转发到远程服务器。
位置参数
用户[:密码]@主机名
远程服务器的ssh-url([用户[:密码]@]主机名)。主机名也可以指定为ssh_config的主机名。所有缺失的信息都将从那里收集
。
。
。
可选参数
-h, --help 显示此帮助信息并退出
-l LOGFILE, --logfile LOGFILE
日志文件的路径
-k private-key-path, --key private-key-path
私钥身份路径(默认为 ~/.ssh/id_rsa)
-p PORT, --port PORT SSH远程端口(默认为22)
-a, --ssh-agent 启用ssh-agent支持
-c ssh config path, --ssh-config ssh config path
ssh配置文件的路径(默认为 ~/.ssh/config)
。
-n known_hosts path, --known-hosts known_hosts path
已知主机的路径
-d, --disable-known-hosts
禁用known_hosts指纹检查(安全警告!)
。
```
###授权密钥魔法
使用 `pysftpjail`,您可以在用户连接到SFTP服务器后立即将其限制在虚拟chroot中。
您可以通过在SSH `authorized_keys` 文件中的用户条目前添加 `pysftpjail` 命令来实现此操作,例如:
```
command="pysftpjail path_to_your_jail" ssh-rsa AAAAB3[...等等]
```
您可能还想添加以下选项:
```
no-port-forwarding,no-x11-forwarding,no-agent-forwarding
```
最终结果是:
```
command="pysftpjail path_to_your_jail",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[...等等]
```
显然,您也可以使用 `pysftpproxy` 做同样的事情。
##自定义
我们提供了两个完整的SFTP存储示例:简单和限制。
无论如何,您可以子类化我们的 [通用抽象存储](pysftpserver/abstractstorage.py) 并将其适配到您的需求。
欢迎任何贡献,就像往常一样。 :+1
###实际自定义:MongoDB / GridFS存储
[MongoDB](http://www.mongodb.org/) 是一个开源的NOSQL文档数据库。
[GridFS](http://docs.mongodb.org/manual/core/gridfs/) 是一个用于在MongoDB数据库中存储和检索任意文件的规范。
以下示例将展示如何构建一个处理MongoDB / GridFS数据库中文件的存储。
####先决条件
我假设您已经在某处运行了一个MongoDB数据库,并且您正在使用一个 [virtualenv](https://virtualenv.readthedocs.org/en/latest/virtualenv.html)。
让我们使用以下命令安装MongoDB Python驱动程序 `pymongo`:
```bash
$ pip install pymongo
```
现在克隆此项目的存储库并使用开发模式安装基本包。
```bash
$ git clone https://github.com/unbit/pysftpserver.git
$ cd pysftpserver
$ python setup.py develop
```
**信息**:开发模式将允许我们修改包的源并全局使用它,而无需重新安装。
现在您已准备好创建存储。
####新的存储类
让我们创建一个新的存储(保存为 `pysftpserver/mongostorage.py`),该存储是 [抽象存储](pysftpserver/abstractstorage.py) 类的子类。
```python
"""MongoDB GridFS SFTP存储."""
from pysftpserver.abstractstorage import SFTPAbstractServerStorage
from pysftpserver.pysftpexceptions import SFTPNotFound
import pymongo
import gridfs
class SFTPServerMongoStorage(SFTPAbstractServerStorage)
"""MongoDB GridFS SFTP存储类."""
def __init__(self, home, remote, port, db_name)
"""家,温馨的家。
注意:您应该将您的家设置为一个合理的值。
指导客户端连接到您的MongoDB。
"""
self.home = "/"
client = pymongo.MongoClient(remote, port)
db = client[db_name]
self.gridfs = gridfs.GridFS(db)
def open(self, filename, flags, mode)
"""返回文件句柄。
filename = filename.decode() # 需要在Python 3中使用
if self.gridfs.exists(filename=filename)
return self.gridfs.find({'filename': filename})[0]
raise SFTPNotFound
def read(self, handle, off, size)
"""从句柄中读取size。偏移量被忽略。
return handle.read(size)
def close(self, handle)
"""关闭文件句柄。
handle.close()
"""
警告
此实现不完整,许多必需的方法都缺失。
"""
```
如你所见,一切都相当简单明了。
在`init`方法中,我们初始化MongoDB客户端,选择要使用的数据库,然后初始化GridFS。
然后,在`open`方法中,我们检查文件是否存在并返回其句柄;在`read`和`close`方法中,我们简单地转发调用到GridFS。
####测试新的存储
我强烈建议您测试您创建的新存储。
以下是一个示例(保存为`pysftpserver/tests/test_server_mongo.py`)
```python
import unittest
import os
from shutil import rmtree
import pymongo
import gridfs
from pysftpserver.server import *
from pysftpserver.mongostorage import SFTPServerMongoStorage
from pysftpserver.tests.utils import *
"""要运行这些测试,您必须在某处运行一个MongoDB实例。”
REMOTE = "localhost"
PORT = 1727
DB_NAME = "mydb"
class Test(unittest.TestCase)
@classmethod
def setUpClass(cls)
client = pymongo.MongoClient(REMOTE, PORT)
db = client[DB_NAME]
cls.gridfs = gridfs.GridFS(db)
def setUp(self)
os.chdir(t_path())
self.home = 'home'
if not os.path.isdir(self.home)
os.mkdir(self.home)
self.server = SFTPServer(
SFTPServerMongoStorage(REMOTE, PORT, DB_NAME),
logfile=t_path('log'),
raise_on_error=True
)
def tearDown(self)
os.chdir(t_path())
rmtree(self.home)
def test_read(self)
s = b"This is a test file."
f_name = "test" # put expects a non byte string!
b_f_name = b"test"
f = self.gridfs.put(s, filename=f_name)
self.server.input_queue = sftpcmd(
SSH2_FXP_OPEN,
sftpstring(b_f_name),
sftpint(SSH2_FXF_CREAT),
sftpint(0)
)
self.server.process()
handle = get_sftphandle(self.server.output_queue)
self.server.output_queue = b'' # reset the output queue
self.server.input_queue = sftpcmd(
SSH2_FXP_READ,
sftpstring(handle),
sftpint64(0),
sftpint(len(s)),
)
self.server.process()
data = get_sftpdata(self.server.output_queue)
self.assertEqual(s, data)
self.server.output_queue = b'' # reset output queue
self.server.input_queue = sftpcmd(
SSH2_FXP_CLOSE,
sftpstring(handle)
)
self.server.process()
# 清理!
self.gridfs.delete(f)
@classmethod
def tearDownClass(cls)
os.unlink(t_path("log")) # comment me to see the log!
rmtree(t_path("home"), ignore_errors=True)
```
####最终结果
最后,您可以使用创建的存储轻松创建一个二进制文件来启动服务器。
将其保存为`bin/pysftpmongo`。
```python
#!/usr/bin/env python
"""pysftpmongo可执行文件。”
import argparse
from pysftpserver.server import SFTPServer
from pysftpserver.mongostorage import SFTPServerMongoStorage
def main()
parser = argparse.ArgumentParser(
description='一个使用MongoDB/GridFS存储的OpenSSH SFTP服务器包装器。
)
parser.add_argument('remote', type=str,
help='MongoDB实例的远程地址')
parser.add_argument('port', type=int,
help='MongoDB实例的远程端口')
parser.add_argument('db_name', type=str,
help='要使用的DB的名称')
parser.add_argument('--logfile', '-l', dest='logfile',
help='日志文件的路径')
args = parser.parse_args()
SFTPServer(
storage=SFTPServerMongoStorage(
args.remote,
args.port,
args.db_name
),
logfile=args.logfile
).run()
if __name__ == '__main__'
main()
```
现在,使用 `chmod` 命令修改二进制文件权限并检查它是否可以顺利启动
```bash
$ chmod +x bin/pysftpmongo
$ bin/pysftpmongo "localhost" 1727 "mydb"
```
最后,您应该编辑 `setup.py` 文件的 `scripts` 字段以包含您的新二进制文件。
现在,运行 `python setup.py install` 将其安装到您的 `$PATH` 中,以便将来方便使用:例如,在 [使用 authorized_keys 文件时](#authorized_keys_magic)。
最终结果的预览(在 `authorized_keys` 文件中)
```
command="pysftpmongo REMOTE_TO_YOUR_DB REMOTE_PORT DB_NAME",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[... 以及如此等等]
```
就是这样!
####本例中使用的代码
本例中使用的所有代码都可以在该存储库的 `examples/mongodb_gridfs` 目录中找到。
##FileZilla 兼容性
FileZilla 需要每次调用 `SSH2_FXP_NAME` 响应(例如每次调用 `readdir` 时)返回的 `longname` 是与 `ls -l` 命令输出相同格式的字符串。
因此,如果您想保持与 FileZilla 的兼容性,请确保在从您的存储返回的统计字典中包含一个适当的 `longname` 字段,就像我们在这里做的那样 [这里](pysftpserver/storage.py#L78)。
##测试
您可以使用 [nose](https://nose.readthedocs.org/en/latest/) 进行测试。
从项目目录中,只需运行
```bash
$ nosetests
$ python setup.py test # 或者
```
项目详情
关闭
pysftpserver-1.4.0.tar.gz 的哈希
算法 | 哈希摘要 | |
---|---|---|
SHA256 | c201be762bdae65a74808536c4c77cdc31ec7203dd85576637b703ab06b62bd9 |
|
MD5 | 4fd943f26b079751cf0f414ebe86d6fa |
|
BLAKE2b-256 | 1be0716c229b29731ae63f2679f319aa7d50315063880a083f5d383a465a970b |