URL: https://github.com/freeipa/freeipa/pull/5170 Author: rcritten Title: #5170: Centralize enable/disable of the ACME service Action: opened
PR body: """ 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. Two ACME configuration templates were removed so that the dogtag defaults would be used, configsources.conf and engine.conf. 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 """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/5170/head:pr5170 git checkout pr5170
From 34e256807c5424e32db10faab563a183c9710b7e Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 2 Oct 2020 15:06:58 -0400 Subject: [PATCH 1/4] Enable importing LDIF files not shipped by IPA This is to be able to import ACME schema provided by dogtag. https://pagure.io/freeipa/issue/8524 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> --- .../profiles/{acmeServerCert.cfg => acmeIPAServerCert.cfg} | 0 ipaserver/install/service.py | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) rename install/share/profiles/{acmeServerCert.cfg => acmeIPAServerCert.cfg} (100%) diff --git a/install/share/profiles/acmeServerCert.cfg b/install/share/profiles/acmeIPAServerCert.cfg similarity index 100% rename from install/share/profiles/acmeServerCert.cfg rename to install/share/profiles/acmeIPAServerCert.cfg diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index 896fb19ca9..df26a0d683 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -347,7 +347,10 @@ def _ldap_mod(self, ldif, sub_dict=None, raise_on_err=True, ldap_uri=None, dm_password=None): pw_name = None fd = None - path = os.path.join(paths.USR_SHARE_IPA_DIR, ldif) + if not os.path.isabs(ldif): + path = os.path.join(paths.USR_SHARE_IPA_DIR, ldif) + else: + path = ldif nologlist = [] if sub_dict is not None: From 2160c1e0ba1bb4f1a47ab32034f4adf99c5d7f51 Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 2 Oct 2020 15:08:10 -0400 Subject: [PATCH 2/4] Let dogtag.py be imported if the api is not initialized This allows non-plugin components to import the RestClient classes. https://pagure.io/freeipa/issue/8524 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/plugins/dogtag.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py index 594f32fab0..805ca0a449 100644 --- a/ipaserver/plugins/dogtag.py +++ b/ipaserver/plugins/dogtag.py @@ -256,7 +256,7 @@ from ipapython import dogtag, ipautil, certdb from ipaserver.masters import find_providing_server -if api.env.in_server: +if api.isdone('finalize') and api.env.in_server: import pki from pki.client import PKIConnection import pki.crypto as cryptoutil @@ -1101,7 +1101,7 @@ def parse_updateCRL_xml(doc): #------------------------------------------------------------------------------- from ipalib import Registry, errors, SkipPluginModule -if api.env.ra_plugin != 'dogtag': +if api.isdone('finalize') and api.env.ra_plugin != 'dogtag': # In this case, abort loading this plugin module... raise SkipPluginModule(reason='dogtag not selected as RA plugin') import os From d2f019ca4b1eb75b599f078dede7cc0bd3a2ca82 Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 2 Oct 2020 15:10:29 -0400 Subject: [PATCH 3/4] 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. Two ACME configuration templates were removed so that the dogtag defaults would be used, configsources.conf and engine.conf. 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 <rcrit...@redhat.com> --- install/share/Makefile.am | 3 +- .../pki-acme-configsources.conf.template | 2 - install/share/pki-acme-engine.conf.template | 2 - install/share/pki-acme-issuer.conf.template | 2 +- install/share/profiles/Makefile.am | 2 +- install/share/profiles/acmeIPAServerCert.cfg | 2 +- ipaplatform/base/paths.py | 3 +- ipapython/dogtag.py | 4 +- ipaserver/install/cainstance.py | 13 ++- ipaserver/install/ipa_acme_manage.py | 87 +++++++++++++------ 10 files changed, 77 insertions(+), 43 deletions(-) delete mode 100644 install/share/pki-acme-configsources.conf.template delete mode 100644 install/share/pki-acme-engine.conf.template diff --git a/install/share/Makefile.am b/install/share/Makefile.am index 1c1cd25db2..f16c7031c8 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -103,10 +103,9 @@ dist_app_DATA = \ ipaca_default.ini \ ipaca_customize.ini \ ipaca_softhsm2.ini \ - pki-acme-configsources.conf.template \ pki-acme-database.conf.template \ - pki-acme-engine.conf.template \ pki-acme-issuer.conf.template \ + pki-acme-realm.conf.template \ ldbm-tuning.ldif \ $(NULL) diff --git a/install/share/pki-acme-configsources.conf.template b/install/share/pki-acme-configsources.conf.template deleted file mode 100644 index f82ac2157c..0000000000 --- a/install/share/pki-acme-configsources.conf.template +++ /dev/null @@ -1,2 +0,0 @@ -engine.class=org.dogtagpki.acme.server.ACMEEngineConfigFileSource -engine.filename=/etc/pki/pki-tomcat/acme/engine.conf diff --git a/install/share/pki-acme-engine.conf.template b/install/share/pki-acme-engine.conf.template deleted file mode 100644 index cb9898c5f2..0000000000 --- a/install/share/pki-acme-engine.conf.template +++ /dev/null @@ -1,2 +0,0 @@ -enabled=false -wildcard=false diff --git a/install/share/pki-acme-issuer.conf.template b/install/share/pki-acme-issuer.conf.template index de2a06fec8..968d08f0aa 100644 --- a/install/share/pki-acme-issuer.conf.template +++ b/install/share/pki-acme-issuer.conf.template @@ -1,5 +1,5 @@ class=org.dogtagpki.acme.issuer.PKIIssuer url=https://$FQDN:8443 -profile=acmeServerCert +profile=acmeIPAServerCert username=$USER password=$PASSWORD diff --git a/install/share/profiles/Makefile.am b/install/share/profiles/Makefile.am index eb53703cc5..a04902ccef 100644 --- a/install/share/profiles/Makefile.am +++ b/install/share/profiles/Makefile.am @@ -7,7 +7,7 @@ app_DATA = \ caIPAserviceCert.UPGRADE.cfg \ IECUserRoles.cfg \ KDCs_PKINIT_Certs.cfg \ - acmeServerCert.cfg \ + acmeIPAServerCert.cfg \ $(NULL) EXTRA_DIST = \ diff --git a/install/share/profiles/acmeIPAServerCert.cfg b/install/share/profiles/acmeIPAServerCert.cfg index e636e29edf..2487056e14 100644 --- a/install/share/profiles/acmeIPAServerCert.cfg +++ b/install/share/profiles/acmeIPAServerCert.cfg @@ -1,4 +1,4 @@ -profileId=acmeServerCert +profileId=acmeIPAServerCert classId=caEnrollImpl desc=ACME profile for use in IPA deployments visible=true diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index ef894dd77a..8365e6c035 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -123,10 +123,9 @@ class BasePathNamespace: PKI_TOMCAT_ALIAS_PWDFILE_TXT = "/etc/pki/pki-tomcat/alias/pwdfile.txt" PKI_TOMCAT_PASSWORD_CONF = "/etc/pki/pki-tomcat/password.conf" PKI_TOMCAT_SERVER_XML = "/etc/pki/pki-tomcat/server.xml" - PKI_ACME_CONFIGSOURCES_CONF = "/etc/pki/pki-tomcat/acme/configsources.conf" 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" diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py index abd96bd456..251281a7eb 100644 --- a/ipapython/dogtag.py +++ b/ipapython/dogtag.py @@ -56,8 +56,8 @@ 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), } diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 4d96a099c1..0e6d9e48c2 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -71,7 +71,7 @@ 'Security Domain Administrators' ] -ACME_AGENT_GROUP = 'ACME Agents' +ACME_AGENT_GROUP = 'Enterprise ACME Administrators' def check_ports(): @@ -770,6 +770,10 @@ def __create_ca_agent(self): 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): @@ -1518,6 +1522,8 @@ def setup_acme(self) -> bool: 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 @@ -1544,11 +1550,9 @@ def setup_acme(self) -> bool: # write configuration files files = [ - ('pki-acme-configsources.conf.template', - paths.PKI_ACME_CONFIGSOURCES_CONF), ('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, @@ -1776,6 +1780,7 @@ def ensure_acme_containers(): DN(('ou', 'orders'), ou_acme), DN(('ou', 'authorizations'), ou_acme), DN(('ou', 'challenges'), ou_acme), + DN(('ou', 'certificates'), ou_acme), ] for rdn in rdns: diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py index ff56d3bf64..d3dcb135af 100644 --- a/ipaserver/install/ipa_acme_manage.py +++ b/ipaserver/install/ipa_acme_manage.py @@ -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 @@ # 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 @@ def run(self): 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') - - 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. + api.bootstrap(in_server=True, confdir=paths.ETC_IPA) + api.finalize() + api.Backend.ldap2.connect() + + 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 From 3fe69947eb2b6318dd846859faa84e26c04c1f37 Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 2 Oct 2020 17:12:23 -0400 Subject: [PATCH 4/4] Temp commit using pki-fedora --- .freeipa-pr-ci.yaml | 2 +- ipatests/prci_definitions/temp_commit.yaml | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.freeipa-pr-ci.yaml b/.freeipa-pr-ci.yaml index abcf8c5b63..8065669008 120000 --- a/.freeipa-pr-ci.yaml +++ b/.freeipa-pr-ci.yaml @@ -1 +1 @@ -ipatests/prci_definitions/gating.yaml \ No newline at end of file +ipatests/prci_definitions/temp_commit.yaml \ No newline at end of file diff --git a/ipatests/prci_definitions/temp_commit.yaml b/ipatests/prci_definitions/temp_commit.yaml index ef2e4bfa90..d8e2a44ac7 100644 --- a/ipatests/prci_definitions/temp_commit.yaml +++ b/ipatests/prci_definitions/temp_commit.yaml @@ -47,7 +47,7 @@ topologies: memory: 14500 jobs: - fedora-latest/build: + pki-fedora/build: requires: [] priority: 100 job: @@ -55,20 +55,21 @@ jobs: args: git_repo: '{git_repo}' git_refspec: '{git_refspec}' - template: &ci-master-latest - name: freeipa/ci-master-f32 - version: 0.0.11 + template: &pki-master-latest + name: freeipa/pki-master-f32 + version: 0.0.3 timeout: 1800 topology: *build - fedora-latest/temp_commit: - requires: [fedora-latest/build] + pki-fedora/temp_commit: + requires: [pki-fedora/build] priority: 50 job: class: RunPytest args: - build_url: '{fedora-latest/build_url}' - test_suite: test_integration/test_REPLACEME.py - template: *ci-master-latest - timeout: 3600 + build_url: '{pki-fedora/build_url}' + update_packages: True + test_suite: test_integration/test_acme.py + template: *pki-master-latest + timeout: 7200 topology: *master_1repl_1client
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org