ftpd: add ftps

This commit is contained in:
ed 2022-02-13 15:46:33 +01:00
parent 76beaae7f2
commit 6ce0e2cd5b
5 changed files with 60 additions and 17 deletions

View file

@ -54,6 +54,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
* [other tricks](#other-tricks) * [other tricks](#other-tricks)
* [searching](#searching) - search by size, date, path/name, mp3-tags, ... * [searching](#searching) - search by size, date, path/name, mp3-tags, ...
* [server config](#server-config) - using arguments or config files, or a mix of both * [server config](#server-config) - using arguments or config files, or a mix of both
* [ftp-server](#ftp-server) - an FTP server can be started using `--ftp 3921`
* [file indexing](#file-indexing) * [file indexing](#file-indexing)
* [upload rules](#upload-rules) - set upload rules using volume flags * [upload rules](#upload-rules) - set upload rules using volume flags
* [compress uploads](#compress-uploads) - files can be autocompressed on upload * [compress uploads](#compress-uploads) - files can be autocompressed on upload
@ -626,12 +627,13 @@ using arguments or config files, or a mix of both:
## ftp-server ## ftp-server
an FTP server can be started using `--ftp 3921` (or any other port) an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit TLS (ftpes)
* based on [pyftpdlib](https://github.com/giampaolo/pyftpdlib) * based on [pyftpdlib](https://github.com/giampaolo/pyftpdlib)
* needs a dedicated port (cannot share with the HTTP/HTTPS API) * needs a dedicated port (cannot share with the HTTP/HTTPS API)
* runs in active mode by default, you probably want `--ftp-r` * uploads are not resumable -- delete and restart if necessary
* uploads are not resumable * runs in active mode by default, you probably want `--ftp-pr 12000-13000`
* if you enable both `ftp` and `ftps`, the port-range will be divided in half
## file indexing ## file indexing

View file

@ -447,9 +447,10 @@ def run_argparse(argv, formatter):
ap2 = ap.add_argument_group('FTP options') ap2 = ap.add_argument_group('FTP options')
ap2.add_argument("--ftp", metavar="PORT", type=int, help="enable FTP server on PORT, for example 3921") ap2.add_argument("--ftp", metavar="PORT", type=int, help="enable FTP server on PORT, for example 3921")
ap2.add_argument("--ftps", metavar="PORT", type=int, help="enable FTPS server on PORT, for example 3990")
ap2.add_argument("--ftp-dbg", action="store_true", help="enable debug logging") ap2.add_argument("--ftp-dbg", action="store_true", help="enable debug logging")
ap2.add_argument("--ftp-nat", metavar="ADDR", type=u, help="the NAT address to use for passive connections") ap2.add_argument("--ftp-nat", metavar="ADDR", type=u, help="the NAT address to use for passive connections")
ap2.add_argument("--ftp-r", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example 12000-13000") ap2.add_argument("--ftp-pr", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example 12000-13000")
ap2 = ap.add_argument_group('opt-outs') ap2 = ap.add_argument_group('opt-outs')
ap2.add_argument("-nw", action="store_true", help="disable writes (benchmark)") ap2.add_argument("-nw", action="store_true", help="disable writes (benchmark)")

View file

@ -2,6 +2,7 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os import os
import sys
import stat import stat
import time import time
import logging import logging
@ -14,6 +15,7 @@ from pyftpdlib.servers import FTPServer
from pyftpdlib.ioloop import IOLoop from pyftpdlib.ioloop import IOLoop
from pyftpdlib.log import config_logging from pyftpdlib.log import config_logging
from .__init__ import E
from .util import Pebkac, fsenc, exclude_dotfiles from .util import Pebkac, fsenc, exclude_dotfiles
from .bos import bos from .bos import bos
from .authsrv import AuthSrv from .authsrv import AuthSrv
@ -275,19 +277,55 @@ class FtpHandler(FTPHandler):
) )
try:
from pyftpdlib.handlers import TLS_FTPHandler
class SftpHandler(FtpHandler, TLS_FTPHandler):
pass
except:
pass
class Ftpd(object): class Ftpd(object):
def __init__(self, hub): def __init__(self, hub):
self.hub = hub self.hub = hub
self.args = hub.args self.args = hub.args
h = FtpHandler hs = []
if self.args.ftp:
hs.append([FtpHandler, self.args.ftp])
if self.args.ftps:
try:
h = SftpHandler
except:
m = "\nftps requires pyopenssl;\nplease run the following:\n\n {} -m pip install --user pyopenssl\n"
print(m.format(sys.executable))
sys.exit(1)
h.certfile = os.path.join(E.cfg, "cert.pem")
h.tls_control_required = True
h.tls_data_required = True
hs.append([h, self.args.ftps])
for h in hs:
h, lp = h
h.hub = hub h.hub = hub
h.args = hub.args h.args = hub.args
h.authorizer = FtpAuth() h.authorizer = FtpAuth()
h.authorizer.hub = hub h.authorizer.hub = hub
if self.args.ftp_r: if self.args.ftp_pr:
p1, p2 = [int(x) for x in self.args.ftp_r.split("-")] p1, p2 = [int(x) for x in self.args.ftp_pr.split("-")]
if self.args.ftp and self.args.ftps:
# divide port range in half
d = int((p2 - p1) / 2)
if lp == self.args.ftp:
p2 = p1 + d
else:
p1 += d + 1
h.passive_ports = list(range(p1, p2 + 1)) h.passive_ports = list(range(p1, p2 + 1))
if self.args.ftp_nat: if self.args.ftp_nat:
@ -298,7 +336,8 @@ class Ftpd(object):
ioloop = IOLoop() ioloop = IOLoop()
for ip in self.args.i: for ip in self.args.i:
FTPServer((ip, int(self.args.ftp)), h, ioloop) for h, lp in hs:
FTPServer((ip, int(lp)), h, ioloop)
t = threading.Thread(target=ioloop.loop) t = threading.Thread(target=ioloop.loop)
t.daemon = True t.daemon = True

View file

@ -105,7 +105,7 @@ class SvcHub(object):
args.th_poke = min(args.th_poke, args.th_maxage, args.ac_maxage) args.th_poke = min(args.th_poke, args.th_maxage, args.ac_maxage)
if args.ftp: if args.ftp or args.ftps:
from .ftpd import Ftpd from .ftpd import Ftpd
self.ftpd = Ftpd(self) self.ftpd = Ftpd(self)

View file

@ -116,6 +116,7 @@ args = {
"thumbnails": ["Pillow"], "thumbnails": ["Pillow"],
"audiotags": ["mutagen"], "audiotags": ["mutagen"],
"ftpd": ["pyftpdlib"], "ftpd": ["pyftpdlib"],
"ftps": ["pyopenssl"],
}, },
"entry_points": {"console_scripts": ["copyparty = copyparty.__main__:main"]}, "entry_points": {"console_scripts": ["copyparty = copyparty.__main__:main"]},
"scripts": ["bin/copyparty-fuse.py", "bin/up2k.py"], "scripts": ["bin/copyparty-fuse.py", "bin/up2k.py"],