From 8c7cdf8583a66f7f7d018a4c36004445787705e2 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 22 Apr 2026 20:32:18 +0000 Subject: [PATCH] add --certkey --- README.md | 10 ++++++++-- copyparty/__main__.py | 3 ++- copyparty/cert.py | 7 +++++-- copyparty/ftpd.py | 2 ++ copyparty/httpconn.py | 2 +- copyparty/svchub.py | 3 +++ 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c8f01048..3e459bf1 100644 --- a/README.md +++ b/README.md @@ -3127,9 +3127,10 @@ when generating hashes using `--ah-cli` for docker or systemd services, make sur ## https -both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default) +both HTTP and HTTPS are accepted by default, but please ignore copyparty's built-in https/tls support and instead use a [reverse proxy](#reverse-proxy) to handle https/tls/ssl -copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well -- but note that HTTP/1 is usually faster than both HTTP/2 and HTTP/3 +* reverseproxies do a better job following [best practices](https://cipherlist.eu/) meaning they are more secure, and probably also have higher performance +* also, copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well -- but note that HTTP/1 is usually faster than both HTTP/2 and HTTP/3 if [cfssl](https://github.com/cloudflare/cfssl/releases/latest) is installed, copyparty will automatically create a CA and server-cert on startup * the certs are written to `--crt-dir` for distribution, see `--help` for the other `--crt` options @@ -3141,6 +3142,11 @@ to install cfssl on windows: * rename them to `cfssl.exe`, `cfssljson.exe`, `cfssl-certinfo.exe` * put them in PATH, for example inside `c:\windows\system32` +if you really wanna give copyparty an existing TLS certificate then do one of the following: +* `--no-crt --cert server.pem` where `server.pem` is a concatenation of key + cert + chain (in that order), or... +* `--no-crt --cert server.crt --certkey server.key` where `server.key` is the key, and `server.crt` is a concatenation of cert + chain (in that order) +* file-extensions don't matter, but all files are expected to be [PEM-style](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/res/insecure.pem) + # recovering from crashes diff --git a/copyparty/__main__.py b/copyparty/__main__.py index f9b60ca4..4ed18eec 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1357,7 +1357,8 @@ def add_tls(ap, cert_path): ap2 = ap.add_argument_group("SSL/TLS options") ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext") ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls") - ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to file containing a concatenation of TLS key and certificate chain") + ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to file containing a concatenation of TLS key and certificate chain (if \033[33m--certkey\033[0m is not set), or just the certificate chain (if \033[33m--certkey\033[0m is set)") + ap2.add_argument("--certkey", metavar="PATH", type=u, default="", help="path to file containing just the certificate key; if this is set, then \033[33m--cert\033[0m should only contain the certificate chain") ap2.add_argument("--ssl-ver", metavar="LIST", type=u, default="", help="set allowed ssl/tls versions; [\033[32mhelp\033[0m] shows available versions; default is what your python version considers safe") ap2.add_argument("--ciphers", metavar="LIST", type=u, default="", help="set allowed ssl/tls ciphers; [\033[32mhelp\033[0m] shows available ciphers") ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info") diff --git a/copyparty/cert.py b/copyparty/cert.py index 18859536..54c523a9 100644 --- a/copyparty/cert.py +++ b/copyparty/cert.py @@ -51,9 +51,12 @@ def ensure_cert(log: "RootLogger", args) -> None: with open(args.cert, "wb") as f: f.write(cert_insec) + if args.certkey and not os.path.isfile(args.certkey): + raise Exception("certificate-key file does not exist: " + args.certkey) + with open(args.cert, "rb") as f: buf = f.read() - o1 = buf.find(b" PRIVATE KEY-") + o1 = buf.find(b" PRIVATE KEY-") if not args.certkey else 0 o2 = buf.find(b" CERTIFICATE-") m = "unsupported certificate format: " if o1 < 0: @@ -252,7 +255,7 @@ def gencert(log: "RootLogger", args, netdevs: dict[str, Netdev]): if args.http_only: return - if args.no_crt or not HAVE_CFSSL: + if args.no_crt or args.certkey or not HAVE_CFSSL: ensure_cert(log, args) return diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index 3912ade9..ccbd423c 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -616,6 +616,8 @@ class Ftpd(object): print(t.format(pybin)) sys.exit(1) + if self.args.certkey: + h1.keyfile = self.args.certkey h1.certfile = self.args.cert h1.tls_control_required = True h1.tls_data_required = True diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py index 30d9d87d..a463943f 100644 --- a/copyparty/httpconn.py +++ b/copyparty/httpconn.py @@ -157,7 +157,7 @@ class HttpConn(object): try: assert ssl # type: ignore # !rm ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ctx.load_cert_chain(self.args.cert) + ctx.load_cert_chain(self.args.cert, self.args.certkey) if self.args.ssl_ver: ctx.options &= ~self.args.ssl_flags_en ctx.options |= self.args.ssl_flags_de diff --git a/copyparty/svchub.py b/copyparty/svchub.py index c23faa33..cee19553 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -1232,6 +1232,9 @@ class SvcHub(object): for x in [x.split(" ") for x in al.sftp_key or []] } + if not al.certkey: + al.certkey = None + mte = ODict.fromkeys(DEF_MTE.split(","), True) al.mte = odfusion(mte, al.mte)