URL: https://github.com/freeipa/freeipa/pull/1635 Author: stlaz Title: #1635: Encrypt httpd key stored on disk Action: opened
PR body: """ This commit adds configuration for HTTPD to encrypt/decrypt its key which we currently store in clear on the disc. A password-reading script is added for mod_ssl. This script is extensible for the future use of directory server with the expectation that key encryption/decription will be handled similarly by its configuration. https://pagure.io/freeipa/issue/7421 """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/1635/head:pr1635 git checkout pr1635
From ff1e674278b55034801c6b41f84b7388d06258f4 Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka <slazn...@redhat.com> Date: Mon, 26 Feb 2018 10:15:05 +0100 Subject: [PATCH] Encrypt httpd key stored on disk This commit adds configuration for HTTPD to encrypt/decrypt its key which we currently store in clear on the disc. A password-reading script is added for mod_ssl. This script is extensible for the future use of directory server with the expectation that key encryption/decription will be handled similarly by its configuration. https://pagure.io/freeipa/issue/7421 --- freeipa.spec.in | 2 ++ install/tools/Makefile.am | 2 ++ install/tools/ipa-httppswd.sh | 1 + install/tools/ipa-pwdreader.sh | 7 +++++++ ipalib/x509.py | 10 ++++++++-- ipaplatform/base/paths.py | 2 ++ ipaserver/install/httpinstance.py | 16 ++++++++++++++-- ipaserver/install/ipa_server_certinstall.py | 17 ++++++++++++++--- 8 files changed, 50 insertions(+), 7 deletions(-) create mode 120000 install/tools/ipa-httppswd.sh create mode 100644 install/tools/ipa-pwdreader.sh diff --git a/freeipa.spec.in b/freeipa.spec.in index cf35e67c81..a913c39954 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -1305,6 +1305,8 @@ fi %{_libexecdir}/ipa/ipa-dnskeysync-replica %{_libexecdir}/ipa/ipa-ods-exporter %{_libexecdir}/ipa/ipa-httpd-kdcproxy +%{_libexecdir}/ipa/ipa-pwdreader.sh +%{_libexecdir}/ipa/ipa-httppswd.sh %{_libexecdir}/ipa/ipa-pki-retrieve-key %{_libexecdir}/ipa/ipa-otpd %dir %{_libexecdir}/ipa/oddjob diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am index 6b9a64a3d2..1e11a144de 100644 --- a/install/tools/Makefile.am +++ b/install/tools/Makefile.am @@ -37,4 +37,6 @@ dist_app_SCRIPTS = \ ipa-custodia-check \ ipa-httpd-kdcproxy \ ipa-pki-retrieve-key \ + ipa-httppswd.sh \ + ipa-pwdreader.sh \ $(NULL) diff --git a/install/tools/ipa-httppswd.sh b/install/tools/ipa-httppswd.sh new file mode 120000 index 0000000000..297e031c1e --- /dev/null +++ b/install/tools/ipa-httppswd.sh @@ -0,0 +1 @@ +ipa-pwdreader.sh \ No newline at end of file diff --git a/install/tools/ipa-pwdreader.sh b/install/tools/ipa-pwdreader.sh new file mode 100644 index 0000000000..e5ec8ec04d --- /dev/null +++ b/install/tools/ipa-pwdreader.sh @@ -0,0 +1,7 @@ +#!/bin/bash +HTTP_PASSWD_LOC="/var/lib/ipa/certs/httpd_passwd.txt" + +if [ "$(basename $0)" == "ipa-httppswd.sh" ] && \ + [ -f "$HTTP_PASSWD_LOC" ]; then + cat "$HTTP_PASSWD_LOC" +fi diff --git a/ipalib/x509.py b/ipalib/x509.py index b49bc96622..7986ddbf5f 100644 --- a/ipalib/x509.py +++ b/ipalib/x509.py @@ -569,20 +569,26 @@ def write_certificate_list(certs, filename): raise errors.FileError(reason=str(e)) -def write_pem_private_key(priv_key, filename): +def write_pem_private_key(priv_key, filename, passwd=None): """ Write a private key to a file in PEM format. Will force 0x600 permissions on file. :param priv_key: cryptography ``PrivateKey`` object + :param passwd: ``bytes`` representing the password to store the + private key with """ + if passwd is not None: + enc_alg = serialization.BestAvailableEncryption(passwd) + else: + enc_alg = serialization.NoEncryption() try: with open(filename, 'wb') as fp: os.fchmod(fp.fileno(), 0o600) fp.write(priv_key.private_bytes( Encoding.PEM, PrivateFormat.TraditionalOpenSSL, - serialization.NoEncryption())) + encryption_algorithm=enc_alg)) except (IOError, OSError) as e: raise errors.FileError(reason=str(e)) diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index 69bf9a2f31..9d25739411 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -53,6 +53,7 @@ class BasePathNamespace(object): HTTPD_SSL_CONF = "/etc/httpd/conf.d/ssl.conf" HTTPD_CERT_FILE = "/var/lib/ipa/certs/httpd.crt" HTTPD_KEY_FILE = "/var/lib/ipa/certs/httpd.key" + HTTPD_PASSWD_FILE = "/var/lib/ipa/certs/httpd_passwd.txt" # only used on Fedora HTTPD_IPA_WSGI_MODULES_CONF = None OLD_IPA_KEYTAB = "/etc/httpd/conf/ipa.keytab" @@ -211,6 +212,7 @@ class BasePathNamespace(object): IPA_DNSKEYSYNCD = "/usr/libexec/ipa/ipa-dnskeysyncd" IPA_HTTPD_KDCPROXY = "/usr/libexec/ipa/ipa-httpd-kdcproxy" IPA_ODS_EXPORTER = "/usr/libexec/ipa/ipa-ods-exporter" + IPA_HTTPD_PASSWD_READER = "/usr/libexec/ipa/ipa-httppswd.sh" DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11" GETSEBOOL = "/usr/sbin/getsebool" GROUPADD = "/usr/sbin/groupadd" diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 6acb9126b1..cc534ae2a7 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -305,6 +305,11 @@ def configure_certmonger_renewal_guard(self): certmonger.stop() def __setup_ssl(self): + with open(paths.HTTPD_PASSWD_FILE, 'wb') as f: + os.fchmod(f.fileno(), 0o600) + pkey_passwd = ipautil.ipa_generate_password().encode('utf-8') + f.write(pkey_passwd) + if self.pkcs12_info: p12_certs, p12_priv_keys = certs.pkcs12_to_certkeys( *self.pkcs12_info) @@ -331,7 +336,8 @@ def __setup_ssl(self): x509.write_certificate(self.cert, paths.HTTPD_CERT_FILE) x509.write_pem_private_key( server_certs_keys[0][1], - paths.HTTPD_KEY_FILE + paths.HTTPD_KEY_FILE, + password=pkey_passwd ) if self.ca_is_configured: @@ -363,6 +369,7 @@ def __setup_ssl(self): dns=[self.fqdn], post_command='restart_httpd', storage='FILE', + passwd_fname=paths.HTTPD_PASSWD_FILE ) finally: if prev_helper is not None: @@ -376,7 +383,7 @@ def __setup_ssl(self): with open(paths.HTTPD_KEY_FILE, 'rb') as f: priv_key = x509.load_pem_private_key( - f.read(), None, backend=x509.default_backend()) + f.read(), pkey_passwd, backend=x509.default_backend()) # Verify we have a valid server cert if (priv_key.public_key().public_numbers() @@ -395,6 +402,11 @@ def configure_mod_ssl_certs(self): installutils.set_directive(paths.HTTPD_SSL_CONF, 'SSLCertificateKeyFile', paths.HTTPD_KEY_FILE, False) + installutils.set_directive( + paths.HTTPD_SSL_CONF, + 'SSLPassPhraseDialog', + 'exec:{passread}'.format(passread=paths.IPA_HTTPD_PASSWD_READER), + False) installutils.set_directive(paths.HTTPD_SSL_CONF, 'SSLCACertificateFile', paths.IPA_CA_CRT, False) diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py index 669d800812..fe3286887e 100644 --- a/ipaserver/install/ipa_server_certinstall.py +++ b/ipaserver/install/ipa_server_certinstall.py @@ -29,6 +29,7 @@ from ipaplatform.paths import paths from ipapython import admintool from ipapython.certdb import NSSDatabase, get_ca_nickname +from ipapython.ipautil import ipa_generate_password from ipapython.dn import DN from ipalib import api, errors from ipaserver.install import certs, dsinstance, installutils, krbinstance @@ -156,7 +157,11 @@ def replace_http_cert(self): host_name=api.env.host ) req_id = self.replace_key_cert_files( - cert, key, paths.HTTPD_CERT_FILE, paths.HTTPD_KEY_FILE, ca_cert, + cert, key, + cert_fname=paths.HTTPD_CERT_FILE, + key_fname=paths.HTTPD_KEY_FILE, + ca_cert=ca_cert, + passwd_fname=paths.HTTPD_PASSWD_FILE, cmgr_post_command='restart_httpd') if req_id is not None: @@ -206,7 +211,7 @@ def load_pkcs12(self, ca_chain_fname=paths.IPA_CA_CRT, **kwargs): return cert, key, ca_cert def replace_key_cert_files( - self, cert, key, cert_fname, key_fname, ca_cert, + self, cert, key, cert_fname, key_fname, ca_cert, passwd_fname=None, profile=None, cmgr_post_command=None ): try: @@ -214,8 +219,13 @@ def replace_key_cert_files( if ca_enabled: certmonger.stop_tracking(certfile=cert_fname) + pkey_passwd = None + if passwd_fname is not None: + with open(passwd_fname, 'rb') as f: + pkey_passwd = f.read() + x509.write_certificate(cert, cert_fname) - x509.write_pem_private_key(key, key_fname) + x509.write_pem_private_key(key, key_fname, pkey_passwd) if ca_enabled: # Start tracking only if the cert was issued by IPA CA @@ -227,6 +237,7 @@ def replace_key_cert_files( if ca_cert == ipa_ca_cert: req_id = certmonger.start_tracking( (cert_fname, key_fname), + pinfile=passwd_fname, storage='FILE', post_command=cmgr_post_command )
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org