Mercurial > libervia-web
comparison src/server/server.py @ 813:6e27604ec95a
server: added --tls_private_key and --tls_chain options. --tls_certificate .pem file will be used for private_key if --tls_private_key is not specified.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 20 Dec 2015 20:01:42 +0100 |
parents | fd6965c16e7e |
children | cf1812a4445e |
comparison
equal
deleted
inserted
replaced
812:fd6965c16e7e | 813:6e27604ec95a |
---|---|
49 import libervia | 49 import libervia |
50 | 50 |
51 try: | 51 try: |
52 import OpenSSL | 52 import OpenSSL |
53 from twisted.internet import ssl | 53 from twisted.internet import ssl |
54 ssl_available = True | 54 except ImportError: |
55 except: | 55 ssl = None |
56 ssl_available = False | |
57 | 56 |
58 from libervia.server.constants import Const as C | 57 from libervia.server.constants import Const as C |
59 from libervia.server.blog import MicroBlog | 58 from libervia.server.blog import MicroBlog |
60 | 59 |
61 | 60 |
1341 else: | 1340 else: |
1342 self._startService() | 1341 self._startService() |
1343 | 1342 |
1344 self.initialised.addCallback(initOk) | 1343 self.initialised.addCallback(initOk) |
1345 | 1344 |
1345 ## TLS related methods ## | |
1346 | |
1347 def _TLSOptionsCheck(self): | |
1348 """Check options coherence if TLS is activated, and update missing values | |
1349 | |
1350 Must be called only if TLS is activated | |
1351 """ | |
1352 if not self.options['tls_certificate']: | |
1353 log.error(u"a TLS certificate is needed to activate HTTPS connection") | |
1354 self.quit(1) | |
1355 if not self.options['tls_private_key']: | |
1356 self.options['tls_private_key'] = self.options['tls_certificate'] | |
1357 | |
1358 | |
1359 if not self.options['tls_private_key']: | |
1360 self.options['tls_private_key'] = self.options['tls_certificate'] | |
1361 | |
1362 def _loadCertificates(self, f): | |
1363 """Read a .pem file with a list of certificates | |
1364 | |
1365 @param f (file): file obj (opened .pem file) | |
1366 @return (list[OpenSSL.crypto.X509]): list of certificates | |
1367 @raise OpenSSL.crypto.Error: error while parsing the file | |
1368 """ | |
1369 # XXX: didn't found any method to load a .pem file with several certificates | |
1370 # so the certificates split is done here | |
1371 certificates = [] | |
1372 buf = [] | |
1373 while True: | |
1374 line = f.readline() | |
1375 buf.append(line) | |
1376 if '-----END CERTIFICATE-----' in line: | |
1377 certificates.append(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, ''.join(buf))) | |
1378 buf=[] | |
1379 elif not line: | |
1380 log.debug(u"{} certificate(s) found".format(len(certificates))) | |
1381 return certificates | |
1382 | |
1383 def _loadPKey(self, f): | |
1384 """Read a private key from a .pem file | |
1385 | |
1386 @param f (file): file obj (opened .pem file) | |
1387 @return (list[OpenSSL.crypto.PKey]): private key object | |
1388 @raise OpenSSL.crypto.Error: error while parsing the file | |
1389 """ | |
1390 return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, f.read()) | |
1391 | |
1392 def _loadCertificate(self, f): | |
1393 """Read a public certificate from a .pem file | |
1394 | |
1395 @param f (file): file obj (opened .pem file) | |
1396 @return (list[OpenSSL.crypto.X509]): public certificate | |
1397 @raise OpenSSL.crypto.Error: error while parsing the file | |
1398 """ | |
1399 return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, f.read()) | |
1400 | |
1401 def _getTLSContextFactory(self): | |
1402 """Load TLS certificate and build the context factory needed for listenSSL""" | |
1403 if ssl is None: | |
1404 raise ImportError(u"Python module pyOpenSSL is not installed!") | |
1405 | |
1406 cert_options = {} | |
1407 | |
1408 for name, option, method in [('privateKey', 'tls_private_key', self._loadPKey), | |
1409 ('certificate', 'tls_certificate', self._loadCertificate), | |
1410 ('extraCertChain', 'tls_chain', self._loadCertificates)]: | |
1411 path = self.options[option] | |
1412 if not path: | |
1413 assert option=='tls_chain' | |
1414 continue | |
1415 log.debug(u"loading {option} from {path}".format(option=option, path=path)) | |
1416 try: | |
1417 with open(path) as f: | |
1418 cert_options[name] = method(f) | |
1419 except IOError as e: | |
1420 log.error(u"Error while reading file {path} for option {option}: {error}".format(path=path, option=option, error=e)) | |
1421 self.quit(2) | |
1422 except OpenSSL.crypto.Error: | |
1423 log.error(u"Error while parsing file {path} for option {option}, are you sure it is a valid .pem file?".format(path=path, option=option)) | |
1424 if option=='tls_private_key' and self.options['tls_certificate'] == path: | |
1425 log.error(u"You are using the same file for private key and public certificate, make sure that both a in {path} or use --tls_private_key option".format(path=path)) | |
1426 self.quit(2) | |
1427 | |
1428 return ssl.CertificateOptions(**cert_options) | |
1429 | |
1430 ## service management ## | |
1431 | |
1346 def _startService(self, dummy=None): | 1432 def _startService(self, dummy=None): |
1347 """Actually start the HTTP(S) server(s) after the profile for Libervia is connected. | 1433 """Actually start the HTTP(S) server(s) after the profile for Libervia is connected. |
1434 | |
1435 @raise ImportError: OpenSSL is not available | |
1348 @raise IOError: the certificate file doesn't exist | 1436 @raise IOError: the certificate file doesn't exist |
1349 @raise OpenSSL.crypto.Error: the certificate file is invalid | 1437 @raise OpenSSL.crypto.Error: the certificate file is invalid |
1350 """ | 1438 """ |
1351 if self.options['connection_type'] in ('https', 'both'): | 1439 if self.options['connection_type'] in ('https', 'both'): |
1352 if ssl is None: | 1440 self._TLSOptionsCheck() |
1353 raise ImportError(u"Python module pyOpenSSL is not installed!") | 1441 context_factory = self._getTLSContextFactory() |
1354 try: | 1442 reactor.listenSSL(self.options['port_https'], self.site, context_factory) |
1355 with open(os.path.expanduser(self.options['ssl_certificate'])) as keyAndCert: | |
1356 try: | |
1357 cert = ssl.PrivateCertificate.loadPEM(keyAndCert.read()) | |
1358 except OpenSSL.crypto.Error as e: | |
1359 log.error(_(u"The file '%s' must contain both private and public parts of the certificate") % self.options['ssl_certificate']) | |
1360 raise e | |
1361 except IOError as e: | |
1362 log.error(_(u"The file '%s' doesn't exist") % self.options['ssl_certificate']) | |
1363 raise e | |
1364 reactor.listenSSL(self.options['port_https'], self.site, cert.options()) | |
1365 if self.options['connection_type'] in ('http', 'both'): | 1443 if self.options['connection_type'] in ('http', 'both'): |
1366 if self.options['connection_type'] == 'both' and self.options['redirect_to_https']: | 1444 if self.options['connection_type'] == 'both' and self.options['redirect_to_https']: |
1367 reactor.listenTCP(self.options['port'], server.Site(RedirectToHTTPS(self.options['port'], self.options['port_https_ext']))) | 1445 reactor.listenTCP(self.options['port'], server.Site(RedirectToHTTPS(self.options['port'], self.options['port_https_ext']))) |
1368 else: | 1446 else: |
1369 reactor.listenTCP(self.options['port'], self.site) | 1447 reactor.listenTCP(self.options['port'], self.site) |