From 1e44d21b043d9bc35a36e2f2313150825b3a8968 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Mon, 23 Sep 2024 18:15:27 +0200 Subject: [PATCH 1/3] Remove setup from Postgresql realm authentication The realm should not modify the user database but it has to be provided and configured in advance. However, if a file is provided it is used during the initialisation and not during the authentication. --- base/est/shared/realm/postgresql/create.sql | 23 +++++++++++++++++++ .../realm/{ => postgresql}/statements.conf | 0 .../cms/realm/PKIPostgreSQLRealm.java | 17 ++++++++++---- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 base/est/shared/realm/postgresql/create.sql rename base/est/shared/realm/{ => postgresql}/statements.conf (100%) diff --git a/base/est/shared/realm/postgresql/create.sql b/base/est/shared/realm/postgresql/create.sql new file mode 100644 index 00000000000..c1e20e94bc9 --- /dev/null +++ b/base/est/shared/realm/postgresql/create.sql @@ -0,0 +1,23 @@ +CREATE TABLE "users" ( + "id" VARCHAR PRIMARY KEY, + "full_name" VARCHAR, + "password" VARCHAR +); + +CREATE TABLE "user_certs" ( + "user_id" VARCHAR NOT NULL, + "cert_id" VARCHAR NOT NULL, + "data" BYTEA, + PRIMARY KEY ("user_id", "cert_id") +); + +CREATE TABLE "groups" ( + "id" VARCHAR PRIMARY KEY, + "description" VARCHAR +); + +CREATE TABLE "group_members" ( + "group_id" VARCHAR NOT NULL, + "user_id" VARCHAR NOT NULL, + PRIMARY KEY ("group_id", "user_id") +); diff --git a/base/est/shared/realm/statements.conf b/base/est/shared/realm/postgresql/statements.conf similarity index 100% rename from base/est/shared/realm/statements.conf rename to base/est/shared/realm/postgresql/statements.conf diff --git a/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java b/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java index 379191d8d85..12240856e59 100644 --- a/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java +++ b/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java @@ -119,17 +119,27 @@ public void initInternal () throws LifecycleException { if (saltLength != null) { handler.setSaltLength(Integer.parseInt(saltLength)); } + + String createFile = info.getProperty("dbcreate.file"); + if (createFile != null) { + try{ + connect(); + setup(createFile); + } catch (Exception e) { + throw new LifecycleException("DB creation failed. Creation file: " + createFile, e); + } + } + } /** * This method will create the tables if they do not exist. */ - public void setup() throws Exception { + public void setup(String createFile) throws Exception { logger.info("Setting up PostgreSQL realm"); - String filename = "/usr/share/pki/acme/realm/postgresql/create.sql"; - String content = new String(Files.readAllBytes(Paths.get(filename))); + String content = new String(Files.readAllBytes(Paths.get(createFile))); String[] stats = content.split(";"); for (String sql : stats) { @@ -169,7 +179,6 @@ public void connect() throws Exception { if (connection == null) { // create the initial connection logger.info("Connecting to " + url); connection = DriverManager.getConnection(url, info); - setup(); return; } From d90c532215736746897c584d0c639935a82b645a Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Mon, 23 Sep 2024 18:17:54 +0200 Subject: [PATCH 2/3] Add est test for Postgresql realm Adding a new test to verify the connection between the EST subsystem and a realm user database handled with Postgresql. Additionally, the realm test on separate instance has been modified to authenticate the EST subsystem into the CA using a certificate. --- .../workflows/est-ds-realm-separate-test.yml | 22 +- .../workflows/est-postgresql-realm-test.yml | 451 ++++++++++++++++++ .github/workflows/est-tests.yml | 5 + base/server/etc/default.cfg | 2 +- 4 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/est-postgresql-realm-test.yml diff --git a/.github/workflows/est-ds-realm-separate-test.yml b/.github/workflows/est-ds-realm-separate-test.yml index 6601e1aa727..a6231b5238a 100644 --- a/.github/workflows/est-ds-realm-separate-test.yml +++ b/.github/workflows/est-ds-realm-separate-test.yml @@ -83,7 +83,7 @@ jobs: 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 + docker exec ca pki pkcs12-cert-import sslserver --pkcs12-file $SHARED/est_server.p12 --pkcs12-password Secret.123 - name: Add CA EST user run: | @@ -91,6 +91,24 @@ jobs: 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: Create CA EST user certificate end store top p12 + run: | + docker exec ca pki nss-cert-request --csr estUser.csr \ + --ext /usr/share/pki/server/certs/admin.conf --subject 'UID=estUser' + + docker exec ca pki \ + -n caadmin \ + ca-cert-issue \ + --csr-file estUser.csr \ + --profile caUserCert \ + --output-file estUser.crt + + docker exec ca pki nss-cert-import --cert estUser.crt estUser + + docker exec ca pki -n caadmin ca-user-cert-add est-ra-1 --input estUser.crt $CERT_ID + + docker exec ca pki pkcs12-cert-import estUser --pkcs12-file $SHARED/est_server.p12 --pkcs12-password Secret.123 --append - name: Configure CA est profile run: | @@ -159,6 +177,8 @@ jobs: -s EST \ -D est_realm_url=ldap://estds.example.com:3389 \ -D pki_ca_uri=https://ca.example.com:8443 \ + -D est_ca_user_password= \ + -D est_ca_user_certificate=estUser \ -D pki_server_pkcs12_path=$SHARED/est_server.p12 \ -D pki_server_pkcs12_password=Secret.123 \ -v diff --git a/.github/workflows/est-postgresql-realm-test.yml b/.github/workflows/est-postgresql-realm-test.yml new file mode 100644 index 00000000000..97ef412f87d --- /dev/null +++ b/.github/workflows/est-postgresql-realm-test.yml @@ -0,0 +1,451 @@ +name: EST with postgresql 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 \ + --network=example \ + --network-alias=ds.example.com \ + ds + + - name: Set up PKI container + run: | + tests/bin/runner-init.sh \ + --hostname=pki.example.com \ + --network=example \ + --network-alias=ca.example.com \ + pki + + - 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 pki -n caadmin ca-profile-add \ + --raw /usr/share/pki/ca/profiles/ca/estServiceCert.cfg + docker exec pki pki -n caadmin ca-profile-enable estServiceCert + docker exec pki pki-server restart --wait + + - name: Create postgresql certificates + run: | + docker exec pki pki nss-cert-request \ + --subject "CN=postgresql.example.com" \ + --ext /usr/share/pki/server/certs/sslserver.conf \ + --csr sslserver.csr + + docker exec pki pki \ + -n caadmin \ + ca-cert-issue \ + --profile caServerCert \ + --csr-file sslserver.csr \ + --subject "CN=postgresql.example.com" \ + --output-file sslserver.crt + + docker exec pki pki nss-cert-import \ + --cert sslserver.crt \ + postgresql + + docker exec pki pk12util -o sslserver.p12 -n postgresql -d /root/.dogtag/nssdb -W secret + docker cp pki:sslserver.p12 . + openssl pkcs12 -in sslserver.p12 -nocerts -out sslserver.key -noenc -password pass:secret + openssl pkcs12 -in sslserver.p12 -nokeys -clcerts -out sslserver.crt -password pass:secret + openssl pkcs12 -in sslserver.p12 -nokeys -cacerts -out ca.crt -password pass:secret + + - name: Create postgresql Docker file + run: | + cat > Dockerfile-Postgresql < 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: Check PKI server base dir after installation + 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 alias -> /var/lib/pki/pki-tomcat/conf/alias + lrwxrwxrwx pkiuser pkiuser bin -> /usr/share/tomcat/bin + drwxrwx--- pkiuser pkiuser ca + drwxrwx--- pkiuser pkiuser common + lrwxrwxrwx pkiuser pkiuser conf -> /etc/pki/pki-tomcat + drwxrwx--- pkiuser pkiuser est + lrwxrwxrwx pkiuser pkiuser lib -> /usr/share/pki/server/lib + lrwxrwxrwx pkiuser pkiuser logs -> /var/log/pki/pki-tomcat + drwxrwx--- pkiuser pkiuser temp + drwxr-xr-x pkiuser pkiuser webapps + drwxrwx--- pkiuser pkiuser work + EOF + + diff expected output + + - name: Check PKI server conf dir after installation + 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 installation + 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 EST conf dir + run: | + # check file types, owners, and permissions + docker exec pki ls -l /etc/pki/pki-tomcat/est \ + | 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 + -rw-rw-r-- pkiuser pkiuser CS.cfg + -rw-rw---- pkiuser pkiuser authorizer.conf + -rw-rw---- pkiuser pkiuser backend.conf + -rw-rw-r-- pkiuser pkiuser realm.conf + EOF + + diff expected output + + + - 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 pkidestroy -i pki-tomcat -s EST -v + + - 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: est-postgresql-basic + path: /tmp/artifacts diff --git a/.github/workflows/est-tests.yml b/.github/workflows/est-tests.yml index 142067972c3..f9431ea57c5 100644 --- a/.github/workflows/est-tests.yml +++ b/.github/workflows/est-tests.yml @@ -47,6 +47,11 @@ jobs: needs: build uses: ./.github/workflows/est-ds-realm-test.yml + est-postgresql-realm-test: + name: EST with postgresql realm + needs: build + uses: ./.github/workflows/est-postgresql-realm-test.yml + est-ds-realm-separate-test: name: EST with ds realm on a separate instance needs: build diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index b6631111a17..aa7bb480fe0 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -688,5 +688,5 @@ 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_realm_statements=/usr/share/pki/est/conf/realm/postgresql/statements.conf est_authorizer_exec_path=/usr/share/pki/est/bin/estauthz From 09c403f66b4c380acf9948e7d2d96f0c8a7c1235 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Wed, 25 Sep 2024 17:28:01 +0200 Subject: [PATCH 3/3] Revert DB setup to the connection method Since DB could be not available before the connection the setup is moved back to the connection method. --- .../cms/realm/PKIPostgreSQLRealm.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java b/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java index 12240856e59..ec8a0651eec 100644 --- a/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java +++ b/base/server/src/main/java/com/netscape/cms/realm/PKIPostgreSQLRealm.java @@ -119,26 +119,18 @@ public void initInternal () throws LifecycleException { if (saltLength != null) { handler.setSaltLength(Integer.parseInt(saltLength)); } - - String createFile = info.getProperty("dbcreate.file"); - if (createFile != null) { - try{ - connect(); - setup(createFile); - } catch (Exception e) { - throw new LifecycleException("DB creation failed. Creation file: " + createFile, e); - } - } - } /** * This method will create the tables if they do not exist. */ - public void setup(String createFile) throws Exception { + public void setup() throws Exception { logger.info("Setting up PostgreSQL realm"); - + String createFile = info.getProperty("dbcreate.file"); + if (createFile == null) { + return; + } String content = new String(Files.readAllBytes(Paths.get(createFile))); String[] stats = content.split(";"); @@ -179,6 +171,7 @@ public void connect() throws Exception { if (connection == null) { // create the initial connection logger.info("Connecting to " + url); connection = DriverManager.getConnection(url, info); + setup(); return; }