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
|
||||||
%{_libexecdir}/ipa/ipa-custodia-check
|
%{_libexecdir}/ipa/ipa-custodia-check
|
||||||
%{_libexecdir}/ipa/ipa-httpd-kdcproxy
|
%{_libexecdir}/ipa/ipa-httpd-kdcproxy
|
||||||
|
%{_libexecdir}/ipa/ipa-httpd-pwdreader
|
||||||
%{_libexecdir}/ipa/ipa-pki-retrieve-key
|
%{_libexecdir}/ipa/ipa-pki-retrieve-key
|
||||||
%{_libexecdir}/ipa/ipa-otpd
|
%{_libexecdir}/ipa/ipa-otpd
|
||||||
%dir %{_libexecdir}/ipa/oddjob
|
%dir %{_libexecdir}/ipa/oddjob
|
||||||
@@ -1458,6 +1459,8 @@ fi
|
|||||||
%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysupgrade
|
%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/pki-ca
|
||||||
%attr(755,root,root) %dir %{_localstatedir}/lib/ipa/certs
|
%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}/lib/ipa/pki-ca/publish
|
||||||
%ghost %{_localstatedir}/named/dyndb-ldap/ipa
|
%ghost %{_localstatedir}/named/dyndb-ldap/ipa
|
||||||
%dir %attr(0700,root,root) %{_sysconfdir}/ipa/custodia
|
%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 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa/pki-ca
|
$(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa/pki-ca
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(localstatedir)/lib/ipa/certs
|
$(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:
|
uninstall-local:
|
||||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
|
||||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
|
||||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/certs
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/certs
|
||||||
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/private
|
||||||
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/passwds
|
||||||
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa
|
-rmdir $(DESTDIR)$(localstatedir)/lib/ipa
|
||||||
|
|
||||||
EXTRA_DIST = README.schema
|
EXTRA_DIST = README.schema
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ dist_app_SCRIPTS = \
|
|||||||
ipa-custodia \
|
ipa-custodia \
|
||||||
ipa-custodia-check \
|
ipa-custodia-check \
|
||||||
ipa-httpd-kdcproxy \
|
ipa-httpd-kdcproxy \
|
||||||
|
ipa-httpd-pwdreader \
|
||||||
ipa-pki-retrieve-key \
|
ipa-pki-retrieve-key \
|
||||||
$(NULL)
|
$(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'
|
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))
|
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
|
Write a private key to a file in PEM format. Will force 0x600 permissions
|
||||||
on file.
|
on file.
|
||||||
|
|
||||||
:param priv_key: cryptography ``PrivateKey`` object
|
: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:
|
try:
|
||||||
with open(filename, 'wb') as fp:
|
with open(filename, 'wb') as fp:
|
||||||
os.fchmod(fp.fileno(), 0o600)
|
os.fchmod(fp.fileno(), 0o600)
|
||||||
fp.write(priv_key.private_bytes(
|
fp.write(priv_key.private_bytes(
|
||||||
Encoding.PEM,
|
Encoding.PEM,
|
||||||
PrivateFormat.TraditionalOpenSSL,
|
PrivateFormat.TraditionalOpenSSL,
|
||||||
serialization.NoEncryption()))
|
encryption_algorithm=enc_alg))
|
||||||
except (IOError, OSError) as e:
|
except (IOError, OSError) as e:
|
||||||
raise errors.FileError(reason=str(e))
|
raise errors.FileError(reason=str(e))
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class BasePathNamespace(object):
|
|||||||
HTTPD_NSS_CONF = "/etc/httpd/conf.d/nss.conf"
|
HTTPD_NSS_CONF = "/etc/httpd/conf.d/nss.conf"
|
||||||
HTTPD_SSL_CONF = "/etc/httpd/conf.d/ssl.conf"
|
HTTPD_SSL_CONF = "/etc/httpd/conf.d/ssl.conf"
|
||||||
HTTPD_CERT_FILE = "/var/lib/ipa/certs/httpd.crt"
|
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
|
# only used on Fedora
|
||||||
HTTPD_IPA_WSGI_MODULES_CONF = None
|
HTTPD_IPA_WSGI_MODULES_CONF = None
|
||||||
OLD_IPA_KEYTAB = "/etc/httpd/conf/ipa.keytab"
|
OLD_IPA_KEYTAB = "/etc/httpd/conf/ipa.keytab"
|
||||||
@@ -211,6 +212,7 @@ class BasePathNamespace(object):
|
|||||||
IPA_DNSKEYSYNCD = "/usr/libexec/ipa/ipa-dnskeysyncd"
|
IPA_DNSKEYSYNCD = "/usr/libexec/ipa/ipa-dnskeysyncd"
|
||||||
IPA_HTTPD_KDCPROXY = "/usr/libexec/ipa/ipa-httpd-kdcproxy"
|
IPA_HTTPD_KDCPROXY = "/usr/libexec/ipa/ipa-httpd-kdcproxy"
|
||||||
IPA_ODS_EXPORTER = "/usr/libexec/ipa/ipa-ods-exporter"
|
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"
|
DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11"
|
||||||
GETSEBOOL = "/usr/sbin/getsebool"
|
GETSEBOOL = "/usr/sbin/getsebool"
|
||||||
GROUPADD = "/usr/sbin/groupadd"
|
GROUPADD = "/usr/sbin/groupadd"
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from ipapython.dn import DN
|
|||||||
import ipapython.errors
|
import ipapython.errors
|
||||||
from ipaserver.install import sysupgrade
|
from ipaserver.install import sysupgrade
|
||||||
from ipalib import api, x509
|
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.constants import constants
|
||||||
from ipaplatform.tasks import tasks
|
from ipaplatform.tasks import tasks
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
@@ -306,6 +306,15 @@ class HTTPInstance(service.Service):
|
|||||||
certmonger.stop()
|
certmonger.stop()
|
||||||
|
|
||||||
def __setup_ssl(self):
|
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:
|
if self.pkcs12_info:
|
||||||
p12_certs, p12_priv_keys = certs.pkcs12_to_certkeys(
|
p12_certs, p12_priv_keys = certs.pkcs12_to_certkeys(
|
||||||
*self.pkcs12_info)
|
*self.pkcs12_info)
|
||||||
@@ -332,7 +341,8 @@ class HTTPInstance(service.Service):
|
|||||||
x509.write_certificate(self.cert, paths.HTTPD_CERT_FILE)
|
x509.write_certificate(self.cert, paths.HTTPD_CERT_FILE)
|
||||||
x509.write_pem_private_key(
|
x509.write_pem_private_key(
|
||||||
server_certs_keys[0][1],
|
server_certs_keys[0][1],
|
||||||
paths.HTTPD_KEY_FILE
|
paths.HTTPD_KEY_FILE,
|
||||||
|
passwd=pkey_passwd
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.ca_is_configured:
|
if self.ca_is_configured:
|
||||||
@@ -364,6 +374,7 @@ class HTTPInstance(service.Service):
|
|||||||
dns=[self.fqdn],
|
dns=[self.fqdn],
|
||||||
post_command='restart_httpd',
|
post_command='restart_httpd',
|
||||||
storage='FILE',
|
storage='FILE',
|
||||||
|
passwd_fname=key_passwd_file
|
||||||
)
|
)
|
||||||
finally:
|
finally:
|
||||||
if prev_helper is not None:
|
if prev_helper is not None:
|
||||||
@@ -377,7 +388,7 @@ class HTTPInstance(service.Service):
|
|||||||
|
|
||||||
with open(paths.HTTPD_KEY_FILE, 'rb') as f:
|
with open(paths.HTTPD_KEY_FILE, 'rb') as f:
|
||||||
priv_key = x509.load_pem_private_key(
|
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
|
# Verify we have a valid server cert
|
||||||
if (priv_key.public_key().public_numbers()
|
if (priv_key.public_key().public_numbers()
|
||||||
@@ -396,6 +407,11 @@ class HTTPInstance(service.Service):
|
|||||||
installutils.set_directive(paths.HTTPD_SSL_CONF,
|
installutils.set_directive(paths.HTTPD_SSL_CONF,
|
||||||
'SSLCertificateKeyFile',
|
'SSLCertificateKeyFile',
|
||||||
paths.HTTPD_KEY_FILE, False)
|
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,
|
installutils.set_directive(paths.HTTPD_SSL_CONF,
|
||||||
'SSLCACertificateFile',
|
'SSLCACertificateFile',
|
||||||
paths.IPA_CA_CRT, False)
|
paths.IPA_CA_CRT, False)
|
||||||
@@ -500,6 +516,8 @@ class HTTPInstance(service.Service):
|
|||||||
paths.HTTP_CCACHE,
|
paths.HTTP_CCACHE,
|
||||||
paths.HTTPD_CERT_FILE,
|
paths.HTTPD_CERT_FILE,
|
||||||
paths.HTTPD_KEY_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_REWRITE_CONF,
|
||||||
paths.HTTPD_IPA_CONF,
|
paths.HTTPD_IPA_CONF,
|
||||||
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import tempfile
|
|||||||
import optparse # pylint: disable=deprecated-module
|
import optparse # pylint: disable=deprecated-module
|
||||||
|
|
||||||
from ipalib import x509
|
from ipalib import x509
|
||||||
|
from ipalib.constants import HTTPD_PASSWD_FILE_FMT
|
||||||
from ipalib.install import certmonger
|
from ipalib.install import certmonger
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipapython import admintool
|
from ipapython import admintool
|
||||||
@@ -155,8 +156,18 @@ class ServerCertInstall(admintool.AdminTool):
|
|||||||
ca_chain_fname=paths.IPA_CA_CRT,
|
ca_chain_fname=paths.IPA_CA_CRT,
|
||||||
host_name=api.env.host
|
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(
|
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')
|
cmgr_post_command='restart_httpd')
|
||||||
|
|
||||||
if req_id is not None:
|
if req_id is not None:
|
||||||
@@ -206,7 +217,7 @@ class ServerCertInstall(admintool.AdminTool):
|
|||||||
return cert, key, ca_cert
|
return cert, key, ca_cert
|
||||||
|
|
||||||
def replace_key_cert_files(
|
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
|
profile=None, cmgr_post_command=None
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
@@ -214,8 +225,13 @@ class ServerCertInstall(admintool.AdminTool):
|
|||||||
if ca_enabled:
|
if ca_enabled:
|
||||||
certmonger.stop_tracking(certfile=cert_fname)
|
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_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:
|
if ca_enabled:
|
||||||
# Start tracking only if the cert was issued by IPA CA
|
# 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:
|
if ca_cert == ipa_ca_cert:
|
||||||
req_id = certmonger.start_tracking(
|
req_id = certmonger.start_tracking(
|
||||||
(cert_fname, key_fname),
|
(cert_fname, key_fname),
|
||||||
|
pinfile=passwd_fname,
|
||||||
storage='FILE',
|
storage='FILE',
|
||||||
post_command=cmgr_post_command
|
post_command=cmgr_post_command
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user