diff --git a/.github/workflows/ca-sequential-test.yml b/.github/workflows/ca-sequential-test.yml index 740068d1089..985ce44a2d3 100644 --- a/.github/workflows/ca-sequential-test.yml +++ b/.github/workflows/ca-sequential-test.yml @@ -1,4 +1,8 @@ name: CA with Sequential Serial Numbers +# +# This test creates a CA subsystem with sequential serial numbers +# for certs and requests, performs enrollments, and verifies that +# the number ranges are maintained properly in CS.cfg and DS. on: workflow_call @@ -27,54 +31,1144 @@ jobs: - name: Create network run: docker network create example + #################################################################################################### + # Create CA with Sequential Serial Numbers + # + # requests: + # - range: 1 - 10 decimal + # - increment: 10 decimal + # - minimum: 5 decimal + # certs: + # - range: 1 - 10 hex (1 - 16 decimal) + # - increment: 10 hex (16 decimal) + # - minimum: 8 decimal + - name: Set up DS container run: | tests/bin/ds-create.sh \ --image=${{ env.DS_IMAGE }} \ --hostname=ds.example.com \ + --network=example \ + --network-alias=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 + tests/bin/runner-init.sh \ + --hostname=pki.example.com \ + --network=example \ + --network-alias=pki.example.com \ + pki - - name: Install CA + - name: Create 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 \ - -D pki_cert_id_generator=legacy \ -D pki_request_id_generator=legacy \ + -D pki_request_number_range_start=1 \ + -D pki_request_number_range_end=10 \ + -D pki_request_number_range_increment=10 \ + -D pki_request_number_range_minimum=5 \ + -D pki_request_number_range_transfer=5 \ + -D pki_cert_id_generator=legacy \ + -D pki_serial_number_range_start=1 \ + -D pki_serial_number_range_end=10 \ + -D pki_serial_number_range_increment=10 \ + -D pki_serial_number_range_minimum=8 \ + -D pki_serial_number_range_transfer=8 \ -v - - name: Check CA certs and keys + - name: Check requests + run: | + docker exec pki pki-server ca-cert-request-find | tee output + + grep "Request ID:" output | wc -l > actual + + # there should be 6 requests + echo "6" > expected + diff expected actual + + - name: Check certs + run: | + docker exec pki pki-server ca-cert-find | tee output + + grep "Serial Number:" output | wc -l > actual + + # there should be 6 certs + echo "6" > expected + diff expected actual + + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 1 - 10 decimal (total: 10, remaining: 4) + cat > expected << EOF + dbs.beginRequestNumber=1 + dbs.endRequestNumber=10 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 1 - 10 hex (total: 16, remaining: 10) + cat > expected << EOF + dbs.beginSerialNumber=1 + dbs.endSerialNumber=10 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be incremented by 10 decimal to 11 + cat > expected << EOF + nextRange: 11 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # ideally, cert nextRange should be incremented by 10 hex (16 decimal) + # to 11 hex (17 decimal), but currently the attribute is always read + # as decimal in: + # - Repository.getNextRange() + # - SubsystemRangeUpdateCLI.updateSerialNumberRange() + cat > expected << EOF + nextRange: 11 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + # serial number management is disabled so there are no range objects created + diff /dev/null output + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + # serial number management is disabled so there are no range objects created + diff /dev/null output + + #################################################################################################### + # Enable serial number management + # + # Restarting PKI server with serial management enabled will trigger + # a new range allocation for requests since the remaining numbers in + # the current range (i.e. 4) is below the minimum (i.e. 5). + # + # For certs there is no new allocation since the remaining numbers + # in the current range (i.e. 10) is still above the minimum (i.e. 8). + + - name: Enable serial number management + run: | + docker exec pki pki-server ca-config-set dbs.enableSerialManagement true + + # disable serial number update background task + docker exec pki pki-server ca-config-set ca.serialNumberUpdateInterval 0 + + # enable serial number update manual job + docker exec pki pki-server ca-config-set jobsScheduler.enabled true + docker exec pki pki-server ca-config-set jobsScheduler.job.serialNumberUpdate.enabled true + + # restart CA subsystem + docker exec pki pki-server ca-redeploy --wait + + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 1 - 10 decimal (total: 10, remaining: 4) + cat > expected << EOF + dbs.beginRequestNumber=1 + dbs.endRequestNumber=10 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 1 - 10 hex (total: 16, remaining: 10) + cat > expected << EOF + dbs.beginSerialNumber=1 + dbs.endSerialNumber=10 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be incremented by 10 decimal + cat > expected << EOF + nextRange: 21 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # cert nextRange should be the same + cat > expected << EOF + nextRange: 11 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + output \ + | sort > actual + + # new request range should be 11 - 20 decimal (total: 10) + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 20 + host: pki.example.com + EOF + + diff expected actual + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + # there should be no new cert range + diff /dev/null output + + #################################################################################################### + # Enroll 10 certs + # + # This will create 10 requests and 10 certs. For requests, since + # the remaining numbers in the current range is below the minimum, + # it will automatically switch to the new range. + # + # For certs, it will exhaust the current range but not switch to a + # new range. + + - name: Install admin cert + run: | + docker exec pki pki-server cert-export \ + --cert-file ca_signing.crt \ + ca_signing + + 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 + + - name: Enroll 10 certs + run: | + docker exec pki pki \ + nss-cert-request \ + --subject "uid=testuser" \ + --ext /usr/share/pki/tools/examples/certs/testuser.conf \ + --csr testuser.csr + + for i in $(seq 1 10); do + docker exec pki pki \ + -n caadmin \ + ca-cert-issue \ + --profile caUserCert \ + --csr-file testuser.csr \ + --output-file testuser.crt + + docker exec pki openssl x509 -in testuser.crt -serial -noout + done + + - name: Check requests + run: | + docker exec pki pki-server ca-cert-request-find | tee output + + grep "Request ID:" output | wc -l > actual + + # there should be 16 requests + echo "16" > expected + diff expected actual + + - name: Check certs + run: | + docker exec pki pki-server ca-cert-find | tee output + + grep "Serial Number:" output | wc -l > actual + + # there should be 16 certs + echo "16" > expected + diff expected actual + + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 11 - 20 decimal (total: 10, remaining: 4) + cat > expected << EOF + dbs.beginRequestNumber=11 + dbs.endRequestNumber=20 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 1 - 10 hex (total: 16, remaining: 0) + cat > expected << EOF + dbs.beginSerialNumber=1 + dbs.endSerialNumber=10 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be the same + cat > expected << EOF + nextRange: 21 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # cert nextRange should be the same + cat > expected << EOF + nextRange: 11 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + output \ + | sort > actual + + # request range objects should be the same + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 20 + host: pki.example.com + EOF + + diff expected actual + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + # cert range objects should be the same + diff /dev/null output + + #################################################################################################### + # Enroll a cert when range is exhausted + # + # This will create one request but fails to create another cert. + # For some reason requests can switch to a new range automatically, + # but certs cannot. + + - name: Enroll a cert when range is exhausted + run: | + docker exec pki pki \ + -n caadmin \ + ca-cert-issue \ + --profile caUserCert \ + --csr-file testuser.csr \ + --output-file testuser.crt \ + > >(tee stdout) 2> >(tee stderr >&2) || true + + # TODO: fix missing request ID and typo + cat > expected << EOF + PKIException: Server Internal Error: Request was completed with errors. + CA has exausted all available serial numbers + EOF + + diff expected stderr + + - name: Check requests + run: | + docker exec pki pki-server ca-cert-request-find | tee output + + grep "Request ID:" output | wc -l > actual + + # there should be 17 requests + echo "17" > expected + diff expected actual + + - name: Check certs run: | - # check certs - docker exec pki pki-server cert-find + docker exec pki pki-server ca-cert-find | tee output - # check keys - echo "Secret.123" > password.txt - docker cp password.txt pki:password.txt - docker exec pki certutil -K \ - -d /var/lib/pki/pki-tomcat/conf/alias \ - -f password.txt | tee output + grep "Serial Number:" output | wc -l > actual - # there should be no orphaned keys - echo "0" > expected - grep "(orphan)" output | wc -l > actual + # there should be 16 certs + echo "16" > expected diff expected actual - # https://github.com/dogtagpki/pki/wiki/Configuring-CA-with-Random-Serial-Numbers-v3 + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 11 - 20 decimal (total: 10, remaining: 3) + cat > expected << EOF + dbs.beginRequestNumber=11 + dbs.endRequestNumber=20 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 1 - 10 hex (total: 16, remaining: 0) + cat > expected << EOF + dbs.beginSerialNumber=1 + dbs.endSerialNumber=10 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be the same + cat > expected << EOF + nextRange: 21 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # cert nextRange should be the same + cat > expected << EOF + nextRange: 11 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + output \ + | sort > actual + + # request range objects should be the same + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 20 + host: pki.example.com + EOF + + diff expected actual + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + # cert range objects should be the same + diff /dev/null output + + #################################################################################################### + # Update serial numbers + # + # This will allocate new ranges for requests and certs since + # the remaining numbers in their ranges are below the minimum. + + - name: Update serial numbers + run: | + docker exec pki pki -n caadmin ca-job-start serialNumberUpdate + + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 11 - 20 decimal (total: 10, remaining: 3) + cat > expected << EOF + dbs.beginRequestNumber=11 + dbs.endRequestNumber=20 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 1 - 10 hex (total: 16, remaining: 0) + cat > expected << EOF + dbs.beginSerialNumber=1 + dbs.endSerialNumber=10 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be incremented by 10 decimal to 31 decimal + cat > expected << EOF + nextRange: 31 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # cert nextRequest should incremented by 10 hex (16 decimal) to 27 decimal + cat > expected << EOF + nextRange: 27 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + rm -f actual + + for DN in $(sed -n 's/^dn: *\(.*\)$/\1/p' output) + do + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b $DN \ + -s base \ + -o ldif_wrap=no \ + -LLL \ + | grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + | sort >> actual + + echo >> actual + done + + # new request range should be 21 - 30 decimal (total: 10) + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 20 + host: pki.example.com + + SecurePort: 8443 + beginRange: 21 + endRange: 30 + host: pki.example.com + + EOF + + diff expected actual + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + output \ + | sort > actual + + # new request range should be 11 - 26 decimal (total: 16) + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 26 + host: pki.example.com + EOF + + diff expected actual + + #################################################################################################### + # Enroll a cert after updating serial numbers + # + # This should create one request and one cert. For certs, + # it should switch to a new range. For requests, there + # should be no changes. + + - name: Enroll a cert after updating serial numbers + run: | + docker exec pki pki \ + -n caadmin \ + ca-cert-issue \ + --profile caUserCert \ + --csr-file testuser.csr \ + --output-file testuser.crt + + docker exec pki openssl x509 -in testuser.crt -text -noout + + - name: Check requests + run: | + docker exec pki pki-server ca-cert-request-find | tee output + + grep "Request ID:" output | wc -l > actual + + # there should be 18 requests + echo "18" > expected + diff expected actual + + - name: Check certs + run: | + docker exec pki pki-server ca-cert-find | tee output + + grep "Serial Number:" output | wc -l > actual + + # there should be 17 certs + echo "17" > expected + diff expected actual + + - name: Check request range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginRequestNumber \ + -e dbs.endRequestNumber \ + -e dbs.requestCloneTransferNumber \ + -e dbs.requestIncrement \ + -e dbs.requestLowWaterMark \ + | tee actual + + # request range should be 11 - 20 decimal (total: 10, remaining: 2) + cat > expected << EOF + dbs.beginRequestNumber=11 + dbs.endRequestNumber=20 + dbs.requestCloneTransferNumber=5 + dbs.requestIncrement=10 + dbs.requestLowWaterMark=5 + EOF + + diff expected actual + + - name: Check cert range config + run: | + docker exec pki pki-server ca-config-find \ + | grep \ + -e dbs.beginSerialNumber \ + -e dbs.endSerialNumber \ + -e dbs.serialCloneTransferNumber \ + -e dbs.serialIncrement \ + -e dbs.serialLowWaterMark \ + | tee actual + + # cert range should be 11 - 20 hex (total: 16, remaining: 15) + cat > expected << EOF + dbs.beginSerialNumber=11 + dbs.endSerialNumber=20 + dbs.serialCloneTransferNumber=8 + dbs.serialIncrement=10 + dbs.serialLowWaterMark=8 + EOF + + diff expected actual + + - name: Check request repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=ca,ou=requests,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # request nextRange should be the same + cat > expected << EOF + nextRange: 31 + serialno: 010 + EOF + + diff expected actual + + - name: Check cert repository + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com \ + -s base \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e serialno: \ + -e nextRange: \ + output \ + | sort > actual + + # cert nextRange should be the same + cat > expected << EOF + nextRange: 27 + serialno: 011 + EOF + + diff expected actual + + - name: Check request range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=requests,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + rm -f actual + + for DN in $(sed -n 's/^dn: *\(.*\)$/\1/p' output) + do + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b $DN \ + -s base \ + -o ldif_wrap=no \ + -LLL \ + | grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + | sort >> actual + + echo >> actual + done + + # request range objects should be the same + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 20 + host: pki.example.com + + SecurePort: 8443 + beginRange: 21 + endRange: 30 + host: pki.example.com + + EOF + + diff expected actual + + - name: Check cert range objects + run: | + docker exec ds ldapsearch \ + -H ldap://ds.example.com:3389 \ + -D "cn=Directory Manager" \ + -w Secret.123 \ + -b ou=certificateRepository,ou=ranges,dc=ca,dc=pki,dc=example,dc=com \ + -s one \ + -o ldif_wrap=no \ + -LLL | tee output + + grep \ + -e SecurePort: \ + -e beginRange: \ + -e endRange: \ + -e host: \ + output \ + | sort > actual + + # cert range objects should be the same + cat > expected << EOF + SecurePort: 8443 + beginRange: 11 + endRange: 26 + host: pki.example.com + EOF + + diff expected actual + + #################################################################################################### + # Enroll a cert with RSNv3 + # + # This should create a request and a cert. The cert + # should be issued with a non-sequential serial number. + - name: Switch to RSNv3 run: | # switch cert request ID generator to RSNv3 @@ -100,29 +1194,47 @@ jobs: # restart CA subsystem docker exec pki pki-server ca-redeploy --wait - - name: Run PKI healthcheck - run: docker exec pki pki-healthcheck --failures-only - - - name: Initialize PKI client + - name: Enroll a cert with RSNv3 run: | - docker exec pki pki-server cert-export ca_signing --cert-file ca_signing.crt + docker exec pki pki \ + -n caadmin \ + ca-cert-issue \ + --profile caUserCert \ + --csr-file testuser.csr \ + --output-file testuser.crt - docker exec pki pki nss-cert-import \ - --cert ca_signing.crt \ - --trust CT,C,C \ - ca_signing + docker exec pki openssl x509 -in testuser.crt -serial -noout | tee output - docker exec pki pki pkcs12-import \ - --pkcs12 /root/.dogtag/pki-tomcat/ca_admin_cert.p12 \ - --pkcs12-password Secret.123 + # serial number should not be 12 hex (18 decimal) + echo "serial=12" >> expected + + rc=0 + diff expected output || rc=$? - - name: Check cert requests in CA + [ $rc -ne 0 ] + + - name: Check requests run: | - docker exec pki pki -n caadmin ca-cert-request-find + docker exec pki pki-server ca-cert-request-find | tee output + + grep "Request ID:" output | wc -l > actual - - name: Check certs in CA + # there should be 19 requests + echo "19" > expected + diff expected actual + + - name: Check certs run: | - docker exec pki pki ca-cert-find + docker exec pki pki-server ca-cert-find | tee output + + grep "Serial Number:" output | wc -l > actual + + # there should be 18 certs + echo "18" > expected + diff expected actual + + #################################################################################################### + # Cleanup - name: Remove CA run: docker exec pki pkidestroy -s CA -v @@ -142,21 +1254,12 @@ jobs: run: | docker exec pki journalctl -x --no-pager -u pki-tomcatd@pki-tomcat.service - - name: Check CA debug log + - name: Check PKI server access log if: always() run: | - docker exec pki find /var/lib/pki/pki-tomcat/logs/ca -name "debug.*" -exec cat {} \; + docker exec pki find /var/log/pki/pki-tomcat -name "localhost_access_log.*" -exec cat {} \; - - name: Gather artifacts + - name: Check CA debug log 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-sequential - path: /tmp/artifacts + docker exec pki find /var/lib/pki/pki-tomcat/logs/ca -name "debug.*" -exec cat {} \; diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg index 0a33b7558f8..bfb798c0939 100644 --- a/base/ca/shared/conf/CS.cfg +++ b/base/ca/shared/conf/CS.cfg @@ -705,6 +705,7 @@ jobsScheduler.impl.RenewalNotificationJob.class=com.netscape.cms.jobs.RenewalNot jobsScheduler.impl.RequestInQueueJob.class=com.netscape.cms.jobs.RequestInQueueJob jobsScheduler.impl.UnpublishExpiredJob.class=com.netscape.cms.jobs.UnpublishExpiredJob jobsScheduler.impl.PruningJob.class=org.dogtagpki.server.ca.job.PruningJob +jobsScheduler.impl.SerialNumberUpdateJob.class=org.dogtagpki.server.ca.job.SerialNumberUpdateJob jobsScheduler.job.certRenewalNotifier.cron=0 3 * * 1-5 jobsScheduler.job.certRenewalNotifier.emailSubject=Certificate Renewal Notification jobsScheduler.job.certRenewalNotifier.emailTemplate=[pki_instance_path]/ca/emails/rnJob1.txt @@ -748,6 +749,8 @@ jobsScheduler.job.unpublishExpiredCerts.summary.recipientEmail= jobsScheduler.job.unpublishExpiredCerts.summary.senderEmail= jobsScheduler.job.pruning.enabled=false jobsScheduler.job.pruning.pluginName=PruningJob +jobsScheduler.job.serialNumberUpdate.enabled=false +jobsScheduler.job.serialNumberUpdate.pluginName=SerialNumberUpdateJob jss._000=## jss._001=## JSS jss._002=## diff --git a/base/ca/src/main/java/org/dogtagpki/server/ca/job/SerialNumberUpdateJob.java b/base/ca/src/main/java/org/dogtagpki/server/ca/job/SerialNumberUpdateJob.java new file mode 100644 index 00000000000..4a2beb85878 --- /dev/null +++ b/base/ca/src/main/java/org/dogtagpki/server/ca/job/SerialNumberUpdateJob.java @@ -0,0 +1,46 @@ +// +// Copyright Red Hat, Inc. +// +// SPDX-License-Identifier: GPL-2.0-or-later +// +package org.dogtagpki.server.ca.job; + +import java.util.Calendar; +import java.util.Date; + +import org.dogtagpki.server.ca.CAEngine; + +import com.netscape.certsrv.base.IExtendedPluginInfo; +import com.netscape.cms.jobs.Job; + +public class SerialNumberUpdateJob extends Job implements IExtendedPluginInfo { + + public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SerialNumberUpdateJob.class); + + public SerialNumberUpdateJob() { + } + + @Override + public String[] getConfigParams() { + return null; + } + + @Override + public String[] getExtendedPluginInfo() { + return null; + } + + @Override + public void run() { + Calendar calendar = Calendar.getInstance(); + Date time = calendar.getTime(); + logger.info("SerialNumberUpdateJob: Running " + mId + " job at " + time); + + try { + CAEngine engine = (CAEngine) super.engine; + engine.updateSerialNumbers(); + } catch (Exception e) { + logger.warn("SerialNumberUpdateJob: " + e.getMessage(), e); + } + } +} diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg index fa2e8928865..f73cb60fd69 100644 --- a/base/server/etc/default.cfg +++ b/base/server/etc/default.cfg @@ -351,8 +351,16 @@ pki_default_ocsp_uri= pki_serial_number_range_start= pki_serial_number_range_end= +pki_serial_number_range_increment= +pki_serial_number_range_minimum= +pki_serial_number_range_transfer= + pki_request_number_range_start= pki_request_number_range_end= +pki_request_number_range_increment= +pki_request_number_range_minimum= +pki_request_number_range_transfer= + pki_replica_number_range_start= pki_replica_number_range_end= diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py index af2ae196020..2929ad4e4da 100644 --- a/base/server/python/pki/server/deployment/__init__.py +++ b/base/server/python/pki/server/deployment/__init__.py @@ -1180,11 +1180,11 @@ def configure_ca(self, subsystem): subsystem.set_config('dbs.request.id.length', self.mdict['pki_request_id_length']) else: # legacy - subsystem.set_config('dbs.beginRequestNumber', '1') - subsystem.set_config('dbs.endRequestNumber', '10000000') - subsystem.set_config('dbs.requestIncrement', '10000000') - subsystem.set_config('dbs.requestLowWaterMark', '2000000') - subsystem.set_config('dbs.requestCloneTransferNumber', '10000') + subsystem.set_config('dbs.beginRequestNumber', '1') # decimal + subsystem.set_config('dbs.endRequestNumber', '10000000') # decimal + subsystem.set_config('dbs.requestIncrement', '10000000') # decimal + subsystem.set_config('dbs.requestLowWaterMark', '2000000') # decimal + subsystem.set_config('dbs.requestCloneTransferNumber', '10000') # decimal subsystem.set_config('dbs.requestRangeDN', 'ou=requests,ou=ranges') request_number_range_start = self.mdict.get('pki_request_number_range_start') @@ -1195,6 +1195,18 @@ def configure_ca(self, subsystem): if request_number_range_end: subsystem.set_config('dbs.endRequestNumber', request_number_range_end) + request_increment = self.mdict.get('pki_request_number_range_increment') + if request_increment: + subsystem.set_config('dbs.requestIncrement', request_increment) + + request_minimum = self.mdict.get('pki_request_number_range_minimum') + if request_minimum: + subsystem.set_config('dbs.requestLowWaterMark', request_minimum) + + request_transfer = self.mdict.get('pki_request_number_range_transfer') + if request_transfer: + subsystem.set_config('dbs.requestCloneTransferNumber', request_transfer) + cert_id_generator = self.mdict['pki_cert_id_generator'] if cert_id_generator == 'random': @@ -1202,12 +1214,13 @@ def configure_ca(self, subsystem): subsystem.set_config('dbs.cert.id.length', self.mdict['pki_cert_id_length']) else: # legacy - subsystem.set_config('dbs.beginSerialNumber', '1') - subsystem.set_config('dbs.endSerialNumber', '10000000') - subsystem.set_config('dbs.serialIncrement', '10000000') - subsystem.set_config('dbs.serialLowWaterMark', '2000000') - subsystem.set_config('dbs.serialCloneTransferNumber', '10000') + subsystem.set_config('dbs.beginSerialNumber', '1') # hex + subsystem.set_config('dbs.endSerialNumber', '10000000') # hex + subsystem.set_config('dbs.serialIncrement', '10000000') # hex + subsystem.set_config('dbs.serialLowWaterMark', '2000000') # hex + subsystem.set_config('dbs.serialCloneTransferNumber', '10000') # hex subsystem.set_config('dbs.serialRangeDN', 'ou=certificateRepository,ou=ranges') + if config.str2bool(self.mdict['pki_random_serial_numbers_enable']): subsystem.set_config('dbs.enableRandomSerialNumbers', 'true') subsystem.set_config('dbs.randomSerialNumberCounter', '0') @@ -1220,6 +1233,18 @@ def configure_ca(self, subsystem): if serial_number_range_end: subsystem.set_config('dbs.endSerialNumber', serial_number_range_end) + serial_increment = self.mdict.get('pki_serial_number_range_increment') + if serial_increment: + subsystem.set_config('dbs.serialIncrement', serial_increment) + + serial_minimum = self.mdict.get('pki_serial_number_range_minimum') + if serial_minimum: + subsystem.set_config('dbs.serialLowWaterMark', serial_minimum) + + serial_transfer = self.mdict.get('pki_serial_number_range_transfer') + if serial_transfer: + subsystem.set_config('dbs.serialCloneTransferNumber', serial_transfer) + replica_number_range_start = self.mdict.get('pki_replica_number_range_start') if replica_number_range_start: subsystem.set_config('dbs.beginReplicaNumber', replica_number_range_start) @@ -1244,11 +1269,11 @@ def configure_kra(self, subsystem): subsystem.set_config('dbs.request.id.length', self.mdict['pki_request_id_length']) else: # legacy - subsystem.set_config('dbs.beginRequestNumber', '1') - subsystem.set_config('dbs.endRequestNumber', '10000000') - subsystem.set_config('dbs.requestIncrement', '10000000') - subsystem.set_config('dbs.requestLowWaterMark', '2000000') - subsystem.set_config('dbs.requestCloneTransferNumber', '10000') + subsystem.set_config('dbs.beginRequestNumber', '1') # decimal + subsystem.set_config('dbs.endRequestNumber', '10000000') # decimal + subsystem.set_config('dbs.requestIncrement', '10000000') # decimal + subsystem.set_config('dbs.requestLowWaterMark', '2000000') # decimal + subsystem.set_config('dbs.requestCloneTransferNumber', '10000') # decimal subsystem.set_config('dbs.requestRangeDN', 'ou=requests,ou=ranges') key_id_generator = self.mdict['pki_key_id_generator'] @@ -1258,11 +1283,11 @@ def configure_kra(self, subsystem): subsystem.set_config('dbs.key.id.length', self.mdict['pki_key_id_length']) else: # legacy - subsystem.set_config('dbs.beginSerialNumber', '1') - subsystem.set_config('dbs.endSerialNumber', '10000000') - subsystem.set_config('dbs.serialIncrement', '10000000') - subsystem.set_config('dbs.serialLowWaterMark', '2000000') - subsystem.set_config('dbs.serialCloneTransferNumber', '10000') + subsystem.set_config('dbs.beginSerialNumber', '1') # hex + subsystem.set_config('dbs.endSerialNumber', '10000000') # hex + subsystem.set_config('dbs.serialIncrement', '10000000') # hex + subsystem.set_config('dbs.serialLowWaterMark', '2000000') # hex + subsystem.set_config('dbs.serialCloneTransferNumber', '10000') # hex subsystem.set_config('dbs.serialRangeDN', 'ou=keyRepository,ou=ranges') if config.str2bool(self.mdict['pki_kra_ephemeral_requests']): diff --git a/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java b/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java index 7e837d815a2..90b905b6dce 100644 --- a/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java +++ b/base/server/src/main/java/com/netscape/cmscore/dbs/Repository.java @@ -539,9 +539,16 @@ public String getNextRange() throws EBaseException { } String nextRange = attr.getStringValues().nextElement(); + + // parse nextRange as decimal BigInteger nextRangeNo = new BigInteger(nextRange); + BigInteger newNextRangeNo = nextRangeNo.add(mIncrementNo); + + // generate new nextRange in decimal String newNextRange = newNextRangeNo.toString(); + + // generate endRange in decimal String endRange = newNextRangeNo.subtract(BigInteger.ONE).toString(); logger.info("Repository: Updating " + DBSubsystem.PROP_NEXT_RANGE + " from " + nextRange + " to " + newNextRange); @@ -563,8 +570,13 @@ public String getNextRange() throws EBaseException { LDAPAttributeSet attrs = new LDAPAttributeSet(); attrs.add(new LDAPAttribute("objectClass", "top")); attrs.add(new LDAPAttribute("objectClass", "pkiRange")); + + // store beginRange as decimal attrs.add(new LDAPAttribute("beginRange", nextRange)); + + // store endRange as decimal attrs.add(new LDAPAttribute("endRange", endRange)); + attrs.add(new LDAPAttribute("cn", nextRange)); attrs.add(new LDAPAttribute("host", cs.getHostname())); attrs.add(new LDAPAttribute("securePort", engine.getEESSLPort())); diff --git a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java index 8268986378c..90ce9ca8ab9 100644 --- a/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java +++ b/base/server/src/main/java/org/dogtagpki/server/cli/SubsystemRangeUpdateCLI.java @@ -109,11 +109,18 @@ public void updateSerialNumberRange( LdapBoundConnection conn = new LdapBoundConnection(socketFactory, connInfo, authInfo); try { + // parse the end of current cert range as decimal + // NOTE: this is a bug, cert range is stored as hex in CS.cfg BigInteger endSerialNumber = new BigInteger(dbConfig.getEndSerialNumber()); - BigInteger nextSerialNumber = endSerialNumber.add(BigInteger.ONE); + + // generate nextRange in decimal + String nextSerialNumber = endSerialNumber.add(BigInteger.ONE).toString(); String serialDN = dbConfig.getSerialDN() + "," + baseDN; - LDAPAttribute attrSerialNextRange = new LDAPAttribute("nextRange", nextSerialNumber.toString()); + + // store nextRange as decimal + LDAPAttribute attrSerialNextRange = new LDAPAttribute("nextRange", nextSerialNumber); + LDAPModification serialmod = new LDAPModification(LDAPModification.REPLACE, attrSerialNextRange); conn.modify(serialDN, serialmod); @@ -145,11 +152,17 @@ public void updateRequestNumberRange( try { logger.info("Updating request ID range"); + // parse the end of current range as decimal BigInteger endRequestNumber = new BigInteger(dbConfig.getEndRequestNumber()); - BigInteger nextRequestNumber = endRequestNumber.add(BigInteger.ONE); + + // generate nextRange in decimal + String nextRequestNumber = endRequestNumber.add(BigInteger.ONE).toString(); String requestDN = dbConfig.getRequestDN() + "," + baseDN; - LDAPAttribute attrRequestNextRange = new LDAPAttribute("nextRange", nextRequestNumber.toString()); + + // store nextRange as decimal + LDAPAttribute attrRequestNextRange = new LDAPAttribute("nextRange", nextRequestNumber); + LDAPModification requestmod = new LDAPModification(LDAPModification.REPLACE, attrRequestNextRange); conn.modify(requestDN, requestmod); diff --git a/base/server/upgrade/11.6.0/02-AddSerialNumberUpdateJob.py b/base/server/upgrade/11.6.0/02-AddSerialNumberUpdateJob.py new file mode 100644 index 00000000000..c9aa990316a --- /dev/null +++ b/base/server/upgrade/11.6.0/02-AddSerialNumberUpdateJob.py @@ -0,0 +1,36 @@ +# +# Copyright Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +import pki.server.upgrade + + +class AddSerialNumberUpdateJob(pki.server.upgrade.PKIServerUpgradeScriptlet): + + def __init__(self): + super().__init__() + self.message = 'Add SerialNumberUpdateJob' + + def upgrade_subsystem(self, instance, subsystem): + + if subsystem.name != 'ca': + return + + self.backup(subsystem.cs_conf) + + class_name = subsystem.config.get('jobsScheduler.impl.SerialNumberUpdateJob.class') + if class_name is None: + subsystem.config['jobsScheduler.impl.SerialNumberUpdateJob.class'] = \ + 'org.dogtagpki.server.ca.job.SerialNumberUpdateJob' + + enabled = subsystem.config.get('jobsScheduler.job.serialNumberUpdate.enabled') + if enabled is None: + subsystem.config['jobsScheduler.job.serialNumberUpdate.enabled'] = 'false' + + plugin_name = subsystem.config.get('jobsScheduler.job.serialNumberUpdate.pluginName') + if plugin_name is None: + subsystem.config['jobsScheduler.job.serialNumberUpdate.pluginName'] = \ + 'SerialNumberUpdateJob' + + subsystem.save()