{ "info": { "author": "Unbit", "author_email": "info@unbit.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 6 - Mature", "Environment :: Other Environment", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Internet :: File Transfer Protocol (FTP)", "Topic :: System :: Shells", "Topic :: System :: System Shells", "Topic :: Utilities" ], "description": "#pysftpserver\nAn OpenSSH SFTP wrapper written in Python.\n\n##Features\n* Possibility to [automatically jail users](#authorized_keys_magic) in a virtual chroot environment as soon as they login.\n* Possibility to [automatically forward SFTP requests to another server](#usage).\n* Compatible with both Python 2 and Python 3.\n* Fully extensible and customizable (examples below).\n* Totally conforms to the [SFTP RFC](https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt).\n\n##Installation\nSimply install pysftpserver with pip:\n```bash\n$ pip install pysftpserver # add the --user flag to install it just for you\n```\n\n**Note**: if you'd like to use the [automatic forwarding storage](#usage) you have to explicitly specify the paramiko dependency:\n```bash\n$ pip install pysftpserver[pysftpproxy]\n```\n\nOtherwise, you could always clone this repository and manually launch `setup.py`:\n```bash\n$ git clone https://github.com/unbit/pysftpserver.git\n$ cd pysftpserver\n$ python setup.py install\n```\n\n##Usage\nWe provide a couple of fully working examples:\n\n* **pysftpjail**: an SFTP storage that jails users in a virtual chroot environment.\n* **pysftpproxy**: an SFTP storage that acts as a proxy, forwarding each request to another SFTP server.\n\nYou'll find both our storages in your `$PATH` after the installation, so you can simply launch them by using the appropriate command line executable / arguments:\n\n```\n$ pysftpjail -h\n\nusage: pysftpjail [-h] [--logfile LOGFILE] [--umask UMASK] chroot\n\nAn OpenSSH SFTP server wrapper that jails the user in a chroot directory.\n\npositional arguments:\n chroot the path of the chroot jail\n\noptional arguments:\n -h, --help show this help message and exit\n --logfile LOGFILE, -l LOGFILE\n path to the logfile\n --umask UMASK, -u UMASK\n set the umask of the SFTP server\n```\n\n```\n$ pysftpproxy -h\n\nusage: pysftpproxy [-h] [-l LOGFILE] [-k private-key-path] [-p PORT] [-a]\n [-c ssh config path] [-n known_hosts path] [-d]\n user[:password]@hostname\n\nAn OpenSSH SFTP server proxy that forwards each request to a remote server.\n\npositional arguments:\n user[:password]@hostname\n the ssh-url ([user[:password]@]hostname) of the remote\n server. The hostname can be specified as a\n ssh_config's hostname too. Every missing information\n will be gathered from there\n\noptional arguments:\n -h, --help show this help message and exit\n -l LOGFILE, --logfile LOGFILE\n path to the logfile\n -k private-key-path, --key private-key-path\n private key identity path (defaults to ~/.ssh/id_rsa)\n -p PORT, --port PORT SSH remote port (defaults to 22)\n -a, --ssh-agent enable ssh-agent support\n -c ssh config path, --ssh-config ssh config path\n path to the ssh-configuration file (default to\n ~/.ssh/config)\n -n known_hosts path, --known-hosts known_hosts path\n path to the openSSH known_hosts file\n -d, --disable-known-hosts\n disable known_hosts fingerprint checking (security\n warning!)\n```\n\n###authorized_keys magic\nWith `pysftpjail` you can jail any user in the virtual chroot as soon as she connects to the SFTP server.\nYou can do it by simply prepending the `pysftpjail` command to the user entry in your SSH `authorized_keys` file, e.g.:\n```\ncommand=\"pysftpjail path_to_your_jail\" ssh-rsa AAAAB3[... and so on]\n```\n\nProbably, you'll want to add the following options too:\n```\nno-port-forwarding,no-x11-forwarding,no-agent-forwarding\n```\n\nAchieving as final result:\n```\ncommand=\"pysftpjail path_to_your_jail\",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[... and so on]\n```\n\nObviusly, you could do the same with `pysftpproxy`.\n\n##Customization\nWe provide two complete examples of SFTP storage: simple and jailed.\nAnyway, you can subclass our [generic abstract storage](pysftpserver/abstractstorage.py) and you can adapt it to your needs.\nAny contribution is welcomed, as always. :+1:\n\n###Real world customization: MongoDB / GridFS storage\n[MongoDB](http://www.mongodb.org/) is an open, NOSQL, document database.\n[GridFS](http://docs.mongodb.org/manual/core/gridfs/) is a specification for storing and retrieving arbitrary files in a MongoDB database.\nThe following example will show how to build a storage that handles files in a MongoDB / GridFS database.\n\n####Preliminary requirements\nI assume you already have a MongoDB database running somewhere and you are using a [`virtualenv`](https://virtualenv.readthedocs.org/en/latest/virtualenv.html).\nLet's install the MongoDB Python driver, `pymongo`, with:\n```bash\n$ pip install pymongo\n```\n\nNow clone this project's repository and install the base package in development mode.\n```bash\n$ git clone https://github.com/unbit/pysftpserver.git\n$ cd pysftpserver\n$ python setup.py develop\n```\n*Info for those who are asking:* development mode will let us modify the source of the packages and use it globally without needing to reinstall it.\n\nNow you're ready to create the storage.\n\n####New storage class\nLet's create a new storage (save it as `pysftpserver/mongostorage.py`) that subclasses the [abstract storage](pysftpserver/abstractstorage.py) class.\n\n```python\n\"\"\"MongoDB GridFS SFTP storage.\"\"\"\n\nfrom pysftpserver.abstractstorage import SFTPAbstractServerStorage\nfrom pysftpserver.pysftpexceptions import SFTPNotFound\nimport pymongo\nimport gridfs\n\n\nclass SFTPServerMongoStorage(SFTPAbstractServerStorage):\n \"\"\"MongoDB GridFS SFTP storage class.\"\"\"\n\n def __init__(self, home, remote, port, db_name):\n \"\"\"Home sweet home.\n\n NOTE: you should set your home to something reasonable.\n Instruct the client to connect to your MongoDB.\n \"\"\"\n self.home = \"/\"\n client = pymongo.MongoClient(remote, port)\n db = client[db_name]\n self.gridfs = gridfs.GridFS(db)\n\n def open(self, filename, flags, mode):\n \"\"\"Return the file handle.\"\"\"\n filename = filename.decode() # needed in Python 3\n if self.gridfs.exists(filename=filename):\n return self.gridfs.find({'filename': filename})[0]\n\n raise SFTPNotFound\n\n def read(self, handle, off, size):\n \"\"\"Read size from the handle. Offset is ignored.\"\"\"\n return handle.read(size)\n\n def close(self, handle):\n \"\"\"Close the file handle.\"\"\"\n handle.close()\n\n \"\"\"\n Warning: \n this implementation is incomplete, many required methods are missing.\n \"\"\"\n```\n\nAs you can see, it's all pretty straight-forward.\n\nIn the `init` method, we initialize the MongoDB client, select the database to use and then we initialize GridFS.\nThen, in the `open` method, we check if the file exists and return it's handler; in the `read` and `close` methods we simply forward the calls to the GridFS.\n\n####Testing the new storage\nI strongly encourage you to test your newly created storage. \nHere's an example (save it as `pysftpserver/tests/test_server_mongo.py`):\n\n```python\nimport unittest\nimport os\nfrom shutil import rmtree\n\nimport pymongo\nimport gridfs\n\nfrom pysftpserver.server import *\nfrom pysftpserver.mongostorage import SFTPServerMongoStorage\nfrom pysftpserver.tests.utils import *\n\n\"\"\"To run this tests you must have an instance of MongoDB running somewhere.\"\"\"\nREMOTE = \"localhost\"\nPORT = 1727\nDB_NAME = \"mydb\"\n\n\nclass Test(unittest.TestCase):\n\n @classmethod\n def setUpClass(cls):\n client = pymongo.MongoClient(REMOTE, PORT)\n db = client[DB_NAME]\n cls.gridfs = gridfs.GridFS(db)\n\n def setUp(self):\n os.chdir(t_path())\n self.home = 'home'\n\n if not os.path.isdir(self.home):\n os.mkdir(self.home)\n\n self.server = SFTPServer(\n SFTPServerMongoStorage(REMOTE, PORT, DB_NAME),\n logfile=t_path('log'),\n raise_on_error=True\n )\n\n def tearDown(self):\n os.chdir(t_path())\n rmtree(self.home)\n\n def test_read(self):\n s = b\"This is a test file.\"\n f_name = \"test\" # put expects a non byte string!\n b_f_name = b\"test\"\n\n f = self.gridfs.put(s, filename=f_name)\n self.server.input_queue = sftpcmd(\n SSH2_FXP_OPEN,\n sftpstring(b_f_name),\n sftpint(SSH2_FXF_CREAT),\n sftpint(0)\n )\n self.server.process()\n handle = get_sftphandle(self.server.output_queue)\n\n self.server.output_queue = b'' # reset the output queue\n self.server.input_queue = sftpcmd(\n SSH2_FXP_READ,\n sftpstring(handle),\n sftpint64(0),\n sftpint(len(s)),\n )\n self.server.process()\n data = get_sftpdata(self.server.output_queue)\n\n self.assertEqual(s, data)\n\n self.server.output_queue = b'' # reset output queue\n self.server.input_queue = sftpcmd(\n SSH2_FXP_CLOSE,\n sftpstring(handle)\n )\n self.server.process()\n\n # Cleanup!\n self.gridfs.delete(f)\n\n @classmethod\n def tearDownClass(cls):\n os.unlink(t_path(\"log\")) # comment me to see the log!\n rmtree(t_path(\"home\"), ignore_errors=True)\n```\n\n####Final results\nFinally, you can create a binary to comfortably launch the server using the created storage.\nSave it as `bin/pysftpmongo`.\n\n```python\n#!/usr/bin/env python\n\"\"\"pysftpmongo executable.\"\"\"\n\nimport argparse\nfrom pysftpserver.server import SFTPServer\nfrom pysftpserver.mongostorage import SFTPServerMongoStorage\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description='An OpenSSH SFTP server wrapper that uses a MongoDB/GridFS storage.'\n )\n\n parser.add_argument('remote', type=str,\n help='the remote address of the MongoDB instance')\n parser.add_argument('port', type=int,\n help='the remote port of the MongoDB instance')\n parser.add_argument('db_name', type=str,\n help='the name of the DB to use')\n parser.add_argument('--logfile', '-l', dest='logfile',\n help='path to the logfile')\n\n args = parser.parse_args()\n SFTPServer(\n storage=SFTPServerMongoStorage(\n args.remote,\n args.port,\n args.db_name\n ),\n logfile=args.logfile\n ).run()\n\n\nif __name__ == '__main__':\n main()\n```\n\nNow, `chmod` the binary and check that it starts without a hitch:\n```bash\n$ chmod +x bin/pysftpmongo\n$ bin/pysftpmongo \"localhost\" 1727 \"mydb\"\n```\n\nFinally, you should edit the `setup.py` `scripts` field to include your new binary. \nNow, running `python setup.py install` will put it somewhere in your `$PATH`, for later ease: e.g. when [using it in the authorized_keys file](#authorized_keys_magic).\n\nA sneak peek of the final result (in the `authorized_keys` file):\n```\ncommand=\"pysftpmongo REMOTE_TO_YOUR_DB REMOTE_PORT DB_NAME\",no-port-forwarding,no-x11-forwarding,no-agent-forwarding ssh-rsa AAAAB3[... and so on]\n```\n\nThat's it!\n\n####Code used in this example\nAll the code used in this example can be found in the [`examples/mongodb_gridfs`](examples/mongodb_gridfs/) directory of this repository.\n\n##FileZilla compatibility\nFileZilla requires the `longname` returned with each `SSH2_FXP_NAME` response (e.g. each time `readdir` is called) to be a string of the same format of the output of `ls -l` (`-rw-r--r-- 1 aldur staff 9596 Dec 29 18:36 README.md`).\n\nSo, if you want to keep compatibility with FileZilla, be sure to include a proper `longname` field to the stats dictionary you return from your storage, as we do [here](pysftpserver/storage.py#L78).\n\n##Tests\nYou can use [nose](https://nose.readthedocs.org/en/latest/) for tests.\nFrom the project directory, simply run:\n```bash\n$ nosetests\n$ python setup.py test # alternatively\n```", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/unbit/pysftpserver", "keywords": "pysftpserver,sftp,openssh,ssh,jail", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "pysftpserver", "package_url": "https://pypi.org/project/pysftpserver/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/pysftpserver/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/unbit/pysftpserver" }, "release_url": "https://pypi.org/project/pysftpserver/1.4.0/", "requires_dist": null, "requires_python": null, "summary": "An OpenSSH SFTP wrapper in Python.", "version": "1.4.0" }, "last_serial": 1382265, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "92fce5652411bc90594413ed72e76bb3", "sha256": "8596a7a4e875fb3e4419b8f6c53a9caad7e4dc4e4db4a22a3b2c3353ce302a28" }, "downloads": -1, "filename": "pysftpserver-1.0.0.tar.gz", "has_sig": false, "md5_digest": "92fce5652411bc90594413ed72e76bb3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7664, "upload_time": "2014-12-05T15:59:23", "url": "https://files.pythonhosted.org/packages/12/46/97d21db944cf64bb81b9623fc8bd5578b60cadd3e9628e37e1a068c06c85/pysftpserver-1.0.0.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "0b22064dd6ac8938f1ff62b2053fe5d4", "sha256": "caf07cd9d326af903d7e724a52b572c2d6b04332c48b8848a519597b5d07eb56" }, "downloads": -1, "filename": "pysftpserver-1.1.0.tar.gz", "has_sig": false, "md5_digest": "0b22064dd6ac8938f1ff62b2053fe5d4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14462, "upload_time": "2014-12-17T14:21:52", "url": "https://files.pythonhosted.org/packages/8d/c6/5fe907d4913a1b1786b78b37725475470992167f97b3780690b0bae2a130/pysftpserver-1.1.0.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "bb44a32bbe934a829d4f1f1164909918", "sha256": "b192772ff56f6dd6cc5fe7fcc9c7eb6d20bc68c976d2900cb606f06548088b13" }, "downloads": -1, "filename": "pysftpserver-1.2.0.tar.gz", "has_sig": false, "md5_digest": "bb44a32bbe934a829d4f1f1164909918", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15572, "upload_time": "2014-12-23T15:39:30", "url": "https://files.pythonhosted.org/packages/2b/dd/9896320a578b2a4b6650a4d75b41b66e69700c6857451346d0c238765c11/pysftpserver-1.2.0.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "7edfc7e07961e7c5eb362fb53db92d24", "sha256": "d4e1bc01001941c7aeb83a40fc8675bcaea86146ce3718c96f42778b85fc1f5f" }, "downloads": -1, "filename": "pysftpserver-1.3.0.tar.gz", "has_sig": false, "md5_digest": "7edfc7e07961e7c5eb362fb53db92d24", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 17032, "upload_time": "2014-12-29T19:24:48", "url": "https://files.pythonhosted.org/packages/f4/a3/3e25575c4a6d3f0317a9418109c88a972b159de5f660dae0076ef0bd316d/pysftpserver-1.3.0.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "4fd943f26b079751cf0f414ebe86d6fa", "sha256": "c201be762bdae65a74808536c4c77cdc31ec7203dd85576637b703ab06b62bd9" }, "downloads": -1, "filename": "pysftpserver-1.4.0.tar.gz", "has_sig": false, "md5_digest": "4fd943f26b079751cf0f414ebe86d6fa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20937, "upload_time": "2015-01-14T14:59:58", "url": "https://files.pythonhosted.org/packages/1b/e0/716c229b29731ae63f2679f319aa7d50315063880a083f5d383a465a970b/pysftpserver-1.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "4fd943f26b079751cf0f414ebe86d6fa", "sha256": "c201be762bdae65a74808536c4c77cdc31ec7203dd85576637b703ab06b62bd9" }, "downloads": -1, "filename": "pysftpserver-1.4.0.tar.gz", "has_sig": false, "md5_digest": "4fd943f26b079751cf0f414ebe86d6fa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20937, "upload_time": "2015-01-14T14:59:58", "url": "https://files.pythonhosted.org/packages/1b/e0/716c229b29731ae63f2679f319aa7d50315063880a083f5d383a465a970b/pysftpserver-1.4.0.tar.gz" } ] }