diff --git a/.github/workflows/run-tests-staging-acmedns.yml b/.github/workflows/run-tests-staging-acmedns.yml index 17e6810..2b023e3 100644 --- a/.github/workflows/run-tests-staging-acmedns.yml +++ b/.github/workflows/run-tests-staging-acmedns.yml @@ -14,8 +14,10 @@ on: env: - DYNU_API_KEY: ${{ secrets.DYNU_API_KEY == '' && '65cXefd35XbYf36546eg5dYcZT6X52Y2' || secrets.DYNU_API_KEY }} - + DYNU_API_KEY: ${{ secrets.DYNU_API_KEY }} + ACMEDNS_API_KEY: ${{ secrets.ACMEDNS_API_KEY }} + ACMEDNS_API_USER: ${{ secrets.ACMEDNS_API_USER }} + ACMEDNS_SUBDOMAIN: ${{ secrets.ACMEDNS_SUBDOMAIN }} jobs: test-ubuntu-acmedns: runs-on: ubuntu-latest diff --git a/.github/workflows/run-tests-staging-dynu.yml b/.github/workflows/run-tests-staging-dynu.yml index 7d935c5..989b12a 100644 --- a/.github/workflows/run-tests-staging-dynu.yml +++ b/.github/workflows/run-tests-staging-dynu.yml @@ -2,7 +2,7 @@ name: Run tests against Staging server using Dynu on: workflow_dispatch env: - DYNU_API_KEY: ${{ secrets.DYNU_API_KEY == '' && '65cXefd35XbYf36546eg5dYcZT6X52Y2' || secrets.DYNU_API_KEY }} + DYNU_API_KEY: ${{ secrets.DYNU_API_KEY }} jobs: test-centos7-dynu: runs-on: ubuntu-latest diff --git a/dns_scripts/dns_add_acmedns b/dns_scripts/dns_add_acmedns index b869ed5..eae8ecc 100755 --- a/dns_scripts/dns_add_acmedns +++ b/dns_scripts/dns_add_acmedns @@ -1,10 +1,5 @@ #!/usr/bin/env bash -. "$(dirname "${BASH_SOURCE}")/../common.shrc" || { - echo "Unable to load shared Bash code" - exit 1 -} >&2 - # ACMEDNS env variables can be set in a config file at domain level acme_config="$DOMAIN_DIR/acme-dns.cfg" [ -s "$acme_config" ] && . "$acme_config" @@ -37,29 +32,13 @@ if [[ -z "$token" ]]; then exit 1 fi -curl_params=( - -H "accept: application/json" - -H "X-Api-Key: $apikey" - -H "X-Api-User: $apiuser" - -H 'Content-Type: application/json' -) - -generate_post_data() -{ - cat </dev/null || { - echo 'Error: DNS challenge not added: unknown error' - exit 1 -} >&2 -exit 0 +curl --fail --silent -X POST "${API}" \ + -H "accept: application/json" \ + -H "X-Api-Key: $apikey" \ + -H "X-Api-User: $apiuser" \ + -H 'Content-Type: application/json' \ + -d '{ + "subdomain": "'"${apisubdomain}"'", + "txt": "'"$token"'" + }' + diff --git a/docker-compose.yml b/docker-compose.yml index a201b17..7740132 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: ports: - 14000:14000 # HTTPS ACME API - 15000:15000 # HTTPS Management API + volumes: + - ./pebble-certificates:/test/certs/localhost networks: acmenet: ipv4_address: 10.30.50.2 diff --git a/pebble-certificates/cert.pem b/pebble-certificates/cert.pem new file mode 100644 index 0000000..d81d29e --- /dev/null +++ b/pebble-certificates/cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgIILDt8c2fMw2IwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgNTM0NWU2MB4XDTI1MDkwMzIzNDAwNVoXDTI3MTAw +MzIzNDAwNVowFDESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAmxTFtw113RK70H9pQmdKs9AxhFmnQ6BdDtp3jOZlWlUO +0BltMXOUML5905etgtCbcC6RdKRtgSAiDfgx3VWiFMJH++4gUtnaB9SN8GhNSPBp +FfSa2JhWPo9HQNUsAZqlGTV4SzcGRqtWvdZxUiOfQ2TcvyXIqsaD19ivvqI1NhT6 +bl3tredTZlzLLM6Wvkw6hfyHrJAPQP8LOlCIeDM4YIce6Gstv6qo9iCD4wJiY4u9 +5HVL7RK8t8JpZAb7VR+dPhbHEvVpjwuYd5Q05OZ280gFyrhbrKLbqst104GOQT4k +QMJGWxGONyTX6np0Dx6O5jU7dvYvjVVawbJwGuaL6wIDAQABo3oweDAOBgNVHQ8B +Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAfBgNV +HSMEGDAWgBSu8RGpErgYUoYnQuwCq+/ggTiEjDAiBgNVHREEGzAZgglsb2NhbGhv +c3SCBnBlYmJsZYcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAAB0gkekXCNOwqWmY +vQ2lLJ8Zk2WzQ9B+VOC27IgxEEuskZyCpyXAbJB9sCGQWZhAARyaI4SPRGGagcug +d1SwDWdPGeSJzF3aDnXDYoP9Zw2KqiqVZTngeoiw8Yn0F8PNriANwRLybouX7mMc +4V7T5+2k4SUs7pFH4KO0a0XBCcjXDjdKuBljftRTXCHzJzfRtmieCCuZlpnp5sHx +hKa/uxKGyyZB+4Y3MrzsiQSCBOr9G4TH9RofmNcawl+tsVe08zLV/XVhrbakKEs7 +Y7MGHSj3BkPFF32NObc0znqWzTaUD9hU+rXWGANM4sXd4dagdnxfrb7i0WYhcUFj +9Try8Q== +-----END CERTIFICATE----- diff --git a/pebble-certificates/key.pem b/pebble-certificates/key.pem new file mode 100644 index 0000000..66be6da --- /dev/null +++ b/pebble-certificates/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAmxTFtw113RK70H9pQmdKs9AxhFmnQ6BdDtp3jOZlWlUO0Blt +MXOUML5905etgtCbcC6RdKRtgSAiDfgx3VWiFMJH++4gUtnaB9SN8GhNSPBpFfSa +2JhWPo9HQNUsAZqlGTV4SzcGRqtWvdZxUiOfQ2TcvyXIqsaD19ivvqI1NhT6bl3t +redTZlzLLM6Wvkw6hfyHrJAPQP8LOlCIeDM4YIce6Gstv6qo9iCD4wJiY4u95HVL +7RK8t8JpZAb7VR+dPhbHEvVpjwuYd5Q05OZ280gFyrhbrKLbqst104GOQT4kQMJG +WxGONyTX6np0Dx6O5jU7dvYvjVVawbJwGuaL6wIDAQABAoIBAGW9W/S6lO+DIcoo +PHL+9sg+tq2gb5ZzN3nOI45BfI6lrMEjXTqLG9ZasovFP2TJ3J/dPTnrwZdr8Et/ +357YViwORVFnKLeSCnMGpFPq6YEHj7mCrq+YSURjlRhYgbVPsi52oMOfhrOIJrEG +ZXPAwPRi0Ftqu1omQEqz8qA7JHOkjB2p0i2Xc/uOSJccCmUDMlksRYz8zFe8wHuD +XvUL2k23n2pBZ6wiez6Xjr0wUQ4ESI02x7PmYgA3aqF2Q6ECDwHhjVeQmAuypMF6 +IaTjIJkWdZCW96pPaK1t+5nTNZ+Mg7tpJ/PRE4BkJvqcfHEOOl6wAE8gSk5uVApY +ZRKGmGkCgYEAzF9iRXYo7A/UphL11bR0gqxB6qnQl54iLhqS/E6CVNcmwJ2d9pF8 +5HTfSo1/lOXT3hGV8gizN2S5RmWBrc9HBZ+dNrVo7FYeeBiHu+opbX1X/C1HC0m1 +wJNsyoXeqD1OFc1WbDpHz5iv4IOXzYdOdKiYEcTv5JkqE7jomqBLQk8CgYEAwkG/ +rnwr4ThUo/DG5oH+l0LVnHkrJY+BUSI33g3eQ3eM0MSbfJXGT7snh5puJW0oXP7Z +Gw88nK3Vnz2nTPesiwtO2OkUVgrIgWryIvKHaqrYnapZHuM+io30jbZOVaVTMR9c +X/7/d5/evwXuP7p2DIdZKQKKFgROm1XnhNqVgaUCgYBD/ogHbCR5RVsOVciMbRlG +UGEt3YmUp/vfMuAsKUKbT2mJM+dWHVlb+LZBa4pC06QFgfxNJi/aAhzSGvtmBEww +xsXbaceauZwxgJfIIUPfNZCMSdQVIVTi2Smcx6UofBz6i/Jw14MEwlvhamaa7qVf +kqflYYwelga1wRNCPopLaQKBgQCWsZqZKQqBNMm0Q9yIhN+TR+2d7QFjqeePoRPl +1qxNejhq25ojE607vNv1ff9kWUGuoqSZMUC76r6FQba/JoNbefI4otd7x/GzM9uS +8MHMJazU4okwROkHYwgLxxkNp6rZuJJYheB4VDTfyyH/ng5lubmY7rdgTQcNyZ5I +majRYQKBgAMKJ3RlII0qvAfNFZr4Y2bNIq+60Z+Qu2W5xokIHCFNly3W1XDDKGFe +CCPHSvQljinke3P9gPt2HVdXxcnku9VkTti+JygxuLkVg7E0/SWwrWfGsaMJs+84 +fK+mTZay2d3v24r9WKEKwLykngYPyZw5+BdWU0E+xx5lGUd3U4gG +-----END RSA PRIVATE KEY----- diff --git a/pebble.minica.pem b/pebble.minica.pem deleted file mode 100644 index a69a4c4..0000000 --- a/pebble.minica.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDCTCCAfGgAwIBAgIIJOLbes8sTr4wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE -AxMVbWluaWNhIHJvb3QgY2EgMjRlMmRiMCAXDTE3MTIwNjE5NDIxMFoYDzIxMTcx -MjA2MTk0MjEwWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAyNGUyZGIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5WgZNoVJandj43kkLyU50vzCZ -alozvdRo3OFiKoDtmqKPNWRNO2hC9AUNxTDJco51Yc42u/WV3fPbbhSznTiOOVtn -Ajm6iq4I5nZYltGGZetGDOQWr78y2gWY+SG078MuOO2hyDIiKtVc3xiXYA+8Hluu -9F8KbqSS1h55yxZ9b87eKR+B0zu2ahzBCIHKmKWgc6N13l7aDxxY3D6uq8gtJRU0 -toumyLbdzGcupVvjbjDP11nl07RESDWBLG1/g3ktJvqIa4BWgU2HMh4rND6y8OD3 -Hy3H8MY6CElL+MOCbFJjWqhtOxeFyZZV9q3kYnk9CAuQJKMEGuN4GU6tzhW1AgMB -AAGjRTBDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB -BQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAF85v -d40HK1ouDAtWeO1PbnWfGEmC5Xa478s9ddOd9Clvp2McYzNlAFfM7kdcj6xeiNhF -WPIfaGAi/QdURSL/6C1KsVDqlFBlTs9zYfh2g0UXGvJtj1maeih7zxFLvet+fqll -xseM4P9EVJaQxwuK/F78YBt0tCNfivC6JNZMgxKF59h0FBpH70ytUSHXdz7FKwix -Mfn3qEb9BXSk0Q3prNV5sOV3vgjEtB4THfDxSz9z3+DepVnW3vbbqwEbkXdk3j82 -2muVldgOUgTwK8eT+XdofVdntzU/kzygSAtAQwLJfn51fS1GvEcYGBc1bDryIqmF -p9BI7gVKtWSZYegicA== ------END CERTIFICATE----- diff --git a/test/36-full-chain-inc-root.bats b/test/36-full-chain-inc-root.bats index 6f06190..e488f4f 100644 --- a/test/36-full-chain-inc-root.bats +++ b/test/36-full-chain-inc-root.bats @@ -32,7 +32,8 @@ EOF check_output_for_errors if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="(STAGING) Doctored Durian Root CA X3" + # https://letsencrypt.org/docs/staging-environment/ + PREFERRED_CHAIN="(STAGING) Pretend Pear X1" else # pebble doesn't support CA Issuers so the fullchain.crt will just contain the certificate (code path means it won't contain the intermediate cert in this case) # This is testing that requesting FULL_CHAIN_INCLUDE_ROOT doesn't fail if there is no CA Issuers in the certificate @@ -76,7 +77,7 @@ EOF assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="(STAGING) Doctored Durian Root CA X3" + PREFERRED_CHAIN="(STAGING) Pretend Pear X1" else # pebble doesn't support CA Issuers so the fullchain.crt will just contain the certificate (code path means it won't contain the intermediate cert in this case) # This is testing that requesting FULL_CHAIN_INCLUDE_ROOT doesn't fail if there is no CA Issuers in the certificate diff --git a/test/Dockerfile-ubuntu-acmedns b/test/Dockerfile-ubuntu-acmedns index 55241a8..d8adbb4 100644 --- a/test/Dockerfile-ubuntu-acmedns +++ b/test/Dockerfile-ubuntu-acmedns @@ -7,10 +7,6 @@ ENV DEBIAN_FRONTEND=noninteractive # Ensure tests in this image use the staging server ENV staging="true" -# 2016ENV dynamic_dns "acme-dns" -ENV ACMEDNS_API_USER=49ac5f6d-74cd-4aca-acfe-f9457af7894c -ENV ACMEDNS_API_KEY=2NPGF8cH7PeTrHZWXImi1prhTsQGz2pdCC7Za5zE -ENV ACMEDNS_SUBDOMAIN=7268181b-7075-4dce-be51-9c20c205cf6e # Update and install required software RUN apt-get update --fix-missing diff --git a/test/README-Testing.md b/test/README-Testing.md index f321b39..0599025 100644 --- a/test/README-Testing.md +++ b/test/README-Testing.md @@ -24,6 +24,11 @@ For dynu.com: - Add DYNU_API_KEY to your repository's environment secrets. The value is your account's API Key. - Add domains \-centos7-getssl.freedns.org, wild-\-centos7.freedns.org, \-ubuntu-getssl.freedns.org, and wild-\-ubuntu-getssl.freedns.org +For ACME DNS (also needs Dynu) + +- Register to get a user, key and subdomain from acme-dns.io (see https://github.com/joohoi/acme-dns?tab=readme-ov-file) +- Create a CNAME _acme-challenge.ubuntu-acmedns-getssl.freeddns.org. to ${ACMEDNS_SUBDOMAIN}.auth.acme-dns.io (this is done automatically in run-test.sh) + To run dynamic DNS tests outside the CI environment, you need accounts without \ in the domain names. Export the environment variable corresponding to the secrets (with the same values). For individual accounts, \ is your github account name. diff --git a/test/run-test.sh b/test/run-test.sh index 379aaaa..f6a7fed 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -1,5 +1,80 @@ #! /usr/bin/env bash +set -e + +function add-dynu-domain() { + domain=$1 + curl --silent --fail-with-body -X POST "https://api.dynu.com/v2/dns" \ + -H "accept: application/json" \ + -H "API-Key: $DYNU_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "'"${domain}"'", + "ipv4Address": "1.2.3.4", + "ttl": 60, + "ipv4": true, + "ipv6": false + }' +} + +function get-dynu-domain-id() { + domain=$1 + curl --silent --fail-with-body -X GET "https://api.dynu.com/v2/dns" \ + -H "accept: application/json" \ + -H "API-Key: $DYNU_API_KEY" | \ + jq -r ".domains[] | select(.name == \"${domain}\") | .id" +} + +function remove-dynu-domain() { + domain=$1 + echo "Removing dynu domain: $domain" + domain_id=$(get-dynu-domain-id "$domain") + echo "Found id for dynu domain: $domain = $domain_id" + if [ -n "$domain_id" ] && [ "$domain_id" != "null" ]; then + curl --silent --fail-with-body -X DELETE "https://api.dynu.com/v2/dns/${domain_id}" \ + -H "accept: application/json" \ + -H "API-Key: $DYNU_API_KEY" + echo "Domain $domain removed successfully" + else + echo "Domain $domain not found or already removed" + fi +} + +function add-dynu-cname() { + subdomain=$1 + domain=$2 + target=$3 + echo "Creating CNAME record: ${subdomain}.${domain} -> ${target}" + domain_id=$(get-dynu-domain-id "$domain") + if [ -n "$domain_id" ] && [ "$domain_id" != "null" ]; then + curl --silent --fail-with-body -X POST "https://api.dynu.com/v2/dns/${domain_id}/record" \ + -H "accept: application/json" \ + -H "API-Key: $DYNU_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "nodeName": "'"${subdomain}"'", + "recordType": "CNAME", + "state": true, + "host": "'"${target}"'" + }' + else + echo "Error: Domain $domain not found" + return 1 + fi +} + +# Cleanup function to remove dynu domains on exit +cleanup() { + if [[ ("$OS" == *"dynu"* || "$OS" == *"acmedns"*)]] && [ -n "$DYNU_API_KEY" ]; then + echo "Cleaning up domains..." + remove-dynu-domain "$ALIAS" + remove-dynu-domain "wild-$ALIAS" + fi +} + +# Set up trap to run cleanup on exit +trap cleanup EXIT + if [ $# -eq 0 ]; then echo "Usage: $(basename "$0") []" echo "e.g. $(basename "$0") alpine bats /getssl/test" @@ -54,10 +129,26 @@ elif [[ "$OS" == *"dynu"* ]]; then ALIAS="${REPO}${OS%-dynu}-getssl.freeddns.org" STAGING="--env STAGING=true --env dynamic_dns=dynu" GETSSL_OS="${OS%-dynu}" + if [ -n "$DYNU_API_KEY" ]; then + echo "Creating Dynu domains for $OS..." + add-dynu-domain "$ALIAS" + add-dynu-domain "wild-$ALIAS" + else + echo "Warning: DYNU_API_KEY not set, skipping domain creation" + fi elif [[ "$OS" == *"acmedns"* ]]; then ALIAS="${REPO}${OS}-getssl.freeddns.org" STAGING="--env STAGING=true --env dynamic_dns=acmedns" GETSSL_OS="${OS%-acmedns}" + if [ -n "$DYNU_API_KEY" ]; then + echo "Creating Dynu domains for $OS..." + add-dynu-domain "$ALIAS" + add-dynu-domain "wild-$ALIAS" + add-dynu-cname "_acme-challenge" "$ALIAS" "${ACMEDNS_SUBDOMAIN}.auth.acme-dns.io" + add-dynu-cname "_acme-challenge" "wild-$ALIAS" "${ACMEDNS_SUBDOMAIN}.auth.acme-dns.io" + else + echo "Warning: DYNU_API_KEY not set, skipping domain creation" + fi elif [[ "$OS" == "bash"* ]]; then GETSSL_OS="alpine" fi @@ -77,6 +168,9 @@ docker run $INT\ --env GITHUB_REPOSITORY="${GITHUB_REPOSITORY}" \ --env DUCKDNS_TOKEN="${DUCKDNS_TOKEN}" \ --env DYNU_API_KEY="${DYNU_API_KEY}" \ + --env ACMEDNS_API_KEY="${ACMEDNS_API_KEY}" \ + --env ACMEDNS_API_USER="${ACMEDNS_API_USER}" \ + --env ACMEDNS_SUBDOMAIN="${ACMEDNS_SUBDOMAIN}" \ -v "$(pwd)":/getssl \ --rm \ --network ${PWD##*/}_acmenet \ @@ -97,3 +191,4 @@ docker run $INT\ --name "getssl-$OS" \ "getssl-$OS" \ $COMMAND + diff --git a/test/test_helper.bash b/test/test_helper.bash index 796809e..66db483 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -166,7 +166,7 @@ GETSSL_CMD_HOST=$GETSSL_HOST export GETSSL_CMD_HOST if [ -z "$STAGING" ] && [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then - wget --quiet --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem 2>&1 + wget --quiet --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem 2>&1 CERT_FILE=/etc/ssl/certs/ca-certificates.crt if [ ! -f $CERT_FILE ]; then CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt