From bd0e674b1253535da688b98dabebd21e15b3dc25 Mon Sep 17 00:00:00 2001 From: Ian Driver Date: Sat, 14 Dec 2019 16:21:54 +0000 Subject: [PATCH 01/14] Only select first URL returned from acme-v02 --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index ab560eb..2d3b69d 100755 --- a/getssl +++ b/getssl @@ -2248,7 +2248,7 @@ for d in $alldomains; do token=$(json_get "$response" "challenges" "type" "http-01" "token") debug token "$token" # get the uri from the http component - uri=$(json_get "$response" "challenges" "type" "http-01" "url") + uri=$(json_get "$response" "challenges" "type" "http-01" "url" | head -n1) debug uri "$uri" fi From e85b4655ccf79114b2a78867ceff049dbaf67fa9 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Wed, 18 Dec 2019 18:08:11 +0100 Subject: [PATCH 02/14] Fix #462 use POST-as-GET requests on ACMEv2 endpoints Ref https://community.letsencrypt.org/t/acme-v2-scheduled-deprecation-of-unauthenticated-resource-gets/74380 --- getssl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index ab560eb..78c10a3 100755 --- a/getssl +++ b/getssl @@ -2185,7 +2185,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - response=$(curl --user-agent "$CURL_USERAGENT" --silent "${AuthLink[$dn]}" 2>/dev/null) + send_signed_request "${AuthLink[$dn]}" "" debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") @@ -2242,7 +2242,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - response=$(curl --user-agent "$CURL_USERAGENT" --silent "${AuthLink[$dn]}" 2>/dev/null) + send_signed_request "${AuthLink[$dn]}" "" debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") @@ -2488,8 +2488,8 @@ reload_service if [[ "$DEACTIVATE_AUTH" == "true" ]]; then debug "in deactivate list is $deactivate_url_list" for deactivate_url in $deactivate_url_list; do - resp=$(curl --user-agent "$CURL_USERAGENT" "$deactivate_url" 2>/dev/null) - d=$(json_get "$resp" "hostname") + send_signed_request "$deactivate_url" "" + d=$(json_get "$response" "hostname") info "deactivating domain $d" debug "deactivating $deactivate_url" send_signed_request "$deactivate_url" "{\"resource\": \"authz\", \"status\": \"deactivated\"}" From 7f1b94c6e2a9c1964461ae243681148b5694f631 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Dec 2019 15:59:58 +0000 Subject: [PATCH 03/14] Fixes for HTTP-01 challenge for POST-as-GET --- getssl | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/getssl b/getssl index 78c10a3..80730c6 100755 --- a/getssl +++ b/getssl @@ -279,7 +279,7 @@ check_challenge_completion() { # checks with the ACME server if our challenge is keyauthorization=$3 debug "sending request to ACME server saying we're ready for challenge" - send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" + send_signed_request "$uri" "{}" # check response from our request to perform challenge if [[ $API -eq 1 ]]; then @@ -294,10 +294,8 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # loop "forever" to keep checking for a response from the ACME server. while true ; do - debug "checking" - if ! get_cr "$uri" ; then - error_exit "$domain:Verify error:$code" - fi + debug "checking if challenge is complete" + send_signed_request "$uri" "" status=$(json_get "$response" status) @@ -853,10 +851,10 @@ get_certificate() { # get certificate for csr, if all domains validated. else # APIv2 send_signed_request "$FinalizeLink" "{\"csr\": \"$der\"}" "needbase64" debug "order link was $OrderLink" - cd=$(curl --user-agent "$CURL_USERAGENT" --silent "$OrderLink") - CertData=$(json_get "$cd" "certificate") + send_signed_request "$OrderLink" "" + CertData=$(json_get "$response" "certificate") debug "CertData is at $CertData" - curl --user-agent "$CURL_USERAGENT" --silent "$CertData" > "$FULL_CHAIN" + send_signed_request "$CertData" "" "" "$FULL_CHAIN" info "Full certificate saved in $FULL_CHAIN" awk -v CERT_FILE="$CERT_FILE" -v CA_CERT="$CA_CERT" 'BEGIN {outfile=CERT_FILE} split_after==1 {outfile=CA_CERT;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > outfile}' "$FULL_CHAIN" info "Certificate saved in $CERT_FILE" @@ -1325,6 +1323,7 @@ set_server_type() { # uses SERVER_TYPE to set REMOTE_PORT and REMOTE_EXTRA REMOTE_PORT=636 elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then REMOTE_PORT=${SERVER_TYPE} + REMOTE_EXTRA="CUSTOM-HTTP-PORT" else info "${DOMAIN}: unknown server type \"$SERVER_TYPE\" in SERVER_TYPE" config_errors=true @@ -1335,6 +1334,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p url=$1 payload=$2 needbase64=$3 + outfile=$4 # save response into this file (certificate data) debug url "$url" @@ -1374,8 +1374,8 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p # Build another header which also contains the previously received nonce and encode it as urlbase64 if [[ $API -eq 1 ]]; then - protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' - protected64="$(printf '%s' "${protected}" | urlbase64)" + protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' + protected64="$(printf '%s' "${protected}" | urlbase64)" else # APIv2 if [[ -z "$KID" ]]; then debug "KID is blank, so using jwk" @@ -1384,7 +1384,6 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p else debug "using KID=${KID}" protected="{\"alg\": \"$jwkalg\", \"kid\": \"$KID\",\"nonce\": \"${nonce}\", \"url\": \"${url}\"}" - debug "protected = $protected" protected64="$(printf '%s' "${protected}" | urlbase64)" fi fi @@ -1415,13 +1414,22 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p code="500" loop_limit=5 while [[ "$code" -eq 500 ]]; do - if [[ "$needbase64" ]] ; then + if [[ "$outfile" ]] ; then + $CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" > $outfile + response=$(cat "$outfile") + elif [[ "$needbase64" ]] ; then response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" | urlbase64) else response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url") fi responseHeaders=$(cat "$CURL_HEADER") + if [[ "$needbase64" && ${response##*()} != "{"* ]]; then + # response is in base64 too, decode + #!FIXME need to use openssl base64 decoder if it exists + response=$(echo "$response" | base64 -d) + fi + debug responseHeaders "$responseHeaders" debug response "$response" code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) @@ -1430,7 +1438,9 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p response_status=$(json_get "$response" status \ | head -1| awk -F'"' '{print $2}') else # APIv2 - if [[ ${response##*()} == "{"* ]]; then + if [[ "$output" && "$response" ]]; then + debug "response written to $outfile" + elif [[ ${response##*()} == "{"* ]]; then response_status=$(json_get "$response" status) else debug "response not in json format" @@ -1915,6 +1925,7 @@ else info "unknown API version" graceful_exit fi +debug "Using API v$API" # if check_remote is true then connect and obtain the current certificate (if not forcing renewal) if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then @@ -2271,7 +2282,11 @@ for d in $alldomains; do done umask "$ORIG_UMASK" - wellknown_url="${CHALLENGE_CHECK_TYPE}://$d/.well-known/acme-challenge/$token" + if [[ "$REMOTE_EXTRA" = "CUSTOM-HTTP-PORT" ]]; then + wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}:${REMOTE_PORT}/.well-known/acme-challenge/$token" + else + wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}/.well-known/acme-challenge/$token" + fi debug wellknown_url "$wellknown_url" if [[ "$SKIP_HTTP_TOKEN_CHECK" == "true" ]]; then From 5df6026c1bf9b9e6bd769e798568b9f171d8a2cd Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Dec 2019 16:00:32 +0000 Subject: [PATCH 04/14] Test using pebble and docker --- docker-compose.yml | 33 +++++++ test/Dockerfile | 30 +++++++ test/run-test.sh | 9 ++ test/test-config/getssl-ubuntu.cfg | 49 +++++++++++ .../nginx-ubuntu-sites-enabled-default | 88 +++++++++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 docker-compose.yml create mode 100644 test/Dockerfile create mode 100644 test/run-test.sh create mode 100644 test/test-config/getssl-ubuntu.cfg create mode 100644 test/test-config/nginx-ubuntu-sites-enabled-default diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..67e21a6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3' +services: + pebble: + image: letsencrypt/pebble:latest + # TODO enable -strict + command: pebble -config /test/config/pebble-config.json + environment: + # with Go 1.13.x which defaults TLS 1.3 to on + GODEBUG: "tls13=1" + ports: + - 14000:14000 # HTTPS ACME API + - 15000:15000 # HTTPS Management API + networks: + acmenet: + ipv4_address: 10.30.50.2 + getssl: + build: + context: . + dockerfile: test/Dockerfile + container_name: getssl + volumes: + - .:/getssl + networks: + acmenet: + ipv4_address: 10.30.50.4 + +networks: + acmenet: + driver: bridge + ipam: + driver: default + config: + - subnet: 10.30.50.0/24 diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 0000000..419d4d0 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:bionic +# bionic = latest 18 version + +# Update and install required software +RUN apt-get update +# TODO work out why default version of awk fails +RUN apt-get install -y git curl dnsutils wget linux-libc-dev make gcc binutils nginx-light gawk +RUN apt-get install -y vim dos2unix # for debugging +# TODO test with drill, dig, host + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-sites-enabled-default /etc/nginx/sites-enabled/default + +# BATS (Bash Automated Testings) +# RUN git clone https://github.com/bats-core/bats-core.git +# RUN bats-core/install.sh /usr/local + +COPY test/test-config/getssl-ubuntu.cfg getssl.cfg + +EXPOSE 80 443 + +# Run eternal loop - for testing +CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] + +# with Pebble +# docker-compose -f "test\docker-compose.yml" up -d --build +# docker exec -it test_getssl /bin/bash +# /getssl/test/run-test.sh diff --git a/test/run-test.sh b/test/run-test.sh new file mode 100644 index 0000000..5e0ba8d --- /dev/null +++ b/test/run-test.sh @@ -0,0 +1,9 @@ +#! /bin/sh + +wget --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem +export CURL_CA_BUNDLE=/root/pebble.minica.pem + +service nginx start +/getssl/getssl -c getssl +cp getssl.cfg /root/.getssl/getssl +/getssl/getssl getssl diff --git a/test/test-config/getssl-ubuntu.cfg b/test/test-config/getssl-ubuntu.cfg new file mode 100644 index 0000000..a4db20f --- /dev/null +++ b/test/test-config/getssl-ubuntu.cfg @@ -0,0 +1,49 @@ +# Uncomment and modify any variables you need +# see https://github.com/srvrco/getssl/wiki/Config-variables for details +# see https://github.com/srvrco/getssl/wiki/Example-config-files for example configs +# +# The staging server is best for testing +#CA="https://acme-staging.api.letsencrypt.org" +# This server issues full certificates, however has rate limits +#CA="https://acme-v01.api.letsencrypt.org" +CA="https://pebble:14000/dir" +SERVER_TYPE="5002" +#PRIVATE_KEY_ALG="rsa" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +# Note: this is Additional domains - so should not include the primary domain. +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +# If these start with ssh: then the next variable is assumed to be the hostname and the rest the location. +# An ssh key will be needed to provide you with access to the remote server. +# Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign. +# If left blank, the username on the local server will be used to authenticate against the remote server. +# If these start with ftp: then the next variables are ftpuserid:ftppassword:servername:ACL_location +# These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge" +# where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain. +ACL=('/var/www/html/.well-known/acme-challenge') +# 'ssh:server5:/var/www/getssltest.hopto.org/web/.well-known/acme-challenge' +# 'ssh:sshuserid@server5:/var/www/getssltest.hopto.org/web/.well-known/acme-challenge' +# 'ftp:ftpuserid:ftppassword:getssltest.hopto.org:/web/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="false" + +# Location for all your certs, these can either be on the server (full path name) +# or using ssh /sftp as for the ACL +DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert +DOMAIN_PEM_LOCATION="" # this is the domain_key, domain cert and CA cert + +# The command needed to reload apache / nginx or whatever you use +RELOAD_CMD="service nginx restart" + +# Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, +# smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which +# will be checked for certificate expiry and also will be checked after +# an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true +#SERVER_TYPE="https" +#CHECK_REMOTE="true" diff --git a/test/test-config/nginx-ubuntu-sites-enabled-default b/test/test-config/nginx-ubuntu-sites-enabled-default new file mode 100644 index 0000000..fe02c8d --- /dev/null +++ b/test/test-config/nginx-ubuntu-sites-enabled-default @@ -0,0 +1,88 @@ +## +# You should look at the following URL's in order to grasp a solid understanding +# of Nginx configuration files in order to fully unleash the power of Nginx. +# http://wiki.nginx.org/Pitfalls +# http://wiki.nginx.org/QuickStart +# http://wiki.nginx.org/Configuration +# +# Generally, you will want to move this file somewhere, and start with a clean +# file but keep this around for reference. Or just disable in sites-enabled. +# +# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. +## + +# Default server configuration +# +server { + listen 5002 default_server; + listen [::]:5002 default_server; + + # SSL configuration + # + listen 5001 ssl default_server; + listen [::]:5001 ssl default_server; + # + # Note: You should disable gzip for SSL traffic. + # See: https://bugs.debian.org/773332 + # + # Read up on ssl_ciphers to ensure a secure configuration. + # See: https://bugs.debian.org/765782 + # + # Self signed certs generated by the ssl-cert package + # Don't use them in a production server! + # + # include snippets/snakeoil.conf; + + root /var/www/html; + + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; + + server_name _; + # ssl_certificate /etc/nginx/pki/server.crt; + # ssl_certificate_key /etc/nginx/pki/private/server.key; + + location / { + # First attempt to serve request as file, then + # as directory, then fall back to displaying a 404. + try_files $uri $uri/ =404; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # include snippets/fastcgi-php.conf; + # + # # With php7.0-cgi alone: + # fastcgi_pass 127.0.0.1:9000; + # # With php7.0-fpm: + # fastcgi_pass unix:/run/php/php7.0-fpm.sock; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} + + +# Virtual Host configuration for example.com +# +# You can move that to a different file under sites-available/ and symlink that +# to sites-enabled/ to enable it. +# +#server { +# listen 80; +# listen [::]:80; +# +# server_name example.com; +# +# root /var/www/example.com; +# index index.html; +# +# location / { +# try_files $uri $uri/ =404; +# } +#} From 5e3d377342c36e7ba391c329539f515b93ce7269 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Dec 2019 16:41:13 +0000 Subject: [PATCH 05/14] Fix a typo and shellcheck warning --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 80730c6..02340b0 100755 --- a/getssl +++ b/getssl @@ -1415,7 +1415,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p loop_limit=5 while [[ "$code" -eq 500 ]]; do if [[ "$outfile" ]] ; then - $CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" > $outfile + $CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" > "$outfile" response=$(cat "$outfile") elif [[ "$needbase64" ]] ; then response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" | urlbase64) @@ -1438,7 +1438,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p response_status=$(json_get "$response" status \ | head -1| awk -F'"' '{print $2}') else # APIv2 - if [[ "$output" && "$response" ]]; then + if [[ "$outfile" && "$response" ]]; then debug "response written to $outfile" elif [[ ${response##*()} == "{"* ]]; then response_status=$(json_get "$response" status) From 5296a0716f4f3ebbca141785cbb55abdc4bdcb38 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 4 Jan 2020 10:20:52 +0000 Subject: [PATCH 06/14] Fix for RHEL6 --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 02340b0..0f1ef47 100755 --- a/getssl +++ b/getssl @@ -2131,7 +2131,7 @@ if [[ $API -eq 2 ]]; then for d in $alldomains; do dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," done - dstring="${dstring: : -1}]" + dstring="${dstring::${#dstring}-1}]" # request NewOrder currently seems to ignore the dates .... # dstring="${dstring},\"notBefore\": \"$(date -d "-1 hour" --utc +%FT%TZ)\"" # dstring="${dstring},\"notAfter\": \"$(date -d "2 days" --utc +%FT%TZ)\"" From 2f3e5da3e81f18d95994120586b1d3c199b579e2 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 7 Jan 2020 13:02:45 +0000 Subject: [PATCH 07/14] Improved test script (http01 and dns01) --- dns_scripts/dns_add_challtestsrv | 7 ++ dns_scripts/dns_del_challtestsrv | 6 ++ docker-compose.yml | 12 ++- getssl | 14 +-- test/Dockerfile-rhel6 | 27 ++++++ test/{Dockerfile => Dockerfile-ubuntu} | 12 +-- test/run-test.sh | 46 +++++++++- test/test-config/getssl-dns01.cfg | 54 +++++++++++ .../{getssl-ubuntu.cfg => getssl-http01.cfg} | 8 +- ...es-enabled-default => nginx-ubuntu-no-ssl} | 9 +- test/test-config/nginx-ubuntu-ssl | 92 +++++++++++++++++++ 11 files changed, 263 insertions(+), 24 deletions(-) create mode 100644 dns_scripts/dns_add_challtestsrv create mode 100644 dns_scripts/dns_del_challtestsrv create mode 100644 test/Dockerfile-rhel6 rename test/{Dockerfile => Dockerfile-ubuntu} (61%) create mode 100644 test/test-config/getssl-dns01.cfg rename test/test-config/{getssl-ubuntu.cfg => getssl-http01.cfg} (91%) rename test/test-config/{nginx-ubuntu-sites-enabled-default => nginx-ubuntu-no-ssl} (93%) create mode 100644 test/test-config/nginx-ubuntu-ssl diff --git a/dns_scripts/dns_add_challtestsrv b/dns_scripts/dns_add_challtestsrv new file mode 100644 index 0000000..601bcfc --- /dev/null +++ b/dns_scripts/dns_add_challtestsrv @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Simple script to update the challtestserv mock DNS server when testing DNS responses + +fulldomain="${1}" +token="${2}" + +curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt diff --git a/dns_scripts/dns_del_challtestsrv b/dns_scripts/dns_del_challtestsrv new file mode 100644 index 0000000..832b136 --- /dev/null +++ b/dns_scripts/dns_del_challtestsrv @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Simple script to update the challtestserv mock DNS server when testing DNS responses + +fulldomain="${1}" + +curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\"}" http://10.30.50.3:8055/clear-txt diff --git a/docker-compose.yml b/docker-compose.yml index 67e21a6..b770b44 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: pebble: image: letsencrypt/pebble:latest # TODO enable -strict - command: pebble -config /test/config/pebble-config.json + command: pebble -config /test/config/pebble-config.json -dnsserver 10.30.50.3:8053 environment: # with Go 1.13.x which defaults TLS 1.3 to on GODEBUG: "tls13=1" @@ -13,10 +13,18 @@ services: networks: acmenet: ipv4_address: 10.30.50.2 + challtestsrv: + image: letsencrypt/pebble-challtestsrv:latest + command: pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 10.30.50.3 + ports: + - 8055:8055 # HTTP Management API + networks: + acmenet: + ipv4_address: 10.30.50.3 getssl: build: context: . - dockerfile: test/Dockerfile + dockerfile: test/Dockerfile-ubuntu container_name: getssl volumes: - .:/getssl diff --git a/getssl b/getssl index 0f1ef47..991f6d8 100755 --- a/getssl +++ b/getssl @@ -288,7 +288,8 @@ check_challenge_completion() { # checks with the ACME server if our challenge is fi else # APIv2 if [[ -n "$code" ]] && [[ ! "$code" == '200' ]] ; then - error_exit "$domain:Challenge error: $code" + detail=$(json_get "$response" detail) + error_exit "$domain:Challenge error: $code:Detail: $detail" fi fi @@ -1323,7 +1324,6 @@ set_server_type() { # uses SERVER_TYPE to set REMOTE_PORT and REMOTE_EXTRA REMOTE_PORT=636 elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then REMOTE_PORT=${SERVER_TYPE} - REMOTE_EXTRA="CUSTOM-HTTP-PORT" else info "${DOMAIN}: unknown server type \"$SERVER_TYPE\" in SERVER_TYPE" config_errors=true @@ -2282,11 +2282,7 @@ for d in $alldomains; do done umask "$ORIG_UMASK" - if [[ "$REMOTE_EXTRA" = "CUSTOM-HTTP-PORT" ]]; then - wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}:${REMOTE_PORT}/.well-known/acme-challenge/$token" - else - wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}/.well-known/acme-challenge/$token" - fi + wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}/.well-known/acme-challenge/$token" debug wellknown_url "$wellknown_url" if [[ "$SKIP_HTTP_TOKEN_CHECK" == "true" ]]; then @@ -2522,6 +2518,8 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" # shellcheck disable=SC2086 + debug openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} + CERT_REMOTE=$(echo \ | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ | openssl x509 -noout -fingerprint 2>/dev/null) @@ -2529,6 +2527,8 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then info "${DOMAIN} - certificate installed OK on server" else + debug Fingerprint on server ${CERT_REMOTE} + debug Fingerprint in file ${CERT_LOCAL} error_exit "${DOMAIN} - certificate obtained but certificate on server is different from the new certificate" fi fi diff --git a/test/Dockerfile-rhel6 b/test/Dockerfile-rhel6 new file mode 100644 index 0000000..5ebb278 --- /dev/null +++ b/test/Dockerfile-rhel6 @@ -0,0 +1,27 @@ +FROM roboxes/rhel6 +# FROM centos:centos6 +# bionic = latest 18 version + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl dnsutils wget # nginx-light + +WORKDIR /root +#RUN mkdir /etc/nginx/pki +#RUN mkdir /etc/nginx/pki/private +#COPY ./test/test-config/nginx-ubuntu-sites-enabled-default /etc/nginx/sites-enabled/default + +# BATS (Bash Automated Testings) +# RUN git clone https://github.com/bats-core/bats-core.git +# RUN bats-core/install.sh /usr/local + +EXPOSE 80 443 + +# Run eternal loop - for testing +CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] + +# with Pebble +# docker-compose -f "docker-compose.yml" up -d --build +# docker exec -it getssl /bin/bash +# /getssl/test/run-test.sh diff --git a/test/Dockerfile b/test/Dockerfile-ubuntu similarity index 61% rename from test/Dockerfile rename to test/Dockerfile-ubuntu index 419d4d0..7f1a8e5 100644 --- a/test/Dockerfile +++ b/test/Dockerfile-ubuntu @@ -1,30 +1,28 @@ -FROM ubuntu:bionic +FROM ubuntu:xenial # bionic = latest 18 version # Update and install required software RUN apt-get update # TODO work out why default version of awk fails -RUN apt-get install -y git curl dnsutils wget linux-libc-dev make gcc binutils nginx-light gawk +RUN apt-get install -y git curl dnsutils wget gawk nginx-light # linux-libc-dev make gcc binutils RUN apt-get install -y vim dos2unix # for debugging # TODO test with drill, dig, host WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private -COPY ./test/test-config/nginx-ubuntu-sites-enabled-default /etc/nginx/sites-enabled/default +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default # BATS (Bash Automated Testings) # RUN git clone https://github.com/bats-core/bats-core.git # RUN bats-core/install.sh /usr/local -COPY test/test-config/getssl-ubuntu.cfg getssl.cfg - EXPOSE 80 443 # Run eternal loop - for testing CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] # with Pebble -# docker-compose -f "test\docker-compose.yml" up -d --build -# docker exec -it test_getssl /bin/bash +# docker-compose -f "docker-compose.yml" up -d --build +# docker exec -it getssl /bin/bash # /getssl/test/run-test.sh diff --git a/test/run-test.sh b/test/run-test.sh index 5e0ba8d..e93ae89 100644 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -1,9 +1,47 @@ -#! /bin/sh +#! /bin/bash + +set -e + +# Test setup +rm -r /root/.getssl wget --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem -export CURL_CA_BUNDLE=/root/pebble.minica.pem +# cat /etc/pki/tls/certs/ca-bundle.crt /root/pebble.minica.pem > /root/pebble-ca-bundle.crt +cat /etc/ssl/certs/ca-certificates.crt /root/pebble.minica.pem > /root/pebble-ca-bundle.crt +export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + +curl -X POST -d '{"host":"getssl", "addresses":["10.30.50.4"]}' http://10.30.50.3:8055/add-a + +# Test #1 - http-01 verification +echo Test \#1 - http-01 verification -service nginx start +cp /getssl/test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +service nginx restart /getssl/getssl -c getssl -cp getssl.cfg /root/.getssl/getssl +cp /getssl/test/test-config/getssl-http01.cfg /root/.getssl/getssl/getssl.cfg +/getssl/getssl -f getssl + +# Test #2 - http-01 forced renewal +echo Test \#2 - http-01 forced renewal + +sleep 5 # There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +/getssl/getssl getssl -f + +# Test cleanup + +rm -r /root/.getssl + +# Test #3 - dns-01 verification +echo Test \#3 - dns-01 verification + +cp /getssl/test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +service nginx restart +/getssl/getssl -c getssl +cp /getssl/test/test-config/getssl-dns01.cfg /root/.getssl/getssl/getssl.cfg /getssl/getssl getssl + +# Test #4 - dns-01 forced renewal +echo Test \#4 - dns-01 forced renewal + +sleep 5 # There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +/getssl/getssl getssl -f diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg new file mode 100644 index 0000000..49c58b5 --- /dev/null +++ b/test/test-config/getssl-dns01.cfg @@ -0,0 +1,54 @@ +# Uncomment and modify any variables you need +# see https://github.com/srvrco/getssl/wiki/Config-variables for details +# see https://github.com/srvrco/getssl/wiki/Example-config-files for example configs +# +# The staging server is best for testing +#CA="https://acme-staging.api.letsencrypt.org" +# This server issues full certificates, however has rate limits +#CA="https://acme-v01.api.letsencrypt.org" +CA="https://pebble:14000/dir" + +VALIDATE_VIA_DNS=true +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" +# AUTH_DNS_SERVER=10.30.50.3 + +#PRIVATE_KEY_ALG="rsa" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +# Note: this is Additional domains - so should not include the primary domain. +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +# If these start with ssh: then the next variable is assumed to be the hostname and the rest the location. +# An ssh key will be needed to provide you with access to the remote server. +# Optionally, you can specify a different userid for ssh/scp to use on the remote server before the @ sign. +# If left blank, the username on the local server will be used to authenticate against the remote server. +# If these start with ftp: then the next variables are ftpuserid:ftppassword:servername:ACL_location +# These should be of the form "/path/to/your/website/folder/.well-known/acme-challenge" +# where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain. +ACL=('/var/www/html/.well-known/acme-challenge') +# 'ssh:server5:/var/www/getssltest.hopto.org/web/.well-known/acme-challenge' +# 'ssh:sshuserid@server5:/var/www/getssltest.hopto.org/web/.well-known/acme-challenge' +# 'ftp:ftpuserid:ftppassword:getssltest.hopto.org:/web/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="false" + +# Location for all your certs, these can either be on the server (full path name) +# or using ssh /sftp as for the ACL +DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert +DOMAIN_PEM_LOCATION="" # this is the domain_key, domain cert and CA cert + +# The command needed to reload apache / nginx or whatever you use +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl /etc/nginx/sites-enabled/default && service nginx restart" + +# Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, +# smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which +# will be checked for certificate expiry and also will be checked after +# an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true +#SERVER_TYPE="https" +#CHECK_REMOTE="true" diff --git a/test/test-config/getssl-ubuntu.cfg b/test/test-config/getssl-http01.cfg similarity index 91% rename from test/test-config/getssl-ubuntu.cfg rename to test/test-config/getssl-http01.cfg index a4db20f..f3dc5ad 100644 --- a/test/test-config/getssl-ubuntu.cfg +++ b/test/test-config/getssl-http01.cfg @@ -7,7 +7,11 @@ # This server issues full certificates, however has rate limits #CA="https://acme-v01.api.letsencrypt.org" CA="https://pebble:14000/dir" -SERVER_TYPE="5002" + +#VALIDATE_VIA_DNS=true +#DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" +#DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" + #PRIVATE_KEY_ALG="rsa" # Additional domains - this could be multiple domains / subdomains in a comma separated list @@ -39,7 +43,7 @@ DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert DOMAIN_PEM_LOCATION="" # this is the domain_key, domain cert and CA cert # The command needed to reload apache / nginx or whatever you use -RELOAD_CMD="service nginx restart" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl /etc/nginx/sites-enabled/default && service nginx restart" # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, # smtps_deprecated, smtps, smtp_submission, xmpp, xmpps, ldaps or a port number which diff --git a/test/test-config/nginx-ubuntu-sites-enabled-default b/test/test-config/nginx-ubuntu-no-ssl similarity index 93% rename from test/test-config/nginx-ubuntu-sites-enabled-default rename to test/test-config/nginx-ubuntu-no-ssl index fe02c8d..c78d646 100644 --- a/test/test-config/nginx-ubuntu-sites-enabled-default +++ b/test/test-config/nginx-ubuntu-no-ssl @@ -14,13 +14,18 @@ # Default server configuration # server { + listen 80 default_server; listen 5002 default_server; listen [::]:5002 default_server; # SSL configuration # - listen 5001 ssl default_server; - listen [::]:5001 ssl default_server; + listen 443 default_server; + listen [::]:443 default_server; + + listen 5001 default_server; + listen [::]:5001 default_server; + # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 diff --git a/test/test-config/nginx-ubuntu-ssl b/test/test-config/nginx-ubuntu-ssl new file mode 100644 index 0000000..9f79407 --- /dev/null +++ b/test/test-config/nginx-ubuntu-ssl @@ -0,0 +1,92 @@ +## +# You should look at the following URL's in order to grasp a solid understanding +# of Nginx configuration files in order to fully unleash the power of Nginx. +# http://wiki.nginx.org/Pitfalls +# http://wiki.nginx.org/QuickStart +# http://wiki.nginx.org/Configuration +# +# Generally, you will want to move this file somewhere, and start with a clean +# file but keep this around for reference. Or just disable in sites-enabled. +# +# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. +## + +# Default server configuration +# +server { + listen 80 default_server; + listen 5002 default_server; + listen [::]:5002 default_server; + + # SSL configuration + # + listen 443 ssl default_server; + listen [::]:443 ssl default_server; + + listen 5001 ssl default_server; + listen [::]:5001 ssl default_server; + # + # Note: You should disable gzip for SSL traffic. + # See: https://bugs.debian.org/773332 + # + # Read up on ssl_ciphers to ensure a secure configuration. + # See: https://bugs.debian.org/765782 + # + # Self signed certs generated by the ssl-cert package + # Don't use them in a production server! + # + # include snippets/snakeoil.conf; + + root /var/www/html; + + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; + + server_name _; + ssl_certificate /etc/nginx/pki/server.crt; + ssl_certificate_key /etc/nginx/pki/private/server.key; + + location / { + # First attempt to serve request as file, then + # as directory, then fall back to displaying a 404. + try_files $uri $uri/ =404; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # include snippets/fastcgi-php.conf; + # + # # With php7.0-cgi alone: + # fastcgi_pass 127.0.0.1:9000; + # # With php7.0-fpm: + # fastcgi_pass unix:/run/php/php7.0-fpm.sock; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} + + +# Virtual Host configuration for example.com +# +# You can move that to a different file under sites-available/ and symlink that +# to sites-enabled/ to enable it. +# +#server { +# listen 80; +# listen [::]:80; +# +# server_name example.com; +# +# root /var/www/example.com; +# index index.html; +# +# location / { +# try_files $uri $uri/ =404; +# } +#} From 3075c30faa4ba3df905ea5df7913c0ff555257b6 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 7 Jan 2020 13:10:57 +0000 Subject: [PATCH 08/14] Update revision history --- getssl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index 991f6d8..5808f42 100755 --- a/getssl +++ b/getssl @@ -189,6 +189,11 @@ # 2019-10-02 issue #425 Case insensitive processing of agreement url because of HTTP/2 (2.12) # 2019-10-07 update DNS checks to allow use of CNAMEs (2.13) # 2019-11-18 Rebased master onto APIv2 and added Content-Type: application/jose+json (2.14) +# 2019-11-20 #453 and #454 Add User-Agent to all curl requests +# 2019-11-22 #456 Fix shellcheck issues +# 2019-11-23 #459 Fix missing chain.crt +# 2019-12-18 #462 Use POST-as-GET for ACMEv2 endpoints +# 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET) (2.15) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -2518,8 +2523,6 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" # shellcheck disable=SC2086 - debug openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} - CERT_REMOTE=$(echo \ | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ | openssl x509 -noout -fingerprint 2>/dev/null) @@ -2527,8 +2530,6 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then info "${DOMAIN} - certificate installed OK on server" else - debug Fingerprint on server ${CERT_REMOTE} - debug Fingerprint in file ${CERT_LOCAL} error_exit "${DOMAIN} - certificate obtained but certificate on server is different from the new certificate" fi fi From a5313f4f31d20165eaf0e7b2d41c91f26b316b62 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 8 Jan 2020 21:51:07 +0000 Subject: [PATCH 09/14] error_exit if rate limited and exit if curl returns nothing --- getssl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index fbfabaa..f0b31f5 100755 --- a/getssl +++ b/getssl @@ -293,7 +293,7 @@ check_challenge_completion() { # checks with the ACME server if our challenge is fi else # APIv2 if [[ -n "$code" ]] && [[ ! "$code" == '200' ]] ; then - detail=$(json_get "$response" detail) + detail=$(echo "$response" | grep "detail" | awk -F\" '{print $4}') error_exit "$domain:Challenge error: $code:Detail: $detail" fi fi @@ -1428,6 +1428,10 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url") fi + if [[ "$response" == "" ]]; then + error_exit "ERROR curl \"$url\" returned nothing" + fi + responseHeaders=$(cat "$CURL_HEADER") if [[ "$needbase64" && ${response##*()} != "{"* ]]; then # response is in base64 too, decode @@ -1462,6 +1466,9 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p error_exit "500 error from ACME server: $response" fi fi + if [[ "$code" -eq 429 ]]; then + error_exit "429 rate limited error from ACME server" + fi done if [[ $response == *"error:badNonce"* ]]; then debug "bad nonce" From 2dbaf3e14d1d65dca263dc49ec235334bab4c022 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 10 Jan 2020 19:24:57 +0000 Subject: [PATCH 10/14] Update templates, clean up test code --- getssl | 12 +++++++----- test/Dockerfile-rhel6 | 5 ----- test/Dockerfile-ubuntu | 5 ----- test/README.md | 20 ++++++++++++++++++++ test/run-test.sh | 14 ++++++++++---- 5 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 test/README.md diff --git a/getssl b/getssl index f0b31f5..5d46286 100755 --- a/getssl +++ b/getssl @@ -193,7 +193,9 @@ # 2019-11-22 #456 Fix shellcheck issues # 2019-11-23 #459 Fix missing chain.crt # 2019-12-18 #462 Use POST-as-GET for ACMEv2 endpoints -# 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET) (2.15) +# 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET) +# 2020-01-08 Error and exit if rate limited, exit if curl returns nothing +# 2020-01-10 Change domain and getssl templates to v2 (2.15) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -212,7 +214,7 @@ CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl" CSR_SUBJECT="/" CURL_USERAGENT="${PROGNAME}/${VERSION}" DEACTIVATE_AUTH="false" -DEFAULT_REVOKE_CA="https://acme-v01.api.letsencrypt.org" +DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" DNS_EXTRA_WAIT="" DNS_WAIT=10 DOMAIN_KEY_LENGTH=4096 @@ -1566,7 +1568,7 @@ write_domain_template() { # write out a template file for a domain. # The staging server is best for testing #CA="https://acme-staging-v02.api.letsencrypt.org/directory" # This server issues full certificates, however has rate limits - #CA="https://acme-v01.api.letsencrypt.org" + #CA="https://acme-v02.api.letsencrypt.org" #PRIVATE_KEY_ALG="rsa" @@ -1619,7 +1621,7 @@ write_getssl_template() { # write out the main template file # The staging server is best for testing (hence set as default) CA="https://acme-staging-v02.api.letsencrypt.org/directory" # This server issues full certificates, however has rate limits - #CA="https://acme-v01.api.letsencrypt.org" + #CA="https://acme-v02.api.letsencrypt.org" #AGREEMENT="$AGREEMENT" @@ -2021,7 +2023,7 @@ if [[ -s "$CERT_FILE" ]]; then enddate_s=$(date_epoc "$enddate") if [[ $(date_renew) -lt "$enddate_s" ]] && [[ $_FORCE_RENEW -ne 1 ]]; then issuer=$(openssl x509 -in "$CERT_FILE" -noout -issuer 2>/dev/null) - if [[ "$issuer" == *"Fake LE Intermediate"* ]] && [[ "$CA" == "https://acme-v01.api.letsencrypt.org" ]]; then + if [[ "$issuer" == *"Fake LE Intermediate"* ]] && [[ "$CA" == "https://acme-v02.api.letsencrypt.org" ]]; then debug "upgrading from fake cert to real" else info "${DOMAIN}: certificate is valid for more than $RENEW_ALLOW days (until $enddate)" diff --git a/test/Dockerfile-rhel6 b/test/Dockerfile-rhel6 index 5ebb278..019da84 100644 --- a/test/Dockerfile-rhel6 +++ b/test/Dockerfile-rhel6 @@ -20,8 +20,3 @@ EXPOSE 80 443 # Run eternal loop - for testing CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] - -# with Pebble -# docker-compose -f "docker-compose.yml" up -d --build -# docker exec -it getssl /bin/bash -# /getssl/test/run-test.sh diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 7f1a8e5..b0f09f8 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -21,8 +21,3 @@ EXPOSE 80 443 # Run eternal loop - for testing CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] - -# with Pebble -# docker-compose -f "docker-compose.yml" up -d --build -# docker exec -it getssl /bin/bash -# /getssl/test/run-test.sh diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..9d0aedd --- /dev/null +++ b/test/README.md @@ -0,0 +1,20 @@ +# Testing + +This directory contains a simple test script which tests creating certificates with Pebble (testing version of the LetsEncrypt server) + +Start up pebble, the challdnstest server for DNS challenges +`docker-compose -f "docker-compose.yml" up -d --build` + +Run the tests +`docker exec -it getssl /getssl/test/run-test.sh` + +Debug (need to set CURL_CA_BUNDLE as pebble uses a local certificate, otherwise you get a "unknown API version" error) +`docker exec -it getssl /bin/bash` +`export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt` +`/getssl/getssl -d getssl` + +# TODO +1. Move to BATS (bash automated testing) instead of run-test.sh +2. Test RHEL6, Debian as well +3. Test SSH, SFTP +4. Test wildcards diff --git a/test/run-test.sh b/test/run-test.sh index e93ae89..b983899 100644 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -3,7 +3,9 @@ set -e # Test setup -rm -r /root/.getssl +if [[ -d /root/.getssl ]]; then + rm -r /root/.getssl +fi wget --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem # cat /etc/pki/tls/certs/ca-bundle.crt /root/pebble.minica.pem > /root/pebble-ca-bundle.crt @@ -24,11 +26,12 @@ cp /getssl/test/test-config/getssl-http01.cfg /root/.getssl/getssl/getssl.cfg # Test #2 - http-01 forced renewal echo Test \#2 - http-01 forced renewal -sleep 5 # There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +# There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +echo Sleeping 20s to allow previous validation to expire +sleep 20 /getssl/getssl getssl -f # Test cleanup - rm -r /root/.getssl # Test #3 - dns-01 verification @@ -43,5 +46,8 @@ cp /getssl/test/test-config/getssl-dns01.cfg /root/.getssl/getssl/getssl.cfg # Test #4 - dns-01 forced renewal echo Test \#4 - dns-01 forced renewal -sleep 5 # There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +# There's a race condition if renew too soon (authlink returns "valid" instead of "pending") +echo Sleeping 30s to allow previous validation to expire +sleep 30 + /getssl/getssl getssl -f From 9895a211d83beb39bfabffde1f034cfaac7b190c Mon Sep 17 00:00:00 2001 From: srvrco Date: Mon, 13 Jan 2020 09:28:03 +0000 Subject: [PATCH 11/14] correcting version number --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 5d46286..e52b3d0 100755 --- a/getssl +++ b/getssl @@ -199,7 +199,7 @@ # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.14" +VERSION="2.15" # defaults ACCOUNT_KEY_LENGTH=4096 From 8203c38b364e28e42dd6951286a70a0df63111d8 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 16 Jan 2020 11:48:02 +0000 Subject: [PATCH 12/14] Disable auth reuse to fix force-renew tests --- docker-compose.yml | 2 ++ test/run-test.sh | 10 ---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b770b44..f4b3567 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: environment: # with Go 1.13.x which defaults TLS 1.3 to on GODEBUG: "tls13=1" + # don't reuse authorizations (breaks testing force renew) + PEBBLE_AUTHZREUSE: 0 ports: - 14000:14000 # HTTPS ACME API - 15000:15000 # HTTPS Management API diff --git a/test/run-test.sh b/test/run-test.sh index b983899..8051922 100644 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -25,10 +25,6 @@ cp /getssl/test/test-config/getssl-http01.cfg /root/.getssl/getssl/getssl.cfg # Test #2 - http-01 forced renewal echo Test \#2 - http-01 forced renewal - -# There's a race condition if renew too soon (authlink returns "valid" instead of "pending") -echo Sleeping 20s to allow previous validation to expire -sleep 20 /getssl/getssl getssl -f # Test cleanup @@ -36,7 +32,6 @@ rm -r /root/.getssl # Test #3 - dns-01 verification echo Test \#3 - dns-01 verification - cp /getssl/test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default service nginx restart /getssl/getssl -c getssl @@ -45,9 +40,4 @@ cp /getssl/test/test-config/getssl-dns01.cfg /root/.getssl/getssl/getssl.cfg # Test #4 - dns-01 forced renewal echo Test \#4 - dns-01 forced renewal - -# There's a race condition if renew too soon (authlink returns "valid" instead of "pending") -echo Sleeping 30s to allow previous validation to expire -sleep 30 - /getssl/getssl getssl -f From f17590af52c0db97fd3105cd66aa187e5240c2ce Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 16 Jan 2020 11:50:55 +0000 Subject: [PATCH 13/14] Revert ready for challenge for ACME v1 --- getssl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index e52b3d0..5352e11 100755 --- a/getssl +++ b/getssl @@ -286,14 +286,15 @@ check_challenge_completion() { # checks with the ACME server if our challenge is keyauthorization=$3 debug "sending request to ACME server saying we're ready for challenge" - send_signed_request "$uri" "{}" # check response from our request to perform challenge if [[ $API -eq 1 ]]; then + send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" if [[ -n "$code" ]] && [[ ! "$code" == '202' ]] ; then error_exit "$domain:Challenge error: $code" fi else # APIv2 + send_signed_request "$uri" "{}" if [[ -n "$code" ]] && [[ ! "$code" == '200' ]] ; then detail=$(echo "$response" | grep "detail" | awk -F\" '{print $4}') error_exit "$domain:Challenge error: $code:Detail: $detail" @@ -303,7 +304,13 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # loop "forever" to keep checking for a response from the ACME server. while true ; do debug "checking if challenge is complete" - send_signed_request "$uri" "" + if [[ $API -eq 1 ]]; then + if ! get_cr "$uri" ; then + error_exit "$domain:Verify error:$code" + fi + else # APIv2 + send_signed_request "$uri" "" + fi status=$(json_get "$response" status) From 197c5f8faa86d146ec69666a58f9f00e0f7ed149 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 17 Jan 2020 20:34:14 +0000 Subject: [PATCH 14/14] Ignore base64 errors --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 5352e11..8269195 100755 --- a/getssl +++ b/getssl @@ -196,10 +196,11 @@ # 2020-01-07 #464 and #486 "json was blank" (change all curl request to use POST-as-GET) # 2020-01-08 Error and exit if rate limited, exit if curl returns nothing # 2020-01-10 Change domain and getssl templates to v2 (2.15) +# 2020-01-17 #473 and #477 Don't use POST-as-GET when sending ready for challenge for ACMEv1 (2.16) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.15" +VERSION="2.16" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1444,8 +1445,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p responseHeaders=$(cat "$CURL_HEADER") if [[ "$needbase64" && ${response##*()} != "{"* ]]; then # response is in base64 too, decode - #!FIXME need to use openssl base64 decoder if it exists - response=$(echo "$response" | base64 -d) + response=$(echo "$response" | base64 -d 2>&1) fi debug responseHeaders "$responseHeaders"