From 073547de6fcaa57d7be416d2c43f76498f812db7 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Tue, 17 Sep 2024 09:25:35 +0200 Subject: [PATCH] Modify pkispawn to deploy EST EST deployment is included in pkispwn. The installation does not perform all the steps done for CA and other subsystems so there is no security domain management and user administration. During the installation there is no DS or other DBs connection which has to be performed by the user before or after the installation. --- .github/workflows/est-ds-realm-test.yml | 322 ++++++++++++++++++ .github/workflows/est-tests.yml | 6 + base/est/CMakeLists.txt | 21 ++ base/est/bin/estauthz | 7 + base/est/conf/realm.conf | 1 - base/est/shared/authorizer.conf | 2 + base/est/shared/backend.conf | 5 + base/est/shared/realm/ds.conf | 7 + base/est/shared/realm/in-memory.conf | 4 + base/est/shared/realm/postgresql.conf | 5 + base/est/shared/realm/statements.conf | 31 ++ base/est/webapps/est/index.jsp | 25 ++ base/server/etc/default.cfg | 29 +- base/server/examples/installation/est.cfg | 9 + .../python/pki/server/deployment/__init__.py | 133 +++++++- .../python/pki/server/deployment/pkiconfig.py | 2 +- .../deployment/scriptlets/initialization.py | 6 +- base/server/python/pki/server/pkispawn.py | 19 +- base/server/python/pki/server/subsystem.py | 102 ++++++ pki.spec | 1 + 20 files changed, 726 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/est-ds-realm-test.yml create mode 100755 base/est/bin/estauthz delete mode 100644 base/est/conf/realm.conf create mode 100644 base/est/shared/authorizer.conf create mode 100644 base/est/shared/backend.conf create mode 100644 base/est/shared/realm/ds.conf create mode 100644 base/est/shared/realm/in-memory.conf create mode 100644 base/est/shared/realm/postgresql.conf create mode 100644 base/est/shared/realm/statements.conf create mode 100644 base/est/webapps/est/index.jsp create mode 100644 base/server/examples/installation/est.cfg diff --git a/.github/workflows/est-ds-realm-test.yml b/.github/workflows/est-ds-realm-test.yml new file mode 100644 index 00000000000..4df14d05ac0 --- /dev/null +++ b/.github/workflows/est-ds-realm-test.yml @@ -0,0 +1,322 @@ +name: EST with ds realm + +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 DS container + run: | + tests/bin/ds-create.sh \ + --image=${{ env.DB_IMAGE }} \ + --hostname=ds.example.com \ + --password=Secret.123 \ + ds + + - name: Connect DS container to network + run: docker network connect example ds --alias ds.example.com + + - name: Set up PKI container + run: | + tests/bin/runner-init.sh pki + env: + HOSTNAME: pki.example.com + + - name: Connect PKI container to network + run: docker network connect example pki --alias pki.example.com + + - name: Install CA + run: | + docker exec pki pkispawn \ + -f /usr/share/pki/server/examples/installation/ca.cfg \ + -s CA \ + -D pki_ds_url=ldap://ds.example.com:3389 \ + -v + + - name: Initialize PKI client + run: | + docker exec pki pki-server cert-export ca_signing --cert-file ca_signing.crt + + docker exec pki pki nss-cert-import \ + --cert ca_signing.crt \ + --trust CT,C,C \ + ca_signing + + docker exec pki pki pkcs12-import \ + --pkcs12 /root/.dogtag/pki-tomcat/ca_admin_cert.p12 \ + --pkcs12-password Secret.123 + + docker exec pki pki info + + - name: Add CA EST user + run: | + docker exec pki pki -n caadmin ca-group-add "EST RA Agents" + docker exec pki pki -n caadmin ca-user-add \ + est-ra-1 --fullName "EST RA 1" --password Secret.est + docker exec pki pki -n caadmin ca-group-member-add "EST RA Agents" est-ra-1 + + - name: Configure CA est profile + run: | + docker exec pki cp /usr/share/pki/ca/profiles/ca/estServiceCert.cfg estServiceCert.cfg + docker exec pki sed -i 's/EST RA Agents/Subsystem Group/' estServiceCert.cfg + docker exec pki pki -n caadmin ca-profile-add \ + --raw ./estServiceCert.cfg + docker exec pki pki -n caadmin ca-profile-enable estServiceCert + docker exec pki pki-server restart --wait + + - name: Install EST + run: | + docker exec pki pkispawn \ + -f /usr/share/pki/server/examples/installation/est.cfg \ + -s EST \ + -D est_realm_url=ldap://ds.example.com:3389 \ + -v + + - name: Check EST backend config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/est/backend.conf + + - name: Check EST authorizer config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/est/authorizer.conf + + - name: Check EST realm config + if: always() + run: | + docker exec pki cat /etc/pki/pki-tomcat/est/realm.conf + + - name: Check webapps + run: | + docker exec pki pki-server webapp-find | tee output + + # CA instance should have ROOT, ca, and pki webapps + echo "ROOT" > expected + echo "ca" >> expected + echo "est" >> expected + echo "pki" >> expected + sed -n 's/^ *Webapp ID: *\(.*\)$/\1/p' output > actual + diff expected actual + + docker exec pki pki-server webapp-show ROOT + docker exec pki pki-server webapp-show ca + docker exec pki pki-server webapp-show est + docker exec pki pki-server webapp-show pki + + - name: Create EST users + run: | + docker exec -i pki ldapadd -x -H ldap://ds.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 + objectClass: cmsuser + uid: est-test-user + sn: EST TEST USER + cn: EST TEST USER + usertype: undefined + 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: Check EST subsystem + run: | + docker exec pki pki-server subsystem-show est | tee output + + # CA instance should have CA subsystem + echo "est" > expected + sed -n 's/^ *Subsystem ID: *\(.*\)$/\1/p' output > actual + diff expected actual + + echo "True" > expected + sed -n 's/^ *Enabled: *\(.*\)$/\1/p' output > actual + diff expected actual + + - name: Test CA certs + run: | + docker exec pki curl -o cacert.p7 -k https://pki.example.com:8443/.well-known/est/cacerts + docker exec pki openssl base64 -d --in cacert.p7 --out cacert.p7.der + docker exec pki openssl pkcs7 --in cacert.p7.der -inform DER -print_certs -out cacert.pem + docker exec pki openssl x509 -in cacert.pem -text -noout | tee actual + docker exec pki openssl x509 -in ca_signing.crt -text -noout | tee expected + diff expected actual + + - name: Install est client + run: | + docker exec pki dnf copr enable -y @pki/libest + docker exec pki dnf install -y libest + + - name: Enroll certificate + run: | + docker exec -e EST_OPENSSL_CACERT=cacert.pem pki estclient -e -s pki.example.com -p 8443 \ + --common-name test.example.com -o . -u est-test-user -h Secret.123 + + docker exec pki openssl base64 -d --in cert-0-0.pkcs7 --out cert-0-0.pkcs7.der + docker exec pki openssl pkcs7 -in cert-0-0.pkcs7.der -inform DER -print_certs -out cert.pem + docker exec pki 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 pki pki-server est-undeploy --wait + docker exec pki pki-server est-remove + + - name: Remove CA + run: docker exec pki pkidestroy -i pki-tomcat -s CA -v + + - name: Check PKI server base dir after removal + run: | + # check file types, owners, and permissions + docker exec pki ls -l /var/lib/pki/pki-tomcat \ + | sed \ + -e '/^total/d' \ + -e 's/^\(\S*\) *\S* *\(\S*\) *\(\S*\) *\S* *\S* *\S* *\S* *\(.*\)$/\1 \2 \3 \4/' \ + | tee output + + # TODO: review permissions + cat > expected << EOF + lrwxrwxrwx pkiuser pkiuser conf -> /etc/pki/pki-tomcat + lrwxrwxrwx pkiuser pkiuser logs -> /var/log/pki/pki-tomcat + EOF + + diff expected output + + - name: Check PKI server conf dir after removal + run: | + # check file types, owners, and permissions + docker exec pki ls -l /etc/pki/pki-tomcat \ + | sed \ + -e '/^total/d' \ + -e 's/^\(\S*\) *\S* *\(\S*\) *\(\S*\) *\S* *\S* *\S* *\S* *\(.*\)$/\1 \2 \3 \4/' \ + | tee output + + # TODO: review permissions + cat > expected << EOF + drwxrwx--- pkiuser pkiuser Catalina + drwxrwx--- pkiuser pkiuser alias + drwxrwx--- pkiuser pkiuser ca + -rw-r--r-- pkiuser pkiuser catalina.policy + lrwxrwxrwx pkiuser pkiuser catalina.properties -> /usr/share/pki/server/conf/catalina.properties + drwxrwx--- pkiuser pkiuser certs + lrwxrwxrwx pkiuser pkiuser context.xml -> /etc/tomcat/context.xml + drwxrwx--- pkiuser pkiuser est + lrwxrwxrwx pkiuser pkiuser logging.properties -> /usr/share/pki/server/conf/logging.properties + -rw-rw---- pkiuser pkiuser password.conf + -rw-rw---- pkiuser pkiuser server.xml + -rw-rw---- pkiuser pkiuser serverCertNick.conf + -rw-rw---- pkiuser pkiuser tomcat.conf + lrwxrwxrwx pkiuser pkiuser web.xml -> /etc/tomcat/web.xml + EOF + + diff expected output + + - name: Check PKI server logs dir after removal + run: | + # check file types, owners, and permissions + docker exec pki ls -l /var/log/pki/pki-tomcat \ + | sed \ + -e '/^total/d' \ + -e 's/^\(\S*\) *\S* *\(\S*\) *\(\S*\) *\S* *\S* *\S* *\S* *\(.*\)$/\1 \2 \3 \4/' \ + | tee output + + DATE=$(date +'%Y-%m-%d') + + # TODO: review permissions + cat > expected << EOF + drwxr-x--- pkiuser pkiuser backup + drwxrwx--- pkiuser pkiuser ca + -rw-rw-r-- pkiuser pkiuser catalina.$DATE.log + drwxrwx--- pkiuser pkiuser est + -rw-rw-r-- pkiuser pkiuser host-manager.$DATE.log + -rw-rw-r-- pkiuser pkiuser localhost.$DATE.log + -rw-r--r-- pkiuser pkiuser localhost_access_log.$DATE.txt + -rw-rw-r-- pkiuser pkiuser manager.$DATE.log + drwxr-xr-x pkiuser pkiuser pki + EOF + + diff expected output + + - name: Check DS server systemd journal + if: always() + run: | + docker exec ds journalctl -x --no-pager -u dirsrv@localhost.service + + - name: Check DS container logs + if: always() + run: | + docker logs ds + + - name: Check PKI server systemd journal + if: always() + run: | + docker exec pki journalctl -x --no-pager -u pki-tomcatd@pki-tomcat.service + + - name: Check CA debug log + if: always() + run: | + docker exec pki find /var/lib/pki/pki-tomcat/logs/ca -name "debug.*" -exec cat {} \; + + - name: Check EST debug log + if: always() + run: | + docker exec pki find /var/lib/pki/pki-tomcat/logs/est -name "debug.*" -exec cat {} \; + + - name: Gather artifacts + if: always() + run: | + tests/bin/ds-artifacts-save.sh ds + tests/bin/pki-artifacts-save.sh pki + continue-on-error: true + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: ca-basic + path: /tmp/artifacts diff --git a/.github/workflows/est-tests.yml b/.github/workflows/est-tests.yml index 0f195a24a61..4d052e56ad9 100644 --- a/.github/workflows/est-tests.yml +++ b/.github/workflows/est-tests.yml @@ -40,3 +40,9 @@ jobs: ansible-playbook -e 'pki_subsystem="est"' tests/ansible/pki-playbook.yml env: ANSIBLE_CONFIG: ${{ github.workspace }}/tests/ansible/ansible.cfg + + + est-ds-realm-test: + name: EST with ds realm + needs: build + uses: ./.github/workflows/est-ds-realm-test.yml diff --git a/base/est/CMakeLists.txt b/base/est/CMakeLists.txt index 4cace31f0d2..73e7f756ef2 100644 --- a/base/est/CMakeLists.txt +++ b/base/est/CMakeLists.txt @@ -96,3 +96,24 @@ install( DESTINATION ${DATA_INSTALL_DIR}/est/webapps/est/WEB-INF/lib ) + +install( + DIRECTORY + shared/ + DESTINATION + ${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}/${PROJECT_NAME}/conf/ + PATTERN + "CMakeLists.txt" EXCLUDE +) + +install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/bin/estauthz + DESTINATION + ${LIBEXEC_INSTALL_DIR} + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ + +) diff --git a/base/est/bin/estauthz b/base/est/bin/estauthz new file mode 100755 index 00000000000..3fce98cfa1c --- /dev/null +++ b/base/est/bin/estauthz @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +import json, sys +ALLOWED_ROLE = 'estclient' +obj = json.loads(sys.stdin.read()) +if not ALLOWED_ROLE in obj['authzData']['principal']['roles']: + print(f'Principal does not have required role {ALLOWED_ROLE!r}') + sys.exit(1) diff --git a/base/est/conf/realm.conf b/base/est/conf/realm.conf deleted file mode 100644 index ccfb9d62813..00000000000 --- a/base/est/conf/realm.conf +++ /dev/null @@ -1 +0,0 @@ -class=org.apache.catalina.realm.MemoryRealm diff --git a/base/est/shared/authorizer.conf b/base/est/shared/authorizer.conf new file mode 100644 index 00000000000..bb126819b72 --- /dev/null +++ b/base/est/shared/authorizer.conf @@ -0,0 +1,2 @@ +class=org.dogtagpki.est.ExternalProcessRequestAuthorizer +executable=/usr/local/libexec/estauthz diff --git a/base/est/shared/backend.conf b/base/est/shared/backend.conf new file mode 100644 index 00000000000..83eeeaa7f64 --- /dev/null +++ b/base/est/shared/backend.conf @@ -0,0 +1,5 @@ +class=org.dogtagpki.est.DogtagRABackend +url=https://fedora:8443 +profile=estServiceCert +username=est-ra-1 +password=est4ever diff --git a/base/est/shared/realm/ds.conf b/base/est/shared/realm/ds.conf new file mode 100644 index 00000000000..150bebfa934 --- /dev/null +++ b/base/est/shared/realm/ds.conf @@ -0,0 +1,7 @@ +class=com.netscape.cms.realm.PKILDAPRealm +url=ldap://localhost.localdomain:389 +authType=BasicAuth +bindDN=cn=Directory Manager +bindPassword=Secret.123 +usersDN=ou=people,dc=est,dc=pki,dc=example,dc=com +groupsDN=ou=groups,dc=est,dc=pki,dc=example,dc=com \ No newline at end of file diff --git a/base/est/shared/realm/in-memory.conf b/base/est/shared/realm/in-memory.conf new file mode 100644 index 00000000000..6182f91923d --- /dev/null +++ b/base/est/shared/realm/in-memory.conf @@ -0,0 +1,4 @@ +class=com.netscape.cms.realm.PKIInMemoryRealm +username=admin +password=Secret.123 +roles=estclient \ No newline at end of file diff --git a/base/est/shared/realm/postgresql.conf b/base/est/shared/realm/postgresql.conf new file mode 100644 index 00000000000..8be846d45a5 --- /dev/null +++ b/base/est/shared/realm/postgresql.conf @@ -0,0 +1,5 @@ +class=com.netscape.cms.realm.PKIPostgreSQLRealm +url=jdbc:postgresql://localhost.localdomain:5432/est +user=est +password=Secret.123 +statements=/usr/share/pki/est/conf/realm/statements.conf \ No newline at end of file diff --git a/base/est/shared/realm/statements.conf b/base/est/shared/realm/statements.conf new file mode 100644 index 00000000000..f8cc8132942 --- /dev/null +++ b/base/est/shared/realm/statements.conf @@ -0,0 +1,31 @@ +getUserByID=\ +SELECT \ + "id", "full_name", "password" \ +FROM \ + "users" \ +WHERE \ + "id" = ? + +getUserByCertID=\ +SELECT \ + u."id", u."full_name", u."password" \ +FROM \ + "users" u, "user_certs" uc \ +WHERE \ + u."id" = uc."user_id" AND uc."cert_id" = ? + +getUserCerts=\ +SELECT \ + "data" \ +FROM \ + "user_certs" \ +WHERE \ + "user_id" = ? + +getUserRoles=\ +SELECT \ + "group_id" \ +FROM \ + "group_members" \ +WHERE \ + "user_id" = ? \ No newline at end of file diff --git a/base/est/webapps/est/index.jsp b/base/est/webapps/est/index.jsp new file mode 100644 index 00000000000..79dca917a14 --- /dev/null +++ b/base/est/webapps/est/index.jsp @@ -0,0 +1,25 @@ + + + + Enrollment over Secure Transport + + + + + diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index 642fb019f19..2f3a7605b40 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -36,8 +36,9 @@ sensitive_parameters= acme_database_bind_password acme_database_password acme_issuer_password - acme_realm_bind_password - acme_realm_password + est_realm_bind_password + est_realm_password + est_ca_password pki_instance_name=pki-tomcat pki_http_port=8080 @@ -663,3 +664,27 @@ pki_registry_enable=False # - acme_realm_groups_dn # # See /usr/share/pki/acme/realm//realm.conf +[EST] +pki_ds_setup=False +pki_security_domain_setup=False +pki_registry_enable=False +pki_ca_uri=https://%(pki_hostname)s:%(pki_https_port)s +est_ca_profile=estServiceCert +est_ca_user_name= +est_ca_user_password= +est_ca_user_password_file= +est_ca_user_certificate= +est_realm_type= +est_realm_custom= +est_realm_url= +est_realm_auth_type=BasicAuth +est_realm_bind_dn=cn=Directory Manager +est_realm_bind_password= +est_realm_nickname= +est_realm_user= +est_realm_username= +est_realm_password= +est_realm_users_dn=ou=people,dc=est,dc=pki,dc=example,dc=com +est_realm_groups_dn=ou=groups,dc=est,dc=pki,dc=example,dc=com +est_realm_statements=/usr/share/pki/est/conf/realm/statements.conf +est_authorizer_exec_path=/usr/libexec/estauthz diff --git a/base/server/examples/installation/est.cfg b/base/server/examples/installation/est.cfg new file mode 100644 index 00000000000..3a64da4694e --- /dev/null +++ b/base/server/examples/installation/est.cfg @@ -0,0 +1,9 @@ +[DEFAULT] +pki_server_database_password=Secret.123 + +[EST] +est_realm_type=ds +est_realm_url=ldap://localhost.localdomain:3389 +est_realm_bind_password=Secret.123 +est_ca_user_name=est-ra-1 +est_ca_user_password=Secret.est diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index 84e189915c8..68d9d523587 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -5365,6 +5365,7 @@ def deploy_acme_webapp(self, subsystem): max_wait=self.startup_timeout, timeout=self.request_timeout) + def spawn_acme(self): subsystem = self.create_acme_subsystem() @@ -5376,13 +5377,137 @@ def spawn_acme(self): self.deploy_acme_webapp(subsystem) + def create_est_subsystem(self): + ''' + See also pki-server acme-create. + ''' + + logger.info('Creating EST subsystem') + + subsystem = pki.server.subsystem.ESTSubsystem(self.instance) + subsystem.create() + return subsystem + + def configure_est_backend(self, subsystem): + logger.info('Configuring EST backend') + props = subsystem.get_backend_config(True) + ca_uri = self.mdict.get('pki_ca_uri') + pki.util.set_property(props, 'url', ca_uri) + + profile = self.mdict.get('est_ca_profile') + pki.util.set_property(props, 'profile', profile) + + username = self.mdict.get('est_ca_user_name') + pki.util.set_property(props, 'username', username) + + password = self.mdict.get('est_ca_user_password') + pki.util.set_property(props, 'password', password) + + password_file = self.mdict.get('est_ca_user_password_file') + pki.util.set_property(props, 'passwordFile', password_file) + + nickname = self.mdict.get('est_ca_user_certificate') + pki.util.set_property(props, 'nickname', nickname) + subsystem.update_backend_config(props) + + def configure_est_authorizer(self, subsystem): + + logger.info('Configuring EST authorizer') + props = subsystem.get_authorizer_config(True) + + authorizer_exec = self.mdict.get('est_authorizer_exec_path') + pki.util.set_property(props, 'executable', authorizer_exec) + subsystem.update_authorizer_config(props) + + def configure_est_realm(self, subsystem): + logger.info('Configuring EST realm') + + if self.mdict['est_realm_custom']: + subsystem.replace_realm_config(self.mdict['est_realm_custom']) + return + + realm_type = self.mdict['est_realm_type'] + props = subsystem.get_realm_config(realm_type=realm_type) + + if realm_type == 'in-memory': + + username = self.mdict.get('est_realm_username') + pki.util.set_property(props, 'username', username) + + password = self.mdict.get('est_realm_password') + pki.util.set_property(props, 'password', password) + + elif realm_type == 'ds': + + url = self.mdict.get('est_realm_url') + pki.util.set_property(props, 'url', url) + + auth_type = props.get('authType') + auth_type = self.mdict.get('est_realm_auth_type', auth_type) + pki.util.set_property(props, 'authType', auth_type) + + if auth_type == 'BasicAuth': + bind_dn = self.mdict.get('est_realm_bind_dn') + pki.util.set_property(props, 'bindDN', bind_dn) + + bind_password = self.mdict.get('est_realm_bind_password') + pki.util.set_property(props, 'bindPassword', bind_password) + + elif auth_type == 'SslClientAuth': + nickname = self.mdict.get('est_realm_nickname') + pki.util.set_property(props, 'nickname', nickname) + + users_dn = self.mdict.get('est_realm_users_dn') + pki.util.set_property(props, 'usersDN', users_dn) + + groups_dn = self.mdict.get('est_realm_groups_dn') + pki.util.set_property(props, 'groupsDN', groups_dn) + + elif realm_type == 'postgresql': + + url = self.mdict.get('est_realm_url') + pki.util.set_property(props, 'url', url) + + user = self.mdict.get('est_realm_user') + pki.util.set_property(props, 'user', user) + + password = self.mdict.get('est_realm_password') + pki.util.set_property(props, 'password', password) + + statements = self.mdict.get('est_realm_statements') + pki.util.set_property(props, 'statements', statements) + subsystem.update_realm_config(props) + + + def deploy_est_webapp(self, subsystem): + ''' + See also pki-server est-deploy. + ''' + + logger.info('Deploying EST webapp') if len(self.instance.get_subsystems()) == 1: - # if this is the first subsystem, start the server - self.instance.start( + # if this is the first subsystem, deploy the subsystem without waiting + subsystem.enable() + + else: + # otherwise, deploy the subsystem and wait until it starts + subsystem.enable( wait=True, max_wait=self.startup_timeout, timeout=self.request_timeout) + def spawn_est(self): + + subsystem = self.create_est_subsystem() + self.instance.add_subsystem(subsystem) + + self.configure_est_backend(subsystem) + self.configure_est_authorizer(subsystem) + self.configure_est_realm(subsystem) + + self.deploy_est_webapp(subsystem) + + def spawn(self): print('Installing ' + self.subsystem_type + ' into ' + self.instance.base_dir + '.') @@ -5406,6 +5531,10 @@ def spawn(self): self.spawn_acme() return + if self.subsystem_type == 'EST': + self.spawn_est() + return + scriptlet = pki.server.deployment.scriptlets.subsystem_layout.PkiScriptlet() scriptlet.deployer = self scriptlet.instance = self.instance diff --git a/base/server/python/pki/server/deployment/pkiconfig.py b/base/server/python/pki/server/deployment/pkiconfig.py index 5793401436b..ae6be596fd8 100644 --- a/base/server/python/pki/server/deployment/pkiconfig.py +++ b/base/server/python/pki/server/deployment/pkiconfig.py @@ -36,7 +36,7 @@ PKI_DEPLOYMENT_DEFAULT_UID = 17 PKI_DEPLOYMENT_DEFAULT_USER = "pkiuser" -PKI_SUBSYSTEMS = ['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME'] +PKI_SUBSYSTEMS = ['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME', 'EST'] PKI_BASE_RESERVED_NAMES = ["alias", "bin", "ca", "common", "conf", "kra", "lib", "logs", "ocsp", "temp", "tks", "tps", "webapps", "work"] diff --git a/base/server/python/pki/server/deployment/scriptlets/initialization.py b/base/server/python/pki/server/deployment/scriptlets/initialization.py index 5639b2448ff..4400b1835ab 100644 --- a/base/server/python/pki/server/deployment/scriptlets/initialization.py +++ b/base/server/python/pki/server/deployment/scriptlets/initialization.py @@ -42,7 +42,8 @@ def verify_sensitive_data(self, deployer): # Verify existence of Admin Password (except for Clones) if configuration_file.subsystem != 'ACME' and \ - not configuration_file.clone: + configuration_file.subsystem != 'EST' and not \ + configuration_file.clone: configuration_file.confirm_data_exists('pki_admin_password') # If HSM, verify absence of all PKCS #12 backup parameters @@ -64,7 +65,8 @@ def verify_sensitive_data(self, deployer): configuration_file.confirm_data_exists('pki_client_database_password') # Verify existence of Client PKCS #12 Password for Admin Cert - if configuration_file.subsystem != 'ACME': + if configuration_file.subsystem != 'ACME' and \ + configuration_file.subsystem != 'EST': configuration_file.confirm_data_exists('pki_client_pkcs12_password') if configuration_file.clone: diff --git a/base/server/python/pki/server/pkispawn.py b/base/server/python/pki/server/pkispawn.py index 43c76c38d52..739877d84fa 100644 --- a/base/server/python/pki/server/pkispawn.py +++ b/base/server/python/pki/server/pkispawn.py @@ -190,8 +190,8 @@ def main(argv): parser.indent = 0 deployer.subsystem_type = parser.read_text( - 'Subsystem (CA/KRA/OCSP/TKS/TPS/ACME)', - options=['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME'], + 'Subsystem (CA/KRA/OCSP/TKS/TPS/ACME/EST)', + options=['CA', 'KRA', 'OCSP', 'TKS', 'TPS', 'ACME', 'EST'], default='CA', case_sensitive=False).upper() print() else: @@ -672,6 +672,9 @@ def main(argv): elif deployer.subsystem_type == 'ACME': print_acme_install_information() + elif deployer.subsystem_type == 'EST': + print_est_install_information() + else: print_final_install_information(parser.mdict, deployer.instance) @@ -693,7 +696,8 @@ def validate_user_deployment_cfg(user_deployment_cfg): '[OCSP]', '[TKS]', '[TPS]', - '[ACME]']: + '[ACME]', + '[EST]']: raise Exception('Invalid deployment configuration section: %s' % line) @@ -951,6 +955,15 @@ def print_acme_install_information(): print(log.PKI_SPAWN_INFORMATION_FOOTER) +def print_est_install_information(): + + print(log.PKI_SPAWN_INFORMATION_HEADER) + + print(log.PKI_ACCESS_URL % (deployer.mdict['pki_hostname'], + deployer.mdict['pki_https_port'], + '.well-known/%s'%deployer.subsystem_type.lower())) + print(log.PKI_SPAWN_INFORMATION_FOOTER) + def print_final_install_information(mdict, instance): diff --git a/base/server/python/pki/server/subsystem.py b/base/server/python/pki/server/subsystem.py index dfa5cc28961..fd356e59e3f 100644 --- a/base/server/python/pki/server/subsystem.py +++ b/base/server/python/pki/server/subsystem.py @@ -2857,6 +2857,108 @@ def update_realm_config(self, config): self.instance.store_properties(self.realm_conf, config) +class ESTSubsystem(PKISubsystem): + + def __init__(self, instance): + super().__init__(instance, 'est') + + @property + def backend_conf(self): + return os.path.join(self.conf_dir, 'backend.conf') + + @property + def authorizer_conf(self): + return os.path.join(self.conf_dir, 'authorizer.conf') + + @property + def realm_conf(self): + return os.path.join(self.conf_dir, 'realm.conf') + + def create(self, exist_ok=False, force=False): + + self.instance.makedirs(self.conf_dir, exist_ok=exist_ok) + + default_conf_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'est', 'conf') + + self.instance.copy( + os.path.join(default_conf_dir, 'backend.conf'), + self.backend_conf, + exist_ok=exist_ok, + force=force) + + self.instance.copy( + os.path.join(default_conf_dir, 'authorizer.conf'), + self.authorizer_conf, + exist_ok=exist_ok, + force=force) + + def get_backend_config(self, default=False): + + if default: + backend_conf = os.path.join(pki.server.PKIServer.SHARE_DIR, 'est', 'conf', 'backend.conf') + else: + backend_conf = self.database_conf + + logger.info('Loading %s', backend_conf) + config = {} + pki.util.load_properties(backend_conf, config) + return config + + def update_backend_config(self, config): + + logger.info('Updating %s', self.backend_conf) + self.instance.store_properties(self.backend_conf, config) + + def get_authorizer_config(self, default=False): + + if default: + authorizer_conf = os.path.join(pki.server.PKIServer.SHARE_DIR, 'est', 'conf', 'authorizer.conf') + else: + issuer_conf = self.authorizer_conf + + logger.info('Loading %s', authorizer_conf) + config = {} + pki.util.load_properties(authorizer_conf, config) + + return config + + def update_authorizer_config(self, config): + + logger.info('Updating %s', self.authorizer_conf) + self.instance.store_properties(self.authorizer_conf, config) + + def get_realm_config(self, realm_type=None): + + template_dir = os.path.join(pki.server.PKIServer.SHARE_DIR, 'est', 'conf', 'realm') + + if realm_type: + # if realm type is specified, load the realm.conf template + realm_conf = os.path.join(template_dir, '%s.conf'%realm_type) + else: + # otherwise, load the current realm.conf in the instance + realm_conf = self.realm_conf + + logger.info('Loading %s', realm_conf) + config = {} + pki.util.load_properties(realm_conf, config) + + return config + + def update_realm_config(self, config): + + logger.info('Updating %s', self.realm_conf) + self.instance.store_properties(self.realm_conf, config) + + def replace_realm_config(self, realm_path): + + logger.info('Replace %s', self.realm_conf) + self.instance.copy( + realm_path, + self.realm_conf, + exist_ok=False, + force=True) + + class PKISubsystemFactory(object): @classmethod diff --git a/pki.spec b/pki.spec index e875d88e66a..2029836a1e1 100644 --- a/pki.spec +++ b/pki.spec @@ -1867,6 +1867,7 @@ fi ################################################################################ %{_datadir}/pki/est/ +%{_libexecdir}/estauthz %if %{without maven} %{_datadir}/java/pki/pki-est.jar