mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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 Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
committed by
Christian Heimes
parent
e7e06f6d78
commit
7cbd9bd429
@@ -1307,6 +1307,7 @@ fi
|
||||
%{_libexecdir}/ipa/ipa-custodia
|
||||
%{_libexecdir}/ipa/ipa-custodia-check
|
||||
%{_libexecdir}/ipa/ipa-httpd-kdcproxy
|
||||
%{_libexecdir}/ipa/ipa-httpd-pwdreader
|
||||
%{_libexecdir}/ipa/ipa-pki-retrieve-key
|
||||
%{_libexecdir}/ipa/ipa-otpd
|
||||
%dir %{_libexecdir}/ipa/oddjob
|
||||
@@ -1458,6 +1459,8 @@ fi
|
||||
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysupgrade
|
||||
%attr(755,root,root) %dir %{_localstatedir}/lib/ipa/pki-ca
|
||||
%attr(755,root,root) %dir %{_localstatedir}/lib/ipa/certs
|
||||
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/private
|
||||
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/passwds
|
||||
%ghost %{_localstatedir}/lib/ipa/pki-ca/publish
|
||||
%ghost %{_localstatedir}/named/dyndb-ldap/ipa
|
||||
%dir %attr(0700,root,root) %{_sysconfdir}/ipa/custodia
|
||||
|
||||
@@ -25,11 +25,15 @@ install-exec-local:
|
||||
$(INSTALL) -d -m 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa/pki-ca
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa/certs
|
||||
$(INSTALL) -d -m 700 $(DESTDIR)$(localstatedir)/lib/ipa/private
|
||||
$(INSTALL) -d -m 700 $(DESTDIR)$(localstatedir)/lib/ipa/passwds
|
||||
|
||||
uninstall-local:
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/certs
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/private
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/passwds
|
||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa
|
||||
|
||||
EXTRA_DIST = README.schema
|
||||
|
||||
@@ -36,5 +36,6 @@ dist_app_SCRIPTS = \
|
||||
ipa-custodia \
|
||||
ipa-custodia-check \
|
||||
ipa-httpd-kdcproxy \
|
||||
ipa-httpd-pwdreader \
|
||||
ipa-pki-retrieve-key \
|
||||
$(NULL)
|
||||
|
||||
36
install/tools/ipa-httpd-pwdreader
Executable file
36
install/tools/ipa-httpd-pwdreader
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# This program is a handler written for Apache mod_ssl's SSLPassPhraseDialog.
|
||||
#
|
||||
# If you'd like to write your custom binary providing passwords to mod_ssl,
|
||||
# see the documentation of the aforementioned directive of the mod_ssl module.
|
||||
|
||||
USAGE="./ipa-pwdreader host:port RSA|DSA|ECC|number"
|
||||
ERR_UNKNOWN_KEY="\
|
||||
ERROR: You seem to be running a non-standard IPA installation.
|
||||
Please extend the /var/libexec/ipa/ipa-pwdreader script to cover your case."
|
||||
|
||||
if [ ! "$#" -eq 2 ]; then
|
||||
echo "Wrong number of arguments!" 1>&2
|
||||
echo "$USAGE" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
case "$1" in
|
||||
"${HOSTNAME}:443" )
|
||||
# Read IPA password
|
||||
# IPA expects the password filename format to be
|
||||
# <hostname>-<port>-<ecryption_algorithm>
|
||||
IPA_PASSWD_PATH="/var/lib/ipa/passwds/${1/:/-}-$2"
|
||||
cat $IPA_PASSWD_PATH
|
||||
;;
|
||||
# ================
|
||||
# Extend for more virtual hosts with
|
||||
# <vhostname>:<vhost_port> )
|
||||
# your_code
|
||||
# ;;
|
||||
# ================
|
||||
*)
|
||||
echo "$ERR_UNKNOWN_KEY" 1>&2
|
||||
exit 1
|
||||
esac
|
||||
@@ -319,3 +319,4 @@ USER_CACHE_PATH = (
|
||||
)
|
||||
|
||||
SOFTHSM_DNSSEC_TOKEN_LABEL = u'ipaDNSSEC'
|
||||
HTTPD_PASSWD_FILE_FMT = "{host}-443-RSA"
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ class BasePathNamespace(object):
|
||||
HTTPD_NSS_CONF = "/etc/httpd/conf.d/nss.conf"
|
||||
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_KEY_FILE = "/var/lib/ipa/private/httpd.key"
|
||||
IPA_PASSWD_DIR = "/var/lib/ipa/passwds"
|
||||
# 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-httpd-pwdreader"
|
||||
DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11"
|
||||
GETSEBOOL = "/usr/sbin/getsebool"
|
||||
GROUPADD = "/usr/sbin/groupadd"
|
||||
|
||||
@@ -43,7 +43,7 @@ from ipapython.dn import DN
|
||||
import ipapython.errors
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipalib import api, x509
|
||||
from ipalib.constants import IPAAPI_USER
|
||||
from ipalib.constants import IPAAPI_USER, HTTPD_PASSWD_FILE_FMT
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipaplatform.paths import paths
|
||||
@@ -306,6 +306,15 @@ class HTTPInstance(service.Service):
|
||||
certmonger.stop()
|
||||
|
||||
def __setup_ssl(self):
|
||||
key_passwd_file = os.path.join(
|
||||
paths.IPA_PASSWD_DIR,
|
||||
HTTPD_PASSWD_FILE_FMT.format(host=api.env.host)
|
||||
)
|
||||
with open(key_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)
|
||||
@@ -332,7 +341,8 @@ class HTTPInstance(service.Service):
|
||||
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,
|
||||
passwd=pkey_passwd
|
||||
)
|
||||
|
||||
if self.ca_is_configured:
|
||||
@@ -364,6 +374,7 @@ class HTTPInstance(service.Service):
|
||||
dns=[self.fqdn],
|
||||
post_command='restart_httpd',
|
||||
storage='FILE',
|
||||
passwd_fname=key_passwd_file
|
||||
)
|
||||
finally:
|
||||
if prev_helper is not None:
|
||||
@@ -377,7 +388,7 @@ class HTTPInstance(service.Service):
|
||||
|
||||
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()
|
||||
@@ -396,6 +407,11 @@ class HTTPInstance(service.Service):
|
||||
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)
|
||||
@@ -500,6 +516,8 @@ class HTTPInstance(service.Service):
|
||||
paths.HTTP_CCACHE,
|
||||
paths.HTTPD_CERT_FILE,
|
||||
paths.HTTPD_KEY_FILE,
|
||||
os.path.join(paths.IPA_PASSWD_DIR,
|
||||
HTTPD_PASSWD_FILE_FMT.format(host=api.env.host)),
|
||||
paths.HTTPD_IPA_REWRITE_CONF,
|
||||
paths.HTTPD_IPA_CONF,
|
||||
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
||||
|
||||
@@ -25,6 +25,7 @@ import tempfile
|
||||
import optparse # pylint: disable=deprecated-module
|
||||
|
||||
from ipalib import x509
|
||||
from ipalib.constants import HTTPD_PASSWD_FILE_FMT
|
||||
from ipalib.install import certmonger
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import admintool
|
||||
@@ -155,8 +156,18 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
ca_chain_fname=paths.IPA_CA_CRT,
|
||||
host_name=api.env.host
|
||||
)
|
||||
|
||||
key_passwd_path = os.path.join(
|
||||
paths.IPA_PASSWD_DIR,
|
||||
HTTPD_PASSWD_FILE_FMT.format(host=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=key_passwd_path,
|
||||
cmgr_post_command='restart_httpd')
|
||||
|
||||
if req_id is not None:
|
||||
@@ -206,7 +217,7 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
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 +225,13 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
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 +243,7 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user