From 99f3bc5835379b700cc503f49a8b22c3bbb5a43e Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Thu, 19 Sep 2024 19:55:51 +0200 Subject: [PATCH] Allow pkispawn to install only EST subsystem Deployment script has been modified to skip configuration for EST, it is done differently from the other subsystems. The remaining steps are performed to create and prepare the instance for EST. --- .../workflows/est-ds-realm-separate-test.yml | 243 ++++++++++++++++++ .github/workflows/est-tests.yml | 5 + base/server/etc/default.cfg | 2 +- .../python/pki/server/deployment/__init__.py | 25 +- base/server/python/pki/server/subsystem.py | 46 +++- 5 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/est-ds-realm-separate-test.yml diff --git a/.github/workflows/est-ds-realm-separate-test.yml b/.github/workflows/est-ds-realm-separate-test.yml new file mode 100644 index 00000000000..5a704f48eca --- /dev/null +++ b/.github/workflows/est-ds-realm-separate-test.yml @@ -0,0 +1,243 @@ +name: EST with ds realm on separate instance + +on: workflow_call + +env: + DB_IMAGE: ${{ vars.DB_IMAGE || 'quay.io/389ds/dirsrv' }} + +jobs: + # docs/installation/ca/Installing_CA.md + test: + name: Test + runs-on: ubuntu-latest + env: + SHARED: /tmp/workdir/pki + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Retrieve PKI images + uses: actions/cache@v4 + with: + key: pki-images-${{ github.sha }} + path: pki-images.tar + + - name: Load PKI images + run: docker load --input pki-images.tar + + - name: Create network + run: docker network create example + + - name: Set up CA DS container + run: | + tests/bin/ds-create.sh \ + --image=${{ env.DB_IMAGE }} \ + --hostname=cads.example.com \ + --password=Secret.123 \ + --network=example \ + --network-alias=cads.example.com \ + cads + + - name: Set up CA container + run: | + tests/bin/runner-init.sh \ + --hostname=ca.example.com \ + --network=example \ + --network-alias=ca.example.com \ + ca + + - name: Install CA + run: | + docker exec ca pkispawn \ + -f /usr/share/pki/server/examples/installation/ca.cfg \ + -s CA \ + -D pki_ds_url=ldap://cads.example.com:3389 \ + -v + + - name: Initialize PKI client + run: | + docker exec ca pki-server cert-export ca_signing --cert-file $SHARED/ca_signing.crt + + docker exec ca pki nss-cert-import \ + --cert $SHARED/ca_signing.crt \ + --trust CT,C,C \ + ca_signing + + docker exec ca pki pkcs12-import \ + --pkcs12 /root/.dogtag/pki-tomcat/ca_admin_cert.p12 \ + --pkcs12-password Secret.123 + + docker exec ca pki info + + - name: Create EST server certificates in p12 + run: | + docker exec ca pki nss-cert-request --csr estSSLServer.csr \ + --ext /usr/share/pki/server/certs/sslserver.conf --subject 'CN=est.example.com' + + docker exec ca pki ca-cert-request-submit --csr-file estSSLServer.csr --profile caServerCert | tee output + REQUEST_ID=$(sed -n "s/^\s*Request ID:\s*\(\S*\)$/\1/p" output) + + docker exec ca pki -n caadmin ca-cert-request-approve $REQUEST_ID --force | tee output + CERT_ID=$(sed -n "s/^\s*Certificate ID:\s*\(\S*\)$/\1/p" output) + + docker exec ca pki -n caadmin ca-cert-export --output-file estSSLServer.crt $CERT_ID + docker exec ca pki nss-cert-import --cert estSSLServer.crt sslserver + + docker exec ca pk12util -d /root/.dogtag/nssdb -o $SHARED/est_server.p12 -n sslserver -W Secret.123 + + - name: Add CA EST user + run: | + docker exec ca pki -n caadmin ca-group-add "EST RA Agents" + docker exec ca pki -n caadmin ca-user-add \ + est-ra-1 --fullName "EST RA 1" --password Secret.est + docker exec ca pki -n caadmin ca-group-member-add "EST RA Agents" est-ra-1 + + - name: Configure CA est profile + run: | + docker exec ca pki -n caadmin ca-profile-add \ + --raw /usr/share/pki/ca/profiles/ca/estServiceCert.cfg + docker exec ca pki -n caadmin ca-profile-enable estServiceCert + docker exec ca pki-server restart --wait + + - name: Set up EST DS container + run: | + tests/bin/ds-create.sh \ + --image=${{ env.DB_IMAGE }} \ + --hostname=estds.example.com \ + --password=Secret.123 \ + --network=example \ + --network-alias=estds.example.com \ + estds + + - name: Create EST users + run: | + docker exec -i estds ldapadd -x -H ldap://estds.example.com:3389 \ + -D "cn=Directory Manager" -w Secret.123 << EOF + dn: dc=est,dc=pki,dc=example,dc=com + objectClass: domain + dc: est + + dn: ou=people,dc=est,dc=pki,dc=example,dc=com + ou: people + objectClass: top + objectClass: organizationalUnit + + dn: ou=groups,dc=est,dc=pki,dc=example,dc=com + ou: groups + objectClass: top + objectClass: organizationalUnit + + dn: uid=est-test-user,ou=people,dc=est,dc=pki,dc=example,dc=com + objectClass: top + objectClass: person + objectClass: organizationalPerson + objectClass: inetOrgPerson + uid: est-test-user + sn: EST TEST USER + cn: EST TEST USER + userPassword: Secret.123 + + dn: cn=estclient,ou=groups,dc=est,dc=pki,dc=example,dc=com + objectClass: top + objectClass: groupOfUniqueNames + cn: estclient + uniqueMember: uid=est-test-user,ou=People,dc=est,dc=pki,dc=example,dc=com + EOF + + - name: Set up EST container + run: | + tests/bin/runner-init.sh \ + --hostname=est.example.com \ + --network=example \ + --network-alias=est.example.com \ + est + + - name: Install EST + run: | + docker exec est pkispawn \ + -f /usr/share/pki/server/examples/installation/est.cfg \ + -s EST \ + -D est_realm_url=ldap://estds.example.com:3389 \ + -D pki_ca_uri=https://ca.example.com:8443 \ + -D pki_server_pkcs12_path=$SHARED/est_server.p12 \ + -D pki_server_pkcs12_password=Secret.123 \ + -v + - name: Test CA certs + run: | + docker exec est curl -o cacert.p7 -k https://est.example.com:8443/.well-known/est/cacerts + + docker exec est openssl base64 -d --in cacert.p7 --out cacert.p7.der + docker exec est openssl pkcs7 --in cacert.p7.der -inform DER -print_certs -out cacert.pem + docker exec est openssl x509 -in cacert.pem -text -noout | tee actual + docker exec est openssl x509 -in $SHARED/ca_signing.crt -text -noout | tee expected + diff expected actual + + - name: Install est client + run: | + docker exec est dnf copr enable -y @pki/libest + docker exec est dnf install -y libest + + - name: Enroll certificate + run: | + docker exec -e EST_OPENSSL_CACERT=cacert.pem est estclient -e -s est.example.com -p 8443 \ + --common-name test.example.com -o . -u est-test-user -h Secret.123 + + docker exec est openssl base64 -d --in cert-0-0.pkcs7 --out cert-0-0.pkcs7.der + docker exec est openssl pkcs7 -in cert-0-0.pkcs7.der -inform DER -print_certs -out cert.pem + docker exec est openssl x509 -in cert.pem -subject -noout | tee actual + echo "subject=CN=test.example.com" > expected + diff expected actual + + - name: Remove EST + run: | + docker exec est pki-server est-undeploy --wait + docker exec est pki-server est-remove + + - name: Remove CA + run: docker exec ca pkidestroy -i pki-tomcat -s CA -v + + - name: Check CA DS server systemd journal + if: always() + run: | + docker exec cads journalctl -x --no-pager -u dirsrv@localhost.service + + - name: Check CA DS container logs + if: always() + run: | + docker logs cads + + - name: Check CA PKI server systemd journal + if: always() + run: | + docker exec ca journalctl -x --no-pager -u pki-tomcatd@pki-tomcat.service + + - name: Check EST PKI server systemd journal + if: always() + run: | + docker exec est journalctl -x --no-pager -u pki-tomcatd@pki-tomcat.service + + - name: Check CA debug log + if: always() + run: | + docker exec ca find /var/lib/pki/pki-tomcat/logs/ca -name "debug.*" -exec cat {} \; + + - name: Check EST debug log + if: always() + run: | + docker exec est find /var/lib/pki/pki-tomcat/logs/est -name "debug.*" -exec cat {} \; + + - name: Gather artifacts + if: always() + run: | + tests/bin/ds-artifacts-save.sh cads + tests/bin/ds-artifacts-save.sh estds + tests/bin/pki-artifacts-save.sh ca + tests/bin/pki-artifacts-save.sh est + continue-on-error: true + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: est-ds-separate + path: /tmp/artifacts diff --git a/.github/workflows/est-tests.yml b/.github/workflows/est-tests.yml index 4d052e56ad9..142067972c3 100644 --- a/.github/workflows/est-tests.yml +++ b/.github/workflows/est-tests.yml @@ -46,3 +46,8 @@ jobs: name: EST with ds realm needs: build uses: ./.github/workflows/est-ds-realm-test.yml + + est-ds-realm-separate-test: + name: EST with ds realm on a separate instance + needs: build + uses: ./.github/workflows/est-ds-realm-separate-test.yml diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index d16f63d2754..b6631111a17 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -669,7 +669,7 @@ pki_registry_enable=False [EST] pki_ds_setup=False pki_security_domain_setup=False -pki_registry_enable=False +pki_registry_enable=True pki_ca_uri=https://%(pki_hostname)s:%(pki_https_port)s est_ca_profile=estServiceCert est_ca_user_name= diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index 80a1e5f230b..9ef3c2af5fc 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -5385,6 +5385,10 @@ def create_est_subsystem(self): subsystem = pki.server.subsystem.ESTSubsystem(self.instance) subsystem.create() + subsystem.create_conf(exist_ok=True) + subsystem.create_registry(exist_ok=True) + subsystem.create_logs(exist_ok=True) + subsystem.add_est_config(exist_ok=True, force=True) return subsystem def configure_est_backend(self, subsystem): @@ -5486,7 +5490,6 @@ def deploy_est_webapp(self, subsystem): if len(self.instance.get_subsystems()) == 1: # if this is the first subsystem, deploy the subsystem without waiting subsystem.enable() - self.instance.start() else: # otherwise, deploy the subsystem and wait until it starts subsystem.enable( @@ -5528,12 +5531,11 @@ def spawn(self): if self.subsystem_type == 'EST': self.spawn_est() - return - - scriptlet = pki.server.deployment.scriptlets.subsystem_layout.PkiScriptlet() - scriptlet.deployer = self - scriptlet.instance = self.instance - scriptlet.spawn(self) + else: + scriptlet = pki.server.deployment.scriptlets.subsystem_layout.PkiScriptlet() + scriptlet.deployer = self + scriptlet.instance = self.instance + scriptlet.spawn(self) scriptlet = pki.server.deployment.scriptlets.security_databases.PkiScriptlet() scriptlet.deployer = self @@ -5555,10 +5557,11 @@ def spawn(self): scriptlet.instance = self.instance scriptlet.spawn(self) - scriptlet = pki.server.deployment.scriptlets.configuration.PkiScriptlet() - scriptlet.deployer = self - scriptlet.instance = self.instance - scriptlet.spawn(self) + if self.subsystem_type != 'EST': + scriptlet = pki.server.deployment.scriptlets.configuration.PkiScriptlet() + scriptlet.deployer = self + scriptlet.instance = self.instance + scriptlet.spawn(self) scriptlet = pki.server.deployment.scriptlets.finalization.PkiScriptlet() scriptlet.deployer = self diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index 3908f93becd..f57c7e1d28a 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -2874,9 +2874,10 @@ def authorizer_conf(self): def realm_conf(self): return os.path.join(self.conf_dir, 'realm.conf') - def create(self, exist_ok=False, force=False): + def create(self, exist_ok=False): + self.instance.makedirs(self.base_dir, exist_ok=exist_ok) - self.instance.makedirs(self.conf_dir, exist_ok=exist_ok) + def add_est_config(self, exist_ok=False, force=False): default_conf_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'est', 'conf') @@ -2966,6 +2967,47 @@ def replace_realm_config(self, realm_path): exist_ok=False, force=True) + def is_ready(self, secure_connection=True, timeout=None): + """ + Wait for EST subsystem to become ready to serve requests. + Since EST do not implement yet status API the check is done agaist the web app + + :param startup_timeout: Total timeout. Unsuccessful status requests will + be retried until this timeout is exceeded. Default: None. + :param request_timeout: Connect/receive timeout for each individual + status request. Default: None. + """ + + server_config = self.instance.get_server_config() + + if secure_connection: + protocol = 'https' + port = server_config.get_secure_port() + + else: + protocol = 'http' + port = server_config.get_unsecure_port() + + # When waiting for a connection to come alive, don't bother verifying + # the certificate at this stage. + connection = pki.client.PKIConnection( + protocol=protocol, + hostname=socket.getfqdn(), + port=port, + accept='application/json', + trust_env=False, + verify=False) + + response = connection.get( + '/est/', + timeout=timeout + ) + if response.status_code == 200: + logger.info('Subsystem status: running') + else: + logger.info('Subsystem status: error') + return response.status_code == 200 + class PKISubsystemFactory(object):