mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Centralize enable/disable of the ACME service
The initial implementation of ACME in dogtag and IPA required that ACME be manually enabled on each CA. dogtag added a REST API that can be access directly or through the `pki acme` CLI tool to enable or disable the service. It also abstracted the database connection and introduced the concept of a realm which defines the DIT for ACME users and groups, the URL and the identity. This is configured in realm.conf. A new group was created, Enterprise ACME Administrators, that controls the users allowed to modify ACME configuration. The IPA RA is added to this group for the ipa-acme-manage tool to authenticate to the API to enable/disable ACME. Related dogtag installation documentation: https://github.com/dogtagpki/pki/blob/master/docs/installation/acme/Configuring_ACME_Database.md https://github.com/dogtagpki/pki/blob/master/docs/installation/acme/Configuring_ACME_Realm.md https://github.com/dogtagpki/pki/blob/master/docs/installation/acme/Installing_PKI_ACME_Responder.md ACME REST API: https://github.com/dogtagpki/pki/wiki/PKI-ACME-Enable-REST-API https://pagure.io/freeipa/issue/8524 Signed-off-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Fraser Tweedale <ftweedal@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Mohammad Rizwan <myusuf@redhat.com>
This commit is contained in:
parent
e13d058a06
commit
c0d55ce6de
@ -106,6 +106,7 @@ dist_app_DATA = \
|
||||
pki-acme-database.conf.template \
|
||||
pki-acme-engine.conf.template \
|
||||
pki-acme-issuer.conf.template \
|
||||
pki-acme-realm.conf.template \
|
||||
ldbm-tuning.ldif \
|
||||
$(NULL)
|
||||
|
||||
|
@ -1,2 +1,11 @@
|
||||
# Parameters read by ACMEEngineConfigFileSource, i.e. these are
|
||||
# expected to be in the file pointed to by the 'filename' directive
|
||||
# above.
|
||||
#
|
||||
# IPA only sets the values it uses.
|
||||
#
|
||||
# Whether to enable the ACME service:
|
||||
enabled=false
|
||||
wildcard=false
|
||||
|
||||
# Whether to accept wildcard DNS identifiers:
|
||||
policy.wildcard=false
|
||||
|
@ -1,5 +1,5 @@
|
||||
class=org.dogtagpki.acme.issuer.PKIIssuer
|
||||
url=https://$FQDN:8443
|
||||
profile=acmeServerCert
|
||||
profile=acmeIPAServerCert
|
||||
username=$USER
|
||||
password=$PASSWORD
|
||||
|
8
install/share/pki-acme-realm.conf.template
Normal file
8
install/share/pki-acme-realm.conf.template
Normal file
@ -0,0 +1,8 @@
|
||||
authType=BasicAuth
|
||||
class=org.dogtagpki.acme.realm.DSRealm
|
||||
groupsDN=ou=groups,o=ipaca
|
||||
usersDN=ou=people,o=ipaca
|
||||
url=ldaps://$FQDN:636
|
||||
configFile=/etc/pki/pki-tomcat/ca/CS.cfg
|
||||
username=$USER
|
||||
password=$PASSWORD
|
@ -7,7 +7,7 @@ app_DATA = \
|
||||
caIPAserviceCert.UPGRADE.cfg \
|
||||
IECUserRoles.cfg \
|
||||
KDCs_PKINIT_Certs.cfg \
|
||||
acmeServerCert.cfg \
|
||||
acmeIPAServerCert.cfg \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -1,4 +1,4 @@
|
||||
profileId=acmeServerCert
|
||||
profileId=acmeIPAServerCert
|
||||
classId=caEnrollImpl
|
||||
desc=ACME profile for use in IPA deployments
|
||||
visible=true
|
||||
|
@ -127,6 +127,7 @@ class BasePathNamespace:
|
||||
PKI_ACME_DATABASE_CONF = "/etc/pki/pki-tomcat/acme/database.conf"
|
||||
PKI_ACME_ENGINE_CONF = "/etc/pki/pki-tomcat/acme/engine.conf"
|
||||
PKI_ACME_ISSUER_CONF = "/etc/pki/pki-tomcat/acme/issuer.conf"
|
||||
PKI_ACME_REALM_CONF = "/etc/pki/pki-tomcat/acme/realm.conf"
|
||||
ETC_REDHAT_RELEASE = "/etc/redhat-release"
|
||||
RESOLV_CONF = "/etc/resolv.conf"
|
||||
SAMBA_KEYTAB = "/etc/samba/samba.keytab"
|
||||
|
@ -56,8 +56,8 @@ INCLUDED_PROFILES = {
|
||||
Profile(u'KDCs_PKINIT_Certs',
|
||||
u'Profile for PKINIT support by KDCs',
|
||||
False),
|
||||
Profile(u'acmeServerCert',
|
||||
u'ACME service certificate profile',
|
||||
Profile(u'acmeIPAServerCert',
|
||||
u'ACME IPA service certificate profile',
|
||||
False),
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ ADMIN_GROUPS = [
|
||||
'Security Domain Administrators'
|
||||
]
|
||||
|
||||
ACME_AGENT_GROUP = 'ACME Agents'
|
||||
ACME_AGENT_GROUP = 'Enterprise ACME Administrators'
|
||||
|
||||
PROFILES_DN = DN(('ou', 'certificateProfiles'), ('ou', 'ca'), ('o', 'ipaca'))
|
||||
|
||||
@ -768,6 +768,12 @@ class CAInstance(DogtagInstance):
|
||||
self.basedn)
|
||||
conn.add_entry_to_group(user_dn, group_dn, 'uniqueMember')
|
||||
|
||||
group_dn = DN(('cn', ACME_AGENT_GROUP), ('ou', 'groups'),
|
||||
self.basedn)
|
||||
conn.add_entry_to_group(user_dn, group_dn, 'uniqueMember')
|
||||
|
||||
conn.disconnect()
|
||||
|
||||
def __get_ca_chain(self):
|
||||
try:
|
||||
return dogtag.get_ca_certchain(ca_host=self.fqdn)
|
||||
@ -1479,6 +1485,8 @@ class CAInstance(DogtagInstance):
|
||||
logger.debug('ACME service is already deployed')
|
||||
return False
|
||||
|
||||
self._ldap_mod('/usr/share/pki/acme/database/ds/schema.ldif')
|
||||
|
||||
configure_acme_acls()
|
||||
|
||||
# create ACME agent group (if not exist already) and user
|
||||
@ -1510,6 +1518,7 @@ class CAInstance(DogtagInstance):
|
||||
('pki-acme-database.conf.template', paths.PKI_ACME_DATABASE_CONF),
|
||||
('pki-acme-engine.conf.template', paths.PKI_ACME_ENGINE_CONF),
|
||||
('pki-acme-issuer.conf.template', paths.PKI_ACME_ISSUER_CONF),
|
||||
('pki-acme-realm.conf.template', paths.PKI_ACME_REALM_CONF),
|
||||
]
|
||||
sub_dict = dict(
|
||||
FQDN=self.fqdn,
|
||||
@ -1732,6 +1741,11 @@ def ensure_acme_containers():
|
||||
DN(('ou', 'orders'), ou_acme),
|
||||
DN(('ou', 'authorizations'), ou_acme),
|
||||
DN(('ou', 'challenges'), ou_acme),
|
||||
DN(('ou', 'certificates'), ou_acme),
|
||||
]
|
||||
|
||||
extensible_rdns = [
|
||||
DN(('ou', 'config'), ou_acme),
|
||||
]
|
||||
|
||||
for rdn in rdns:
|
||||
@ -1741,6 +1755,13 @@ def ensure_acme_containers():
|
||||
ou=[rdn[0][0].value],
|
||||
)
|
||||
|
||||
for rdn in extensible_rdns:
|
||||
ensure_entry(
|
||||
DN(rdn, ('o', 'ipaca')),
|
||||
objectclass=['top', 'organizationalUnit', 'extensibleObject'],
|
||||
ou=[rdn[0][0].value],
|
||||
)
|
||||
|
||||
|
||||
def ensure_entry(dn, **attrs):
|
||||
"""Ensure an entry exists.
|
||||
|
@ -3,13 +3,16 @@
|
||||
#
|
||||
|
||||
import enum
|
||||
import pathlib
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipalib import _
|
||||
from ipalib.facts import is_ipa_configured
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.admintool import AdminTool
|
||||
from ipapython.directivesetter import DirectiveSetter
|
||||
from ipapython import cookie, dogtag
|
||||
from ipaserver.install import cainstance
|
||||
from ipalib.facts import is_ipa_configured
|
||||
|
||||
from ipaserver.plugins.dogtag import RestClient
|
||||
|
||||
# Manages the FreeIPA ACME service on a per-server basis.
|
||||
#
|
||||
@ -20,6 +23,49 @@ from ipalib.facts import is_ipa_configured
|
||||
# remove this program, or make it a wrapper for the API commands.
|
||||
|
||||
|
||||
class acme_state(RestClient):
|
||||
|
||||
def _request(self, url):
|
||||
return dogtag.https_request(
|
||||
self.ca_host, 8443,
|
||||
url=url,
|
||||
cafile=self.ca_cert,
|
||||
client_certfile=paths.RA_AGENT_PEM,
|
||||
client_keyfile=paths.RA_AGENT_KEY,
|
||||
method='POST'
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
status, resp_headers, _unused = self._request('/acme/login')
|
||||
cookies = cookie.Cookie.parse(resp_headers.get('set-cookie', ''))
|
||||
if status != 200 or len(cookies) == 0:
|
||||
raise errors.RemoteRetrieveError(
|
||||
reason=_('Failed to authenticate to CA REST API')
|
||||
)
|
||||
object.__setattr__(self, 'cookie', str(cookies[0]))
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
"""Log out of the REST API"""
|
||||
headers = dict(Cookie=self.cookie)
|
||||
status, unused, _unused = self._request('/acme/logout')
|
||||
object.__setattr__(self, 'cookie', None)
|
||||
if status != 204:
|
||||
raise RuntimeError('Failed to logout')
|
||||
|
||||
def enable(self):
|
||||
headers = dict(Cookie=self.cookie)
|
||||
status, unused, _unused = self._request('/acme/enable')
|
||||
if status != 200:
|
||||
raise RuntimeError('Failed to enable ACME')
|
||||
|
||||
def disable(self):
|
||||
headers = dict(Cookie=self.cookie)
|
||||
status, unused, _unused = self._request('/acme/disable')
|
||||
if status != 200:
|
||||
raise RuntimeError('Failed to disble ACME')
|
||||
|
||||
|
||||
class Command(enum.Enum):
|
||||
ENABLE = 'enable'
|
||||
DISABLE = 'disable'
|
||||
@ -52,28 +98,17 @@ class IPAACMEManage(AdminTool):
|
||||
print("CA is not installed on this server.")
|
||||
return 1
|
||||
|
||||
if self.command == Command.ENABLE:
|
||||
directive = 'enabled'
|
||||
value = 'true'
|
||||
elif self.command == Command.DISABLE:
|
||||
directive = 'enabled'
|
||||
value = 'false'
|
||||
else:
|
||||
raise RuntimeError('programmer error: unhandled enum case')
|
||||
api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
|
||||
api.finalize()
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
with DirectiveSetter(
|
||||
paths.PKI_ACME_ENGINE_CONF,
|
||||
separator='=',
|
||||
quotes=False,
|
||||
) as ds:
|
||||
ds.set(directive, value)
|
||||
|
||||
# Work around a limitation in PKI ACME service file watching
|
||||
# where renames (what DirectiveSetter does) are not detected.
|
||||
# It will be fixed, but keeping the workaround will do no harm.
|
||||
pathlib.Path(paths.PKI_ACME_ENGINE_CONF).touch()
|
||||
|
||||
# Nothing else to do; the Dogtag ACME service monitors engine.conf
|
||||
# for updates and reconfigures itself as required.
|
||||
state = acme_state(api)
|
||||
with state as ca_api:
|
||||
if self.command == Command.ENABLE:
|
||||
ca_api.enable()
|
||||
elif self.command == Command.DISABLE:
|
||||
ca_api.disable()
|
||||
else:
|
||||
raise RuntimeError('programmer error: unhandled enum case')
|
||||
|
||||
return 0
|
||||
|
@ -1112,7 +1112,9 @@ def ca_upgrade_schema(ca):
|
||||
return False
|
||||
|
||||
# ACME schema file moved in pki-server-10.9.0-0.3
|
||||
# ACME database connections were abstrated in pki-acme-10.10.0
|
||||
for path in [
|
||||
'/usr/share/pki/acme/conf/database/ds/schema.ldif',
|
||||
'/usr/share/pki/acme/conf/database/ldap/schema.ldif',
|
||||
'/usr/share/pki/acme/database/ldap/schema.ldif',
|
||||
]:
|
||||
|
Loading…
Reference in New Issue
Block a user