From 98f9fcc7208c85b15362779b75a7e6909016b6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o-Paul=20Bodin?= Date: Fri, 3 Oct 2025 17:16:47 +0200 Subject: [PATCH] Add mutual TLS authentication with ca support --- servefile/servefile.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/servefile/servefile.py b/servefile/servefile.py index 2de955b..609cc68 100755 --- a/servefile/servefile.py +++ b/servefile/servefile.py @@ -766,7 +766,7 @@ def handle_one_request(self, *args, **kwargs): class SecureThreadedHTTPServer(ThreadedHTTPServer): - def __init__(self, pubKey, privKey, server_address, RequestHandlerClass, bind_and_activate=True): + def __init__(self, pubKey, privKey, camtlsKey, server_address, RequestHandlerClass, bind_and_activate=True): ThreadedHTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate) # choose TLS1.2 or TLS1, if available @@ -788,6 +788,10 @@ def __init__(self, pubKey, privKey, server_address, RequestHandlerClass, bind_an ctx.use_certificate_file(pubKey) ctx.use_privatekey_file(privKey) + if camtlsKey: + ctx.load_verify_locations(camtlsKey) + ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, None) + self.bsocket = socket.socket(self.address_family, self.socket_type) self.socket = SSL.Connection(ctx, self.bsocket) @@ -838,6 +842,7 @@ def __init__(self, target, port=8080, serveMode=0, useSSL=False): self.dirCreated = False self.useSSL = useSSL self.cert = self.key = None + self.camtls = None self.auth = None self.maxUploadSize = 0 self.listenIPv4 = True @@ -892,10 +897,11 @@ def getIPs(self): return ips return None - def setSSLKeys(self, cert, key): + def setSSLKeys(self, cert, key, camtls): """ Set SSL cert/key. Can be either path to file or pyopenssl X509/PKey object. """ self.cert = cert self.key = key + self.camtls = camtls def setMaxUploadSize(self, limit): """ Set the maximum upload size in byte """ @@ -960,6 +966,9 @@ def _getCert(self): def _getKey(self): return self.key + def _getCAmTLS(self): + return self.camtls + def setAuth(self, user, password, realm=None): if not user or not password: raise ServeFileException("User and password both need to be at least one character.") @@ -981,7 +990,7 @@ def _createServer(self, handler, withv6=False): if not self._getKey(): self.genKeyPair() try: - server = SecureThreadedHTTPServer(self._getCert(), self._getKey(), + server = SecureThreadedHTTPServer(self._getCert(), self._getKey(), self._getCAmTLS(), (listenIp, self.port), handler, bind_and_activate=False) except SSL.Error as e: raise ServeFileException("SSL error: Could not read SSL public/private key " @@ -1167,6 +1176,8 @@ def main(): "will also be searched for a cert") parser.add_argument('--cert', type=str, help="Certfile to use for SSL") + parser.add_argument('--camtls', type=str, + help="Certfile for mutual tls") parser.add_argument('-a', '--auth', type=str, metavar='user:password', help="Set user and password for HTTP basic authentication") parser.add_argument('--realm', type=str, default=None, @@ -1216,7 +1227,11 @@ def main(): print("Error: Please specify a key along with your cert.") sys.exit(1) - if not args.ssl and (args.cert or args.key): + if args.camtls and (not args.cert or not args.key): + print("Error: You need to specify server cert with --cert and server cert key with --key.") + sys.exit(1) + + if not args.ssl and (args.cert or args.key or args.camtls): print("Error: You need to enable ssl with --ssl when specifying certs/keys.") sys.exit(1) @@ -1276,7 +1291,10 @@ def main(): server.setMaxUploadSize(maxUploadSize) if args.ssl and args.key: cert = args.cert or args.key - server.setSSLKeys(cert, args.key) + if args.camtls: + server.setSSLKeys(cert, args.key, args.camtls) + else: + server.setSSLKeys(cert, args.key, None) if args.auth: user, password = args.auth.split(":", 1) server.setAuth(user, password, args.realm)