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

Reply via email to