diff --git a/README.md b/README.md index 1f460b0a..cc41186e 100644 --- a/README.md +++ b/README.md @@ -1459,7 +1459,7 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical * **warning:** nginx-QUIC (HTTP/3) is still experimental and can make uploads much slower, so HTTP/1.1 is recommended for now * depending on server/client, HTTP/1.1 can also be 5x faster than HTTP/2 -for improved security (and a tiny performance boost) consider listening on a unix-socket with `-i /tmp/party.sock` instead of `-i 127.0.0.1` +for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it) example webserver configs: @@ -1900,7 +1900,7 @@ some notes on hardening * cors doesn't work right otherwise * if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml` * this returns html documents as plaintext, and also disables markdown rendering -* when running behind a reverse-proxy, listen on a unix-socket with `-i /tmp/party.sock` instead of `-i 127.0.0.1` for tighter access control (plus you get a tiny performance boost for free) +* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or `--help-bind` safety profiles: diff --git a/copyparty/__main__.py b/copyparty/__main__.py index a609fce1..1f845531 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -527,6 +527,41 @@ def showlic() -> None: def get_sects(): return [ + [ + "bind", + "configure listening", + dedent( + """ + \033[33m-i\033[0m takes a comma-separated list of interfaces to listen on; + IP-addresses and/or unix-sockets (Unix Domain Sockets) + + the default (\033[32m-i ::\033[0m) means all IPv4 and IPv6 addresses + + \033[32m-i 0.0.0.0\033[0m listens on all IPv4 NICs/subnets + \033[32m-i 127.0.0.1\033[0m listens on IPv4 localhost only + \033[32m-i 127.1\033[0m listens on IPv4 localhost only + \033[32m-i 127.1,192.168.123.1\033[0m = IPv4 localhost and 192.168.123.1 + + \033[33m-p\033[0m takes a comma-separated list of tcp ports to listen on; + the default is \033[32m-p 3923\033[0m but as root you can \033[32m-p 80,443,3923\033[0m + + when running behind a reverse-proxy, it's recommended to + use unix-sockets for improved performance and security; + + \033[32m-i unix:770:www:\033[33m/tmp/a.sock\033[0m listens on \033[33m/tmp/a.sock\033[0m with + permissions \033[33m0770\033[0m; only accessible to members of the \033[33mwww\033[0m + group. This is the best approach. Alternatively, + + \033[32m-i unix:777:\033[33m/tmp/a.sock\033[0m sets perms \033[33m0777\033[0m so anyone can + access it; bad unless it's inside a restricted folder + + \033[32m-i unix:\033[33m/tmp/a.sock\033[0m keeps umask-defined permissions + (usually \033[33m0600\033[0m) and the same user/group as copyparty + + \033[33m-p\033[0m (tcp ports) is ignored for unix sockets + """ + ), + ], [ "accounts", "accounts and volumes", @@ -969,8 +1004,8 @@ def add_upload(ap): def add_network(ap): ap2 = ap.add_argument_group('network options') - ap2.add_argument("-i", metavar="IP", type=u, default="::", help="ip to bind (comma-sep.) and/or [\033[32munix:/tmp/a.sock\033[0m], default: all IPv4 and IPv6") - ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range); ignored for unix-sockets") + ap2.add_argument("-i", metavar="IP", type=u, default="::", help="IPs and/or unix-sockets to listen on (see \033[33m--help-bind\033[0m). Default: all IPv4 and IPv6") + ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to listen on (comma/range); ignored for unix-sockets") ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 in mDNS replies, even if the NIC has routable IPs (breaks some mDNS clients)") ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m2\033[0m]=outermost-proxy, [\033[32m3\033[0m]=second-proxy, [\033[32m-1\033[0m]=closest-proxy") ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from") diff --git a/copyparty/httpsrv.py b/copyparty/httpsrv.py index 62bc2210..d4ecbd2d 100644 --- a/copyparty/httpsrv.py +++ b/copyparty/httpsrv.py @@ -365,7 +365,7 @@ class HttpSrv(object): cip = cip[7:] addr = (cip, saddr[1]) else: - addr = (ip, sck.fileno()) + addr = ("127.8.3.7", sck.fileno()) except (OSError, socket.error) as ex: if self.stopping: break diff --git a/copyparty/tcpsrv.py b/copyparty/tcpsrv.py index b18f4788..f4d97bed 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -226,10 +226,22 @@ class TcpSrv(object): self.log("tcpsrv", msg, c) def _listen(self, ip: str, port: int) -> None: + uds_perm = uds_gid = -1 if "unix:" in ip: tcp = False ipv = socket.AF_UNIX - ip = ip.split("unix:")[1] + uds = ip.split(":") + ip = uds[-1] + if len(uds) > 2: + uds_perm = int(uds[1], 8) + if len(uds) > 3: + try: + uds_gid = int(uds[2]) + except: + import grp + + uds_gid = grp.getgrnam(uds[2]).gr_gid + elif ":" in ip: tcp = True ipv = socket.AF_INET6 @@ -265,7 +277,13 @@ class TcpSrv(object): srv.bind(ip) else: tf = "%s.%d" % (ip, os.getpid()) + if os.path.exists(tf): + os.unlink(tf) srv.bind(tf) + if uds_gid != -1: + os.chown(tf, -1, uds_gid) + if uds_perm != -1: + os.chmod(tf, uds_perm) atomic_move(self.nlog, tf, ip, VF_CAREFUL) sport = srv.getsockname()[1] if tcp else port