From cbdf925839ca7b99c121c2fe549bc425d1201c80 Mon Sep 17 00:00:00 2001 From: Scott Gustafson Date: Tue, 15 Aug 2017 10:20:57 -0600 Subject: [PATCH 001/337] moving dig to be the first command to check as nslookup is failing on ubuntu vm Signed-off-by: Scott Gustafson --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 7f3713e..b903ab8 100755 --- a/getssl +++ b/getssl @@ -1445,7 +1445,7 @@ get_os requires which requires openssl requires curl -requires nslookup drill dig host DNS_CHECK_FUNC +requires dig nslookup drill host DNS_CHECK_FUNC requires awk requires tr requires date From 69ef4ff80edf807e5a732644b175cee8df8a722e Mon Sep 17 00:00:00 2001 From: Scott Gustafson Date: Tue, 15 Aug 2017 11:04:25 -0600 Subject: [PATCH 002/337] if using dig we need to check for either an SOA or A type record. Signed-off-by: Scott Gustafson --- getssl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index b903ab8..4b845c0 100755 --- a/getssl +++ b/getssl @@ -389,13 +389,22 @@ check_config() { # check the config files for all obvious errors config_errors=true fi # check domain exist - if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + if [[ "$DNS_CHECK_FUNC" == "drill" ]]; then if [[ "$($DNS_CHECK_FUNC "${d}" SOA|grep -c "^${d}")" -ge 1 ]]; then debug "found IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" config_errors=true fi + elif [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" -t SOA|grep -c "^${d}")" -ge 1 ]]; then + debug "found SOA IP for ${d}" + elif [[ "$($DNS_CHECK_FUNC "${d}" -t A|grep -c "^${d}")" -ge 1 ]]; then + debug "found A IP for ${d}" + else + info "${DOMAIN}: DNS lookup failed for ${d}" + config_errors=true + fi elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "^${d}")" -ge 1 ]]; then debug "found IP for ${d}" From b0803b02a59e2a695597ef25f980a9de570c17a3 Mon Sep 17 00:00:00 2001 From: Brian Hechinger Date: Thu, 2 Nov 2017 10:19:41 -0400 Subject: [PATCH 003/337] Adding the Route53 dns script and some docs. --- dns_scripts/DNS_ROUTE53.md | 10 +++++ dns_scripts/dns_route53.py | 87 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 dns_scripts/DNS_ROUTE53.md create mode 100755 dns_scripts/dns_route53.py diff --git a/dns_scripts/DNS_ROUTE53.md b/dns_scripts/DNS_ROUTE53.md new file mode 100644 index 0000000..30ee075 --- /dev/null +++ b/dns_scripts/DNS_ROUTE53.md @@ -0,0 +1,10 @@ +# Do DNS-01 verification using Route53 + +I was not about to implement this in BASH, sorry guys. I'd like you to have it, however. + +It's pretty simple to use. + +1. pip install boto3 dnspython +2. ln -s dns_route53.py dns_add_route53 +3. ln -s dns_route53.py dns_del_route53 +4. Use it just like the other scripts diff --git a/dns_scripts/dns_route53.py b/dns_scripts/dns_route53.py new file mode 100755 index 0000000..8805269 --- /dev/null +++ b/dns_scripts/dns_route53.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +import boto3, sys, time +from os.path import basename +import dns.resolver + +client = boto3.client('route53') + +name = sys.argv[0] +fqdn = sys.argv[1] +challenge = sys.argv[2] + +bname = basename(name) +if bname == 'dns_add_route53': + action = 'UPSERT' +elif bname == 'dns_del_route53': + action = 'DELETE' +else: + print("No such action: {a}".format(a=bname)) + sys.exit(1) + +try: + response = client.list_hosted_zones() +except Exception as e: + print("Oops: {e!r}".format(e=e)) + sys.exit(1) + +zone_id = "" +zone_list = dict() +for zone in response['HostedZones']: + if not zone['Config']['PrivateZone']: + zone_list[zone['Name']] = zone['Id'] + +for key in sorted(zone_list.iterkeys(), key=len, reverse=True): + if key in "{z}.".format(z=fqdn): + zone_id = zone_list[key] + +if zone_id == "": + print("We didn't find the zone") + sys.exit(1) + +challenge_fqdn = "_acme-challenge.{f}".format(f=fqdn) +try: + response = client.change_resource_record_sets( + HostedZoneId=zone_id, + ChangeBatch={ + 'Comment': 'getssl/Letsencrypt verification', + 'Changes': [ + { + 'Action': action, + 'ResourceRecordSet': { + 'Name': challenge_fqdn, + 'Type': 'TXT', + 'TTL': 300, + 'ResourceRecords': [{'Value': "\"{c}\"".format(c=challenge)}] + } + }, + ] + } + ) +except Exception as e: + print("Oops: {e!r}".format(e=e)) + sys.exit(1) + +waiting = 0 +if action == 'UPSERT': + # Wait until we see the record before returning. The ACME server's timeout is too short. + # But only if we're adding the record. Don't care how long it takes to delete. + while (True): + try: + my_resolver = dns.resolver.Resolver(configure=False) + my_resolver.nameservers = ['8.8.8.8', '8.8.4.4'] + results = my_resolver.query(challenge_fqdn, 'TXT') + data = str(results.response.answer[0][0]).strip('\"') + if data == challenge: + print("found {f} entry".format(f=challenge_fqdn)) + else: + print("found {f} entry but it has bad data: {d}".format(f=challenge_fqdn, + d=data)) + break + + except dns.resolver.NXDOMAIN: + waiting += 10 + print("Didn't find {f} entry yet, sleeping... ({w}s)".format(f=challenge_fqdn, + w=waiting)) + time.sleep(10) + pass \ No newline at end of file From eb8290ee9ad8764c3ae88beff7b1204073c102ad Mon Sep 17 00:00:00 2001 From: Colin 't Hart Date: Tue, 15 May 2018 11:33:47 +0200 Subject: [PATCH 004/337] Update getssl Grammar fixes. --- getssl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index 7f3713e..967cd05 100755 --- a/getssl +++ b/getssl @@ -173,7 +173,7 @@ # 2016-12-28 tidied up upgrade tmpfile handling (1.95) # 2017-01-01 update comments # 2017-01-01 create stable release 2.0 (2.00) -# 2017-01-02 Added option to limit amount of old versions to keep (2.01) +# 2017-01-02 Added option to limit number of old versions to keep (2.01) # 2017-01-03 Created check_config function to list all obvious config issues (2.02) # 2017-01-10 force renew if FORCE_RENEWAL file exists (2.03) # 2017-01-12 added drill, dig or host as alternatives to nslookup (2.04) @@ -463,7 +463,7 @@ check_getssl_upgrade() { # check if a more recent version of code is available a shopt -s -o noglob IFS=$'\n' getssl_versions=($(sort <<< "${getssl_versions[*]}")) shopt -u -o noglob - # Remove entries until given amount of old versions to keep is reached + # Remove entries until given number of old versions to keep is reached while [[ ${#getssl_versions[@]} -gt $_KEEP_VERSIONS ]]; do debug "removing old version ${getssl_versions[0]}" rm "${getssl_versions[0]}" @@ -937,15 +937,15 @@ help_message() { # print out the help message Options: -a, --all Check all certificates - -d, --debug Outputs debug information + -d, --debug Output debug information -c, --create Create default config files -f, --force Force renewal of cert (overrides expiry checks) -h, --help Display this help message and exit -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) - -Q, --mute Like -q, but mutes notification about successful upgrade + -Q, --mute Like -q, but also mute notification about successful upgrade -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) -u, --upgrade Upgrade getssl if a more recent version is available - -k, --keep "#" Maximum amount of old getssl versions to keep when upgrading + -k, --keep "#" Maximum number of old getssl versions to keep when upgrading -U, --nocheck Do not check if a more recent version is available -w working_dir "Working directory" From 398d1df99d3f413716fc046d27f9b29009529ac1 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:44:08 +0000 Subject: [PATCH 005/337] spelling: acme --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 7f3713e..7df04fc 100755 --- a/getssl +++ b/getssl @@ -50,7 +50,7 @@ # 2016-05-04 Improve check for if DNS_DEL_COMMAND is blank. (0.31) # 2016-05-06 Setting umask to 077 for security of private keys etc. (0.32) # 2016-05-20 update to reflect changes in staging ACME server json (0.33) -# 2016-05-20 tidying up checking of json following AMCE changes. (0.34) +# 2016-05-20 tidying up checking of json following ACME changes. (0.34) # 2016-05-21 added AUTH_DNS_SERVER to getssl.cfg as optional definition of authoritative DNS server (0.35) # 2016-05-21 added DNS_WAIT to getssl.cfg as (default = 10 seconds as before) (0.36) # 2016-05-21 added PUBLIC_DNS_SERVER option, for forcing use of an external DNS server (0.37) From 11f652dd63e76ae741169f6f74d26e62acee93ab Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:43:56 +0000 Subject: [PATCH 006/337] spelling: allow --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 7df04fc..6edf753 100755 --- a/getssl +++ b/getssl @@ -121,7 +121,7 @@ # 2016-10-05 improved the check for CSR with domain in subject (1.54) # 2016-10-06 prints update info on what was included in latest updates (1.55) # 2016-10-06 when using -a flag, ignore folders in working directory which aren't domains (1.56) -# 2016-10-12 alllow multiple tokens in DNS challenge (1.57) +# 2016-10-12 allow multiple tokens in DNS challenge (1.57) # 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servres, not just one primary server (1.58) # 2016-10-14 added archive of chain and private key for each cert, and purge old archives (1.59) # 2016-10-17 updated info comment on failed cert due to rate limits. (1.60) From 401ba2e305fcecf76e7f15d9a25289e5437d2265 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:43:46 +0000 Subject: [PATCH 007/337] spelling: archive --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 6edf753..56a92e8 100755 --- a/getssl +++ b/getssl @@ -245,7 +245,7 @@ ORIGCMD="$0 $*" # Define all functions (in alphabetical order) cert_archive() { # Archive certificate file by copying files to dated archive dir. - debug "creating an achive copy of current new certs" + debug "creating an archive copy of current new certs" date_time=$(date +%Y_%m_%d_%H_%M) mkdir -p "${DOMAIN_DIR}/archive/${date_time}" umask 077 From 71cbde40d88d4f36d35017241bd4c953b6bd77ba Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:35:18 +0000 Subject: [PATCH 008/337] spelling: macos --- README.md | 2 +- getssl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 879d8f3..484b1ec 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. ## Features -* **Bash** - It runs on virtually all unix machines, including BSD, most Linux distributions, MAC OSX. +* **Bash** - It runs on virtually all unix machines, including BSD, most Linux distributions, macOS. * **Get certificates for remote servers** - The tokens used to provide validation of domain ownership, and the certificates themselves can be automatically copied to remote servers (via ssh, sftp or ftp for tokens). The script doesn't need to run on the server itself. This can be useful if you don't have access to run such scripts on the server itself, e.g. if it's a shared server. * **Runs as a daily cron** - so certificates will be automatically renewed when required. * **Automatic certificate renewals** diff --git a/getssl b/getssl index 56a92e8..b5d41e0 100755 --- a/getssl +++ b/getssl @@ -676,7 +676,7 @@ date_epoc() { # convert the date into epoch time date_fmt() { # format date from epoc time to YYYY-MM-DD if [[ "$os" == "bsd" ]]; then #uses older style date function. date -j -f "%s" "$1" +%F - elif [[ "$os" == "mac" ]]; then # MAC OSX uses older BSD style date. + elif [[ "$os" == "mac" ]]; then # macOS uses older BSD style date. date -j -f "%s" "$1" +%F else date -d "@$1" +%F From 7b8f1b831fc66b24e4fdc00c12e9cd0933c5ada1 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:45:54 +0000 Subject: [PATCH 009/337] spelling: mac os x --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index b5d41e0..246f5c8 100755 --- a/getssl +++ b/getssl @@ -114,7 +114,7 @@ # 2016-09-27 added additional debug info issue #119 (1.47) # 2016-09-27 removed IPv6 switch in favour of checking both IPv4 and IPv6 (1.48) # 2016-09-28 Add -Q, or --mute, switch to mute notifications about successfully upgrading getssl (1.49) -# 2016-09-30 improved portability to work natively on FreeBSD, Slackware and OSX (1.50) +# 2016-09-30 improved portability to work natively on FreeBSD, Slackware and Mac OS X (1.50) # 2016-09-30 comment out PRIVATE_KEY_ALG from the domain template Issue #125 (1.51) # 2016-10-03 check remote certificate for right domain before saving to local (1.52) # 2016-10-04 allow existing CSR with domain name in subject (1.53) From bf96cb6d028dcbb9b910b41ba499e8c6a5d20407 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:46:21 +0000 Subject: [PATCH 010/337] spelling: permission --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 246f5c8..944d44c 100755 --- a/getssl +++ b/getssl @@ -127,7 +127,7 @@ # 2016-10-17 updated info comment on failed cert due to rate limits. (1.60) # 2016-10-17 fix error messages when using 1.0.1e-fips (1.61) # 2016-10-20 set secure permissions when generating account key (1.62) -# 2016-10-20 set permsissions to 700 for getssl script during upgrade (1.63) +# 2016-10-20 set permissions to 700 for getssl script during upgrade (1.63) # 2016-10-20 add option to revoke a certificate (1.64) # 2016-10-21 set revocation server default to acme-v01.api.letsencrypt.org (1.65) # 2016-10-21 bug fix for revocation on different servers. (1.66) From 8ce75abc3d168d97e0d2248b8fe540e5db7663df Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:46:34 +0000 Subject: [PATCH 011/337] spelling: private --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 944d44c..53aba49 100755 --- a/getssl +++ b/getssl @@ -1736,7 +1736,7 @@ else create_key "$ACCOUNT_KEY_TYPE" "$ACCOUNT_KEY" "$ACCOUNT_KEY_LENGTH" fi -# if not reusing priavte key, then remove the old keys +# if not reusing private key, then remove the old keys if [[ "$REUSE_PRIVATE_KEY" != "true" ]]; then if [[ -s "$DOMAIN_DIR/${DOMAIN}.key" ]]; then rm -f "$DOMAIN_DIR/${DOMAIN}.key" From a07e2a9757ee5bb118932b2d2277167e99fa17d9 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:46:57 +0000 Subject: [PATCH 012/337] spelling: secure --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 53aba49..42e2290 100755 --- a/getssl +++ b/getssl @@ -527,7 +527,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. fi elif [[ "${to:0:4}" == "ftp:" ]] ; then if [[ "$cert" != "challenge token" ]] ; then - error_exit "ftp is not a sercure method for copying certificates or keys" + error_exit "ftp is not a secure method for copying certificates or keys" fi debug "using ftp to copy the file from $from" ftpuser=$(echo "$to"| awk -F: '{print $2}') From 76b8897e9c59564bca6bfbe095fd8223526449bb Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:47:09 +0000 Subject: [PATCH 013/337] spelling: server --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 42e2290..9bf8ce7 100755 --- a/getssl +++ b/getssl @@ -122,7 +122,7 @@ # 2016-10-06 prints update info on what was included in latest updates (1.55) # 2016-10-06 when using -a flag, ignore folders in working directory which aren't domains (1.56) # 2016-10-12 allow multiple tokens in DNS challenge (1.57) -# 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servres, not just one primary server (1.58) +# 2016-10-14 added CHECK_ALL_AUTH_DNS option to check all DNS servers, not just one primary server (1.58) # 2016-10-14 added archive of chain and private key for each cert, and purge old archives (1.59) # 2016-10-17 updated info comment on failed cert due to rate limits. (1.60) # 2016-10-17 fix error messages when using 1.0.1e-fips (1.61) From 84f87b54dbe81f1df1292b90cbd2d6538fa7b32a Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:47:52 +0000 Subject: [PATCH 014/337] spelling: upgrading --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 9bf8ce7..692448f 100755 --- a/getssl +++ b/getssl @@ -1709,7 +1709,7 @@ if [[ -s "$CERT_FILE" ]]; then 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 - debug "upgradeing from fake cert to real" + debug "upgrading from fake cert to real" else info "${DOMAIN}: certificate is valid for more than $RENEW_ALLOW days (until $enddate)" # everything is OK, so exit. From 1ff407bd2d387f75faa9ecf3709db47310daa4cc Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Thu, 9 Aug 2018 04:48:07 +0000 Subject: [PATCH 015/337] spelling: verifying --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 484b1ec..42c7cdd 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,9 @@ and it should run, providing output like; ``` Registering account Verify each domain -Verifing yourdomain.com +Verifying yourdomain.com Verified yourdomain.com -Verifing www.yourdomain.com +Verifying www.yourdomain.com Verified www.yourdomain.com Verification completed, obtaining certificate. Certificate saved in /home/user/.getssl/yourdomain.com/yourdomain.com.crt From 0f9e831b3a5cefdaa54685ee39a5ca10729f9701 Mon Sep 17 00:00:00 2001 From: In/Progress Date: Sun, 2 Dec 2018 19:29:17 +0000 Subject: [PATCH 016/337] Allow file copying via davs (WebDAV over HTTPS) --- getssl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/getssl b/getssl index 7f3713e..2c7d4a8 100755 --- a/getssl +++ b/getssl @@ -564,6 +564,20 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. lcd $fromdir put $fromfile _EOF + elif [[ "${to:0:5}" == "davs:" ]] ; then + debug "using davs to copy the file from $from" + davsuser=$(echo "$to"| awk -F: '{print $2}') + davspass=$(echo "$to"| awk -F: '{print $3}') + davshost=$(echo "$to"| awk -F: '{print $4}') + davsport=$(echo "$to"| awk -F: '{print $5}') + davslocn=$(echo "$to"| awk -F: '{print $6}') + davsdirn=$(dirname "$davslocn") + davsfile=$(basename "$davslocn") + fromdir=$(dirname "$from") + fromfile=$(basename "$from") + debug "davs user=$davsuser - pass=$davspass - host=$davshost port=$davsport dir=$davsdirn file=$davsfile" + debug "from dir=$fromdir file=$fromfile" + curl -u "${davsuser}:${davspass}" -T "${fromdir}/${fromfile}" "https://${davshost}:${davsport}${davsdirn}/${davsfile}" else if ! mkdir -p "$(dirname "$to")" ; then error_exit "cannot create ACL directory $(basename "$to")" From f7324f35d711c4479fb286381d97b41585e142e1 Mon Sep 17 00:00:00 2001 From: In/Progress Date: Sun, 2 Dec 2018 20:08:40 +0000 Subject: [PATCH 017/337] Add documentation for ACL transfer via davs --- getssl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 2c7d4a8..70878d7 100755 --- a/getssl +++ b/getssl @@ -1313,10 +1313,13 @@ write_domain_template() { # write out a template file for a domain. # 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. + # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, + # password, host, port (explicitly needed even if using default port 443) and path on the server. #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge') + # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') #Set USE_SINGLE_ACL="true" to use a single ACL for all checks #USE_SINGLE_ACL="false" From 3a379ecebb875e37f73318768db6227d068b5cf7 Mon Sep 17 00:00:00 2001 From: In/Progress Date: Sun, 2 Dec 2018 20:19:46 +0000 Subject: [PATCH 018/337] Update Revision history as contribution guidelines suggest --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 70878d7..46764ca 100755 --- a/getssl +++ b/getssl @@ -184,10 +184,11 @@ # 2017-01-30 issue #243 compatibility with bash 3.0 (2.08) # 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09) # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) +# 2018-12-02 allow file transfer using WebDAV over HTTPS (2.11) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.10" +VERSION="2.11" # defaults ACCOUNT_KEY_LENGTH=4096 From 5343070654fdde11d3993a768b0ee4c8104eb120 Mon Sep 17 00:00:00 2001 From: Andy - Serverco <47356093+serverco-a@users.noreply.github.com> Date: Wed, 20 Mar 2019 18:18:16 +0000 Subject: [PATCH 019/337] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 28 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..116d2a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Operating system (please complete the following information):** + - OS: [e.g. Debian 9, Ubuntu 18.04, freeBSD ] + - Bash Version [e.g. GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..425aacb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From d62800b454bd4ddd00804625244c257a5025a745 Mon Sep 17 00:00:00 2001 From: Emily Karisch Date: Wed, 5 Sep 2018 11:20:46 +0200 Subject: [PATCH 020/337] Make a stricter comparison for matching against zone names This adds a dot in front of the fqdn and the zone name, so the comparison will only check against full DNS parts of the fqdn and the zone name. This will prevent to match a fqdn against an unrelated zone name which overlaps with the wanted zone in the naming. Fixes #398 --- dns_scripts/dns_route53.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_route53.py b/dns_scripts/dns_route53.py index 8805269..a972dfa 100755 --- a/dns_scripts/dns_route53.py +++ b/dns_scripts/dns_route53.py @@ -32,7 +32,7 @@ for zone in response['HostedZones']: zone_list[zone['Name']] = zone['Id'] for key in sorted(zone_list.iterkeys(), key=len, reverse=True): - if key in "{z}.".format(z=fqdn): + if ".{z}".format(z=key) in ".{z}.".format(z=fqdn): zone_id = zone_list[key] if zone_id == "": @@ -84,4 +84,4 @@ if action == 'UPSERT': print("Didn't find {f} entry yet, sleeping... ({w}s)".format(f=challenge_fqdn, w=waiting)) time.sleep(10) - pass \ No newline at end of file + pass From a31e4fafd94b240324898348e3e7f898b62e1db2 Mon Sep 17 00:00:00 2001 From: Emily Karisch Date: Mon, 30 Sep 2019 14:54:34 +0200 Subject: [PATCH 021/337] Use HTTP 1.1 as workaround atm Fixes #423 --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 7f3713e..45a3a8e 100755 --- a/getssl +++ b/getssl @@ -184,10 +184,11 @@ # 2017-01-30 issue #243 compatibility with bash 3.0 (2.08) # 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09) # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) +# 2017-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.11) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.10" +VERSION="2.11" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1135,7 +1136,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p CURL_HEADER="$TEMP_DIR/curl.header" dp="$TEMP_DIR/curl.dump" - CURL="curl --silent --dump-header $CURL_HEADER " + CURL="curl --http1.1 --silent --dump-header $CURL_HEADER " if [[ ${_USE_DEBUG} -eq 1 ]]; then CURL="$CURL --trace-ascii $dp " fi From 3f0b749e2b5a67b00312ad4fd91cbfbeba38d355 Mon Sep 17 00:00:00 2001 From: Emily Karisch Date: Tue, 1 Oct 2019 13:22:09 +0200 Subject: [PATCH 022/337] Check curl version if it supports the --http1.1 flag Co-authored-by: Robert Eden --- getssl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 45a3a8e..1a42c78 100755 --- a/getssl +++ b/getssl @@ -1136,7 +1136,14 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p CURL_HEADER="$TEMP_DIR/curl.header" dp="$TEMP_DIR/curl.dump" - CURL="curl --http1.1 --silent --dump-header $CURL_HEADER " + + CURL="curl " + if [[ "$($CURL -V | head -1 | cut -d' ' -f2 )" > "7.33" ]]; then + CURL="$CURL --http1.1 " + fi + + CURL="$CURL --silent --dump-header $CURL_HEADER " + if [[ ${_USE_DEBUG} -eq 1 ]]; then CURL="$CURL --trace-ascii $dp " fi From 4e7e13ca6c42984af2475d164f211a5cc271d606 Mon Sep 17 00:00:00 2001 From: Robbie Mckennie Date: Wed, 2 Oct 2019 21:19:02 +1300 Subject: [PATCH 023/337] Nsupdate one-liner (#393) Pipe the commands to nsupdate --- dns_scripts/dns_add_nsupdate | 8 +------- dns_scripts/dns_del_nsupdate | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 9e8ebfe..891614e 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -7,10 +7,4 @@ dnskeyfile="path/to/bla.key" fulldomain="$1" token="$2" -updatefile=$(mktemp) - -printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" > "${updatefile}" - -nsupdate -k "${dnskeyfile}" -v "${updatefile}" - -rm -f "${updatefile}" +printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" | nsupdate -k "${dnskeyfile}" -v diff --git a/dns_scripts/dns_del_nsupdate b/dns_scripts/dns_del_nsupdate index 62291b7..808b21c 100755 --- a/dns_scripts/dns_del_nsupdate +++ b/dns_scripts/dns_del_nsupdate @@ -6,10 +6,4 @@ dnskeyfile="path/to/bla.key" fulldomain="$1" token="$2" -updatefile=$(mktemp) - -printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" > "${updatefile}" - -nsupdate -k "${dnskeyfile}" -v "${updatefile}" - -rm -f "${updatefile}" +printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" | nsupdate -k "${dnskeyfile}" -v From 867ae36e962beca8a2e83a622822e6d431a8550f Mon Sep 17 00:00:00 2001 From: Emily Karisch Date: Wed, 2 Oct 2019 10:56:42 +0200 Subject: [PATCH 024/337] Fix changelog date --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index d415ddf..2269070 100755 --- a/getssl +++ b/getssl @@ -184,7 +184,7 @@ # 2017-01-30 issue #243 compatibility with bash 3.0 (2.08) # 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09) # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) -# 2017-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.11) +# 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.11) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From 70f5443a163ca7224399efeaf760e7c53c388233 Mon Sep 17 00:00:00 2001 From: Emily Karisch Date: Wed, 2 Oct 2019 10:51:25 +0200 Subject: [PATCH 025/337] Case insensitive processing of agreement url because of HTTP/2 Fixes #425 --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 2269070..274fe6a 100755 --- a/getssl +++ b/getssl @@ -185,10 +185,11 @@ # 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09) # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) # 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.11) +# 2019-10-02 issue #425 Case insensitive processing of agreement url because of HTTP/2 (2.12) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.11" +VERSION="2.12" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1483,7 +1484,7 @@ if [[ $_REVOKE -eq 1 ]]; then fi # get latest agreement from CA (as default) -AGREEMENT=$(curl -I "${CA}/terms" 2>/dev/null | awk '$1 ~ "Location:" {print $2}'|tr -d '\r') +AGREEMENT=$(curl -I "${CA}/terms" 2>/dev/null | awk 'tolower($1) ~ "location:" {print $2}'|tr -d '\r') # if nothing in command line, print help and exit. if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then From 358244c9e945ad3ecbcce1e29062023ba0a00c49 Mon Sep 17 00:00:00 2001 From: "Paul B. Henson" Date: Wed, 5 Dec 2018 16:08:14 -0800 Subject: [PATCH 026/337] Allow dns checks to deal with CNAME records Some environments might delegate dns validation to a subzone by using CNAME records. These changes allow getssl to validate dns updates when the verification name is a CNAME to the TXT record rather than a TXT record itself. --- getssl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index 274fe6a..5549200 100755 --- a/getssl +++ b/getssl @@ -186,10 +186,11 @@ # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) # 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.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) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.12" +VERSION="2.13" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1990,13 +1991,13 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ - | grep ^_acme|awk -F'"' '{ print $2}') + | grep '300 IN TXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ - | grep ^_acme|awk -F'"' '{ print $2}') + | grep 'descriptive text'|awk -F'"' '{ print $2}') else check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ - | grep ^_acme|awk -F'"' '{ print $2}') + | grep 'text ='|awk -F'"' '{ print $2}') fi debug "expecting $auth_key" debug "${ns} gave ... $check_result" From dd2fe394faef383423737abf4eaaa431232b0fd9 Mon Sep 17 00:00:00 2001 From: nikdow Date: Sat, 26 Oct 2019 18:34:52 +1100 Subject: [PATCH 027/337] Issue #442 looks for a default .cfg file in "$WORKING_DIR/getssl_default.cfg" --- getssl | 104 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/getssl b/getssl index f5bf77e..bc20d8e 100755 --- a/getssl +++ b/getssl @@ -1285,56 +1285,60 @@ usage() { # echos out the program usage } write_domain_template() { # write out a template file for a domain. - cat > "$1" <<- _EOF_domain_ - # 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" - - #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="${EX_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/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/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/ssl/${DOMAIN}.crt" - #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" - #CA_CERT_LOCATION="/etc/ssl/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="" - - # 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" - _EOF_domain_ + if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then + cp "$1" $WORKING_DIR/getssl_default.cfg + else + cat > "$1" <<- _EOF_domain_ + # 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" + + #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="${EX_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/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/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/ssl/${DOMAIN}.crt" + #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" + #CA_CERT_LOCATION="/etc/ssl/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="" + + # 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" + _EOF_domain_ + fi } write_getssl_template() { # write out the main template file From 15bfed6ec9d2fc3061ab331d7b0248784310b7ef Mon Sep 17 00:00:00 2001 From: cbdweb Date: Sat, 26 Oct 2019 18:56:32 +1100 Subject: [PATCH 028/337] fix up indents in here file --- getssl | 96 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/getssl b/getssl index bc20d8e..4e86b7b 100755 --- a/getssl +++ b/getssl @@ -1290,54 +1290,54 @@ write_domain_template() { # write out a template file for a domain. else cat > "$1" <<- _EOF_domain_ # 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" - - #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="${EX_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/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/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/ssl/${DOMAIN}.crt" - #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" - #CA_CERT_LOCATION="/etc/ssl/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="" - - # 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" - _EOF_domain_ + # 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" + + #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="${EX_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/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/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/ssl/${DOMAIN}.crt" + #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" + #CA_CERT_LOCATION="/etc/ssl/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="" + + # 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" + _EOF_domain_ fi } From 254214bd8bb3864d4548cd744d7d7b73fc871880 Mon Sep 17 00:00:00 2001 From: cbdweb Date: Sat, 26 Oct 2019 18:59:44 +1100 Subject: [PATCH 029/337] fix copy command --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 4e86b7b..945a4bd 100755 --- a/getssl +++ b/getssl @@ -1286,7 +1286,7 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then - cp "$1" $WORKING_DIR/getssl_default.cfg + cp $WORKING_DIR/getssl_default.cfg "$1" else cat > "$1" <<- _EOF_domain_ # Uncomment and modify any variables you need From 9a2ca60c3162dc1f5a328e7236d490c663f1ecf7 Mon Sep 17 00:00:00 2001 From: cbdweb Date: Sat, 26 Oct 2019 19:07:13 +1100 Subject: [PATCH 030/337] use sed to substitute ${domain}. Is there a better way? --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 945a4bd..e8fc270 100755 --- a/getssl +++ b/getssl @@ -1287,6 +1287,7 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then cp $WORKING_DIR/getssl_default.cfg "$1" + sed -i 's/\$\{domain\}/${domain}/g "$1" else cat > "$1" <<- _EOF_domain_ # Uncomment and modify any variables you need From 4524ca945598cea98fd7671c9742288aef788924 Mon Sep 17 00:00:00 2001 From: aws02 user Date: Sat, 26 Oct 2019 19:49:26 +1100 Subject: [PATCH 031/337] bug fixes; substitute for $domain in default .cfg file --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index e8fc270..44c21f8 100755 --- a/getssl +++ b/getssl @@ -1287,7 +1287,7 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then cp $WORKING_DIR/getssl_default.cfg "$1" - sed -i 's/\$\{domain\}/${domain}/g "$1" + sed -i -e 's/\$domain/'"${DOMAIN}"'/g' "$1" else cat > "$1" <<- _EOF_domain_ # Uncomment and modify any variables you need From f97f3bfb19dfd179aae0130cac9c9bd218713d9b Mon Sep 17 00:00:00 2001 From: aws02 user Date: Sun, 27 Oct 2019 13:25:27 +1100 Subject: [PATCH 032/337] use envsubst --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 44c21f8..5b77078 100755 --- a/getssl +++ b/getssl @@ -1287,7 +1287,9 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then cp $WORKING_DIR/getssl_default.cfg "$1" - sed -i -e 's/\$domain/'"${DOMAIN}"'/g' "$1" + export DOMAIN="$DOMAIN" + export EX_SANS="$EX_SANS" + envsubst < $WORKING_DIR/getssl_default.cfg > "$1" else cat > "$1" <<- _EOF_domain_ # Uncomment and modify any variables you need From e7d1774b41f374d2fe424a5d11a55e5cfcced14c Mon Sep 17 00:00:00 2001 From: aws02 user Date: Sun, 27 Oct 2019 13:34:20 +1100 Subject: [PATCH 033/337] remove cp command --- getssl | 1 - 1 file changed, 1 deletion(-) diff --git a/getssl b/getssl index 5b77078..0469281 100755 --- a/getssl +++ b/getssl @@ -1286,7 +1286,6 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then - cp $WORKING_DIR/getssl_default.cfg "$1" export DOMAIN="$DOMAIN" export EX_SANS="$EX_SANS" envsubst < $WORKING_DIR/getssl_default.cfg > "$1" From 394a350496e33f1591e8f9c649d7f8783c554bd2 Mon Sep 17 00:00:00 2001 From: srvrco Date: Thu, 4 Jan 2018 07:08:35 +0000 Subject: [PATCH 034/337] draft version to work with APIv2 --- getssl | 587 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 455 insertions(+), 132 deletions(-) diff --git a/getssl b/getssl index f5bf77e..e0235a4 100755 --- a/getssl +++ b/getssl @@ -184,6 +184,7 @@ # 2017-01-30 issue #243 compatibility with bash 3.0 (2.08) # 2017-01-30 issue #243 additional compatibility with bash 3.0 (2.09) # 2017-02-18 add OCSP Must-Staple to the domain csr generation (2.10) +# 2018-01-04 updating to use the updated letsencrypt APIv2 # 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.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) @@ -241,6 +242,7 @@ _UPGRADE_CHECK=1 _USE_DEBUG=0 config_errors="false" LANG=C +API=1 # store copy of original command in case of upgrading script and re-running ORIGCMD="$0 $*" @@ -278,8 +280,14 @@ check_challenge_completion() { # checks with the ACME server if our challenge is send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" # check response from our request to perform challenge - if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then - error_exit "$domain:Challenge error: $code" + if [[ $API -eq 1 ]]; then + if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then + error_exit "$domain:Challenge error: $code" + fi + else # APIv2 + if [[ ! -z "$code" ]] && [[ ! "$code" == '200' ]] ; then + error_exit "$domain:Challenge error: $code" + fi fi # loop "forever" to keep checking for a response from the ACME server. @@ -807,36 +815,45 @@ get_certificate() { # get certificate for csr, if all domains validated. der=$(openssl req -in "$gc_csr" -outform DER | urlbase64) debug "der $der" - send_signed_request "$URL_new_cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" - - # convert certificate information into correct format and save to file. - CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r') - debug "certdata location = $CertData" - if [[ "$CertData" ]] ; then - echo -----BEGIN CERTIFICATE----- > "$gc_certfile" - curl --silent "$CertData" | openssl base64 -e >> "$gc_certfile" - echo -----END CERTIFICATE----- >> "$gc_certfile" - info "Certificate saved in $CERT_FILE" - fi + if [[ $API -eq 1 ]]; then + send_signed_request "$URL_new_cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" + # convert certificate information into correct format and save to file. + CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r') + debug "certdata location = $CertData" + if [[ "$CertData" ]] ; then + echo -----BEGIN CERTIFICATE----- > "$gc_certfile" + curl --silent "$CertData" | openssl base64 -e >> "$gc_certfile" + echo -----END CERTIFICATE----- >> "$gc_certfile" + info "Certificate saved in $CERT_FILE" + fi - # If certificate wasn't a valid certificate, error exit. - if [[ -z "$CertData" ]] ; then - response2=$(echo "$response" | fold -w64 |openssl base64 -d) - debug "response was $response" - error_exit "Sign failed: $(echo "$response2" | grep "detail")" - fi + # If certificate wasn't a valid certificate, error exit. + if [[ -z "$CertData" ]] ; then + response2=$(echo "$response" | fold -w64 |openssl base64 -d) + debug "response was $response" + error_exit "Sign failed: $(echo "$response2" | grep "detail")" + fi - # get a copy of the CA certificate. - IssuerData=$(grep -i '^Link' "$CURL_HEADER" \ - | cut -d " " -f 2\ - | cut -d ';' -f 1 \ - | sed 's///g') - if [[ "$IssuerData" ]] ; then - echo -----BEGIN CERTIFICATE----- > "$gc_cafile" - curl --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" - echo -----END CERTIFICATE----- >> "$gc_cafile" - info "The intermediate CA cert is in $gc_cafile" + # get a copy of the CA certificate. + IssuerData=$(grep -i '^Link' "$CURL_HEADER" \ + | cut -d " " -f 2\ + | cut -d ';' -f 1 \ + | sed 's///g') + if [[ "$IssuerData" ]] ; then + echo -----BEGIN CERTIFICATE----- > "$gc_cafile" + curl --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" + echo -----END CERTIFICATE----- >> "$gc_cafile" + info "The intermediate CA cert is in $gc_cafile" + fi + else # APIv2 + send_signed_request "$FinalizeLink" "{\"csr\": \"$der\"}" "needbase64" + debug "order link was $OrderLink" + cd=$(curl --silent $OrderLink) + CertData=$(json_get "$cd" "certificate") + debug "CertData is at $CertData" + curl --silent "$CertData" > "$CERT_FILE" + info "Certificate saved in $CERT_FILE" fi } @@ -966,28 +983,206 @@ info() { # write out info as long as the quiet flag has not been set. fi } -json_get() { # get the value corresponding to $2 in the JSON passed as $1. - # remove newlines, so it's a single chunk of JSON - json_data=$( echo "$1" | tr '\n' ' ') - # if $3 is defined, this is the section which the item is in. - if [[ ! -z "$3" ]]; then - jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}') - if [[ "$2" == "uri" ]]; then - jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}') - jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}') +json_awk() { +echo $1 | awk ' +{ + tokenize($0) # while(get_token()) {print TOKEN} + if (0 == parse()) { + apply(JPATHS, NJPATHS) + } +} + +function apply (ary,size,i) { + for (i=1; i NTOKENS) to = NTOKENS + for (i = from; i < ITOKENS; i++) + context = context sprintf("%s ", TOKENS[i]) + context = context "<<" got ">> " + for (i = ITOKENS + 1; i <= to; i++) + context = context sprintf("%s ", TOKENS[i]) + scream("json_awk expected <" expected "> but got <" got "> at input token " ITOKENS "\n" context) +} + +function reset() { + TOKEN=""; delete TOKENS; NTOKENS=ITOKENS=0 + delete JPATHS; NJPATHS=0 + VALUE="" +} + +function scream(msg) { + FAILS[FILENAME] = FAILS[FILENAME] (FAILS[FILENAME]!="" ? "\n" : "") msg + msg = FILENAME ": " msg + print msg >"/dev/stderr" +} + +function tokenize(a1,pq,pb,ESCAPE,CHAR,STRING,NUMBER,KEYWORD,SPACE) { + SPACE="[[:space:]]+" + gsub(/\"[^[:cntrl:]\"\\]*((\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})[^[:cntrl:]\"\\]*)*\"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[[:space:]]+|./, "\n&", a1) + gsub("\n" SPACE, "\n", a1) + sub(/^\n/, "", a1) + ITOKENS=0 # get_token() helper + return NTOKENS = split(a1, TOKENS, /\n/) +}' +} + +json_get() { # get values from json + if [[ -z "$1" ]] || [[ "$1" == "null" ]]; then + echo "json was blank" + return + fi + if [[ $API = 1 ]]; then + # remove newlines, so it's a single chunk of JSON + json_data=$( echo "$1" | tr '\n' ' ') + # if $3 is defined, this is the section which the item is in. + if [[ ! -z "$3" ]]; then + jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}') + if [[ "$2" == "uri" ]]; then + jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}') + jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}') + else + jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + fi else - jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + fi + # check number of quotes + jg_q=${jg_result//[^\"]/} + # if 2 quotes, assume it's a quoted variable and just return the data within the quotes. + if [[ ${#jg_q} -eq 2 ]]; then + echo "$jg_result" | awk -F'"' '{print $2}' + else + echo "$jg_result" fi else - jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') - fi - # check number of quotes - jg_q=${jg_result//[^\"]/} - # if 2 quotes, assume it's a quoted variable and just return the data within the quotes. - if [[ ${#jg_q} -eq 2 ]]; then - echo "$jg_result" | awk -F'"' '{print $2}' - else - echo "$jg_result" + if [[ ! -z "$6" ]]; then + full=$(json_awk "$1") + section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') + echo "$full" | grep "^..${5}\",$section" | awk '{print $2}' | tr -d '"' + elif [[ ! -z "$5" ]]; then + full=$(json_awk "$1") + section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') + echo "$full" | grep "^..${2}\",$section" | grep "$5" | awk '{print $2}' | tr -d '"' + elif [[ ! -z "$3" ]]; then + json_awk "$1" | grep "^..${2}...${3}" | awk '{print $2}' | tr -d '"' + elif [[ ! -z "$2" ]]; then + json_awk "$1" | grep "^..${2}" | awk '{print $2}' | tr -d '"' + else + json_awk "$1" + fi fi } @@ -1134,7 +1329,6 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p needbase64=$3 debug url "$url" - debug payload "$payload" CURL_HEADER="$TEMP_DIR/curl.header" dp="$TEMP_DIR/curl.dump" @@ -1152,57 +1346,100 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p # convert payload to url base 64 payload64="$(printf '%s' "${payload}" | urlbase64)" - debug payload64 "$payload64" # get nonce from ACME server - nonceurl="$CA/directory" - nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') - - debug nonce "$nonce" - - # Build header with just our public key and algorithm information - header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}' - - # Build another header which also contains the previously received nonce and encode it as urlbase64 - protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' - protected64="$(printf '%s' "${protected}" | urlbase64)" - debug protected "$protected" - - # Sign header with nonce and our payload with our private key and encode signature as urlbase64 - sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg" - - # Send header + extended header + payload + signature to the acme-server - body="{\"header\": ${header}," - body="${body}\"protected\": \"${protected64}\"," - body="${body}\"payload\": \"${payload64}\"," - body="${body}\"signature\": \"${signed64}\"}" - debug "header, payload and signature = $body" - - code="500" - loop_limit=5 - while [[ "$code" -eq 500 ]]; do - if [[ "$needbase64" ]] ; then - response=$($CURL -X POST --data "$body" "$url" | urlbase64) + if [[ $API -eq 1 ]]; then + nonceurl="$CA/directory" + nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') + else # APIv2 + nonce=$($CURL -I "$URL_newNonce" | grep "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') + fi + + nonceproblem="true" + while [[ "$nonceproblem" == "true" ]]; do + + debug nonce "$nonce" + + # Build header with just our public key and algorithm information + header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}' + + # 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)" + else # APIv2 + if [[ -z "$KID" ]]; then + debug "KID is blank, so using jwk" + protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' + protected64="$(printf '%s' "${protected}" | urlbase64)" + else + debug "using KID=${KID}" + protected="{\"alg\": \"$jwkalg\", \"kid\": \"$KID\",\"nonce\": \"${nonce}\", \"url\": \"${url}\"}" + debug "protected = $protected" + protected64="$(printf '%s' "${protected}" | urlbase64)" + fi + fi + + # Sign header with nonce and our payload with our private key and encode signature as urlbase64 + sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg" + + # Send header + extended header + payload + signature to the acme-server + if [[ $API -eq 1 ]]; then + debug "header = $header" + debug "protected = $protected" + debug "payload = $payload" + body="{\"header\": ${header}," + body="${body}\"protected\": \"${protected64}\"," + body="${body}\"payload\": \"${payload64}\"," + body="${body}\"signature\": \"${signed64}\"}" + debug "header, payload and signature = $body" else - response=$($CURL -X POST --data "$body" "$url") + debug "protected = $protected" + debug "payload = $payload" + body="{" + body="${body}\"protected\": \"${protected64}\"," + body="${body}\"payload\": \"${payload64}\"," + body="${body}\"signature\": \"${signed64}\"}" + debug "header, payload and signature = $body" fi + + code="500" + loop_limit=5 + while [[ "$code" -eq 500 ]]; do + if [[ "$needbase64" ]] ; then + response=$($CURL -X POST --data "$body" "$url" | urlbase64) + else + response=$($CURL -X POST --data "$body" "$url") + fi - responseHeaders=$(cat "$CURL_HEADER") - debug responseHeaders "$responseHeaders" - debug response "$response" - code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) - debug code "$code" - response_status=$(json_get "$response" status \ - | head -1| awk -F'"' '{print $2}') - debug "response status = $response_status" - - if [[ "$code" -eq 500 ]]; then - info "error on acme server - trying again ...." - sleep 2 - loop_limit=$((loop_limit - 1)) - if [[ $loop_limit -lt 1 ]]; then - error_exit "500 error from ACME server: $response" + responseHeaders=$(cat "$CURL_HEADER") + debug responseHeaders "$responseHeaders" + debug response "$response" + code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) + debug code "$code" + if [[ $API -eq 1 ]]; then + response_status=$(json_get "$response" status \ + | head -1| awk -F'"' '{print $2}') + else # APIv2 + response_status=$(json_get "$response" status) fi + debug "response status = $response_status" + if [[ "$code" -eq 500 ]]; then + info "error on acme server - trying again ...." + debug "loop_limit = $loop_limit" + sleep 5 + loop_limit=$((loop_limit - 1)) + if [[ $loop_limit -lt 1 ]]; then + error_exit "500 error from ACME server: $response" + fi + fi + done + if [[ $response == *"error:badNonce"* ]]; then + debug "bad nonce" + nonce=$(echo "$responseHeaders" | grep "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') + debug "trying new nonce $nonce" + else + nonceproblem="false" fi done } @@ -1319,11 +1556,11 @@ write_domain_template() { # write out a template file for a domain. # 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/ssl/${DOMAIN}.crt" - #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" - #CA_CERT_LOCATION="/etc/ssl/chain.crt" + #DOMAIN_CERT_LOCATION="/etc/ssl/${DOMAIN}.crt" # this is domain cert + #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" # this is domain key + #CA_CERT_LOCATION="/etc/ssl/chain.crt" # this is CA cert #DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert - #DOMAIN_PEM_LOCATION="" # this is the domain_key, 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="" @@ -1630,11 +1867,31 @@ if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then info "${DOMAIN}: forcing renewal (due to FORCE_RENEWAL file)" fi +echo "testing with API1" # Obtain CA resource locations -ca_all_loc=$(curl "${CA}/directory" 2>/dev/null) -URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') -URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') -URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') +if [[ $API -eq 1 ]]; then + ca_all_loc=$(curl "${CA}/directory" 2>/dev/null) + debug "ca_all_loc from ${CA}/dir gives $ca_all_loc" + URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') + URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') + URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') + if [[ -z "$URL_new_reg" ]]; then + API=2 + debug "API=1 failed, setting API=2" + fi +fi +echo "testing with api2" +if [[ $API -eq 2 ]]; then + ca_all_loc=$(curl "${CA}/dir" 2>/dev/null) + debug "ca_all_loc from ${CA}/dir gives $ca_all_loc" + URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') + URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') + URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') + if [[ -z "$ca_all_loc" ]]; then + debug "unknown API type $API" + graceful_exit + fi +fi # 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 @@ -1786,23 +2043,40 @@ fi # currently the code registers every time, and gets an "already registered" back if it has been. get_signing_params "$ACCOUNT_KEY" -if [[ "$ACCOUNT_EMAIL" ]] ; then - regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' -else - regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' -fi - info "Registering account" # send the request to the ACME server. -send_signed_request "$URL_new_reg" "$regjson" +if [[ $API -eq 1 ]]; then + if [[ "$ACCOUNT_EMAIL" ]] ; then + regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' + else + regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' + fi + send_signed_request "$URL_new_reg" "$regjson" +elif [[ $API -eq 2 ]]; then + if [[ "$ACCOUNT_EMAIL" ]] ; then + regjson='{"termsOfServiceAgreed": true, "contact": ["mailto: '$ACCOUNT_EMAIL'"]}' + else + regjson='{"termsOfServiceAgreed": true}' + fi + send_signed_request "$URL_newAccount" "$regjson" +else + debug "cant determine account API" + graceful_exit +fi if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then info "Registered" + KID=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + debug "KID=_$KID}_" echo "$response" > "$TEMP_DIR/account.json" elif [[ "$code" == '409' ]] ; then debug "Already registered" +elif [[ "$code" == '200' ]] ; then + KID=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + debug responseHeaders "$responseHeaders" + debug "Already registered account, KID=_${KID}_" else - error_exit "Error registering account ... $(json_get "$response" detail)" + error_exit "Error registering account ...$responseHeaders ... $(json_get "$response" detail)" fi # end of registering account with CA @@ -1811,10 +2085,33 @@ info "Verify each domain" # loop through domains for cert ( from SANS list) if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//,/ } + alldomains=${SANS//,/ } else alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") fi + +if [[ $API -eq 2 ]]; then + dstring="[" + for d in $alldomains; do + dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," + done + dstring="${dstring: : -1}]" + #new URL_newOrder + request="{\"identifiers\": $dstring}" + send_signed_request "$URL_newOrder" "$request" + OrderLink=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + debug "Order link $OrderLink" + FinalizeLink=$(json_get "$response" "finalize") + debug "finalise link $FinalizeLink" + dn=0 + for d in $alldomains; do + # get authorizations link + AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") + debug "authorizations link for $d - ${AuthLink[$dn]}" + ((dn++)) + done +fi + dn=0 for d in $alldomains; do # $d is domain in current loop, which is number $dn for ACL @@ -1826,13 +2123,17 @@ for d in $alldomains; do fi # request a challenge token from ACME server - request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}" - send_signed_request "$URL_new_authz" "$request" - - debug "completed send_signed_request" - # check if we got a valid response and token, if not then error exit - if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then - error_exit "new-authz error: $response" + if [[ $API -eq 1 ]]; then + request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}" + send_signed_request "$URL_new_authz" "$request" + debug "completed send_signed_request" + + # check if we got a valid response and token, if not then error exit + if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then + error_exit "new-authz error: $response" + fi + else + response_status="" fi if [[ $response_status == "valid" ]]; then @@ -1848,13 +2149,24 @@ for d in $alldomains; do else PREVIOUSLY_VALIDATED="false" if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification - # get the dns component of the ACME response - # get the token from the dns component - token=$(json_get "$response" "token" "dns-01") - debug token "$token" - # get the uri from the dns component - uri=$(json_get "$response" "uri" "dns-01") - debug uri "$uri" + if [[ $API -eq 1 ]]; then + # get the dns component of the ACME response + # get the token from the dns component + token=$(json_get "$response" "token" "dns-01") + debug token "$token" + # get the uri from the dns component + uri=$(json_get "$response" "uri" "dns-01") + debug uri "$uri" + else # APIv2 + response=$(curl --silent ${AuthLink[$dn]} 2>/dev/null) + debug "authlink response = $response" + # get the token from the http-01 component + token=$(json_get "$response" "challenges" "type" "dns-01" "token") + debug token "$token" + # get the uri from the http component + uri=$(json_get "$response" "challenges" "type" "dns-01" "url") + debug uri "$uri" + fi keyauthorization="$token.$thumbprint" debug keyauthorization "$keyauthorization" @@ -1895,12 +2207,23 @@ for d in $alldomains; do _EOF_ else # set up the correct http token for verification - # get the token from the http component - token=$(json_get "$response" "token" "http-01") - debug token "$token" - # get the uri from the http component - uri=$(json_get "$response" "uri" "http-01") - debug uri "$uri" + if [[ $API -eq 1 ]]; then + # get the token from the http component + token=$(json_get "$response" "token" "http-01") + debug token "$token" + # get the uri from the http component + uri=$(json_get "$response" "uri" "http-01") + debug uri "$uri" + else # APIv2 + response=$(curl --silent ${AuthLink[$dn]} 2>/dev/null) + debug "authlink response = $response" + # get the token from the http-01 component + 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") + debug uri "$uri" + fi #create signed authorization key from token. keyauthorization="$token.$thumbprint" From fb9c8703775e2a1cf549039583e0649a8e51817e Mon Sep 17 00:00:00 2001 From: srvrco Date: Thu, 4 Jan 2018 10:42:38 +0000 Subject: [PATCH 035/337] updateing getssl for APIv2 --- getssl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index e0235a4..1fa8be0 100755 --- a/getssl +++ b/getssl @@ -849,7 +849,7 @@ 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 --silent $OrderLink) + cd=$(curl --silent "$OrderLink") CertData=$(json_get "$cd" "certificate") debug "CertData is at $CertData" curl --silent "$CertData" > "$CERT_FILE" @@ -983,7 +983,8 @@ info() { # write out info as long as the quiet flag has not been set. fi } -json_awk() { +json_awk() { # AWK json converter used for API2 - needs tidying up ;) +# shellcheck disable=SC2086 echo $1 | awk ' { tokenize($0) # while(get_token()) {print TOKEN} @@ -1421,7 +1422,12 @@ 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 - response_status=$(json_get "$response" status) + if [[ ${response##*()} == "{"* ]]; then + response_status=$(json_get "$response" status) + else + debug "response not in json format" + debug "$response" + fi fi debug "response status = $response_status" if [[ "$code" -eq 500 ]]; then @@ -2158,7 +2164,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - response=$(curl --silent ${AuthLink[$dn]} 2>/dev/null) + response=$(curl --silent "${AuthLink[$dn]}" 2>/dev/null) debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") @@ -2215,7 +2221,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - response=$(curl --silent ${AuthLink[$dn]} 2>/dev/null) + response=$(curl --silent "${AuthLink[$dn]}" 2>/dev/null) debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") From 034ce548504a317e4c4f0ef6ccf854658a2ebc4d Mon Sep 17 00:00:00 2001 From: srvrco Date: Thu, 4 Jan 2018 11:30:33 +0000 Subject: [PATCH 036/337] modifying debugs --- getssl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/getssl b/getssl index 1fa8be0..57e9296 100755 --- a/getssl +++ b/getssl @@ -1873,7 +1873,6 @@ if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then info "${DOMAIN}: forcing renewal (due to FORCE_RENEWAL file)" fi -echo "testing with API1" # Obtain CA resource locations if [[ $API -eq 1 ]]; then ca_all_loc=$(curl "${CA}/directory" 2>/dev/null) @@ -1886,7 +1885,6 @@ if [[ $API -eq 1 ]]; then debug "API=1 failed, setting API=2" fi fi -echo "testing with api2" if [[ $API -eq 2 ]]; then ca_all_loc=$(curl "${CA}/dir" 2>/dev/null) debug "ca_all_loc from ${CA}/dir gives $ca_all_loc" @@ -1894,7 +1892,7 @@ if [[ $API -eq 2 ]]; then URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') if [[ -z "$ca_all_loc" ]]; then - debug "unknown API type $API" + debug "unknown API type" graceful_exit fi fi From 692920eec75e80ae3215caf05fcf5100cfb61a9e Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 5 Jan 2018 08:56:18 +0000 Subject: [PATCH 037/337] completed initial testing on acme-staging-v02.api.letsencrypt.org --- getssl | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/getssl b/getssl index 57e9296..2ac3fc7 100755 --- a/getssl +++ b/getssl @@ -1353,7 +1353,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p nonceurl="$CA/directory" nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') else # APIv2 - nonce=$($CURL -I "$URL_newNonce" | grep "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') + nonce=$($CURL -I "$URL_newNonce" | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') fi nonceproblem="true" @@ -1874,27 +1874,36 @@ if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then fi # Obtain CA resource locations -if [[ $API -eq 1 ]]; then +ca_all_loc=$(curl "${CA}" 2>/dev/null) +debug "ca_all_loc from ${CA} gives $ca_all_loc" +# APIv1 +URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') +URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') +URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') +#API v2 +URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') +URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') +URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') +if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then ca_all_loc=$(curl "${CA}/directory" 2>/dev/null) - debug "ca_all_loc from ${CA}/dir gives $ca_all_loc" + debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" + # APIv1 URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') - if [[ -z "$URL_new_reg" ]]; then - API=2 - debug "API=1 failed, setting API=2" - fi -fi -if [[ $API -eq 2 ]]; then - ca_all_loc=$(curl "${CA}/dir" 2>/dev/null) - debug "ca_all_loc from ${CA}/dir gives $ca_all_loc" + #API v2 URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') - if [[ -z "$ca_all_loc" ]]; then - debug "unknown API type" - graceful_exit - fi +fi + +if [[ ! -z "$URL_new_reg" ]]; then + API=1 +elif [[ ! -z "$URL_newAccount" ]]; then + API=2 +else + info "unknown API version" + graceful_exit fi # if check_remote is true then connect and obtain the current certificate (if not forcing renewal) @@ -2074,11 +2083,13 @@ if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then debug "KID=_$KID}_" echo "$response" > "$TEMP_DIR/account.json" elif [[ "$code" == '409' ]] ; then - debug "Already registered" + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') + debug responseHeaders "$responseHeaders" + debug "Already registered KID=$KID" elif [[ "$code" == '200' ]] ; then - KID=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') debug responseHeaders "$responseHeaders" - debug "Already registered account, KID=_${KID}_" + debug "Already registered account, KID=${KID}" else error_exit "Error registering account ...$responseHeaders ... $(json_get "$response" detail)" fi @@ -2100,7 +2111,9 @@ if [[ $API -eq 2 ]]; then dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," done dstring="${dstring: : -1}]" - #new URL_newOrder + # 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)\"" request="{\"identifiers\": $dstring}" send_signed_request "$URL_newOrder" "$request" OrderLink=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') From a1b322fdec949a2a794059584654418b043ef855 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 5 Jan 2018 09:16:03 +0000 Subject: [PATCH 038/337] adding Torsten's joker.com API --- dns_scripts/dns_add_joker | 44 ++++++++++++++++++++++++++++++++++++ dns_scripts/dns_add_nsupdate | 11 +++++++-- dns_scripts/dns_del_joker | 44 ++++++++++++++++++++++++++++++++++++ dns_scripts/dns_del_nsupdate | 11 ++++++++- 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 dns_scripts/dns_add_joker create mode 100644 dns_scripts/dns_del_joker diff --git a/dns_scripts/dns_add_joker b/dns_scripts/dns_add_joker new file mode 100644 index 0000000..c345a52 --- /dev/null +++ b/dns_scripts/dns_add_joker @@ -0,0 +1,44 @@ +#!/bin/bash + +FULLDOMAIN=$1 +TOKEN=$2 +TMPFILE=$(mktemp /tmp/dns_add_joker.XXXXXXX) + +USERNAME="youruser" +PASSWORD="yourpassword" + +# Verify that required parameters are set +if [[ -z "${FULLDOMAIN}" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi + +if [[ -z "${TOKEN}" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi + +DOMAIN_ROOT=$(echo "${FULLDOMAIN}" | awk -F\. '{print $(NF-1) FS $NF}') + +SID=$(curl --silent -X POST https://dmapi.joker.com/request/login \ + -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ + -H "application/x-www-form-urlencoded" -d "username=${USERNAME}&password=${PASSWORD}" \ + -i -k 2>/dev/null | grep Auth-Sid | awk '{ print $2 }') + +## put zone data in tempfile +curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \ + -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ + -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \ + tail -n +7 >${TMPFILE} + +## add txt record +printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>${TMPFILE} + +## generate encoded url data +URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') + +## write new zonefile to joker +curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 + +## remove tempfile +rm -f ${TMPFILE} diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 891614e..14896d8 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -3,8 +3,15 @@ # example of script to add token to local dns using nsupdate dnskeyfile="path/to/bla.key" - +server="localhost" fulldomain="$1" token="$2" -printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" | nsupdate -k "${dnskeyfile}" -v +updatefile=$(mktemp) + +printf "server ${server}\n" > "${updatefile}" +printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" + +nsupdate -k "${dnskeyfile}" -v "${updatefile}" + +rm -f "${updatefile}" diff --git a/dns_scripts/dns_del_joker b/dns_scripts/dns_del_joker new file mode 100644 index 0000000..f1869c8 --- /dev/null +++ b/dns_scripts/dns_del_joker @@ -0,0 +1,44 @@ +#!/bin/bash + +FULLDOMAIN=$1 +TOKEN=$2 +TMPFILE=$(mktemp /tmp/dns_add_joker.XXXXXXX) + +USERNAME="youruser" +PASSWORD="yourpassword" + +# Verify that required parameters are set +if [[ -z "${FULLDOMAIN}" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi + +if [[ -z "${TOKEN}" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi + +DOMAIN_ROOT=$(echo "${FULLDOMAIN}" | awk -F\. '{print $(NF-1) FS $NF}') + +SID=$(curl --silent -X POST https://dmapi.joker.com/request/login \ + -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ + -H "application/x-www-form-urlencoded" -d "username=${USERNAME}&password=${PASSWORD}" \ + -i -k 2>/dev/null | grep Auth-Sid | awk '{ print $2 }') + +## put zone data in tempfile +curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \ + -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ + -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \ + tail -n +7 >${TMPFILE} + +## remove txt record +sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" ${TMPFILE} + +## generate encoded url data +URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') + +## write new zonefile to joker +curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 + +## remove tempfile +rm -f ${TMPFILE} diff --git a/dns_scripts/dns_del_nsupdate b/dns_scripts/dns_del_nsupdate index 808b21c..be41a7b 100755 --- a/dns_scripts/dns_del_nsupdate +++ b/dns_scripts/dns_del_nsupdate @@ -3,7 +3,16 @@ # example of script to add token to local dns using nsupdate dnskeyfile="path/to/bla.key" +server="localhost" + fulldomain="$1" token="$2" -printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" | nsupdate -k "${dnskeyfile}" -v +updatefile=$(mktemp) + +printf "server ${server}\n" > "${updatefile}" +printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" + +nsupdate -k "${dnskeyfile}" -v "${updatefile}" + +rm -f "${updatefile}" From c946d42a127c145691b29515eb4eb20e2c33242d Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 5 Jan 2018 11:49:41 +0000 Subject: [PATCH 039/337] adding SSH_OPTS as requested --- README.md | 1 + getssl | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 42c7cdd..8549bda 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for auto * **Simple and easy to use** * **Detailed debug info** - Whilst it shouldn't be needed, detailed debug information is available. * **Reload services** - After a new certificate is obtained then the relevant services (e.g. apache/nginx/postfix) can be reloaded. +* **ACME v1 and V2** - Supports both ACME versions 1 and 2 ## Installation Since the script is only one file, you can use the following command for a quick installation of GetSSL only: diff --git a/getssl b/getssl index 2ac3fc7..17668d1 100755 --- a/getssl +++ b/getssl @@ -196,7 +196,7 @@ VERSION="2.13" # defaults ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY_TYPE="rsa" -CA="https://acme-staging.api.letsencrypt.org" +CA="https://acme-staging-v02.api.letsencrypt.org/directory" CA_CERT_LOCATION="" CHALLENGE_CHECK_TYPE="http" CHECK_ALL_AUTH_DNS="false" @@ -534,7 +534,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. debug "servername $servername" debug "file $tofile" # shellcheck disable=SC2029 - ssh "$servername" "chown $TOKEN_USER_ID $tofile" + ssh "$SSH_OPTS" "$servername" "chown $TOKEN_USER_ID $tofile" fi elif [[ "${to:0:4}" == "ftp:" ]] ; then if [[ "$cert" != "challenge token" ]] ; then @@ -1229,9 +1229,9 @@ reload_service() { # Runs a command to reload services ( via ssh if needed) sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') command=${RELOAD_CMD:(( ${#sshhost} + 5))} debug "running following command to reload cert" - debug "ssh $sshhost ${command}" + debug "ssh $SSH_OPTS $sshhost ${command}" # shellcheck disable=SC2029 - ssh "$sshhost" "${command}" 1>/dev/null 2>&1 + ssh "$SSH_OPTS" "$sshhost" "${command}" 1>/dev/null 2>&1 # allow 2 seconds for services to restart sleep 2 else @@ -1442,7 +1442,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p done if [[ $response == *"error:badNonce"* ]]; then debug "bad nonce" - nonce=$(echo "$responseHeaders" | grep "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') + nonce=$(echo "$responseHeaders" | grep -i "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') debug "trying new nonce $nonce" else nonceproblem="false" @@ -1534,7 +1534,7 @@ write_domain_template() { # write out a template file for a domain. # 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" + #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" @@ -1586,7 +1586,7 @@ write_getssl_template() { # write out the main template file # see https://github.com/srvrco/getssl/wiki/Config-variables for details # # The staging server is best for testing (hence set as default) - CA="https://acme-staging.api.letsencrypt.org" + 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" @@ -2283,9 +2283,9 @@ for d in $alldomains; do sshhost=$(echo "${t_loc}"| awk -F: '{print $2}') command="rm -f ${t_loc:(( ${#sshhost} + 5))}/${token:?}" debug "running following command to remove token" - debug "ssh $sshhost ${command}" + debug "ssh $SSH_OPTS $sshhost ${command}" # shellcheck disable=SC2029 - ssh "$sshhost" "${command}" 1>/dev/null 2>&1 + ssh "$SSH_OPTS" "$sshhost" "${command}" 1>/dev/null 2>&1 rm -f "${TEMP_DIR:?}/${token:?}" elif [[ "${t_loc:0:4}" == "ftp:" ]] ; then debug "using ftp to remove token file" From a41fa6bad864b6d9ecae7862777890017923ad3d Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 5 Jan 2018 12:59:15 +0000 Subject: [PATCH 040/337] adding godaddy dns scripts and related updates --- dns_scripts/GoDaddy-README.txt | 60 +++++ dns_scripts/dns_add_godaddy | 40 ++++ dns_scripts/dns_add_joker | 8 +- dns_scripts/dns_add_nsupdate | 36 ++- dns_scripts/dns_add_ovh | 66 +++--- dns_scripts/dns_del_godaddy | 38 +++ dns_scripts/dns_del_joker | 8 +- dns_scripts/dns_del_nsupdate | 39 ++- dns_scripts/dns_del_ovh | 70 +++--- dns_scripts/dns_godaddy | 418 +++++++++++++++++++++++++++++++++ 10 files changed, 691 insertions(+), 92 deletions(-) create mode 100644 dns_scripts/GoDaddy-README.txt create mode 100755 dns_scripts/dns_add_godaddy mode change 100644 => 100755 dns_scripts/dns_add_joker mode change 100644 => 100755 dns_scripts/dns_add_ovh create mode 100755 dns_scripts/dns_del_godaddy mode change 100644 => 100755 dns_scripts/dns_del_joker mode change 100644 => 100755 dns_scripts/dns_del_ovh create mode 100644 dns_scripts/dns_godaddy diff --git a/dns_scripts/GoDaddy-README.txt b/dns_scripts/GoDaddy-README.txt new file mode 100644 index 0000000..d58ba73 --- /dev/null +++ b/dns_scripts/GoDaddy-README.txt @@ -0,0 +1,60 @@ +Using GoDaddy DNS for LetsEncrypt domain validation. + +Quick guide to setting up getssl for domain validation of +GoDaddy DNS domains. + +There are two prerequisites to using getssl with GoDaddy DNS: + +1) Obtain an API access key from developer.godaddy.com + At first sign-up, you will be required to take a "test" key. + This is NOT what you need. Accept it, then get a "Production" + key. At this writing, there is no charge - but you must have + a GoDaddy customer account. + + You must get the API key for the account which owns the domain + that you want to get certificates for. If the domains that you + manage are owned by more than one account, get a key for each. + + The access key consists of a "Key" and a "Secret". You need + both. + +2) Obtain JSON.sh - https://github.com/dominictarr/JSON.sh + +With those in hand, the installation procedure is: + +1) Put JSON.sh in the getssl DNS scripts directory + Default: /usr/share/getssl/dns_scripts + +2) Open your config file (the global file in ~/.getssl/getssl.cfg + or the per-account file in ~/.getssl/example.net/getssl.cfg + +3) Set the following options: + VALIDATE_VIA_DNS="true" + DNS_ADD_COMMAND="/usr/share/getssl/dns_scripts/dns_add_godaddy" + DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_del_godaddy" + # The API key for your account/this domain + export GODADDY_KEY="..." GODADDY_SECRET="..." + + 4) Set any other options that you wish (per the standard + directions.) Use the test CA to make sure that + everything is setup correctly. + +That's it. getssl example.net will now validate with DNS. + +To trace record additions and removals, run getssl as +GODADDY_TRACE=Y getssl example.net + +There are additional options, which are documented in the +*godaddy" files and dns_godaddy -h. + +Copyright (2017) Timothe Litt litt at acm _dot org + +This sofware may be freely used providing this notice is included with +all copies. The name of the author may not be used to endorse +any other product or derivative work. No warranty is provided +and the user assumes all responsibility for use of this software. + +Report any issues to https://github.com/tlhackque/getssl/issues. + +Enjoy. + diff --git a/dns_scripts/dns_add_godaddy b/dns_scripts/dns_add_godaddy new file mode 100755 index 0000000..dfd3b3b --- /dev/null +++ b/dns_scripts/dns_add_godaddy @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (2017) Timothe Litt litt at acm _dot org + +# Add token to GoDaddy dns using dns_godaddy + +# You do not have to customize this script. +# +# Obtain the Key and Secret from https://developer.godaddy.com/getstarted +# You must obtain a "Production" key - NOT the "Test" key you're required +# to get first. +# +# Obtain JSON.sh from https://github.com/dominictarr/JSON.sh +# Place it in (or softlink it to) the same directory as $GODADDY_SCRIPT, +# or specify its location with GODADDY_JSON The default is +# /usr/share/getssl/dns_scripts/ +# +# Define GODADDY_KEY and GO_DADDY_SECRET in your account or domain getssl.cfg +# +# See GoDaddy-README.txt for complete instructions. + +fulldomain="$1" +token="$2" + +[ -z "$GODADDY_SCRIPT" ] && GODADDY_SCRIPT="/usr/share/getssl/dns_scripts/dns_godaddy" +[[ "$GODADDY_SCRIPT" =~ ^~ ]] && \ + eval 'GODADDY_SCRIPT=`readlink -nf ' $GODADDY_SCRIPT '`' + +if [ ! -x "$GODADDY_SCRIPT" ]; then + echo "$GODADDY_SCRIPT: not found. Please install, softlink or set GODADDY_SCRIPT to its full path" + echo "See GoDaddy-README.txt for complete instructions." + exit 3 +fi + +# JSON.sh is not (currently) used by add + +export GODADDY_KEY +export GODADDY_SECRET + +$GODADDY_SCRIPT -q add "${fulldomain}" "_acme-challenge.${fulldomain}." "${token}" diff --git a/dns_scripts/dns_add_joker b/dns_scripts/dns_add_joker old mode 100644 new mode 100755 index c345a52..c100886 --- a/dns_scripts/dns_add_joker +++ b/dns_scripts/dns_add_joker @@ -29,16 +29,16 @@ SID=$(curl --silent -X POST https://dmapi.joker.com/request/login \ curl --silent -X POST https://dmapi.joker.com/request/dns-zone-get \ -H "Accept: application/json" -H "User-Agent: getssl/0.1" \ -H "application/x-www-form-urlencoded" -d "domain=${DOMAIN_ROOT}&auth-sid=${SID}" | \ - tail -n +7 >${TMPFILE} + tail -n +7 >"${TMPFILE}" ## add txt record -printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>${TMPFILE} +printf "_acme-challenge.%s. TXT 0 \"%s \" 300\n\n" "${FULLDOMAIN}" "${TOKEN}" >>"${TMPFILE}" ## generate encoded url data -URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') +URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') ## write new zonefile to joker curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 ## remove tempfile -rm -f ${TMPFILE} +rm -f "${TMPFILE}" diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 14896d8..13b0fc9 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -2,16 +2,38 @@ # example of script to add token to local dns using nsupdate -dnskeyfile="path/to/bla.key" -server="localhost" fulldomain="$1" token="$2" -updatefile=$(mktemp) +# VARIABLES: +# +# DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required +# DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires +# some special action: mounting a disk, decrypting a file.. +# Called with the operation 'add' and action 'open" / 'close' -printf "server ${server}\n" > "${updatefile}" -printf "update add _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" -nsupdate -k "${dnskeyfile}" -v "${updatefile}" +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi -rm -f "${updatefile}" + options="-k ${DNS_NSUPDATE_KEYFILE}" +fi + +# Note that blank line is a "send" command to nsupdate + +nsupdate "${options}" -v <${TMPFILE} + tail -n +7 >"${TMPFILE}" ## remove txt record -sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" ${TMPFILE} +sed -i "/_acme-challenge.${FULLDOMAIN}.*${TOKEN}.*/d" "${TMPFILE}" ## generate encoded url data -URLDATA=$(cat ${TMPFILE} | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') +URLDATA=$(cat "${TMPFILE}" | sed 's/ /%20/g' | sed 's/"/%22/g' | sed ':a;N;$!ba;s/\n/%0A/g') ## write new zonefile to joker curl --silent --output /dev/null "https://dmapi.joker.com/request/dns-zone-put?domain=${DOMAIN_ROOT}&zone=${URLDATA}&auth-sid=${SID}" 2>&1 ## remove tempfile -rm -f ${TMPFILE} +rm -f "${TMPFILE}" diff --git a/dns_scripts/dns_del_nsupdate b/dns_scripts/dns_del_nsupdate index be41a7b..e8442e3 100755 --- a/dns_scripts/dns_del_nsupdate +++ b/dns_scripts/dns_del_nsupdate @@ -1,18 +1,39 @@ #!/bin/bash -# example of script to add token to local dns using nsupdate - -dnskeyfile="path/to/bla.key" -server="localhost" +# example of script to remove token from local dns using nsupdate fulldomain="$1" token="$2" -updatefile=$(mktemp) +# VARIABLES: +# +# DNS_NSUPDATE_KEYFILE - path to a TSIG key file, if required +# DNS_NSUPDATE_GETKEY - command to execute if access to the key file requires +# some special action: dismounting a disk, encrypting a +# file... Called with the operation 'del' and action +# 'open" / 'close' + +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi + + options="-k ${DNS_NSUPDATE_KEYFILE}" +fi + +# Note that blank line is a "send" command to nsupdate + +nsupdate "${options}" -v < "${updatefile}" -printf "update delete _acme-challenge.%s. 300 in TXT \"%s\"\n\n" "${fulldomain}" "${token}" >> "${updatefile}" +sts=$? -nsupdate -k "${dnskeyfile}" -v "${updatefile}" +if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'close' "${fulldomain}" ; then + exit $(( sts + ( $? * 10 ) )) + fi +fi -rm -f "${updatefile}" +exit ${sts} diff --git a/dns_scripts/dns_del_ovh b/dns_scripts/dns_del_ovh old mode 100644 new mode 100755 index c242de0..bf7fe09 --- a/dns_scripts/dns_del_ovh +++ b/dns_scripts/dns_del_ovh @@ -1,35 +1,35 @@ -#!/bin/bash - -domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g')) -challenge="$2" - -# Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/ -# permissions needed are /domain/zone/* in GET,POST,DELETE -applicationKey="YourAK" -applicationSecret="YourAS" -consumerKey="YourCK" - -topDomain=${domains[2]} -subDomain=${domains[1]%%.} - -function send -{ - method=$1 - url=$2 - body=$3 - ts=$(date +%s) - - sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1) - curl -X ${method} -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" https://eu.api.ovh.com/1.0${url} -} - -# Creation request -oldResult=$(send GET "/domain/zone/${topDomain}/record?fieldType=TXT&subDomain=${subDomain}" ""|sed -e 's/\[//' -e 's/\]//') - -for num in ${oldResult//,/ } -do - send DELETE "/domain/zone/${topDomain}/record/${num}" "" -done - -# Refresh request -send POST /domain/zone/${topDomain}/refresh "" +#!/bin/bash + +domains=($(echo "$1"|sed -e 's/^\(\([a-zA-Z0-9.-]*\?\)\.\)*\([a-zA-Z0-9-]\+\.[a-zA-Z-]\+\)$/"\1" _acme-challenge.\2 \3/g')) +#challenge="$2" + +# Please, do not forget to ask for your credentials at https://eu.api.ovh.com/createToken/ +# permissions needed are /domain/zone/* in GET,POST,DELETE +applicationKey="YourAK" +applicationSecret="YourAS" +consumerKey="YourCK" + +topDomain=${domains[2]} +subDomain=${domains[1]%%.} + +function send +{ + method=$1 + url=$2 + body=$3 + ts=$(date +%s) + + sign=\$1\$$(echo -n "${applicationSecret}+${consumerKey}+${method}+https://eu.api.ovh.com/1.0${url}+${body}+${ts}"|sha1sum|cut -d" " -f1) + curl -X "${method}" -H "Content-Type: application/json" -H "X-Ovh-Application: ${applicationKey}" -H "X-Ovh-Timestamp: ${ts}" -H "X-Ovh-Signature: ${sign}" -H "X-Ovh-Consumer: ${consumerKey}" -d "${body}" "https://eu.api.ovh.com/1.0${url}" +} + +# Creation request +oldResult=$(send GET "/domain/zone/${topDomain}/record?fieldType=TXT&subDomain=${subDomain}" ""|sed -e 's/\[//' -e 's/\]//') + +for num in ${oldResult//,/ } +do + send DELETE "/domain/zone/${topDomain}/record/${num}" "" +done + +# Refresh request +send POST "/domain/zone/${topDomain}/refresh" "" diff --git a/dns_scripts/dns_godaddy b/dns_scripts/dns_godaddy new file mode 100644 index 0000000..4443bd4 --- /dev/null +++ b/dns_scripts/dns_godaddy @@ -0,0 +1,418 @@ +#!/bin/bash + +# Copyright (2017) Timothe Litt litt at acm _dot org + +VERSION="1.0.1" +PROG="`basename $0`" + +# This script is used to update TXT records in GoDaddy DNS server +# It depends on JSON.sh from https://github.com/dominictarr/JSON.sh +# Place it in (or softlink it to) the same directory as this script, +# or specify its location with GODADDY_JSON +# +# See the usage text below, 00GoDaddy-README.txt, dns_add_godaddy +# and dns_del_godaddy for additional information. +# +# It may be freely used providing this notice is included with +# all copies. The name of the author may not be used to endorse +# any other product or derivative work. No warranty is provided +# and the user assumes all responsibility for use of this software. +# +# Bug reports are welcome at https://github.com/tlhackque/getssl/issues. + +API='https://api.godaddy.com/v1/domains' +APISIGNUP='https://developer.godaddy.com/getstarted' +GETJSON='https://github.com/dominictarr/JSON.sh' + +VERB="y" +DEBUG="$GODADDY_DEBUG" +[ -z "$JSON" ] && JSON="$GODADDY_JSON" +[ -z "$JSON" ] && JSON="`dirname $0`/JSON.sh" + +while getopts 'dhj:k:s:t:qv' opt; do + case $opt in + d) DEBUG="Y" ;; + j) JSON="$OPTARG" ;; + k) GODADDY_KEY="$OPTARG" ;; + s) GODADDY_SECRET="$OPTARG" ;; + t) TRACE="$OPTARG" ;; + q) VERB= ;; + v) echo "dns_godaddy version $VERSION"; exit 0 ;; + *) + cat <&2 +$0: requires JSON.sh as "$JSON" + +The full path to JSON.sh can be specified with -j, or the +GODADDY_JSON environment variable. + +You can obtain a copy from $GETJSON + +Then place or softlink it to $JSON or set GODADDY_JSON. +EOF + exit 2 +fi + +if [ -z "$GODADDY_KEY" ] || [ -z "$GODADDY_SECRET" ]; then + echo "GODADDY_KEY and GODADDY secret must be defined" >&2 + exit 3 +fi + +[ -n "$DEBUG" ] && VERB="y" +[ -n "$GODADDY_TRACE" ] && VERB="Y" +[ -n "$GODADDY_TFILE" ] && TRACE="$GODADDY_TFILE" + +# Get parameters & validate + +op="$1" +if ! [[ "$op" =~ ^(add|del)$ ]]; then + echo "Operation must be \"add\" or \"del\"" >&2 + exit 3 +fi +domain="$2" +domain="${domain%'.'}" +if [ -z "$domain" ]; then + echo "'domain' parameter is required, see -h" >&2 + exit 3 +fi +name="$3" +if [ -z "$name" ]; then + echo "'name' parameter is required, see -h" >&2 + exit 3 +fi +! [[ "$name" =~ [.]$ ]] && name="${name}.${domain}." +data="$4" +if [ -z "$data" ]; then + echo "'data' parameter is required, see -h" >&2 + exit 3 +fi + +if [ "$op" = 'del' ]; then + ttl= +elif [ -z "$5" ]; then + ttl="600" # GoDaddy minimum TTL is 600 +elif ! [[ "$5" =~ ^[0-9]+$ ]]; then + echo "TTL $5 is not numeric" >&2 + exit 3 +elif [ $5 -lt 600 ]; then + [ -n "$VERB" ] && \ + echo "$5 is less than GoDaddy minimum of 600; increased to 600" >&2 + ttl="600" +else + ttl="$5" +fi + +# --- Done with parameters + +[ -n "$DEBUG" ] && \ + echo "$PROG: $op $domain $name \"$data\" $ttl" >&2 + +# Authorization header has secret and key + +authhdr="Authorization: sso-key $GODADDY_KEY:$GODADDY_SECRET" + +if [ -n "$TRACE" ]; then + function timestamp { local tm="`LC_TIME=C date '+%T.%N'`" + local class="$1"; shift + echo "${tm:0:15} ** ${class}: $*" >>"$TRACE" + } + timestamp 'Info' "$PROG" "V$VERSION" 'Starting new protocol trace' + timestamp 'Args' "$@" + curl --help | grep -q -- --trace-time && CURL_TFLAGS="--trace-time" # 7.14.0 + function curl { + command curl ${CURL_TFLAGS} --trace-ascii % "$@" 2>>"$TRACE" + } + [ -n "$VERB" ] && echo "Appending protocol trace to $TRACE" +fi + +[ -n "$DEBUG" ] && echo "$authhdr" >&2 + +if [ "$op" = "add" ]; then + # May need to retry due to zone cuts + + while [[ "$domain" =~ [^.]+\.[^.]+ ]]; do + + url="$API/$domain/records/TXT/$name" + + request='{"data":"'$data'","ttl":'$ttl'}' + [ -n "$DEBUG" ] && cat >&2 <&2 <&2 + exit $sts + fi + if ! echo "$result" | grep -q '^HTTP/.* 200 '; then + code="`echo "$result" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" + msg="`echo "$result" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" + if [ "$code" = "DUPLICATE_RECORD" ]; then + if [ -n "$VERB" ]; then + echo "$msg in $domain" >&2 + fi + exit 0 # Duplicate record is still success + fi + if [ "$code" = 'UNKNOWN_DOMAIN' ]; then + if [[ "$domain" =~ ^([^.]+)\.([^.]+\.[^.]+.*) ]]; then + [ -n "$DEBUG" ] && \ + echo "$domain unknown, trying ${BASH_REMATCH[2]}" >&2 + domain="${BASH_REMATCH[2]}" + continue; + fi + fi + echo "Request failed $msg" >&2 + exit 1 + fi + [ -n "$VERB" ] && echo "$domain: added $name $ttl TXT \"$data\"" >&2 + exit 0 + done +fi + + +# ----- Delete + +# There is no delete API +# But, it is possible to replace all TXT records. +# +# So, first query for all TXT records + +# May need to retry due to zone cuts + +while [[ "$domain" =~ [^.]+\.[^.]+ ]]; do + + url="$API/$domain/records/TXT" + [ -n "$DEBUG" ] && echo "Query for TXT records to: $url" >&2 + + current="$(curl -i -s -X GET --config - "$url" <&2 + exit $sts + fi + [ -n "$DEBUG" ] && cat >&2 <&2 + domain="${BASH_REMATCH[2]}" + continue; + fi + fi + echo "Request failed $msg" >&2 + exit 1 + fi + # Remove headers + + current="$(echo "$current" | sed -e'0,/^\r*$/d')" + break +done + + # The zone cut is known, so the replace can't fail due to UNKNOWN domain + +if [ "$current" = '[]' ]; then # No TXT records in zone + [ -n "$VERB" ] && echo "$domain: $name TXT \"$data\" does not exist" >&2 + [ -n "$DEBUG" ] && echo "No TXT records in $domain" >&2 + exit 1 # Intent was to change, so error status +fi + +[ -n "$DEBUG" ] && echo "Response is valid" + +# Prepare request to replace TXT RRSET + +# Parse JSON and select only the record structures, which are [index] { ...} + +current="$(echo "$current" | $JSON | sed -n -e'/^\[[0-9][0-9]*\]/{ s/^\[[0-9][0-9]*\]//; p}')" +base="$current" + +[ -n "$DEBUG" ] && cat >&2 <&2 + exit 1 # Intent was to change DNS, so this is an error +fi + +# Remove whitespace and insert needed commmas +# +fmtnew="$new" +new=$(echo "$new" | sed -e"s/}/},/g; \$s/},/}/;" | tr -d '\t\n') + +if [ -z "$new" ]; then + [ -n "$VERB" ] && echo "Replacing last TXT record with a dummy (see -h)" >&2 + new='{"type":"TXT","name":"_dummy.record_","data":"_This record is not used_","ttl":601}' + dummy="t" + TAB=$'\t' + fmtnew="${TAB}$new" + if [ "$fmtnew" = "$base" ]; then + [ -n "$VERB" ] && echo "This tool can't delete a placeholder when it is the only TXT record" >&2 + exit 0 # Not really success, but retrying won't help. + fi +fi + +request="[$new]" + +[ -n "$DEBUG" ] && cat >&2 <&2 <&2 <&2 + exit $sts +fi +if ! echo "$result" | grep -q '^HTTP/.* 200 '; then + result="$(echo "$result" | sed -e'0,/^\r*$/d')" + code="`echo "$result" | grep '"code":' | sed -e's/^.*"code":"//; s/\".*$//'`" + msg="`echo "$result" | grep '"message":' | sed -e's/^.*"message":"//; s/\".*$//'`" + echo "Request failed $msg" >&2 + exit 1 +fi + +if [ -n "$VERB" ]; then + if [ -n "$dummy" ]; then + echo "$domain: replaced $name TXT \"$data\" with a placeholder" >&2 + else + echo "$domain: deleted $name TXT \"$data\"" >&2 + fi +fi +exit 0 + From 051eb8d105690b2c6e187902f8756496572d2267 Mon Sep 17 00:00:00 2001 From: srvrco Date: Fri, 5 Jan 2018 13:09:49 +0000 Subject: [PATCH 041/337] adding SSH_OPTS (shellcheck) --- getssl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 17668d1..23781d7 100755 --- a/getssl +++ b/getssl @@ -534,7 +534,8 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. debug "servername $servername" debug "file $tofile" # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "$servername" "chown $TOKEN_USER_ID $tofile" + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$servername" "chown $TOKEN_USER_ID $tofile" fi elif [[ "${to:0:4}" == "ftp:" ]] ; then if [[ "$cert" != "challenge token" ]] ; then @@ -1231,7 +1232,8 @@ reload_service() { # Runs a command to reload services ( via ssh if needed) debug "running following command to reload cert" debug "ssh $SSH_OPTS $sshhost ${command}" # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "$sshhost" "${command}" 1>/dev/null 2>&1 + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 # allow 2 seconds for services to restart sleep 2 else @@ -2285,7 +2287,8 @@ for d in $alldomains; do debug "running following command to remove token" debug "ssh $SSH_OPTS $sshhost ${command}" # shellcheck disable=SC2029 - ssh "$SSH_OPTS" "$sshhost" "${command}" 1>/dev/null 2>&1 + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 rm -f "${TEMP_DIR:?}/${token:?}" elif [[ "${t_loc:0:4}" == "ftp:" ]] ; then debug "using ftp to remove token file" From 13be6817b375c36e50ae5b03a5e3723da0c7390d Mon Sep 17 00:00:00 2001 From: srvrco Date: Sun, 7 Jan 2018 10:48:12 +0000 Subject: [PATCH 042/337] adding various updates / from issues to beta --- getssl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/getssl b/getssl index 23781d7..79ab8da 100755 --- a/getssl +++ b/getssl @@ -378,7 +378,7 @@ check_config() { # check the config files for all obvious errors fi dn=0 - tmplist=$(mktemp) + tmplist=$(mktemp 2>/dev/null || mktemp -t getssl) for d in $alldomains; do # loop over domains (dn is domain number) debug "checking domain $d" if [[ "$(grep "^${d}$" "$tmplist")" = "$d" ]]; then @@ -401,7 +401,7 @@ check_config() { # check the config files for all obvious errors fi # check domain exist if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" SOA|grep -c "^${d}")" -ge 1 ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "${d}")" -ge 1 ]]; then debug "found IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" @@ -436,7 +436,7 @@ check_config() { # check the config files for all obvious errors } check_getssl_upgrade() { # check if a more recent version of code is available available - TEMP_UPGRADE_FILE="$(mktemp)" + TEMP_UPGRADE_FILE="$(mktemp 2>/dev/null || mktemp -t getssl)" curl --silent "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE" errcode=$? if [[ $errcode -eq 60 ]]; then @@ -631,7 +631,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) if [[ ! -s "$csr_file" ]] || [[ "$_RECREATE_CSR" == "1" ]]; then info "creating domain csr - $csr_file" # create a temporary config file, for portability. - tmp_conf=$(mktemp) + tmp_conf=$(mktemp 2>/dev/null || mktemp -t getssl) cat "$SSLCONF" > "$tmp_conf" printf "[SAN]\n%s" "$SANLIST" >> "$tmp_conf" # add OCSP Must-Staple to the domain csr @@ -1251,7 +1251,7 @@ revoke_certificate() { # revoke a certificate ACCOUNT_KEY="$REVOKE_KEY" # need to set the revoke key as "account_key" since it's used in send_signed_request. get_signing_params "$REVOKE_KEY" - TEMP_DIR=$(mktemp -d) + TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) debug "revoking from $CA" rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64) send_signed_request "$URL_revoke" "{\"resource\": \"revoke-cert\", \"certificate\": \"$rcertdata\"}" @@ -1579,6 +1579,7 @@ write_domain_template() { # write out a template file for a domain. # an update to confirm correct certificate is running (if CHECK_REMOTE) is set to true #SERVER_TYPE="https" #CHECK_REMOTE="true" + #CHECK_REMOTE_WAIT="2" # wait 2 seconds before checking the remote server _EOF_domain_ } @@ -2406,6 +2407,7 @@ cert_archive debug "Certificates obtained and archived locally, will now copy to specified locations" # copy certs to the correct location (creating concatenated files as required) +umask 077 copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" @@ -2471,7 +2473,7 @@ if [[ ! -z "$DOMAIN_PEM_LOCATION" ]]; then fi fi # end of copying certs. - +umask "$ORIG_UMASK" # Run reload command to restart apache / nginx or whatever system reload_service From ad1f76cdec41cab44303d2f5ceb0671f740928a2 Mon Sep 17 00:00:00 2001 From: srvrco Date: Sun, 7 Jan 2018 14:49:54 +0000 Subject: [PATCH 043/337] correct bug for cert order location --- getssl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/getssl b/getssl index 79ab8da..3fedb65 100755 --- a/getssl +++ b/getssl @@ -260,11 +260,11 @@ cert_archive() { # Archive certificate file by copying files to dated archive d cp "$CA_CERT" "${DOMAIN_DIR}/archive/${date_time}/chain.crt" cat "$CERT_FILE" "$CA_CERT" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.crt" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cp "${CERT_FILE::-4}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.crt" + cp "${CERT_FILE%.*}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.crt" cp "$DOMAIN_DIR/${DOMAIN}.ec.csr" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.csr" cp "$DOMAIN_DIR/${DOMAIN}.ec.key" "${DOMAIN_DIR}/archive/${date_time}/${DOMAIN}.ec.key" - cp "${CA_CERT::-4}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/chain.ec.crt" - cat "${CERT_FILE::-4}.ec.crt" "${CA_CERT::-4}.ec.crt" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.ec.crt" + cp "${CA_CERT%.*}.ec.crt" "${DOMAIN_DIR}/archive/${date_time}/chain.ec.crt" + cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "${DOMAIN_DIR}/archive/${date_time}/fullchain.ec.crt" fi umask "$ORIG_UMASK" debug "purging old GetSSL archives" @@ -665,8 +665,8 @@ create_key() { # create a domain key (if it doesn't already exist) esac umask "$ORIG_UMASK" # remove csr on generation of new domain key - if [[ -e "${key_loc::-4}.csr" ]]; then - rm -f "${key_loc::-4}.csr" + if [[ -e "${key_loc%.*}.csr" ]]; then + rm -f "${key_loc%.*}.csr" fi fi } @@ -2119,7 +2119,7 @@ if [[ $API -eq 2 ]]; then # dstring="${dstring},\"notAfter\": \"$(date -d "2 days" --utc +%FT%TZ)\"" request="{\"identifiers\": $dstring}" send_signed_request "$URL_newOrder" "$request" - OrderLink=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') debug "Order link $OrderLink" FinalizeLink=$(json_get "$response" "finalize") debug "finalise link $FinalizeLink" @@ -2397,8 +2397,8 @@ get_certificate "$DOMAIN_DIR/${DOMAIN}.csr" \ "$CA_CERT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then get_certificate "$DOMAIN_DIR/${DOMAIN}.ec.csr" \ - "${CERT_FILE::-4}.ec.crt" \ - "${CA_CERT::-4}.ec.crt" + "${CERT_FILE%.*}.ec.crt" \ + "${CA_CERT%.*}.ec.crt" fi # create Archive of new certs and keys. @@ -2415,18 +2415,18 @@ copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then if [[ ! -z "$DOMAIN_CERT_LOCATION" ]]; then copy_file_to_location "ec domain certificate" \ - "${CERT_FILE::-4}.ec.crt" \ - "${DOMAIN_CERT_LOCATION::-4}.ec.crt" + "${CERT_FILE%.*}.ec.crt" \ + "${DOMAIN_CERT_LOCATION%.*}.ec.crt" fi if [[ ! -z "$DOMAIN_KEY_LOCATION" ]]; then copy_file_to_location "ec private key" \ "$DOMAIN_DIR/${DOMAIN}.ec.key" \ - "${DOMAIN_KEY_LOCATION::-4}.ec.key" + "${DOMAIN_KEY_LOCATION%.*}.ec.key" fi if [[ ! -z "$CA_CERT_LOCATION" ]]; then copy_file_to_location "ec CA certificate" \ - "${CA_CERT::-4}.ec.crt" \ - "${CA_CERT_LOCATION::-4}.ec.crt" + "${CA_CERT%.*}.ec.crt" \ + "${CA_CERT_LOCATION%.*}.ec.crt" fi fi @@ -2440,7 +2440,7 @@ if [[ ! -z "$DOMAIN_CHAIN_LOCATION" ]]; then cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem" copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "${CERT_FILE::-4}.ec.crt" "${CA_CERT::-4}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" + cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}.ec" fi fi @@ -2454,7 +2454,7 @@ if [[ ! -z "$DOMAIN_KEY_CERT_LOCATION" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE::-4}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}.ec" fi fi @@ -2468,7 +2468,7 @@ if [[ ! -z "$DOMAIN_PEM_LOCATION" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE::-4}.ec.crt" "${CA_CERT::-4}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}.ec" fi fi From c3fe2d5214607184c6c6b82b316f89475eb1f472 Mon Sep 17 00:00:00 2001 From: Damir Mitrovic Date: Mon, 18 Nov 2019 17:54:35 +1000 Subject: [PATCH 044/337] Fixes 374 --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 3fedb65..e346cc1 100755 --- a/getssl +++ b/getssl @@ -1410,9 +1410,9 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p loop_limit=5 while [[ "$code" -eq 500 ]]; do if [[ "$needbase64" ]] ; then - response=$($CURL -X POST --data "$body" "$url" | urlbase64) + response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" | urlbase64) else - response=$($CURL -X POST --data "$body" "$url") + response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url") fi responseHeaders=$(cat "$CURL_HEADER") From 1fcac20db1ce62368f652e0f14049ed4c204befb Mon Sep 17 00:00:00 2001 From: Damir Mitrovic Date: Mon, 18 Nov 2019 18:04:52 +1000 Subject: [PATCH 045/337] Updated version history and increment version counter --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index e346cc1..e6191e1 100755 --- a/getssl +++ b/getssl @@ -188,10 +188,11 @@ # 2019-09-30 issue #423 Use HTTP 1.1 as workaround atm (2.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) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.13" +VERSION="2.14" # defaults ACCOUNT_KEY_LENGTH=4096 From 1c169ba60369742ffb4787213c501f801769a5da Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 20 Nov 2019 15:34:03 +0000 Subject: [PATCH 046/337] Ignore case finding location in responseHeaders --- getssl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index e6191e1..456da38 100755 --- a/getssl +++ b/getssl @@ -1406,7 +1406,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p body="${body}\"signature\": \"${signed64}\"}" debug "header, payload and signature = $body" fi - + code="500" loop_limit=5 while [[ "$code" -eq 500 ]]; do @@ -2062,7 +2062,7 @@ get_signing_params "$ACCOUNT_KEY" info "Registering account" # send the request to the ACME server. -if [[ $API -eq 1 ]]; then +if [[ $API -eq 1 ]]; then if [[ "$ACCOUNT_EMAIL" ]] ; then regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' else @@ -2079,11 +2079,11 @@ elif [[ $API -eq 2 ]]; then else debug "cant determine account API" graceful_exit -fi +fi if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then info "Registered" - KID=$(echo "$responseHeaders" | grep location | awk '{print $2}'| tr -d '\r\n ') + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') debug "KID=_$KID}_" echo "$response" > "$TEMP_DIR/account.json" elif [[ "$code" == '409' ]] ; then From a3af231a15a09099fb87978e9ef0295156929b30 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 20 Nov 2019 16:02:43 +0000 Subject: [PATCH 047/337] Add User-Agent header (fixes #453) --- getssl | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/getssl b/getssl index 456da38..f3e60a4 100755 --- a/getssl +++ b/getssl @@ -205,6 +205,7 @@ CHECK_REMOTE="true" CHECK_REMOTE_WAIT=0 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" DNS_EXTRA_WAIT="" @@ -438,7 +439,7 @@ check_config() { # check the config files for all obvious errors check_getssl_upgrade() { # check if a more recent version of code is available available TEMP_UPGRADE_FILE="$(mktemp 2>/dev/null || mktemp -t getssl)" - curl --silent "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE" + curl --user-agent "$CURL_USERAGENT" --silent "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE" errcode=$? if [[ $errcode -eq 60 ]]; then error_exit "curl needs updating, your version does not support SNI (multiple SSL domains on a single IP)" @@ -824,7 +825,7 @@ get_certificate() { # get certificate for csr, if all domains validated. debug "certdata location = $CertData" if [[ "$CertData" ]] ; then echo -----BEGIN CERTIFICATE----- > "$gc_certfile" - curl --silent "$CertData" | openssl base64 -e >> "$gc_certfile" + curl --user-agent "$CURL_USERAGENT" --silent "$CertData" | openssl base64 -e >> "$gc_certfile" echo -----END CERTIFICATE----- >> "$gc_certfile" info "Certificate saved in $CERT_FILE" fi @@ -844,17 +845,17 @@ get_certificate() { # get certificate for csr, if all domains validated. | sed 's/>//g') if [[ "$IssuerData" ]] ; then echo -----BEGIN CERTIFICATE----- > "$gc_cafile" - curl --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" + curl --user-agent "$CURL_USERAGENT" --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" echo -----END CERTIFICATE----- >> "$gc_cafile" info "The intermediate CA cert is in $gc_cafile" fi else # APIv2 send_signed_request "$FinalizeLink" "{\"csr\": \"$der\"}" "needbase64" debug "order link was $OrderLink" - cd=$(curl --silent "$OrderLink") + cd=$(curl --user-agent "$CURL_USERAGENT" --silent "$OrderLink") CertData=$(json_get "$cd" "certificate") debug "CertData is at $CertData" - curl --silent "$CertData" > "$CERT_FILE" + curl --user-agent "$CURL_USERAGENT" --silent "$CertData" > "$CERT_FILE" info "Certificate saved in $CERT_FILE" fi } @@ -862,7 +863,7 @@ get_certificate() { # get certificate for csr, if all domains validated. get_cr() { # get curl response url="$1" debug url "$url" - response=$(curl --silent "$url") + response=$(curl --user-agent "$CURL_USERAGENT" --silent "$url") ret=$? debug response "$response" code=$(json_get "$response" status) @@ -1342,7 +1343,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p CURL="$CURL --http1.1 " fi - CURL="$CURL --silent --dump-header $CURL_HEADER " + CURL="$CURL --user-agent $CURL_USERAGENT --silent --dump-header $CURL_HEADER " if [[ ${_USE_DEBUG} -eq 1 ]]; then CURL="$CURL --trace-ascii $dp " @@ -1726,13 +1727,13 @@ if [[ $_REVOKE -eq 1 ]]; then else CA=$REVOKE_CA fi - URL_revoke=$(curl "${CA}/directory" 2>/dev/null | grep "revoke-cert" | awk -F'"' '{print $4}') + URL_revoke=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null | grep "revoke-cert" | awk -F'"' '{print $4}') revoke_certificate graceful_exit fi # get latest agreement from CA (as default) -AGREEMENT=$(curl -I "${CA}/terms" 2>/dev/null | awk 'tolower($1) ~ "location:" {print $2}'|tr -d '\r') +AGREEMENT=$(curl --user-agent "$CURL_USERAGENT" -I "${CA}/terms" 2>/dev/null | awk 'tolower($1) ~ "location:" {print $2}'|tr -d '\r') # if nothing in command line, print help and exit. if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then @@ -1878,7 +1879,7 @@ if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then fi # Obtain CA resource locations -ca_all_loc=$(curl "${CA}" 2>/dev/null) +ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) debug "ca_all_loc from ${CA} gives $ca_all_loc" # APIv1 URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') @@ -1889,7 +1890,7 @@ URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}' URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then - ca_all_loc=$(curl "${CA}/directory" 2>/dev/null) + ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" # APIv1 URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') @@ -2179,7 +2180,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - response=$(curl --silent "${AuthLink[$dn]}" 2>/dev/null) + response=$(curl --user-agent "$CURL_USERAGENT" --silent "${AuthLink[$dn]}" 2>/dev/null) debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") @@ -2236,7 +2237,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - response=$(curl --silent "${AuthLink[$dn]}" 2>/dev/null) + response=$(curl --user-agent "$CURL_USERAGENT" --silent "${AuthLink[$dn]}" 2>/dev/null) debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") @@ -2273,7 +2274,7 @@ for d in $alldomains; do else sleep "$HTTP_TOKEN_CHECK_WAIT" # check that we can reach the challenge ourselves, if not, then error - if [[ ! "$(curl -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then + if [[ ! "$(curl --user-agent "$CURL_USERAGENT" -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then error_exit "for some reason could not reach $wellknown_url - please check it manually" fi fi @@ -2482,7 +2483,7 @@ 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 "$deactivate_url" 2>/dev/null) + resp=$(curl --user-agent "$CURL_USERAGENT" "$deactivate_url" 2>/dev/null) d=$(json_get "$resp" "hostname") info "deactivating domain $d" debug "deactivating $deactivate_url" From 564410d371fc2ee6841c2df20115001348e2df90 Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Thu, 21 Nov 2019 15:38:48 +0100 Subject: [PATCH 048/337] * fix shellcheck issues --- getssl | 72 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/getssl b/getssl index f3e60a4..3a6b5c2 100755 --- a/getssl +++ b/getssl @@ -283,11 +283,11 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # check response from our request to perform challenge if [[ $API -eq 1 ]]; then - if [[ ! -z "$code" ]] && [[ ! "$code" == '202' ]] ; then + if [[ -n "$code" ]] && [[ ! "$code" == '202' ]] ; then error_exit "$domain:Challenge error: $code" fi else # APIv2 - if [[ ! -z "$code" ]] && [[ ! "$code" == '200' ]] ; then + if [[ -n "$code" ]] && [[ ! "$code" == '200' ]] ; then error_exit "$domain:Challenge error: $code" fi fi @@ -468,12 +468,13 @@ check_getssl_upgrade() { # check if a more recent version of code is available a # Obtain all locally stored old versions in getssl_versions declare -a getssl_versions shopt -s nullglob - for getssl_version in $0.v*; do + for getssl_version in "$0".v*; do getssl_versions[${#getssl_versions[@]}]="$getssl_version" done shopt -u nullglob # Explicitly sort the getssl_versions array to make sure shopt -s -o noglob + # shellcheck disable=SC2207 IFS=$'\n' getssl_versions=($(sort <<< "${getssl_versions[*]}")) shopt -u -o noglob # Remove entries until given number of old versions to keep is reached @@ -499,7 +500,7 @@ clean_up() { # Perform pre-exit housekeeping if [[ $VALIDATE_VIA_DNS == "true" ]]; then # Tidy up DNS entries if things failed part way though. shopt -s nullglob - for dnsfile in $TEMP_DIR/dns_verify/*; do + for dnsfile in "$TEMP_DIR"/dns_verify/*; do # shellcheck source=/dev/null . "$dnsfile" debug "attempting to clean up DNS entry for $d" @@ -507,10 +508,10 @@ clean_up() { # Perform pre-exit housekeeping done shopt -u nullglob fi - if [[ ! -z "$DOMAIN_DIR" ]]; then + if [[ -n "$DOMAIN_DIR" ]]; then rm -rf "${TEMP_DIR:?}" fi - if [[ ! -z "$TEMP_UPGRADE_FILE" ]] && [[ -f "$TEMP_UPGRADE_FILE" ]]; then + if [[ -n "$TEMP_UPGRADE_FILE" ]] && [[ -f "$TEMP_UPGRADE_FILE" ]]; then rm -f "$TEMP_UPGRADE_FILE" fi } @@ -530,7 +531,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. scp $from ${to:4}" fi debug "userid $TOKEN_USER_ID" - if [[ "$cert" == "challenge token" ]] && [[ ! -z "$TOKEN_USER_ID" ]]; then + if [[ "$cert" == "challenge token" ]] && [[ -n "$TOKEN_USER_ID" ]]; then servername=$(echo "$to" | awk -F":" '{print $2}') tofile=$(echo "$to" | awk -F":" '{print $3}') debug "servername $servername" @@ -591,7 +592,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. error_exit "cannot copy $from to $to" fi fi - if [[ "$cert" == "challenge token" ]] && [[ ! -z "$TOKEN_USER_ID" ]]; then + if [[ "$cert" == "challenge token" ]] && [[ -n "$TOKEN_USER_ID" ]]; then chown "$TOKEN_USER_ID" "$to" fi fi @@ -736,7 +737,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n else res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d") fi - if [[ ! -z "$res" ]]; then # domain is a CNAME so get main domain + if [[ -n "$res" ]]; then # domain is a CNAME so get main domain gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') fi if [[ -z "$gad_s" ]]; then #checking for CNAMEs @@ -1152,7 +1153,7 @@ json_get() { # get values from json # remove newlines, so it's a single chunk of JSON json_data=$( echo "$1" | tr '\n' ' ') # if $3 is defined, this is the section which the item is in. - if [[ ! -z "$3" ]]; then + if [[ -n "$3" ]]; then jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}') if [[ "$2" == "uri" ]]; then jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}') @@ -1172,17 +1173,17 @@ json_get() { # get values from json echo "$jg_result" fi else - if [[ ! -z "$6" ]]; then + if [[ -n "$6" ]]; then full=$(json_awk "$1") section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') echo "$full" | grep "^..${5}\",$section" | awk '{print $2}' | tr -d '"' - elif [[ ! -z "$5" ]]; then + elif [[ -n "$5" ]]; then full=$(json_awk "$1") section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') echo "$full" | grep "^..${2}\",$section" | grep "$5" | awk '{print $2}' | tr -d '"' - elif [[ ! -z "$3" ]]; then + elif [[ -n "$3" ]]; then json_awk "$1" | grep "^..${2}...${3}" | awk '{print $2}' | tr -d '"' - elif [[ ! -z "$2" ]]; then + elif [[ -n "$2" ]]; then json_awk "$1" | grep "^..${2}" | awk '{print $2}' | tr -d '"' else json_awk "$1" @@ -1203,7 +1204,7 @@ os_esed() { # Use different sed version for different os types (extended regex) purge_archive() { # purge archive of old, invalid, certificates arcdir="$1/archive" debug "purging archives in ${arcdir}/" - for padir in $arcdir/????_??_??_??_??; do + for padir in "$arcdir"/????_??_??_??_??; do # check each directory if [[ -d "$padir" ]]; then tstamp=$(basename "$padir"| awk -F"_" '{print $1"-"$2"-"$3" "$4":"$5}') @@ -1226,7 +1227,7 @@ purge_archive() { # purge archive of old, invalid, certificates } reload_service() { # Runs a command to reload services ( via ssh if needed) - if [[ ! -z "$RELOAD_CMD" ]]; then + if [[ -n "$RELOAD_CMD" ]]; then info "reloading SSL services" if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') @@ -1270,16 +1271,16 @@ requires() { # check if required function is available if [[ "$i" == "${!#}" ]]; then # if on last variable then exit as not found error_exit "this script requires one of: ${*:1:$(($#-1))}" fi - res=$(which "$i" 2>/dev/null) + res=$(command -v "$i" 2>/dev/null) debug "checking for $i ... $res" - if [[ ! -z "$res" ]]; then # if function found, then set variable to function and return + if [[ -n "$res" ]]; then # if function found, then set variable to function and return debug "function $i found at $res - setting ${!#} to $i" eval "${!#}=\$i" return fi done else # only one value, so check it. - result=$(which "$1" 2>/dev/null) + result=$(command -v "$1" 2>/dev/null) debug "checking for required $1 ... $result" if [[ -z "$result" ]]; then error_exit "This script requires $1 installed" @@ -1339,6 +1340,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p dp="$TEMP_DIR/curl.dump" CURL="curl " + # shellcheck disable=SC2072 if [[ "$($CURL -V | head -1 | cut -d' ' -f2 )" > "7.33" ]]; then CURL="$CURL --http1.1 " fi @@ -1672,11 +1674,11 @@ while [[ -n ${1+defined} ]]; do _UPGRADE_CHECK=0 ;; -w) shift; WORKING_DIR="$1" ;; - -* | --*) + -*) usage error_exit "Unknown option $1" ;; *) - if [[ ! -z $DOMAIN ]]; then + if [[ -n $DOMAIN ]]; then error_exit "invalid command line $DOMAIN - it appears to contain more than one domain" fi DOMAIN="$1" @@ -1784,7 +1786,7 @@ if [[ ${_CHECK_ALL} -eq 1 ]]; then error_exit "DOMAIN_STORAGE not found - $DOMAIN_STORAGE" fi - for dir in ${DOMAIN_STORAGE}/*; do + for dir in "${DOMAIN_STORAGE}"/*; do if [[ -d "$dir" ]]; then debug "Checking $dir" cmd="$0 -U" # No update checks when calling recursively @@ -1832,7 +1834,7 @@ if [[ ${_CREATE_CONFIG} -eq 1 ]]; then | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null \ | openssl x509 2>/dev/null) EX_SANS="www.${DOMAIN}" - if [[ ! -z "${EX_CERT}" ]]; then + if [[ -n "${EX_CERT}" ]]; then EX_SANS=$(echo "$EX_CERT" \ | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) @@ -1902,9 +1904,9 @@ if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') fi -if [[ ! -z "$URL_new_reg" ]]; then +if [[ -n "$URL_new_reg" ]]; then API=1 -elif [[ ! -z "$URL_newAccount" ]]; then +elif [[ -n "$URL_newAccount" ]]; then API=2 else info "unknown API version" @@ -1918,7 +1920,7 @@ if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then EX_CERT=$(echo \ | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ | openssl x509 2>/dev/null) - if [[ ! -z "$EX_CERT" ]]; then # if obtained a cert + if [[ -n "$EX_CERT" ]]; then # if obtained a cert if [[ -s "$CERT_FILE" ]]; then # if local exists CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) else # since local doesn't exist leave empty so that the domain validation will happen @@ -2151,7 +2153,7 @@ for d in $alldomains; do debug "completed send_signed_request" # check if we got a valid response and token, if not then error exit - if [[ ! -z "$code" ]] && [[ ! "$code" == '201' ]] ; then + if [[ -n "$code" ]] && [[ ! "$code" == '201' ]] ; then error_exit "new-authz error: $response" fi else @@ -2319,7 +2321,7 @@ done # end of ... loop through domains for cert ( from SANS list) # perform validation if via DNS challenge if [[ $VALIDATE_VIA_DNS == "true" ]]; then # loop through dns-variable files to check if dns has been changed - for dnsfile in $TEMP_DIR/dns_verify/*; do + for dnsfile in "$TEMP_DIR"/dns_verify/*; do if [[ -e "$dnsfile" ]]; then debug "loading DNSfile: $dnsfile" # shellcheck source=/dev/null @@ -2372,7 +2374,7 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then fi # loop through dns-variable files to let the ACME server check the challenges - for dnsfile in $TEMP_DIR/dns_verify/*; do + for dnsfile in "$TEMP_DIR"/dns_verify/*; do if [[ -e "$dnsfile" ]]; then debug "loading DNSfile: $dnsfile" # shellcheck source=/dev/null @@ -2415,17 +2417,17 @@ copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - if [[ ! -z "$DOMAIN_CERT_LOCATION" ]]; then + if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then copy_file_to_location "ec domain certificate" \ "${CERT_FILE%.*}.ec.crt" \ "${DOMAIN_CERT_LOCATION%.*}.ec.crt" fi - if [[ ! -z "$DOMAIN_KEY_LOCATION" ]]; then + if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then copy_file_to_location "ec private key" \ "$DOMAIN_DIR/${DOMAIN}.ec.key" \ "${DOMAIN_KEY_LOCATION%.*}.ec.key" fi - if [[ ! -z "$CA_CERT_LOCATION" ]]; then + if [[ -n "$CA_CERT_LOCATION" ]]; then copy_file_to_location "ec CA certificate" \ "${CA_CERT%.*}.ec.crt" \ "${CA_CERT_LOCATION%.*}.ec.crt" @@ -2433,7 +2435,7 @@ if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then fi # if DOMAIN_CHAIN_LOCATION is not blank, then create and copy file. -if [[ ! -z "$DOMAIN_CHAIN_LOCATION" ]]; then +if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then if [[ "$(dirname "$DOMAIN_CHAIN_LOCATION")" == "." ]]; then to_location="${DOMAIN_DIR}/${DOMAIN_CHAIN_LOCATION}" else @@ -2447,7 +2449,7 @@ if [[ ! -z "$DOMAIN_CHAIN_LOCATION" ]]; then fi fi # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. -if [[ ! -z "$DOMAIN_KEY_CERT_LOCATION" ]]; then +if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then if [[ "$(dirname "$DOMAIN_KEY_CERT_LOCATION")" == "." ]]; then to_location="${DOMAIN_DIR}/${DOMAIN_KEY_CERT_LOCATION}" else @@ -2461,7 +2463,7 @@ if [[ ! -z "$DOMAIN_KEY_CERT_LOCATION" ]]; then fi fi # if DOMAIN_PEM_LOCATION is not blank, then create and copy file. -if [[ ! -z "$DOMAIN_PEM_LOCATION" ]]; then +if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then if [[ "$(dirname "$DOMAIN_PEM_LOCATION")" == "." ]]; then to_location="${DOMAIN_DIR}/${DOMAIN_PEM_LOCATION}" else From d9669d240f67d4f5023b4d58ffeab86577671450 Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Thu, 21 Nov 2019 16:05:37 +0100 Subject: [PATCH 049/337] * add IGNORE_PERIOD_CHECK flag --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index f3e60a4..9d39c9e 100755 --- a/getssl +++ b/getssl @@ -226,6 +226,7 @@ SERVER_TYPE="https" SKIP_HTTP_TOKEN_CHECK="false" SSLCONF="$(openssl version -d 2>/dev/null| cut -d\" -f2)/openssl.cnf" OCSP_MUST_STAPLE="false" +IGNORE_PERIOD_CHECK="false" TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" @@ -1795,7 +1796,7 @@ if [[ ${_CHECK_ALL} -eq 1 ]]; then cmd="$cmd -q" fi # check if $dir looks like a domain name (contains a period) - if [[ $(basename "$dir") == *.* ]]; then + if [[ $(basename "$dir") == *.* || "$IGNORE_PERIOD_CHECK" == "true" ]]; then cmd="$cmd -w $WORKING_DIR $(basename "$dir")" debug "CMD: $cmd" eval "$cmd" From dec01f1810fea2287bb2294909f1f2356cead487 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 22 Nov 2019 18:39:36 +0000 Subject: [PATCH 050/337] Split fullchain into cert and ca cert --- getssl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index f3e60a4..e7646ba 100755 --- a/getssl +++ b/getssl @@ -855,7 +855,9 @@ get_certificate() { # get certificate for csr, if all domains validated. cd=$(curl --user-agent "$CURL_USERAGENT" --silent "$OrderLink") CertData=$(json_get "$cd" "certificate") debug "CertData is at $CertData" - curl --user-agent "$CURL_USERAGENT" --silent "$CertData" > "$CERT_FILE" + curl --user-agent "$CURL_USERAGENT" --silent "$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" fi } @@ -1759,6 +1761,7 @@ ACCOUNT_KEY="${ACCOUNT_KEY:=$WORKING_DIR/account.key}" DOMAIN_STORAGE="${DOMAIN_STORAGE:=$WORKING_DIR}" DOMAIN_DIR="$DOMAIN_STORAGE/$DOMAIN" CERT_FILE="$DOMAIN_DIR/${DOMAIN}.crt" +FULL_CHAIN="$DOMAIN_DIR/fullchain.crt" CA_CERT="$DOMAIN_DIR/chain.crt" TEMP_DIR="$DOMAIN_DIR/tmp" if [[ "$os" == "mingw" ]]; then From bd0e674b1253535da688b98dabebd21e15b3dc25 Mon Sep 17 00:00:00 2001 From: Ian Driver Date: Sat, 14 Dec 2019 16:21:54 +0000 Subject: [PATCH 051/337] 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 052/337] 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 053/337] 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 054/337] 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 055/337] 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 813276ee80df41d934a462ea6a89340d6215e108 Mon Sep 17 00:00:00 2001 From: Tsaukpaetra Date: Tue, 31 Dec 2019 02:53:02 -0700 Subject: [PATCH 056/337] Create dns_freedns.sh Should probably be fixed up, I've only hacked up @dkerr64's version for acme.sh to work on my local instance. It assumes you're using curl, I didn't bother stealing acme.sh's whole library of functions. DNS_ADD_COMMAND="~/.getssl/dns_freedns.sh add" DNS_DEL_COMMAND="~/.getssl/dns_freedns.sh rm" --- dns_scripts/dns_freedns.sh | 702 +++++++++++++++++++++++++++++++++++++ 1 file changed, 702 insertions(+) create mode 100644 dns_scripts/dns_freedns.sh diff --git a/dns_scripts/dns_freedns.sh b/dns_scripts/dns_freedns.sh new file mode 100644 index 0000000..a6571e1 --- /dev/null +++ b/dns_scripts/dns_freedns.sh @@ -0,0 +1,702 @@ +#!/usr/bin/env sh + +#This file name is "dns_freedns.sh" +#So, here must be a method dns_freedns_add() +#Which will be called by acme.sh to add the txt record to your api system. +#returns 0 means success, otherwise error. +# +#Author: David Kerr +#Report Bugs here: https://github.com/dkerr64/acme.sh +#or here... https://github.com/Neilpang/acme.sh/issues/2305 +# +######## Public functions ##################### + +# Export FreeDNS userid and password in following variables... +# FREEDNS_User=username +# FREEDNS_Password=password +# login cookie is saved in acme account config file so userid / pw +# need to be set only when changed. + +#Usage: dns_freedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_freedns_add() { + fulldomain="_acme-challenge.$1" + txtvalue="$2" + FREEDNS_COOKIE="$(cat $(dirname "$(readlink -f "$0")")/freednscookie.dat)" + + echo "Info: Add TXT record using FreeDNS" + #echo "Debug: fulldomain: $fulldomain" + #echo "Debug: txtvalue: $txtvalue" + + if [ -z "$FREEDNS_User" ] || [ -z "$FREEDNS_Password" ]; then + FREEDNS_User="" + FREEDNS_Password="" + if [ -z "$FREEDNS_COOKIE" ]; then + echo "ERROR: You did not specify the FreeDNS username and password yet." + echo "ERROR: Please export as FREEDNS_User / FREEDNS_Password and try again." + return 1 + fi + using_cached_cookies="true" + else + FREEDNS_COOKIE="$(_freedns_login "$FREEDNS_User" "$FREEDNS_Password")" + if [ -z "$FREEDNS_COOKIE" ]; then + return 1 + fi + using_cached_cookies="false" + fi + + #echo "Debug: FreeDNS login cookies: $FREEDNS_COOKIE (cached = $using_cached_cookies)" + + echo "$FREEDNS_COOKIE">$(dirname "$(readlink -f "$0")")/freednscookie.dat + + # We may have to cycle through the domain name to find the + # TLD that we own... + i=1 + wmax="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" + while [ "$i" -lt "$wmax" ]; do + # split our full domain name into two parts... + sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" + i="$(_math "$i" + 1)" + top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" + #echo "Debug: sub_domain: $sub_domain" + #echo "Debug: top_domain: $top_domain" + + DNSdomainid="$(_freedns_domain_id "$top_domain")" + if [ "$?" = "0" ]; then + echo "Info:Domain $top_domain found at FreeDNS, domain_id $DNSdomainid" + break + else + echo "Info:Domain $top_domain not found at FreeDNS, try with next level of TLD" + fi + done + + if [ -z "$DNSdomainid" ]; then + # If domain ID is empty then something went wrong (top level + # domain not found at FreeDNS). + echo "ERROR: Domain $top_domain not found at FreeDNS" + return 1 + fi + + # Add in new TXT record with the value provided + #echo "Debug: Adding TXT record for $fulldomain, $txtvalue" + _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" + return $? +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_freedns_rm() { + fulldomain="_acme-challenge.$1" + txtvalue="$2" + + echo "Info:Delete TXT record using FreeDNS" + #echo "Debug: fulldomain: $fulldomain" + #echo "Debug: txtvalue: $txtvalue" + + # Need to read cookie from conf file again in case new value set + # during login to FreeDNS when TXT record was created. + FREEDNS_COOKIE="$(cat $(dirname "$(readlink -f "$0")")/freednscookie.dat)" + #echo "Debug: FreeDNS login cookies: $FREEDNS_COOKIE" + + TXTdataid="$(_freedns_data_id "$fulldomain" "TXT")" + if [ "$?" != "0" ]; then + echo "Info:Cannot delete TXT record for $fulldomain, record does not exist at FreeDNS" + return 1 + fi + #echo "Debug: Data ID's found, $TXTdataid" + + # now we have one (or more) TXT record data ID's. Load the page + # for that record and search for the record txt value. If match + # then we can delete it. + lines="$(echo "$TXTdataid" | wc -l)" + #echo "Debug: Found $lines TXT data records for $fulldomain" + i=0 + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" + dataid="$(echo "$TXTdataid" | sed -n "${i}p")" + #echo "Debug: $dataid" + + htmlpage="$(_freedns_retrieve_data_page "$FREEDNS_COOKIE" "$dataid")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + echo "ERROR: Has your FreeDNS username and password changed? If so..." + echo "ERROR: Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 + fi + + echo "$htmlpage" | grep "value=\""$txtvalue"\"" >/dev/null + if [ "$?" = "0" ]; then + # Found a match... delete the record and return + echo "Info:Deleting TXT record for $fulldomain, $txtvalue" + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$dataid" + return $? + fi + done + + # If we get this far we did not find a match + # Not necessarily an error, but log anyway. + echo "Info:Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS" + return 0 +} + +#################### Private functions below ################################## + +# usage: _freedns_login username password +# print string "cookie=value" etc. +# returns 0 success +_freedns_login() { + export _H1="Accept-Language:en-US" + username="$1" + password="$2" + url="https://freedns.afraid.org/zc.php?step=2" + + #echo "Debug: Login to FreeDNS as user $username" + data="username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" + #echo "$data" + + if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then + HTTP_HEADER="$(_mktemp)" + fi + htmlpage="$(curl -L --silent --dump-header $HTTP_HEADER -X POST -H "$_H1" -H "$_H2" --data "$data" "$url")" + + if [ "$?" != "0" ]; then + echo "ERROR: FreeDNS login failed for user $username bad RC from _post" + return 1 + fi + + cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)" + + # if cookies is not empty then logon successful + if [ -z "$cookies" ]; then + #echo "Debug3: htmlpage: $htmlpage" + echo "ERROR: FreeDNS login failed for user $username. Check $HTTP_HEADER file" + return 1 + fi + + printf "%s" "$cookies" + return 0 +} + +# usage _freedns_retrieve_subdomain_page login_cookies +# echo page retrieved (html) +# returns 0 success +_freedns_retrieve_subdomain_page() { + export _H1="Cookie:$1" + export _H2="Accept-Language:en-US" + url="https://freedns.afraid.org/subdomain/" + + #echo "Debug: Retrieve subdomain page from FreeDNS" + + htmlpage="$(curl -L --silent -H "$_H1" -H "$_H2" "$url")" + + if [ "$?" != "0" ]; then + echo "ERROR: FreeDNS retrieve subdomains failed bad RC from _get" + return 1 + elif [ -z "$htmlpage" ]; then + echo "ERROR: FreeDNS returned empty subdomain page" + return 1 + fi + + #echo "Debug3: htmlpage: $htmlpage" + + printf "%s" "$htmlpage" + return 0 +} + +# usage _freedns_retrieve_data_page login_cookies data_id +# echo page retrieved (html) +# returns 0 success +_freedns_retrieve_data_page() { + export _H1="Cookie:$1" + export _H2="Accept-Language:en-US" + data_id="$2" + url="https://freedns.afraid.org/subdomain/edit.php?data_id=$2" + + #echo "Debug: Retrieve data page for ID $data_id from FreeDNS" + + htmlpage="$(curl -L --silent -H "$_H1" -H "$_H2" "$url")" + + if [ "$?" != "0" ]; then + echo "ERROR: FreeDNS retrieve data page failed bad RC from _get" + return 1 + elif [ -z "$htmlpage" ]; then + echo "ERROR: FreeDNS returned empty data page" + return 1 + fi + + #echo "Debug3: htmlpage: $htmlpage" + + printf "%s" "$htmlpage" + return 0 +} + +# usage _freedns_add_txt_record login_cookies domain_id subdomain value +# returns 0 success +_freedns_add_txt_record() { + export _H1="Cookie:$1" + export _H2="Accept-Language:en-US" + domain_id="$2" + subdomain="$3" + value="$(printf '%s' "$4" | _url_encode)" + url="https://freedns.afraid.org/subdomain/save.php?step=2" + + if [ -z "$HTTP_HEADER" ] || ! touch "$HTTP_HEADER"; then + HTTP_HEADER="$(_mktemp)" + fi + htmlpage="$(curl -L --silent --dump-header $HTTP_HEADER -X POST -H "$_H1" -H "$_H2" --data "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")" + + if [ "$?" != "0" ]; then + echo "ERROR: FreeDNS failed to add TXT record for $subdomain bad RC from _post" + return 1 + elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then + #echo "Debug3: htmlpage: $(cat $HTTP_HEADER)" + echo "ERROR: FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file" + return 1 + elif _contains "$htmlpage" "security code was incorrect"; then + #echo "Debug3: htmlpage: $htmlpage" + echo "ERROR: FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code" + echo "ERROR: Note that you cannot use automatic DNS validation for FreeDNS public domains" + return 1 + fi + + #echo "Debug3: htmlpage: $htmlpage" + echo "Info:Added acme challenge TXT record for $fulldomain at FreeDNS" + return 0 +} + +# usage _freedns_delete_txt_record login_cookies data_id +# returns 0 success +_freedns_delete_txt_record() { + export _H1="Cookie:$1" + export _H2="Accept-Language:en-US" + data_id="$2" + url="https://freedns.afraid.org/subdomain/delete2.php" + + htmlheader="$(curl -L --silent -I -H "$_H1" -H "$_H2" "$url?data_id%5B%5D=$data_id&submit=delete+selected")" + + if [ "$?" != "0" ]; then + echo "ERROR: FreeDNS failed to delete TXT record for $data_id bad RC from _get" + return 1 + elif ! _contains "$htmlheader" "200 OK"; then + #echo "Debug2: htmlheader: $htmlheader" + echo "ERROR: FreeDNS failed to delete TXT record $data_id" + return 1 + fi + + echo "Info:Deleted acme challenge TXT record for $fulldomain at FreeDNS" + return 0 +} + +# usage _freedns_domain_id domain_name +# echo the domain_id if found +# return 0 success +_freedns_domain_id() { + # Start by escaping the dots in the domain name + search_domain="$(echo "$1" | sed 's/\./\\./g')" + + # Sometimes FreeDNS does not return the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our domain ID + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + echo "ERROR: Has your FreeDNS username and password changed? If so..." + echo "ERROR: Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 + fi + + domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ + | grep "$search_domain\|$search_domain(.*)" \ + | sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' \ + | cut -d = -f 2)" + # The above beauty extracts domain ID from the html page... + # strip out all blank space and new lines. Then insert newlines + # before each table row + # search for the domain within each row (which may or may not have + # a text string in brackets (.*) after it. + # And finally extract the domain ID. + if [ -n "$domain_id" ]; then + printf "%s" "$domain_id" + return 0 + fi + #echo "Debug:Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" + done + #echo "Debug:Domain $search_domain not found after retry" + return 1 +} + +# usage _freedns_data_id domain_name record_type +# echo the data_id(s) if found +# return 0 success +_freedns_data_id() { + # Start by escaping the dots in the domain name + search_domain="$(echo "$1" | sed 's/\./\\./g')" + record_type="$2" + + # Sometimes FreeDNS does not return the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our domain ID + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + echo "ERROR: Has your FreeDNS username and password changed? If so..." + echo "ERROR: Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 + fi + + data_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ + | grep "$record_type" \ + | grep "$search_domain" \ + | sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' \ + | cut -d = -f 2)" + # The above beauty extracts data ID from the html page... + # strip out all blank space and new lines. Then insert newlines + # before each table row + # search for the record type withing each row (e.g. TXT) + # search for the domain within each row (which is within a + # anchor. And finally extract the domain ID. + if [ -n "$data_id" ]; then + printf "%s" "$data_id" + return 0 + fi + #echo "Debug:Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" + done + #echo "Debug:Domain $search_domain not found after retry" + return 1 +} + +#### BEGIN things shamefully ripped from https://github.com/Neilpang/acme.sh/blob/master/acme.sh + +#_ascii_hex str +#this can only process ascii chars, should only be used when od command is missing as a backup way. +_ascii_hex() { + _debug2 "Using _ascii_hex" + _str="$1" + _str_len=${#_str} + _h_i=1 + while [ "$_h_i" -le "$_str_len" ]; do + _str_c="$(printf "%s" "$_str" | cut -c "$_h_i")" + printf " %02x" "'$_str_c" + _h_i="$(_math "$_h_i" + 1)" + done +} + +#stdin output hexstr splited by one space +#input:"abc" +#output: " 61 62 63" +_hex_dump() { + if _exists od; then + od -A n -v -t x1 | tr -s " " | sed 's/ $//' | tr -d "\r\t\n" + elif _exists hexdump; then + hexdump -v -e '/1 ""' -e '/1 " %02x" ""' + elif _exists xxd; then + xxd -ps -c 20 -i | sed "s/ 0x/ /g" | tr -d ",\n" | tr -s " " + else + str=$(cat) + _ascii_hex "$str" + fi +} + +#url encode, no-preserved chars +#A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +#41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a + +#a b c d e f g h i j k l m n o p q r s t u v w x y z +#61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a + +#0 1 2 3 4 5 6 7 8 9 - _ . ~ +#30 31 32 33 34 35 36 37 38 39 2d 5f 2e 7e + +#stdin stdout +_url_encode() { + _hex_str=$(_hex_dump) + for _hex_code in $_hex_str; do + #upper case + case "${_hex_code}" in + "41") + printf "%s" "A" + ;; + "42") + printf "%s" "B" + ;; + "43") + printf "%s" "C" + ;; + "44") + printf "%s" "D" + ;; + "45") + printf "%s" "E" + ;; + "46") + printf "%s" "F" + ;; + "47") + printf "%s" "G" + ;; + "48") + printf "%s" "H" + ;; + "49") + printf "%s" "I" + ;; + "4a") + printf "%s" "J" + ;; + "4b") + printf "%s" "K" + ;; + "4c") + printf "%s" "L" + ;; + "4d") + printf "%s" "M" + ;; + "4e") + printf "%s" "N" + ;; + "4f") + printf "%s" "O" + ;; + "50") + printf "%s" "P" + ;; + "51") + printf "%s" "Q" + ;; + "52") + printf "%s" "R" + ;; + "53") + printf "%s" "S" + ;; + "54") + printf "%s" "T" + ;; + "55") + printf "%s" "U" + ;; + "56") + printf "%s" "V" + ;; + "57") + printf "%s" "W" + ;; + "58") + printf "%s" "X" + ;; + "59") + printf "%s" "Y" + ;; + "5a") + printf "%s" "Z" + ;; + + #lower case + "61") + printf "%s" "a" + ;; + "62") + printf "%s" "b" + ;; + "63") + printf "%s" "c" + ;; + "64") + printf "%s" "d" + ;; + "65") + printf "%s" "e" + ;; + "66") + printf "%s" "f" + ;; + "67") + printf "%s" "g" + ;; + "68") + printf "%s" "h" + ;; + "69") + printf "%s" "i" + ;; + "6a") + printf "%s" "j" + ;; + "6b") + printf "%s" "k" + ;; + "6c") + printf "%s" "l" + ;; + "6d") + printf "%s" "m" + ;; + "6e") + printf "%s" "n" + ;; + "6f") + printf "%s" "o" + ;; + "70") + printf "%s" "p" + ;; + "71") + printf "%s" "q" + ;; + "72") + printf "%s" "r" + ;; + "73") + printf "%s" "s" + ;; + "74") + printf "%s" "t" + ;; + "75") + printf "%s" "u" + ;; + "76") + printf "%s" "v" + ;; + "77") + printf "%s" "w" + ;; + "78") + printf "%s" "x" + ;; + "79") + printf "%s" "y" + ;; + "7a") + printf "%s" "z" + ;; + #numbers + "30") + printf "%s" "0" + ;; + "31") + printf "%s" "1" + ;; + "32") + printf "%s" "2" + ;; + "33") + printf "%s" "3" + ;; + "34") + printf "%s" "4" + ;; + "35") + printf "%s" "5" + ;; + "36") + printf "%s" "6" + ;; + "37") + printf "%s" "7" + ;; + "38") + printf "%s" "8" + ;; + "39") + printf "%s" "9" + ;; + "2d") + printf "%s" "-" + ;; + "5f") + printf "%s" "_" + ;; + "2e") + printf "%s" "." + ;; + "7e") + printf "%s" "~" + ;; + #other hex + *) + printf '%%%s' "$_hex_code" + ;; + esac + done +} + +_exists() { + cmd="$1" + if [ -z "$cmd" ]; then + _usage "Usage: _exists cmd" + return 1 + fi + + if eval type type >/dev/null 2>&1; then + eval type "$cmd" >/dev/null 2>&1 + elif command >/dev/null 2>&1; then + command -v "$cmd" >/dev/null 2>&1 + else + which "$cmd" >/dev/null 2>&1 + fi + ret="$?" + #echo "Debug3: $cmd exists=$ret" + return $ret +} + +_head_n() { + head -n "$1" +} + +_mktemp() { + if _exists mktemp; then + if mktemp 2>/dev/null; then + return 0 + elif _contains "$(mktemp 2>&1)" "-t prefix" && mktemp -t "$PROJECT_NAME" 2>/dev/null; then + #for Mac osx + return 0 + fi + fi + if [ -d "/tmp" ]; then + echo "/tmp/${PROJECT_NAME}wefADf24sf.$(_time).tmp" + return 0 + elif [ "$LE_TEMP_DIR" ] && mkdir -p "$LE_TEMP_DIR"; then + echo "/$LE_TEMP_DIR/wefADf24sf.$(_time).tmp" + return 0 + fi + _err "Can not create temp file." +} + +#a + b +_math() { + _m_opts="$@" + printf "%s" "$(($_m_opts))" +} + +_contains() { + _str="$1" + _sub="$2" + echo "$_str" | grep -- "$_sub" >/dev/null 2>&1 +} + +##Now actually do something with that function +case "$1" in + + add) + dns_freedns_add $2 $3 + ;; + rm) + dns_freedns_rm $2 $3 + ;; +esac From 5296a0716f4f3ebbca141785cbb55abdc4bdcb38 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 4 Jan 2020 10:20:52 +0000 Subject: [PATCH 057/337] 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 058/337] 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 059/337] 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 060/337] 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 061/337] 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 062/337] 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 063/337] 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 064/337] 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 770277c7ad72b7c78ebcfdec8ba5a7ac4a5089c8 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:27:11 +0100 Subject: [PATCH 065/337] Add support for CloudDNS --- dns_scripts/dns_add_clouddns | 94 ++++++++++++++++++++++++++++++++ dns_scripts/dns_del_clouddns | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100755 dns_scripts/dns_add_clouddns create mode 100755 dns_scripts/dns_del_clouddns diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns new file mode 100755 index 0000000..5236269 --- /dev/null +++ b/dns_scripts/dns_add_clouddns @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Need to add your email address and API key to clouddns below or set as env variables +email=${CLOUDDNS_EMAIL:-''} +password=${CLOUDDNS_PASSWORD:-''} +client=${CLOUDDNS_CLIENT:-''} + +# This script adds a token to clouddns DNS for the ACME challenge +# usage dns_add_clouddns "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in clouddns etc) + +fulldomain="${1}" +token="${2}" +API='https://admin.vshosting.cloud/clouddns' +LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi +if [[ -z "$email" ]]; then + echo "CLOUDDNS_EMAIL (email) parameter not set" + exit 1 +fi +if [[ -z "$password" ]]; then + echo "CLOUDDNS_PASSWORD (password) parameter not set" + exit 1 +fi +if [[ -z "$client" ]]; then + echo "CLOUDDNS_CLIENT (id) parameter not set" + exit 1 +fi + +# Login to clouddns to get accessToken +resp=$(curl --silent -X POST -H 'Content-Type: application/json' "$LOGIN_API" \ + --data "{\"email\": \"$email\", \"password\": \"$password\"}") +re='"accessToken":"([^,]*)",' # Match access token +if [[ "${resp// }" =~ $re ]]; then + access_token="${BASH_REMATCH[1]}" +fi +if [[ -z "$access_token" ]]; then + echo 'Could not get access token; check your credentials' + exit 3 +fi +curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) + +# Get main domain +domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}") +re='domainType":"[^"]*","id":"([^,]*)",' # Match domain id +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your CloudDNS account' + exit 3 +fi + +# Add challenge record +txt_record="_acme-challenge.$domain_root." +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/record-txt" \ + --data "{\"type\":\"TXT\",\"name\":\"$txt_record\",\"value\":\"$token\",\"domainId\":\"$domain_id\"}") + +# If adding record failed (error:) then print error message +if [[ "${resp// }" == *'"error"'* ]]; then + if [[ "${resp// }" == *'"code":4136'* ]]; then + echo "DNS challenge token already exists" + exit + fi + re='"message":"([^"]+)"' + if [[ "$resp" =~ $re ]]; then + echo "Error: DNS challenge not added: ${BASH_REMATCH[1]}" + exit 3 + else + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 + fi +fi + +# Publish challenge record +resp=$(curl --silent "${curl_params[@]}" -X PUT "$API/domain/$domain_id/publish" \ + --data "{\"soaTtl\":300}") diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns new file mode 100755 index 0000000..ddac2b6 --- /dev/null +++ b/dns_scripts/dns_del_clouddns @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# Need to add your email address and API key to clouddns below or set as env variables +email=${CLOUDDNS_EMAIL:-''} +password=${CLOUDDNS_PASSWORD:-''} +client=${CLOUDDNS_CLIENT:-''} + +# This script adds a token to clouddns DNS for the ACME challenge +# usage dns_add_clouddns "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in clouddns etc) + +fulldomain="${1}" +token="${2}" +API='https://admin.vshosting.cloud/clouddns' +LOGIN_API='https://admin.vshosting.cloud/api/public/auth/login' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi +if [[ -z "$email" ]]; then + echo "CLOUDDNS_EMAIL (email) parameter not set" + exit 1 +fi +if [[ -z "$password" ]]; then + echo "CLOUDDNS_PASSWORD (password) parameter not set" + exit 1 +fi +if [[ -z "$client" ]]; then + echo "CLOUDDNS_CLIENT (id) parameter not set" + exit 1 +fi + +# Login to clouddns to get accessToken +resp=$(curl --silent -X POST -H 'Content-Type: application/json' "$LOGIN_API" \ + --data "{\"email\": \"$email\", \"password\": \"$password\"}") +re='"accessToken":"([^,]*)",' # Match access token +if [[ "${resp// }" =~ $re ]]; then + access_token="${BASH_REMATCH[1]}" +fi +if [[ -z "$access_token" ]]; then + echo 'Could not get access token; check your credentials' + exit 3 +fi +curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) + +# Get main domain and challenge record +domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +txt_record="_acme-challenge.$domain_root." + +# Get domain id +curl_domainid_body="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}" +resp=$(curl --silent "${curl_params[@]}" -X POST -d "$curl_domainid_body" "$API/domain/search") +re='domainType":"[^"]*","id":"([^,]*)",' # Find result section +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your CloudDNS account' + exit 3 +fi + +# Get challenge record ID +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/domain/$domain_id" ) +re="\"lastDomainRecordList\".*\"id\":\"([^,]*)\"[^}]*\"name\":\"$txt_record\"," # Match domain id +if [[ "${resp//[$'\t\r\n ']}" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + echo 'Challenge record does not exist' + exit 3 +fi + +# Remove challenge record +resp=$(curl --silent "${curl_params[@]}" -X DELETE "$API/record/$record_id") + +# If removing record failed (error:) then print error message +if [[ "${resp// }" == *'"error"'* ]]; then + re='"message":"([^"]+)"' + if [[ "$resp" =~ $re ]]; then + echo "Error: DNS challenge not removed: ${BASH_REMATCH[1]}" + exit 3 + else + echo "Error: DNS challenge not removed: unknown error - ${resp}" + exit 3 + fi +fi + +# Publish challenge record deletion +resp=$(curl --silent "${curl_params[@]}" -X PUT "$API/domain/$domain_id/publish" \ + --data "{\"soaTtl\":300}") From 6c9f0e7655beb760b33a764f60b4ebd5c49ee445 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:30:24 +0100 Subject: [PATCH 066/337] Update changelog --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index e52b3d0..f1e2989 100755 --- a/getssl +++ b/getssl @@ -196,6 +196,7 @@ # 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-16 Add support for CloudDNS # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From b8b70f7ceea852bc6194a32cb9959d68c81efeb1 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Thu, 16 Jan 2020 18:49:08 +0100 Subject: [PATCH 067/337] Support certificates with SANs --- dns_scripts/dns_add_clouddns | 2 +- dns_scripts/dns_del_clouddns | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns index 5236269..a4e3f81 100755 --- a/dns_scripts/dns_add_clouddns +++ b/dns_scripts/dns_add_clouddns @@ -69,7 +69,7 @@ if [[ -z "$domain_id" ]]; then fi # Add challenge record -txt_record="_acme-challenge.$domain_root." +txt_record="_acme-challenge.$fulldomain." resp=$(curl --silent "${curl_params[@]}" -X POST "$API/record-txt" \ --data "{\"type\":\"TXT\",\"name\":\"$txt_record\",\"value\":\"$token\",\"domainId\":\"$domain_id\"}") diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns index ddac2b6..ec22c91 100755 --- a/dns_scripts/dns_del_clouddns +++ b/dns_scripts/dns_del_clouddns @@ -54,7 +54,7 @@ curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: applica # Get main domain and challenge record domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') -txt_record="_acme-challenge.$domain_root." +txt_record="_acme-challenge.$fulldomain." # Get domain id curl_domainid_body="{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}, {\"name\": \"domainName\", \"operator\": \"eq\", \"value\": \"$domain_root.\"}]}" From 197c5f8faa86d146ec69666a58f9f00e0f7ed149 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 17 Jan 2020 20:34:14 +0000 Subject: [PATCH 068/337] 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" From 6138f4ab1f27908eca837e26f362f9e254a40f9e Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Tue, 21 Jan 2020 09:42:05 +0100 Subject: [PATCH 069/337] make markdownlint happy --- test/README.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/README.md b/test/README.md index 9d0aedd..086c58a 100644 --- a/test/README.md +++ b/test/README.md @@ -1,19 +1,31 @@ # Testing -This directory contains a simple test script which tests creating certificates with Pebble (testing version of the LetsEncrypt server) +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` + +```sh +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` +```sh +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) + +```sh +docker exec -it getssl /bin/bash +export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +/getssl/getssl -d getssl +``` + +## TODO -# TODO 1. Move to BATS (bash automated testing) instead of run-test.sh 2. Test RHEL6, Debian as well 3. Test SSH, SFTP From 8db7744b3ee20c1b204057b5bc1df3b7742817c3 Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Tue, 21 Jan 2020 09:48:34 +0100 Subject: [PATCH 070/337] implement the suggested fix in #277 --- getssl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 939b351..78e9df3 100755 --- a/getssl +++ b/getssl @@ -234,7 +234,6 @@ SERVER_TYPE="https" SKIP_HTTP_TOKEN_CHECK="false" SSLCONF="$(openssl version -d 2>/dev/null| cut -d\" -f2)/openssl.cnf" OCSP_MUST_STAPLE="false" -IGNORE_PERIOD_CHECK="false" TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" @@ -1831,8 +1830,8 @@ if [[ ${_CHECK_ALL} -eq 1 ]]; then if [[ ${_QUIET} -eq 1 ]]; then cmd="$cmd -q" fi - # check if $dir looks like a domain name (contains a period) - if [[ $(basename "$dir") == *.* || "$IGNORE_PERIOD_CHECK" == "true" ]]; then + # check if $dir is a directory with a getssl.cfg in it + if [[ -f "$dir/getssl.cfg" ]]; then cmd="$cmd -w $WORKING_DIR $(basename "$dir")" debug "CMD: $cmd" eval "$cmd" From 8c7cd703b583e4095c5e41f338388743e8b88c63 Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Tue, 21 Jan 2020 10:06:47 +0100 Subject: [PATCH 071/337] lint CONTRIBUTING.md --- CONTRIBUTING.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 482a7aa..3c8242c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,31 @@ # How to contribute -If you are happy writing in bash, please create a PR for any changes you'd like to see included (or bug fixes). +If you are happy writing in bash, please create a PR for any changes +you'd like to see included (or bug fixes). -If you aren't happy writing in bash, please open an issue with as much detail as possible about the issue or what you'd like to see added / improved. +If you aren't happy writing in bash, please open an issue with as much +detail as possible about the issue or what you'd like to see added / +improved. ## Submitting changes -Please update the 'revision history' and version number at the top of the code (without this I can't easily do a merge) +Please update the 'revision history' and version number at the top of +the code (without this I can't easily do a merge) -Please update just one issue per PR. If there are multiple issues, please provide separate PR's one per issue. +Please update just one issue per PR. If there are multiple issues, +please provide separate PR's one per issue. ## Coding conventions -Please see the guidelines at https://github.com/srvrco/getssl/wiki/Bash-Style-guide +Please see the guidelines at ## Testing -Please test with [shellcheck](https://github.com/koalaman/shellcheck), although this will also be tested on github ( via travis) on all PRs. +Please test with [shellcheck](https://github.com/koalaman/shellcheck), +although this will also be tested on github (via travis) on all PRs. -Please remember that the system is used across a wide range of platforms, so if you have access to multiple operating systems, please test on all. +Please remember that the system is used across a wide range of +platforms, so if you have access to multiple operating systems, please +test on all. - -Thanks :) +Thanks :) From 2c1a894224ec57b674d367c5cbd5f0ee3ef70ab7 Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Tue, 21 Jan 2020 10:07:08 +0100 Subject: [PATCH 072/337] lint bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 116d2a5..135ea05 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,6 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -21,8 +22,9 @@ Steps to reproduce the behavior: A clear and concise description of what you expected to happen. **Operating system (please complete the following information):** - - OS: [e.g. Debian 9, Ubuntu 18.04, freeBSD ] - - Bash Version [e.g. GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)] + +- OS: [e.g. Debian 9, Ubuntu 18.04, freeBSD ] +- Bash Version [e.g. GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)] **Additional context** Add any other context about the problem here. From 0891d37372bbb86677108aae019c4e454ecf6e7f Mon Sep 17 00:00:00 2001 From: Yannic Haupenthal Date: Tue, 21 Jan 2020 10:07:18 +0100 Subject: [PATCH 073/337] lint README.md (and fix some punctuation) --- README.md | 170 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 8549bda..1d0d3fc 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,76 @@ # getssl -Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. + +Obtain SSL certificates from the letsencrypt.org ACME server. Suitable +for automating the process on remote servers. ## Features -* **Bash** - It runs on virtually all unix machines, including BSD, most Linux distributions, macOS. -* **Get certificates for remote servers** - The tokens used to provide validation of domain ownership, and the certificates themselves can be automatically copied to remote servers (via ssh, sftp or ftp for tokens). The script doesn't need to run on the server itself. This can be useful if you don't have access to run such scripts on the server itself, e.g. if it's a shared server. -* **Runs as a daily cron** - so certificates will be automatically renewed when required. + +* **Bash** - It runs on virtually all unix machines, including BSD, most + Linux distributions, macOS. +* **Get certificates for remote servers** - The tokens used to provide + validation of domain ownership, and the certificates themselves can be + automatically copied to remote servers (via ssh, sftp or ftp for + tokens). The script doesn't need to run on the server itself. This can + be useful if you don't have access to run such scripts on the server + itself, e.g. if it's a shared server. +* **Runs as a daily cron** - so certificates will be automatically + renewed when required. * **Automatic certificate renewals** -* **Checks certificates are correctly loaded**. After installation of a new certificate it will test the port specified ( see [Server-Types](#server-types) for options ) that the certificate is actually being used correctly. -* **Automatically updates** - The script can automatically update itself with bug fixes etc if required. -* **Extensively configurable** - With a simple configuration file for each certificate it is possible to configure it exactly for your needs, whether a simple single domain or multiple domains across multiple servers on the same certificate. +* **Checks certificates are correctly loaded** - After installation of a + new certificate it will test the port specified ( see + [Server-Types](#server-types) for options ) that the certificate is + actually being used correctly. +* **Automatically updates** - The script can automatically update itself + with bug fixes etc if required. +* **Extensively configurable** - With a simple configuration file for + each certificate it is possible to configure it exactly for your + needs, whether a simple single domain or multiple domains across + multiple servers on the same certificate. * **Supports http and dns challenges** - Full ACME implementation * **Simple and easy to use** -* **Detailed debug info** - Whilst it shouldn't be needed, detailed debug information is available. -* **Reload services** - After a new certificate is obtained then the relevant services (e.g. apache/nginx/postfix) can be reloaded. +* **Detailed debug info** - Whilst it shouldn't be needed, detailed + debug information is available. +* **Reload services** - After a new certificate is obtained then the + relevant services (e.g. apache/nginx/postfix) can be reloaded. * **ACME v1 and V2** - Supports both ACME versions 1 and 2 ## Installation -Since the script is only one file, you can use the following command for a quick installation of GetSSL only: -``` + +Since the script is only one file, you can use the following command for +a quick installation of GetSSL only: + +```sh curl --silent https://raw.githubusercontent.com/srvrco/getssl/master/getssl > getssl ; chmod 700 getssl ``` -This will copy the getssl Bash script to the current location and change the permissions to make it executable for you. -For a more comprehensive installation (e.g. install also helper scripts) use the provided Makefile with each release tarball. Use the `install` target. +This will copy the getssl Bash script to the current location and change +the permissions to make it executable for you. + +For a more comprehensive installation (e.g. install also helper scripts) +use the provided Makefile with each release tarball. Use the `install` +target. You'll find the latest version in the git repository: -``` +```sh git clone https://github.com/srvrco/getssl.git ``` -For Arch Linux there are packages in the AUR, see [here](https://aur.archlinux.org/packages/getssl/) and [there](https://aur.archlinux.org/packages/getssl-git/). +For Arch Linux there are packages in the AUR, see +[here](https://aur.archlinux.org/packages/getssl/) and +[there](https://aur.archlinux.org/packages/getssl-git/). -If you use puppet, there is a [GetSSL Puppet module](https://github.com/dthielking/puppet_getssl) by dthielking +If you use puppet, there is a [GetSSL Puppet +module](https://github.com/dthielking/puppet_getssl) by dthielking ## Overview -GetSSL was written in standard bash ( so it can be run on a server, a desktop computer, or even a virtualbox) and add the checks, and certificates to a remote server ( providing you have a ssh with key, sftp or ftp access to the remote server). +GetSSL was written in standard bash ( so it can be run on a server, a +desktop computer, or even a virtualbox) and add the checks, and +certificates to a remote server ( providing you have a ssh with key, +sftp or ftp access to the remote server). -``` +```getssl getssl ver. 2.02 Obtain SSL certificates from the letsencrypt.org ACME server @@ -63,27 +95,36 @@ Options: Once you have obtained the script (see Installation above), the next step is to use -```./getssl -c yourdomain.com``` +```sh +./getssl -c yourdomain.com +``` -where yourdomain.com is the primary domain name that you want to create a certificate for. This will create the following folders and files. +where yourdomain.com is the primary domain name that you want to create +a certificate for. This will create the following folders and files. -``` +```sh ~/.getssl ~/.getssl/getssl.cfg ~/.getssl/yourdomain.com ~/.getssl/yourdomain.com/getssl.cfg ``` -You can then edit ~/.getssl/getssl.cfg to set the values you want as the default for the majority of your certificates. - -Then edit ~/.getssl/yourdomain.com/getssl.cfg to have the values you want for this specific domain (make sure to uncomment and specify correct `ACL` option, since it is required). +You can then edit `~/.getssl/getssl.cfg` to set the values you want as the +default for the majority of your certificates. -You can then just run; +Then edit `~/.getssl/yourdomain.com/getssl.cfg` to have the values you +want for this specific domain (make sure to uncomment and specify +correct `ACL` option, since it is required). -```getssl yourdomain.com ``` +You can then just run: -and it should run, providing output like; +```sh +getssl yourdomain.com ``` + +and it should run, providing output like: + +```sh Registering account Verify each domain Verifying yourdomain.com @@ -98,30 +139,41 @@ copying private key to ssh:server5:/home/yourdomain/ssl/domain.key copying CA certificate to ssh:server5:/home/yourdomain/ssl/chain.crt reloading SSL services ``` -**This will (by default) use the staging server, so should give you a certificate that isn't trusted ( Fake Let's Encrypt).** + +**This will (by default) use the staging server, so should give you a +certificate that isn't trusted ( Fake Let's Encrypt).** Change the server in your config file to get a fully valid certificate. -**Note:** Verification is done via port 80 (http), port 443 (https) or dns. The certificate can be used (and checked with getssl) on alternate ports. +**Note:** Verification is done via port 80 (http), port 443 (https) or +dns. The certificate can be used (and checked with getssl) on alternate +ports. ## Automating updates I use the following cron -``` + +```cron 23 5 * * * /root/scripts/getssl -u -a -q ``` -The cron will automatically update getssl and renew any certificates, only giving output if there are issues / errors. + +The cron will automatically update getssl and renew any certificates, +only giving output if there are issues / errors. * The -u flag updates getssl if there is a more recent version available. * The -a flag automatically renews any certificates that are due for renewal. -* The -q flag is "quiet" so that it only outputs and emails me if there was an error / issue. +* The -q flag is "quiet" so that it only outputs and emails me if there + was an error / issue. ## Structure -The design aim was to provide flexibility in running the code. The default working directory is ~/.getssl ( which can be modified via the command line) +The design aim was to provide flexibility in running the code. The +default working directory is `~/.getssl` (which can be modified via the +command line). -Within the **working directory** is a config file, getssl.cfg which is a simple bash file containing variables, an example of which is +Within the **working directory** is a config file `getssl.cfg` which is a +simple bash file containing variables, an example of which is: -``` +```getssl # Uncomment and modify any variables you need # The staging server is best for testing (hence set as default) CA="https://acme-staging.api.letsencrypt.org" @@ -143,9 +195,11 @@ RENEW_ALLOW="30" SSLCONF="/usr/lib/ssl/openssl.cnf" ``` -then, within the **working directory** there will be a folder for each certificate (based on its domain name). Within that folder will be a config file (again called getssl.cfg). An example of which is; +then, within the **working directory** there will be a folder for each +certificate (based on its domain name). Within that folder will be a +config file (again called `getssl.cfg`). An example of which is: -``` +```getssl # 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 @@ -195,19 +249,27 @@ RELOAD_CMD="service apache2 reload" #CHECK_REMOTE="true" ``` -If a location for a file starts with ssh: it is assumed the next part of the file is the hostname, followed by a colon, and then the path. -Files will be securely copied using scp, and it assumes that you have a key on the server ( for passwordless access). You can set the user, port etc for the server in your .ssh/config file +If a location for a file starts with `ssh:` it is assumed the next part +of the file is the hostname, followed by a colon, and then the path. +Files will be securely copied using scp, and it assumes that you have a +key on the server (for passwordless access). You can set the user, +port etc for the server in your `.ssh/config` file. -If an ACL starts with ftp: or sftp: it as assumed that the line is in the format "ftp:UserID:Password:servername:/path/to/acme-challenge". sftp requires sshpass. -Note: FTP can be used for copying tokens only and can **not** be used for uploading private key or certificates as it's not a secure method of transfer. +If an ACL starts with `ftp:` or `sftp:` it as assumed that the line is +in the format "ftp:UserID:Password:servername:/path/to/acme-challenge". +sftp requires sshpass. +Note: FTP can be used for copying tokens only +and can **not** be used for uploading private key or certificates as +it's not a secure method of transfer. ssh can also be used for the reload command if using on remote servers. Multiple locations can be defined for a file by separating the locations with a semi-colon. +A typical config file for `example.com` and `www.example.com` on the +same server would be: -A typical config file for example.com and www.example.com on the same server would be -``` +```getssl # uncomment and modify any variables you need # The staging server is best for testing CA="https://acme-staging.api.letsencrypt.org" @@ -231,6 +293,7 @@ RELOAD_CMD="service apache2 reload" ``` ## Server-Types + OpenSSL has built-in support for getting the certificate from a number of SSL services these are available in getssl to check if the certificate is installed correctly @@ -252,23 +315,32 @@ these are available in getssl to check if the certificate is installed correctly | ldaps | 636 | | | port number | | | - ## Revoke a certificate In general revoking a certificate is not required. Usage: `getssl -r path/to/cert path/to/key [CA_server]` -You need to specify both the certificate you want to revoke, and the account or private domain key which was used to sign / obtain the original certificate. The CA_server is an optional parameter and defaults to Let's Encrypt ( "https://acme-v01.api.letsencrypt.org" ) as that is currently the only Certificate Authority using the ACME protocol. - +You need to specify both the certificate you want to revoke, and the +account or private domain key which was used to sign / obtain the +original certificate. The `CA_server` is an optional parameter and +defaults to Let's Encrypt ("") as +that is currently the only Certificate Authority using the ACME +protocol. ## Elliptic curve keys -You can use Elliptic curve keys for both the account key and the domain key (different of course, don't use the same key for both). prime256v1 (NIST P-256) and secp384r1 (NIST P-384) are both fully supported. secp521r1 (NIST P-521) is included in the code, but not currently supported by Let's Encrypt). +You can use Elliptic curve keys for both the account key and the domain +key (different of course, don't use the same key for both). prime256v1 +(NIST P-256) and secp384r1 (NIST P-384) are both fully supported. +secp521r1 (NIST P-521) is included in the code, but not currently +supported by Let's Encrypt). ## Issues / problems / help -If you have any issues, please log them at https://github.com/srvrco/getssl/issues + +If you have any issues, please log them at There are additional help pages on the [wiki](https://github.com/srvrco/getssl/wiki) -If you have any suggestions for improvements then pull requests are welcomed, or raise an issue. +If you have any suggestions for improvements then pull requests are +welcomed, or raise an issue. From b1e177f45ea50e84be5f979cad967d823c37a637 Mon Sep 17 00:00:00 2001 From: Radek SPRTA Date: Wed, 22 Jan 2020 05:26:01 +0100 Subject: [PATCH 074/337] Handle domains of .co.uk type --- dns_scripts/dns_add_clouddns | 11 ++++++++++- dns_scripts/dns_del_clouddns | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_clouddns b/dns_scripts/dns_add_clouddns index a4e3f81..f20d1ab 100755 --- a/dns_scripts/dns_add_clouddns +++ b/dns_scripts/dns_add_clouddns @@ -53,7 +53,16 @@ fi curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) # Get main domain -domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}]}") +domain_slice="$fulldomain" +while [[ -z "$domain_root" ]]; do + if [[ "${resp// }" =~ domainName\":\"$domain_slice ]]; then + domain_root="$domain_slice" + _debug domain_root "$domain_root" + fi + domain_slice="${domain_slice#[^\.]*.}" +done # Get domain id resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ diff --git a/dns_scripts/dns_del_clouddns b/dns_scripts/dns_del_clouddns index ec22c91..0b25121 100755 --- a/dns_scripts/dns_del_clouddns +++ b/dns_scripts/dns_del_clouddns @@ -53,7 +53,16 @@ fi curl_params=( -H "Authorization: Bearer $access_token" -H 'Content-Type: application/json' ) # Get main domain and challenge record -domain_root=$(echo "$fulldomain" | awk -F\. '{print $(NF-1) FS $NF}') +resp=$(curl --silent "${curl_params[@]}" -X POST "$API/domain/search" \ + --data "{\"search\": [{\"name\": \"clientId\", \"operator\": \"eq\", \"value\": \"$client\"}]}") +domain_slice="$fulldomain" +while [[ -z "$domain_root" ]]; do + if [[ "${resp// }" =~ domainName\":\"$domain_slice ]]; then + domain_root="$domain_slice" + _debug domain_root "$domain_root" + fi + domain_slice="${domain_slice#[^\.]*.}" +done txt_record="_acme-challenge.$fulldomain." # Get domain id From 25ab41135d550c6e7313838a008dad850057807d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 22 Jan 2020 22:38:45 +0000 Subject: [PATCH 075/337] Fix json_get for 6 parameters when >9 domains --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 8269195..0646ce4 100755 --- a/getssl +++ b/getssl @@ -1192,7 +1192,7 @@ json_get() { # get values from json if [[ -n "$6" ]]; then full=$(json_awk "$1") section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') - echo "$full" | grep "^..${5}\",$section" | awk '{print $2}' | tr -d '"' + echo "$full" | grep "^..${5}\",$section\]" | awk '{print $2}' | tr -d '"' elif [[ -n "$5" ]]; then full=$(json_awk "$1") section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') From 839209d5285e1b87093b934128cf9449095a4ec6 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 24 Jan 2020 18:58:20 +0000 Subject: [PATCH 076/337] Use urlbase64_decode instead of base64 -d --- getssl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 8269195..48b2655 100755 --- a/getssl +++ b/getssl @@ -1445,7 +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 - response=$(echo "$response" | base64 -d 2>&1) + response=$(urlbase64_decode "$response") fi debug responseHeaders "$responseHeaders" @@ -1561,6 +1561,18 @@ urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:' } +# base64url decode +# From: https://gist.github.com/alvis/89007e96f7958f2686036d4276d28e47 +urlbase64_decode() { + INPUT=$1 # $(if [ -z "$1" ]; then echo -n $(cat -); else echo -n "$1"; fi) + MOD=$(($(echo -n "$INPUT" | wc -c) % 4)) + PADDING=$(if [ $MOD -eq 2 ]; then echo -n '=='; elif [ $MOD -eq 3 ]; then echo -n '=' ; fi) + echo -n "$INPUT$PADDING" | + sed s/-/+/g | + sed s/_/\\//g | + openssl base64 -d -A +} + usage() { # echos out the program usage echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet]"\ "[-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] domain" From 0040d84d22b4b6832c51855a96a78d6642377729 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 24 Jan 2020 19:05:18 +0000 Subject: [PATCH 077/337] Update revision history --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 0646ce4..32df8d1 100755 --- a/getssl +++ b/getssl @@ -197,6 +197,7 @@ # 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) +# 2020-01-22 #475 and #483 Fix grep regex for >9 subdomains in json_get # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From 52883615229334216d00507b0346fd04efba1310 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 24 Jan 2020 20:17:15 +0000 Subject: [PATCH 078/337] Test using BATS --- docker-compose.yml | 54 ++++++++++++++-- test/1-simple-http01.bats | 28 +++++++++ test/2-simple-dns01.bats | 28 +++++++++ test/4-more-than-10-hosts.bats | 39 ++++++++++++ test/Dockerfile-centos6 | 22 +++++++ test/Dockerfile-rhel6 | 22 ------- ...{Dockerfile-ubuntu => Dockerfile-ubuntu18} | 10 +-- test/README.md | 26 +++++--- test/run-test.sh | 47 +++----------- test/test-config/getssl-dns01.cfg | 2 +- test/test-config/getssl-http01-10-hosts.cfg | 28 +++++++++ test/test-config/getssl-http01.cfg | 6 +- test/test-config/nginx-ubuntu-no-ssl | 63 ------------------- test/test_helper.bash | 44 +++++++++++++ 14 files changed, 274 insertions(+), 145 deletions(-) create mode 100644 test/1-simple-http01.bats create mode 100644 test/2-simple-dns01.bats create mode 100644 test/4-more-than-10-hosts.bats create mode 100644 test/Dockerfile-centos6 delete mode 100644 test/Dockerfile-rhel6 rename test/{Dockerfile-ubuntu => Dockerfile-ubuntu18} (66%) create mode 100644 test/test-config/getssl-http01-10-hosts.cfg create mode 100644 test/test_helper.bash diff --git a/docker-compose.yml b/docker-compose.yml index f4b3567..cbe52e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,8 +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 + # Don't re-use authorisations (breaks force renew test scripts) + # PEBBLE_AUTHZREUSE: "0" ports: - 14000:14000 # HTTPS ACME API - 15000:15000 # HTTPS Management API @@ -23,16 +23,60 @@ services: networks: acmenet: ipv4_address: 10.30.50.3 - getssl: + getssl-ubuntu18: build: context: . - dockerfile: test/Dockerfile-ubuntu - container_name: getssl + dockerfile: test/Dockerfile-ubuntu18 + container_name: getssl-ubuntu18 volumes: - .:/getssl + environment: + GETSSL_HOST: ubuntu18.getssl.test + GETSSL_IP: 10.30.50.4 + NGINX_CONFIG: /etc/nginx/sites-enabled/default networks: acmenet: ipv4_address: 10.30.50.4 + aliases: + - ubuntu18.getssl.test + - a.ubuntu18.getssl.test + - b.ubuntu18.getssl.test + - c.ubuntu18.getssl.test + - d.ubuntu18.getssl.test + - e.ubuntu18.getssl.test + - f.ubuntu18.getssl.test + - g.ubuntu18.getssl.test + - h.ubuntu18.getssl.test + - i.ubuntu18.getssl.test + - j.ubuntu18.getssl.test + - k.ubuntu18.getssl.test + getssl-centos6: + build: + context: . + dockerfile: test/Dockerfile-centos6 + container_name: getssl-centos6 + volumes: + - .:/getssl + environment: + GETSSL_HOST: centos6.getssl.test + GETSSL_IP: 10.30.50.5 + NGINX_CONFIG: /etc/nginx/conf.d/default.conf + networks: + acmenet: + ipv4_address: 10.30.50.5 + aliases: + - centos6.getssl.test + - a.centos6.getssl.test + - b.centos6.getssl.test + - c.centos6.getssl.test + - d.centos6.getssl.test + - e.centos6.getssl.test + - f.centos6.getssl.test + - g.centos6.getssl.test + - h.centos6.getssl.test + - i.centos6.getssl.test + - j.centos6.getssl.test + - k.centos6.getssl.test networks: acmenet: diff --git a/test/1-simple-http01.bats b/test/1-simple-http01.bats new file mode 100644 index 0000000..40416b2 --- /dev/null +++ b/test/1-simple-http01.bats @@ -0,0 +1,28 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create new certificate using HTTP-01 verification" { + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of certificate using HTTP-01" { + #!FIXME test certificate has been updated + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + cleanup_environment +} diff --git a/test/2-simple-dns01.bats b/test/2-simple-dns01.bats new file mode 100644 index 0000000..e1a37ec --- /dev/null +++ b/test/2-simple-dns01.bats @@ -0,0 +1,28 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create new certificate using DNS-01 verification" { + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of certificate using DNS-01" { + #!FIXME test certificate has been updated + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + cleanup_environment +} diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats new file mode 100644 index 0000000..0493197 --- /dev/null +++ b/test/4-more-than-10-hosts.bats @@ -0,0 +1,39 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificates for more than 10 hosts using HTTP-01 verification" { + CONFIG_FILE="getssl-http01-10-hosts.cfg" + setup_environment + + # Add 11 hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c d e f g h i j k; do + curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of more than 10 certificates using HTTP-01" { + #!FIXME test certificate has been updated + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + + # Remove all the dns aliases + cleanup_environment + for prefix in a b c d e f g h i j k; do + curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + done +} diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 new file mode 100644 index 0000000..f370a0b --- /dev/null +++ b/test/Dockerfile-centos6 @@ -0,0 +1,22 @@ +FROM centos:centos6 + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl dnsutils wget nginx + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +EXPOSE 80 443 + +# Run eternal loop - for testing +CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] diff --git a/test/Dockerfile-rhel6 b/test/Dockerfile-rhel6 deleted file mode 100644 index 019da84..0000000 --- a/test/Dockerfile-rhel6 +++ /dev/null @@ -1,22 +0,0 @@ -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"] diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu18 similarity index 66% rename from test/Dockerfile-ubuntu rename to test/Dockerfile-ubuntu18 index b0f09f8..6dd92c6 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu18 @@ -1,8 +1,8 @@ -FROM ubuntu:xenial +FROM ubuntu:bionic # bionic = latest 18 version # Update and install required software -RUN apt-get update +RUN apt-get update --fix-missing # TODO work out why default version of awk fails 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 @@ -14,8 +14,10 @@ RUN mkdir /etc/nginx/pki/private 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 +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local EXPOSE 80 443 diff --git a/test/README.md b/test/README.md index 086c58a..9157aa8 100644 --- a/test/README.md +++ b/test/README.md @@ -12,21 +12,29 @@ docker-compose -f "docker-compose.yml" up -d --build Run the tests ```sh -docker exec -it getssl /getssl/test/run-test.sh +docker exec -it getssl bats /getssl/test ``` -Debug (need to set `CURL_CA_BUNDLE` as pebble uses a local certificate, +Run individual test + +```sh +docker exec -it getssl bats /getssl/test/ +``` + +Debug (uses helper script to set `CURL_CA_BUNDLE` as pebble uses a local certificate, otherwise you get a "unknown API version" error) ```sh -docker exec -it getssl /bin/bash -export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt -/getssl/getssl -d getssl +docker exec -it getssl- /getssl/test/run-test.sh ` + +eg. + +```sh +docker exec -it getssl-ubuntu18 /getssl/test/run-test.sh getssl-http01.cfg ``` ## 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 +1. Test RHEL6, Debian as well +2. Test SSH, SFTP +3. Test wildcards diff --git a/test/run-test.sh b/test/run-test.sh index 8051922..b6f7c72 100644 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -1,43 +1,14 @@ -#! /bin/bash +#!/usr/bin/env bash -set -e +# This runs getssl outside of the BATS framework for debugging, etc, against pebble +# Usage: /getssl/test/run-test.sh getssl-http01.cfg -# Test setup -if [[ -d /root/.getssl ]]; then - rm -r /root/.getssl -fi +CONFIG_FILE=$1 +source /getssl/test/test_helper.bash -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 -cat /etc/ssl/certs/ca-certificates.crt /root/pebble.minica.pem > /root/pebble-ca-bundle.crt +setup_environment 3>&1 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 - -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-http01.cfg /root/.getssl/getssl/getssl.cfg -/getssl/getssl -f getssl - -# Test #2 - http-01 forced renewal -echo Test \#2 - http-01 forced renewal -/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 -/getssl/getssl getssl -f +"${CODE_DIR}/getssl" -c "$GETSSL_HOST" 3>&1 +cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" +"${CODE_DIR}/getssl" -f -d "$GETSSL_HOST" 3>&1 diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 49c58b5..790dee2 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -44,7 +44,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="cp /getssl/test/test-config/nginx-ubuntu-ssl /etc/nginx/sites-enabled/default && service nginx restart" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && service nginx restart >&3-" # 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/getssl-http01-10-hosts.cfg b/test/test-config/getssl-http01-10-hosts.cfg new file mode 100644 index 0000000..f521d52 --- /dev/null +++ b/test/test-config/getssl-http01-10-hosts.cfg @@ -0,0 +1,28 @@ +# 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 + +CA="https://pebble:14000/dir" + +# 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="a.${GETSSL_HOST},b.${GETSSL_HOST},c.${GETSSL_HOST},d.${GETSSL_HOST},e.${GETSSL_HOST},f.${GETSSL_HOST},g.${GETSSL_HOST},h.${GETSSL_HOST},i.${GETSSL_HOST},j.${GETSSL_HOST},k.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && service nginx restart >&3-" + +#SERVER_TYPE="https" +#CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01.cfg b/test/test-config/getssl-http01.cfg index f3dc5ad..305dd49 100644 --- a/test/test-config/getssl-http01.cfg +++ b/test/test-config/getssl-http01.cfg @@ -43,11 +43,11 @@ 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" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && service nginx restart >&3-" # 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" +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/nginx-ubuntu-no-ssl b/test/test-config/nginx-ubuntu-no-ssl index c78d646..e7b046e 100644 --- a/test/test-config/nginx-ubuntu-no-ssl +++ b/test/test-config/nginx-ubuntu-no-ssl @@ -1,16 +1,3 @@ -## -# 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 { @@ -26,18 +13,6 @@ server { listen 5001 default_server; listen [::]:5001 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 @@ -52,42 +27,4 @@ server { # 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; -# } -#} diff --git a/test/test_helper.bash b/test/test_helper.bash new file mode 100644 index 0000000..6f506fd --- /dev/null +++ b/test/test_helper.bash @@ -0,0 +1,44 @@ +INSTALL_DIR=/root +CODE_DIR=/getssl + + +setup_environment() { + # One-off test setup + if [[ -d ${INSTALL_DIR}/.getssl ]]; then + rm -r ${INSTALL_DIR}/.getssl + fi + + if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then + wget --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/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 + fi + cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt + fi + + curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a + cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl ${NGINX_CONFIG} + service nginx restart >&3- +} + + +cleanup_environment() { + curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a +} + + +init_getssl() { + # Run initialisation (create account key, etc) + run ${CODE_DIR}/getssl -c "$GETSSL_HOST" + assert_success + [ -d "$INSTALL_DIR/.getssl" ] +} + + +create_certificate() { + # Create certificate + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + run ${CODE_DIR}/getssl "$GETSSL_HOST" + #!FIXME test certificate has been placed in the expected location +} From b3480db63b498798de9295c8d1153eb145c7dbd9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 24 Jan 2020 20:17:52 +0000 Subject: [PATCH 079/337] Fix for "already verified" problem seen with ACMEv2 during testing --- getssl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index 622fcb1..a542c0b 100755 --- a/getssl +++ b/getssl @@ -1608,13 +1608,13 @@ write_domain_template() { # write out a template file for a domain. # 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. - # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, - # password, host, port (explicitly needed even if using default port 443) and path on the server. + # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, + # password, host, port (explicitly needed even if using default port 443) and path on the server. #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' - # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') #Set USE_SINGLE_ACL="true" to use a single ACL for all checks #USE_SINGLE_ACL="false" @@ -2212,7 +2212,7 @@ for d in $alldomains; do error_exit "new-authz error: $response" fi else - response_status="" + send_signed_request "${AuthLink[$dn]}" "" fi if [[ $response_status == "valid" ]]; then @@ -2237,7 +2237,6 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - 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") From 08a6485911c22d71adc561d9331f7008a98ee1a7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 26 Jan 2020 14:09:39 +0000 Subject: [PATCH 080/337] Update revision history --- getssl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/getssl b/getssl index 6c3870f..763677f 100755 --- a/getssl +++ b/getssl @@ -200,6 +200,8 @@ # 2020-01-22 #475 and #483 Fix grep regex for >9 subdomains in json_get # 2020-01-24 Add support for CloudDNS # 2020-01-24 allow file transfer using WebDAV over HTTPS +# 2020-01-26 Use urlbase64_decode() instead of base64 -d +# 2020-01-26 Fix "already verified" error for ACMEv2 # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From d8a0e4ca237545b6515d29beba193c9edf4d07c4 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 28 Jan 2020 19:47:15 +0100 Subject: [PATCH 081/337] Check output for "Error", "Failed" and "Warning" --- test/1-simple-http01.bats | 6 ++++++ test/2-simple-dns01.bats | 6 ++++++ test/4-more-than-10-hosts.bats | 7 ++++++- test/Dockerfile-ubuntu18 | 3 +++ test/README.md | 6 +++--- test/{run-test.sh => debug-test.sh} | 4 ++-- test/run-all-tests.sh | 4 ++++ 7 files changed, 30 insertions(+), 6 deletions(-) rename test/{run-test.sh => debug-test.sh} (79%) create mode 100644 test/run-all-tests.sh diff --git a/test/1-simple-http01.bats b/test/1-simple-http01.bats index 40416b2..4c55304 100644 --- a/test/1-simple-http01.bats +++ b/test/1-simple-http01.bats @@ -17,6 +17,9 @@ setup() { init_getssl create_certificate assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } @@ -24,5 +27,8 @@ setup() { #!FIXME test certificate has been updated run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment } diff --git a/test/2-simple-dns01.bats b/test/2-simple-dns01.bats index e1a37ec..9d9f44b 100644 --- a/test/2-simple-dns01.bats +++ b/test/2-simple-dns01.bats @@ -17,6 +17,9 @@ setup() { init_getssl create_certificate assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } @@ -24,5 +27,8 @@ setup() { #!FIXME test certificate has been updated run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment } diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index 0493197..01e364d 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -23,6 +23,9 @@ setup() { init_getssl create_certificate assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } @@ -30,7 +33,9 @@ setup() { #!FIXME test certificate has been updated run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' # Remove all the dns aliases cleanup_environment for prefix in a b c d e f g h i j k; do diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 6dd92c6..1b3765c 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -13,6 +13,9 @@ RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +# Prevent "Can't load /root/.rnd into RNG" error from openssl +RUN touch /root/.rnd + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core RUN git clone https://github.com/jasonkarns/bats-support /bats-support diff --git a/test/README.md b/test/README.md index 9157aa8..98ff929 100644 --- a/test/README.md +++ b/test/README.md @@ -12,7 +12,7 @@ docker-compose -f "docker-compose.yml" up -d --build Run the tests ```sh -docker exec -it getssl bats /getssl/test +test/run-all-tests.sh ``` Run individual test @@ -25,12 +25,12 @@ Debug (uses helper script to set `CURL_CA_BUNDLE` as pebble uses a local certifi otherwise you get a "unknown API version" error) ```sh -docker exec -it getssl- /getssl/test/run-test.sh ` +docker exec -it getssl- /getssl/test/debug-test.sh ` eg. ```sh -docker exec -it getssl-ubuntu18 /getssl/test/run-test.sh getssl-http01.cfg +docker exec -it getssl-ubuntu18 /getssl/test/debug-test.sh getssl-http01.cfg ``` ## TODO diff --git a/test/run-test.sh b/test/debug-test.sh similarity index 79% rename from test/run-test.sh rename to test/debug-test.sh index b6f7c72..23d1983 100644 --- a/test/run-test.sh +++ b/test/debug-test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # This runs getssl outside of the BATS framework for debugging, etc, against pebble -# Usage: /getssl/test/run-test.sh getssl-http01.cfg +# Usage: /getssl/test/debug-test.sh getssl-http01.cfg CONFIG_FILE=$1 source /getssl/test/test_helper.bash @@ -11,4 +11,4 @@ export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt "${CODE_DIR}/getssl" -c "$GETSSL_HOST" 3>&1 cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" -"${CODE_DIR}/getssl" -f -d "$GETSSL_HOST" 3>&1 +"${CODE_DIR}/getssl" -f "$GETSSL_HOST" 3>&1 diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh new file mode 100644 index 0000000..d0749e1 --- /dev/null +++ b/test/run-all-tests.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +docker exec -it getssl-centos6 bats /getssl/test +docker exec -it getssl-ubuntu18 bats /getssl/test From 2a5824afeff449e35d513a457f0f1ea8560bd78c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 29 Jan 2020 15:27:57 +0100 Subject: [PATCH 082/337] Show error message if awk doesn't support json_awk --- docker-compose.yml | 16 ++++++++++++++++ getssl | 11 +++++++++++ test/5-old-awk-error.bats | 25 +++++++++++++++++++++++++ test/Dockerfile-ubuntu18-no-gawk | 17 +++++++++++++++++ test/run-all-tests.sh | 1 + 5 files changed, 70 insertions(+) create mode 100644 test/5-old-awk-error.bats create mode 100644 test/Dockerfile-ubuntu18-no-gawk diff --git a/docker-compose.yml b/docker-compose.yml index cbe52e2..d031d30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,6 +77,22 @@ services: - i.centos6.getssl.test - j.centos6.getssl.test - k.centos6.getssl.test + getssl-ubuntu18-no-gawk: + build: + context: . + dockerfile: test/Dockerfile-ubuntu18-no-gawk + container_name: getssl-ubuntu18-no-gawk + volumes: + - .:/getssl + environment: + GETSSL_HOST: ubuntu18-no-gawk.getssl.test + GETSSL_IP: 10.30.50.6 + NGINX_CONFIG: /etc/nginx/sites-enabled/default + networks: + acmenet: + ipv4_address: 10.30.50.6 + aliases: + - ubuntu18-no-gawk.getssl.test networks: acmenet: diff --git a/getssl b/getssl index 763677f..810a30a 100755 --- a/getssl +++ b/getssl @@ -15,6 +15,8 @@ # For usage, run "getssl -h" or see https://github.com/srvrco/getssl +# ACMEv2 process is documented at https://tools.ietf.org/html/rfc8555#section-7.4 + # Revision history: # 2016-01-08 Created (v0.1) # 2016-01-11 type correction and upload to github (v0.2) @@ -202,6 +204,7 @@ # 2020-01-24 allow file transfer using WebDAV over HTTPS # 2020-01-26 Use urlbase64_decode() instead of base64 -d # 2020-01-26 Fix "already verified" error for ACMEv2 +# 2020-01-29 Check awk new enough to support json_awk # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -1982,6 +1985,14 @@ else fi debug "Using API v$API" +# Check if awk supports json_awk (required for ACMEv2) +if [[ $API -eq 2 ]]; then + json_awk_test=$(json_awk '{ "test": "1" }' 2>/dev/null) + if [[ "${json_awk_test}" == "" ]]; then + error_exit "Your version of awk does not work with json_awk (see http://github.com/step-/JSON.awk/issues/6), please install a newer version of mawk or gawk" + fi +fi + # 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 debug "getting certificate for $DOMAIN from remote server" diff --git a/test/5-old-awk-error.bats b/test/5-old-awk-error.bats new file mode 100644 index 0000000..4a00b31 --- /dev/null +++ b/test/5-old-awk-error.bats @@ -0,0 +1,25 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Check getssl fails if an old version of awk is installed" { + CONFIG_FILE="getssl-http01.cfg" + # Make sure this test only runs on an image running an old version of awk + awk_version=$(awk -V 2>/dev/null) || true + if [[ "$awk_version" == "" ]]; then + setup_environment + init_getssl + create_certificate + assert_failure + assert_output "getssl: Your version of awk does not work with json_awk (see http://github.com/step-/JSON.awk/issues/6), please install a newer version of mawk or gawk" + fi +} diff --git a/test/Dockerfile-ubuntu18-no-gawk b/test/Dockerfile-ubuntu18-no-gawk new file mode 100644 index 0000000..809708a --- /dev/null +++ b/test/Dockerfile-ubuntu18-no-gawk @@ -0,0 +1,17 @@ +FROM ubuntu:bionic +# bionic = latest 18 version + +# Update and install required software +RUN apt-get update --fix-missing +RUN apt-get install -y git curl dnsutils wget nginx-light + +WORKDIR /root + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +# Run eternal loop - for testing +CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh index d0749e1..7372e5b 100644 --- a/test/run-all-tests.sh +++ b/test/run-all-tests.sh @@ -2,3 +2,4 @@ docker exec -it getssl-centos6 bats /getssl/test docker exec -it getssl-ubuntu18 bats /getssl/test +docker exec -it getssl-ubuntu18-no-gawk bats /getssl/test/5-old-awk-error.bats From b785c42bab857d1f5f041dbe969cd443433b096f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 29 Jan 2020 18:04:51 +0100 Subject: [PATCH 083/337] Less fragile way of deciding whether to run old awk test --- docker-compose.yml | 1 + test/5-old-awk-error.bats | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d031d30..97bb1e5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -88,6 +88,7 @@ services: GETSSL_HOST: ubuntu18-no-gawk.getssl.test GETSSL_IP: 10.30.50.6 NGINX_CONFIG: /etc/nginx/sites-enabled/default + TEST_AWK: "yes" networks: acmenet: ipv4_address: 10.30.50.6 diff --git a/test/5-old-awk-error.bats b/test/5-old-awk-error.bats index 4a00b31..0f234a2 100644 --- a/test/5-old-awk-error.bats +++ b/test/5-old-awk-error.bats @@ -14,8 +14,7 @@ setup() { @test "Check getssl fails if an old version of awk is installed" { CONFIG_FILE="getssl-http01.cfg" # Make sure this test only runs on an image running an old version of awk - awk_version=$(awk -V 2>/dev/null) || true - if [[ "$awk_version" == "" ]]; then + if [[ "$TEST_AWK" != "" ]]; then setup_environment init_getssl create_certificate From 26f224bd3c458f7b33bf311c50f3477cd7eec93c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 31 Jan 2020 19:09:51 +0000 Subject: [PATCH 084/337] Create stale.yml --- .github/workflows/stale.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..8699ac5 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,17 @@ +name: "Close stale issues" +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue will be closed as no response' + stale-issue-label: 'needs more information' + exempt-issue-label: 'enhancement' + days-before-stale: 60 + days-before-close: 30 From 410a3c9087b0c2ca01cf55a23c5875e66195c90f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 5 Feb 2020 12:51:43 +0000 Subject: [PATCH 085/337] Fix epoch_date for busybox and json_awk for gawk v5 --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 810a30a..c1bb890 100755 --- a/getssl +++ b/getssl @@ -716,7 +716,7 @@ date_epoc() { # convert the date into epoch time elif [[ "$os" == "mac" ]]; then date -j -f "%b %d %T %Y %Z" "$1" +%s elif [[ "$os" == "busybox" ]]; then - de_ld=$(echo "$1" | awk '{print $1 $2 $3 $4}') + de_ld=$(echo "$1" | awk '{print $1 " " $2 " " $3 " " $4}') date -D "%b %d %T %Y" -d "$de_ld" +%s else date -d "$1" +%s @@ -1174,7 +1174,7 @@ function scream(msg) { function tokenize(a1,pq,pb,ESCAPE,CHAR,STRING,NUMBER,KEYWORD,SPACE) { SPACE="[[:space:]]+" - gsub(/\"[^[:cntrl:]\"\\]*((\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})[^[:cntrl:]\"\\]*)*\"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[[:space:]]+|./, "\n&", a1) + gsub(/"[^[:cntrl:]"\\]*((\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})[^[:cntrl:]"\\]*)*"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[[:space:]]+|./, "\n&", a1) gsub("\n" SPACE, "\n", a1) sub(/^\n/, "", a1) ITOKENS=0 # get_token() helper From 886b456ae338a7bfa800ea27d697e68ce39ed6f7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 5 Feb 2020 12:52:25 +0000 Subject: [PATCH 086/337] Test improvements and add Alpine Linux --- docker-compose.yml | 33 ++++++++++++++++++--- test/Dockerfile-alpine | 20 +++++++++++++ test/Dockerfile-centos6 | 2 +- test/Dockerfile-ubuntu18 | 2 +- test/Dockerfile-ubuntu18-no-gawk | 2 +- test/alpine-supervisord.conf | 14 +++++++++ test/restart-nginx | 8 +++++ test/test-config/getssl-dns01.cfg | 29 +++--------------- test/test-config/getssl-http01-10-hosts.cfg | 8 ++--- test/test-config/getssl-http01.cfg | 30 ++----------------- test/test_helper.bash | 3 +- 11 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 test/Dockerfile-alpine create mode 100644 test/alpine-supervisord.conf create mode 100644 test/restart-nginx diff --git a/docker-compose.yml b/docker-compose.yml index 97bb1e5..ebd5369 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,8 +7,6 @@ services: environment: # with Go 1.13.x which defaults TLS 1.3 to on GODEBUG: "tls13=1" - # Don't re-use authorisations (breaks force renew test scripts) - # PEBBLE_AUTHZREUSE: "0" ports: - 14000:14000 # HTTPS ACME API - 15000:15000 # HTTPS Management API @@ -77,6 +75,33 @@ services: - i.centos6.getssl.test - j.centos6.getssl.test - k.centos6.getssl.test + getssl-alpine: + build: + context: . + dockerfile: test/Dockerfile-alpine + container_name: getssl-alpine + volumes: + - .:/getssl + environment: + GETSSL_HOST: alpine.getssl.test + GETSSL_IP: 10.30.50.6 + NGINX_CONFIG: /etc/nginx/conf.d/default.conf + networks: + acmenet: + ipv4_address: 10.30.50.6 + aliases: + - alpine.getssl.test + - a.alpine.getssl.test + - b.alpine.getssl.test + - c.alpine.getssl.test + - d.alpine.getssl.test + - e.alpine.getssl.test + - f.alpine.getssl.test + - g.alpine.getssl.test + - h.alpine.getssl.test + - i.alpine.getssl.test + - j.alpine.getssl.test + - k.alpine.getssl.test getssl-ubuntu18-no-gawk: build: context: . @@ -86,12 +111,12 @@ services: - .:/getssl environment: GETSSL_HOST: ubuntu18-no-gawk.getssl.test - GETSSL_IP: 10.30.50.6 + GETSSL_IP: 10.30.50.7 NGINX_CONFIG: /etc/nginx/sites-enabled/default TEST_AWK: "yes" networks: acmenet: - ipv4_address: 10.30.50.6 + ipv4_address: 10.30.50.7 aliases: - ubuntu18-no-gawk.getssl.test diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine new file mode 100644 index 0000000..e9ee6c3 --- /dev/null +++ b/test/Dockerfile-alpine @@ -0,0 +1,20 @@ +FROM alpine:latest + +RUN apk --no-cache add supervisor openssl git curl bind-tools wget gawk nginx bash +# RUN apk --no-cache add vim dos2unix # for debugging + +WORKDIR /root +RUN mkdir /run/nginx +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +COPY ./test/alpine-supervisord.conf /etc/supervisord.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +# Use supervisord to run nginx in the background +ENTRYPOINT /usr/bin/supervisord -c /etc/supervisord.conf diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index f370a0b..a82bf1f 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -19,4 +19,4 @@ RUN /bats-core/install.sh /usr/local EXPOSE 80 443 # Run eternal loop - for testing -CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] +CMD tail -f /dev/null diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 1b3765c..31554cd 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -25,4 +25,4 @@ RUN /bats-core/install.sh /usr/local EXPOSE 80 443 # Run eternal loop - for testing -CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] +CMD tail -f /dev/null diff --git a/test/Dockerfile-ubuntu18-no-gawk b/test/Dockerfile-ubuntu18-no-gawk index 809708a..3eb9732 100644 --- a/test/Dockerfile-ubuntu18-no-gawk +++ b/test/Dockerfile-ubuntu18-no-gawk @@ -14,4 +14,4 @@ RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing -CMD ["/bin/bash", "-c", "while :; do sleep 10; done"] +CMD tail -f /dev/null diff --git a/test/alpine-supervisord.conf b/test/alpine-supervisord.conf new file mode 100644 index 0000000..8eec585 --- /dev/null +++ b/test/alpine-supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +logfile=/tmp/supervisord.log +childlogdir=/tmp +pidfile = /tmp/supervisord.pid + +[program:nginx] +command=nginx -g 'daemon off;' +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +autorestart=false +startretries=0 diff --git a/test/restart-nginx b/test/restart-nginx new file mode 100644 index 0000000..d35f60f --- /dev/null +++ b/test/restart-nginx @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if [ "$GETSSL_HOST" = "alpine.getssl.test" ]; then + killall -HUP nginx >&3- + sleep 5 +else + service nginx restart >&3- +fi diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 790dee2..98637b0 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -2,35 +2,17 @@ # 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" @@ -44,11 +26,8 @@ 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 ${NGINX_CONFIG} && service nginx restart >&3-" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" -# 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" +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01-10-hosts.cfg b/test/test-config/getssl-http01-10-hosts.cfg index f521d52..d5c364e 100644 --- a/test/test-config/getssl-http01-10-hosts.cfg +++ b/test/test-config/getssl-http01-10-hosts.cfg @@ -5,7 +5,6 @@ CA="https://pebble:14000/dir" # 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="a.${GETSSL_HOST},b.${GETSSL_HOST},c.${GETSSL_HOST},d.${GETSSL_HOST},e.${GETSSL_HOST},f.${GETSSL_HOST},g.${GETSSL_HOST},h.${GETSSL_HOST},i.${GETSSL_HOST},j.${GETSSL_HOST},k.${GETSSL_HOST}" # Acme Challenge Location. @@ -22,7 +21,8 @@ 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 ${NGINX_CONFIG} && service nginx restart >&3-" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" -#SERVER_TYPE="https" -#CHECK_REMOTE="true" +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01.cfg b/test/test-config/getssl-http01.cfg index 305dd49..f7d75ea 100644 --- a/test/test-config/getssl-http01.cfg +++ b/test/test-config/getssl-http01.cfg @@ -2,34 +2,13 @@ # 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" - -#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. +# Acme Challenge Location. 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" @@ -43,11 +22,8 @@ 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 ${NGINX_CONFIG} && service nginx restart >&3-" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" -# 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 +# Define the server type and confirm correct certificate is installed SERVER_TYPE="https" CHECK_REMOTE="true" diff --git a/test/test_helper.bash b/test/test_helper.bash index 6f506fd..0ac9a43 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -19,7 +19,7 @@ setup_environment() { curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl ${NGINX_CONFIG} - service nginx restart >&3- + /getssl/test/restart-nginx } @@ -40,5 +40,4 @@ create_certificate() { # Create certificate cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" run ${CODE_DIR}/getssl "$GETSSL_HOST" - #!FIXME test certificate has been placed in the expected location } From 978de4bda0a088478a447d80f802e3f48565d66e Mon Sep 17 00:00:00 2001 From: Robert de Bath Date: Thu, 6 Feb 2020 06:30:34 +0000 Subject: [PATCH 087/337] Change -debug=1 to -debug for nslookup Debian dnsutils 1:9.11.5.P4+dfsg-5.1 errors on -debug=1 Dnsutils version 1:9.10.3.dfsg.P4-12.3+deb9u4 and version 1:9.2.1-2.woody.2 (from 2003) accept both -debug=1 and -debug so I've changed this globally. --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index c1bb890..2f1a26b 100755 --- a/getssl +++ b/getssl @@ -813,7 +813,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n return fi - res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d" ${gad_s}) + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then # this is a Non-authoritative server, need to check for an authoritative one. @@ -826,9 +826,9 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi if [[ -z "$gad_s" ]]; then - res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d") + res=$(nslookup -debug -type=soa -type=ns "$gad_d") else - res=$(nslookup -debug=1 -type=soa -type=ns "$gad_d" "${gad_s}") + res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}") fi if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then From 86e27920189e42ee41dcba1038541b4cb23e0c70 Mon Sep 17 00:00:00 2001 From: Robert de Bath Date: Thu, 6 Feb 2020 14:13:10 +0000 Subject: [PATCH 088/337] Fix regex for non-gnu versions of awk. Recent versions of gawk have upgraded the regex processing with l10n based character classes. Replace use of these so other versions of awk can be used. --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 2f1a26b..b7b9094 100755 --- a/getssl +++ b/getssl @@ -1027,7 +1027,7 @@ info() { # write out info as long as the quiet flag has not been set. json_awk() { # AWK json converter used for API2 - needs tidying up ;) # shellcheck disable=SC2086 -echo $1 | awk ' +echo "$1" | tr -d '\n' | awk ' { tokenize($0) # while(get_token()) {print TOKEN} if (0 == parse()) { @@ -1173,8 +1173,8 @@ function scream(msg) { } function tokenize(a1,pq,pb,ESCAPE,CHAR,STRING,NUMBER,KEYWORD,SPACE) { - SPACE="[[:space:]]+" - gsub(/"[^[:cntrl:]"\\]*((\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})[^[:cntrl:]"\\]*)*"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[[:space:]]+|./, "\n&", a1) + SPACE="[ \t\n]+" + gsub(/"[^\001-\037"\\]*((\\[^u\001-\037]|\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])[^\001-\037"\\]*)*"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[ \t\n]+|./, "\n&", a1) gsub("\n" SPACE, "\n", a1) sub(/^\n/, "", a1) ITOKENS=0 # get_token() helper From 6b17701cdce2de30b8408b08d7d121271904943f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 6 Feb 2020 17:25:30 +0000 Subject: [PATCH 089/337] Use default awk on most images, add debian:latest and ubuntu:latest --- docker-compose.yml | 135 +++++++++++------- test/5-old-awk-error.bats | 24 ---- test/Dockerfile-alpine | 10 +- test/Dockerfile-centos6 | 2 + ...ile-ubuntu18-no-gawk => Dockerfile-debian} | 10 +- test/Dockerfile-ubuntu | 23 +++ test/Dockerfile-ubuntu18 | 9 +- test/run-all-tests.sh | 4 +- test/test_helper.bash | 2 +- 9 files changed, 134 insertions(+), 85 deletions(-) delete mode 100644 test/5-old-awk-error.bats rename test/{Dockerfile-ubuntu18-no-gawk => Dockerfile-debian} (71%) create mode 100644 test/Dockerfile-ubuntu diff --git a/docker-compose.yml b/docker-compose.yml index ebd5369..09a4264 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,33 +21,33 @@ services: networks: acmenet: ipv4_address: 10.30.50.3 - getssl-ubuntu18: + getssl-alpine: build: context: . - dockerfile: test/Dockerfile-ubuntu18 - container_name: getssl-ubuntu18 + dockerfile: test/Dockerfile-alpine + container_name: getssl-alpine volumes: - .:/getssl environment: - GETSSL_HOST: ubuntu18.getssl.test - GETSSL_IP: 10.30.50.4 - NGINX_CONFIG: /etc/nginx/sites-enabled/default + GETSSL_HOST: alpine.getssl.test + GETSSL_IP: 10.30.50.10 + NGINX_CONFIG: /etc/nginx/conf.d/default.conf networks: acmenet: - ipv4_address: 10.30.50.4 + ipv4_address: 10.30.50.10 aliases: - - ubuntu18.getssl.test - - a.ubuntu18.getssl.test - - b.ubuntu18.getssl.test - - c.ubuntu18.getssl.test - - d.ubuntu18.getssl.test - - e.ubuntu18.getssl.test - - f.ubuntu18.getssl.test - - g.ubuntu18.getssl.test - - h.ubuntu18.getssl.test - - i.ubuntu18.getssl.test - - j.ubuntu18.getssl.test - - k.ubuntu18.getssl.test + - alpine.getssl.test + - a.alpine.getssl.test + - b.alpine.getssl.test + - c.alpine.getssl.test + - d.alpine.getssl.test + - e.alpine.getssl.test + - f.alpine.getssl.test + - g.alpine.getssl.test + - h.alpine.getssl.test + - i.alpine.getssl.test + - j.alpine.getssl.test + - k.alpine.getssl.test getssl-centos6: build: context: . @@ -57,11 +57,11 @@ services: - .:/getssl environment: GETSSL_HOST: centos6.getssl.test - GETSSL_IP: 10.30.50.5 + GETSSL_IP: 10.30.50.11 NGINX_CONFIG: /etc/nginx/conf.d/default.conf networks: acmenet: - ipv4_address: 10.30.50.5 + ipv4_address: 10.30.50.11 aliases: - centos6.getssl.test - a.centos6.getssl.test @@ -75,50 +75,89 @@ services: - i.centos6.getssl.test - j.centos6.getssl.test - k.centos6.getssl.test - getssl-alpine: + getssl-debian: build: context: . - dockerfile: test/Dockerfile-alpine - container_name: getssl-alpine + dockerfile: test/Dockerfile-debian + container_name: getssl-debian volumes: - .:/getssl environment: - GETSSL_HOST: alpine.getssl.test - GETSSL_IP: 10.30.50.6 - NGINX_CONFIG: /etc/nginx/conf.d/default.conf + GETSSL_HOST: debian.getssl.test + GETSSL_IP: 10.30.50.12 + NGINX_CONFIG: /etc/nginx/sites-enabled/default networks: acmenet: - ipv4_address: 10.30.50.6 + ipv4_address: 10.30.50.12 aliases: - - alpine.getssl.test - - a.alpine.getssl.test - - b.alpine.getssl.test - - c.alpine.getssl.test - - d.alpine.getssl.test - - e.alpine.getssl.test - - f.alpine.getssl.test - - g.alpine.getssl.test - - h.alpine.getssl.test - - i.alpine.getssl.test - - j.alpine.getssl.test - - k.alpine.getssl.test - getssl-ubuntu18-no-gawk: + - debian.getssl.test + - a.debian.getssl.test + - b.debian.getssl.test + - c.debian.getssl.test + - d.debian.getssl.test + - e.debian.getssl.test + - f.debian.getssl.test + - g.debian.getssl.test + - h.debian.getssl.test + - i.debian.getssl.test + - j.debian.getssl.test + - k.debian.getssl.test + getssl-ubuntu: build: context: . - dockerfile: test/Dockerfile-ubuntu18-no-gawk - container_name: getssl-ubuntu18-no-gawk + dockerfile: test/Dockerfile-ubuntu + container_name: getssl-ubuntu volumes: - .:/getssl environment: - GETSSL_HOST: ubuntu18-no-gawk.getssl.test - GETSSL_IP: 10.30.50.7 + GETSSL_HOST: ubuntu.getssl.test + GETSSL_IP: 10.30.50.13 NGINX_CONFIG: /etc/nginx/sites-enabled/default - TEST_AWK: "yes" networks: acmenet: - ipv4_address: 10.30.50.7 + ipv4_address: 10.30.50.13 aliases: - - ubuntu18-no-gawk.getssl.test + - ubuntu.getssl.test + - a.ubuntu.getssl.test + - b.ubuntu.getssl.test + - c.ubuntu.getssl.test + - d.ubuntu.getssl.test + - e.ubuntu.getssl.test + - f.ubuntu.getssl.test + - g.ubuntu.getssl.test + - h.ubuntu.getssl.test + - i.ubuntu.getssl.test + - j.ubuntu.getssl.test + - k.ubuntu.getssl.test + getssl-ubuntu18: + build: + context: . + dockerfile: test/Dockerfile-ubuntu18 + container_name: getssl-ubuntu18 + volumes: + - .:/getssl + environment: + GETSSL_HOST: ubuntu18.getssl.test + GETSSL_IP: 10.30.50.14 + NGINX_CONFIG: /etc/nginx/sites-enabled/default + networks: + acmenet: + ipv4_address: 10.30.50.14 + aliases: + - ubuntu18.getssl.test + - a.ubuntu18.getssl.test + - b.ubuntu18.getssl.test + - c.ubuntu18.getssl.test + - d.ubuntu18.getssl.test + - e.ubuntu18.getssl.test + - f.ubuntu18.getssl.test + - g.ubuntu18.getssl.test + - h.ubuntu18.getssl.test + - i.ubuntu18.getssl.test + - j.ubuntu18.getssl.test + - k.ubuntu18.getssl.test + + networks: acmenet: diff --git a/test/5-old-awk-error.bats b/test/5-old-awk-error.bats deleted file mode 100644 index 0f234a2..0000000 --- a/test/5-old-awk-error.bats +++ /dev/null @@ -1,24 +0,0 @@ -#! /usr/bin/env bats - -load '/bats-support/load.bash' -load '/bats-assert/load.bash' -load '/getssl/test/test_helper.bash' - - -# This is run for every test -setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt -} - - -@test "Check getssl fails if an old version of awk is installed" { - CONFIG_FILE="getssl-http01.cfg" - # Make sure this test only runs on an image running an old version of awk - if [[ "$TEST_AWK" != "" ]]; then - setup_environment - init_getssl - create_certificate - assert_failure - assert_output "getssl: Your version of awk does not work with json_awk (see http://github.com/step-/JSON.awk/issues/6), please install a newer version of mawk or gawk" - fi -} diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index e9ee6c3..ff69490 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -1,14 +1,15 @@ FROM alpine:latest -RUN apk --no-cache add supervisor openssl git curl bind-tools wget gawk nginx bash -# RUN apk --no-cache add vim dos2unix # for debugging +# Note this image uses busybox awk instead of gawk + +RUN apk --no-cache add supervisor openssl git curl bind-tools wget nginx bash WORKDIR /root + +# Create nginx directories in standard places RUN mkdir /run/nginx RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private -COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default -COPY ./test/alpine-supervisord.conf /etc/supervisord.conf # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core @@ -17,4 +18,5 @@ RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert RUN /bats-core/install.sh /usr/local # Use supervisord to run nginx in the background +COPY ./test/alpine-supervisord.conf /etc/supervisord.conf ENTRYPOINT /usr/bin/supervisord -c /etc/supervisord.conf diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index a82bf1f..9149dad 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -1,5 +1,7 @@ FROM centos:centos6 +# Note this image uses gawk + # Update and install required software RUN yum -y update RUN yum -y install epel-release diff --git a/test/Dockerfile-ubuntu18-no-gawk b/test/Dockerfile-debian similarity index 71% rename from test/Dockerfile-ubuntu18-no-gawk rename to test/Dockerfile-debian index 3eb9732..c4c88a1 100644 --- a/test/Dockerfile-ubuntu18-no-gawk +++ b/test/Dockerfile-debian @@ -1,11 +1,17 @@ -FROM ubuntu:bionic -# bionic = latest 18 version +FROM debian:latest + +# Note this image uses mawk 1.3 # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils wget nginx-light WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private + +# Prevent "Can't load /root/.rnd into RNG" error from openssl +# RUN touch /root/.rnd # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu new file mode 100644 index 0000000..290100d --- /dev/null +++ b/test/Dockerfile-ubuntu @@ -0,0 +1,23 @@ +FROM ubuntu:latest + +# Note this image uses mawk1.3 + +# Update and install required software +RUN apt-get update --fix-missing +RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y vim dos2unix # for debugging +# TODO test with drill, dig, host + +WORKDIR /root + +# Prevent "Can't load /root/.rnd into RNG" error from openssl +RUN touch /root/.rnd + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 31554cd..ebe7607 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -1,12 +1,11 @@ FROM ubuntu:bionic -# bionic = latest 18 version +# bionic = 18 LTS (long term support) + +# Note this image uses gawk # Update and install required software RUN apt-get update --fix-missing -# TODO work out why default version of awk fails -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 +RUN apt-get install -y git curl dnsutils wget gawk nginx-light WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh index 7372e5b..b526c63 100644 --- a/test/run-all-tests.sh +++ b/test/run-all-tests.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +docker exec -it getssl-alpine bats /getssl/test docker exec -it getssl-centos6 bats /getssl/test +docker exec -it getssl-debian bats /getssl/test +docker exec -it getssl-ubuntu bats /getssl/test docker exec -it getssl-ubuntu18 bats /getssl/test -docker exec -it getssl-ubuntu18-no-gawk bats /getssl/test/5-old-awk-error.bats diff --git a/test/test_helper.bash b/test/test_helper.bash index 0ac9a43..b33ee0b 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -18,7 +18,7 @@ setup_environment() { fi curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a - cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl ${NGINX_CONFIG} + cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" /getssl/test/restart-nginx } From d42f08a97497ca7d1da834bc7949239a0b1f548e Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 9 Feb 2020 17:32:40 +0000 Subject: [PATCH 090/337] Update release notes and version --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index b7b9094..de52ba3 100755 --- a/getssl +++ b/getssl @@ -205,10 +205,12 @@ # 2020-01-26 Use urlbase64_decode() instead of base64 -d # 2020-01-26 Fix "already verified" error for ACMEv2 # 2020-01-29 Check awk new enough to support json_awk +# 2020-02-05 Fix epoch_date for busybox +# 2020-02-06 Bugfixes for json_awk and nslookup to support old awk versions (2.17) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.16" +VERSION="2.17" # defaults ACCOUNT_KEY_LENGTH=4096 From 19a9445811bbbdeba74afe393264545d73a28e48 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 11 Feb 2020 07:14:50 +0000 Subject: [PATCH 091/337] Add SCP_OPTS and SFTP_OPTS --- getssl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index de52ba3..f048e6c 100755 --- a/getssl +++ b/getssl @@ -207,6 +207,7 @@ # 2020-01-29 Check awk new enough to support json_awk # 2020-02-05 Fix epoch_date for busybox # 2020-02-06 Bugfixes for json_awk and nslookup to support old awk versions (2.17) +# 2020-02-11 Add SCP_OPTS and SFTP_OPTS # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -549,8 +550,9 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. info "copying $cert to $to" debug "copying from $from to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then - debug "using scp scp -q $from ${to:4}" - if ! scp -q "$from" "${to:4}" >/dev/null 2>&1 ; then + debug "using scp -q $SCP_OPTS $from ${to:4}" + # shellcheck disable=SC2086 + if ! scp -q $SCP_OPTS "$from" "${to:4}" >/dev/null 2>&1 ; then error_exit "problem copying file to the server using scp. scp $from ${to:4}" fi @@ -596,9 +598,10 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. ftpfile=$(basename "$ftplocn") fromdir=$(dirname "$from") fromfile=$(basename "$from") - debug "sftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" + debug "sftp $SFTP_OPTS user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" debug "from dir=$fromdir file=$fromfile" - sshpass -p "$ftppass" sftp "$ftpuser@$ftphost" <<- _EOF + # shellcheck disable=SC2086 + sshpass -p "$ftppass" sftp $SFTP_OPTS "$ftpuser@$ftphost" <<- _EOF cd $ftpdirn lcd $fromdir put $fromfile From dcb107ed7d599bd69420c6acf54daa852d4ae411 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 11 Feb 2020 07:42:11 +0000 Subject: [PATCH 092/337] Add SSH_OPTS to template --- getssl | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/getssl b/getssl index f048e6c..2c15739 100755 --- a/getssl +++ b/getssl @@ -1607,15 +1607,19 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. cat > "$1" <<- _EOF_domain_ + # This file is read second (and per domain if running with the -a option) + # and overwrites any settings from the first file + # # 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-v02.api.letsencrypt.org/directory" + #CA="https://acme-staging-v02.api.letsencrypt.org/" # This server issues full certificates, however has rate limits #CA="https://acme-v02.api.letsencrypt.org" + # Private key types - can be rsa, prime256v1, secp384r1 or secp521r1 #PRIVATE_KEY_ALG="rsa" # Additional domains - this could be multiple domains / subdomains in a comma separated list @@ -1630,15 +1634,19 @@ write_domain_template() { # write out a template file for a domain. # 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. - # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, - # password, host, port (explicitly needed even if using default port 443) and path on the server. + # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, + # password, host, port (explicitly needed even if using default port 443) and path on the server. #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' - # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') + + # Specify SSH options, e.g. non standard port in SSH_OPTS + # (Can also use SCP_OPTS and SFTP_OPTS) + # SSH_OPTS=-p 12345 - #Set USE_SINGLE_ACL="true" to use a single ACL for all checks + # 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) @@ -1652,6 +1660,9 @@ write_domain_template() { # write out a template file for a domain. # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" + # Uncomment the following line to prevent non-interactive renewals of certificates + #PREVENT_NON_INTERACTIVE_RENEWAL="true" + # 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 @@ -1664,27 +1675,36 @@ write_domain_template() { # write out a template file for a domain. write_getssl_template() { # write out the main template file cat > "$1" <<- _EOF_getssl_ + # This file is read first and is common to all domains + # # Uncomment and modify any variables you need # see https://github.com/srvrco/getssl/wiki/Config-variables for details # # The staging server is best for testing (hence set as default) - CA="https://acme-staging-v02.api.letsencrypt.org/directory" + CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits #CA="https://acme-v02.api.letsencrypt.org" + # The agreement that must be signed with the CA, if not defined the default agreement will be used #AGREEMENT="$AGREEMENT" # Set an email address associated with your account - generally set at account level rather than domain. #ACCOUNT_EMAIL="me@example.com" ACCOUNT_KEY_LENGTH=4096 ACCOUNT_KEY="$WORKING_DIR/account.key" + + # Account key and private key types - can be rsa, prime256v1, secp384r1 or secp521r1 + #ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" #REUSE_PRIVATE_KEY="true" # The command needed to reload apache / nginx or whatever you use #RELOAD_CMD="" + # The time period within which you want to allow renewal of a certificate # this prevents hitting some of the rate limits. + # Creating a file called FORCE_RENEWAL in the domain directory allows one-off overrides + # of this setting RENEW_ALLOW="30" # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, From 83de0e3910b7b708b377517a49bcff9582f77dc4 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 12 Feb 2020 14:21:04 +0000 Subject: [PATCH 093/337] Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502) --- getssl | 3123 +++++++++-------- test/3-dual-rsa-ecdsa.bats | 43 + test/Dockerfile-debian | 3 - test/README.md | 6 +- .../getssl-dns01-dual-rsa-ecdsa.cfg | 37 + .../getssl-http01-dual-rsa-ecdsa.cfg | 33 + 6 files changed, 1681 insertions(+), 1564 deletions(-) create mode 100644 test/3-dual-rsa-ecdsa.bats create mode 100644 test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa.cfg diff --git a/getssl b/getssl index 2c15739..0d593d7 100755 --- a/getssl +++ b/getssl @@ -297,11 +297,12 @@ check_challenge_completion() { # checks with the ACME server if our challenge is domain=$2 keyauthorization=$3 - debug "sending request to ACME server saying we're ready for challenge" + info "sending request to ACME server saying we're ready for challenge" # 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 @@ -315,7 +316,7 @@ 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" + info "checking if challenge is complete" if [[ $API -eq 1 ]]; then if ! get_cr "$uri" ; then error_exit "$domain:Verify error:$code" @@ -548,7 +549,6 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. IFS=\; read -r -a copy_locations <<<"$3" for to in "${copy_locations[@]}"; do info "copying $cert to $to" - debug "copying from $from to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then debug "using scp -q $SCP_OPTS $from ${to:4}" # shellcheck disable=SC2086 @@ -715,6 +715,29 @@ create_key() { # create a domain key (if it doesn't already exist) fi } +create_order() { + dstring="[" + for d in $alldomains; do + dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," + done + 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)\"" + request="{\"identifiers\": $dstring}" + send_signed_request "$URL_newOrder" "$request" + OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') + debug "Order link $OrderLink" + FinalizeLink=$(json_get "$response" "finalize") + dn=0 + for d in $alldomains; do + # get authorizations link + AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") + debug "authorizations link for $d - ${AuthLink[$dn]}" + ((dn++)) + done +} + date_epoc() { # convert the date into epoch time if [[ "$os" == "bsd" ]]; then date -j -f "%b %d %T %Y %Z" "$1" +%s @@ -757,1746 +780,1724 @@ error_exit() { # give error message on error exit exit 1 } -get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) - gad_d="$1" # domain name - gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER - - if [[ "$os" == "cygwin" ]]; then - all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ - | grep "primary name server" \ - | awk '{print $NF}') - if [[ -z "$all_auth_dns_servers" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - fi - primary_ns="$all_auth_dns_servers" - return +fulfill_challenges() { +dn=0 +for d in $alldomains; do + # $d is domain in current loop, which is number $dn for ACL + info "Verifying $d" + if [[ "$USE_SINGLE_ACL" == "true" ]]; then + DOMAIN_ACL="${ACL[0]}" + else + DOMAIN_ACL="${ACL[$dn]}" fi - if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d") - else - res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d") - fi - if [[ -n "$res" ]]; then # domain is a CNAME so get main domain - gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') - fi - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d") - else - res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d") - fi - if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else - all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') - fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') - fi - return - fi + # request a challenge token from ACME server + if [[ $API -eq 1 ]]; then + request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}" + send_signed_request "$URL_new_authz" "$request" + debug "completed send_signed_request" - if [[ "$DNS_CHECK_FUNC" == "host" ]]; then - if [[ -z "$gad_s" ]]; then - res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server") - else - res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server") - fi - if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else - all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ') - fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + # check if we got a valid response and token, if not then error exit + if [[ -n "$code" ]] && [[ ! "$code" == '201' ]] ; then + error_exit "new-authz error: $response" fi - return + else + send_signed_request "${AuthLink[$dn]}" "" fi - res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) - - if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then - # this is a Non-authoritative server, need to check for an authoritative one. - gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') - if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - # if domain name doesn't exist, then find auth servers for next level up - gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + if [[ $response_status == "valid" ]]; then + info "$d is already validated" + if [[ "$DEACTIVATE_AUTH" == "true" ]]; then + deactivate_url="$(echo "$responseHeaders" | awk ' $1 ~ "^Location" {print $2}' | tr -d "\r")" + deactivate_url_list+=" $deactivate_url " + debug "url added to deactivate list ${deactivate_url}" + debug "deactivate list is now $deactivate_url_list" fi - fi - - if [[ -z "$gad_s" ]]; then - res=$(nslookup -debug -type=soa -type=ns "$gad_d") + # increment domain-counter + ((dn++)) else - res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}") - fi + PREVIOUSLY_VALIDATED="false" + if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification + if [[ $API -eq 1 ]]; then + # get the dns component of the ACME response + # get the token from the dns component + token=$(json_get "$response" "token" "dns-01") + # get the uri from the dns component + uri=$(json_get "$response" "uri" "dns-01") + debug uri "$uri" + else # APIv2 + debug "authlink response = $response" + # get the token from the http-01 component + token=$(json_get "$response" "challenges" "type" "dns-01" "token") + # get the uri from the http component + uri=$(json_get "$response" "challenges" "type" "dns-01" "url") + debug uri "$uri" + fi - if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then - gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') - elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') - fi + keyauthorization="$token.$thumbprint" + debug keyauthorization "$keyauthorization" - all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \ - | awk ' $2 ~ "nameserver" {print $4}' \ - | sed 's/\.$//g'| tr '\n' ' ') - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') - fi -} + #create signed authorization key from token. + auth_key=$(printf '%s' "$keyauthorization" | openssl dgst -sha256 -binary \ + | openssl base64 -e \ + | tr -d '\n\r' \ + | sed -e 's:=*$::g' -e 'y:+/:-_:') + debug auth_key "$auth_key" -get_certificate() { # get certificate for csr, if all domains validated. - gc_csr=$1 # the csr file - gc_certfile=$2 # The filename for the certificate - gc_cafile=$3 # The filename for the CA certificate + debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key" + if ! eval "$DNS_ADD_COMMAND" "$d" "$auth_key" ; then + error_exit "DNS_ADD_COMMAND failed for domain $d" + fi - der=$(openssl req -in "$gc_csr" -outform DER | urlbase64) - debug "der $der" - if [[ $API -eq 1 ]]; then - send_signed_request "$URL_new_cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" - # convert certificate information into correct format and save to file. - CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r') - debug "certdata location = $CertData" - if [[ "$CertData" ]] ; then - echo -----BEGIN CERTIFICATE----- > "$gc_certfile" - curl --user-agent "$CURL_USERAGENT" --silent "$CertData" | openssl base64 -e >> "$gc_certfile" - echo -----END CERTIFICATE----- >> "$gc_certfile" - info "Certificate saved in $CERT_FILE" - fi + # find a primary / authoritative DNS server for the domain + if [[ -z "$AUTH_DNS_SERVER" ]]; then + get_auth_dns "$d" + else + primary_ns="$AUTH_DNS_SERVER" + fi + debug primary_ns "$primary_ns" - # If certificate wasn't a valid certificate, error exit. - if [[ -z "$CertData" ]] ; then - response2=$(echo "$response" | fold -w64 |openssl base64 -d) - debug "response was $response" - error_exit "Sign failed: $(echo "$response2" | grep "detail")" - fi + # make a directory to hold pending dns-challenges + if [[ ! -d "$TEMP_DIR/dns_verify" ]]; then + mkdir "$TEMP_DIR/dns_verify" + fi - # get a copy of the CA certificate. - IssuerData=$(grep -i '^Link' "$CURL_HEADER" \ - | cut -d " " -f 2\ - | cut -d ';' -f 1 \ - | sed 's///g') - if [[ "$IssuerData" ]] ; then - echo -----BEGIN CERTIFICATE----- > "$gc_cafile" - curl --user-agent "$CURL_USERAGENT" --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" - echo -----END CERTIFICATE----- >> "$gc_cafile" - info "The intermediate CA cert is in $gc_cafile" - fi - else # APIv2 - send_signed_request "$FinalizeLink" "{\"csr\": \"$der\"}" "needbase64" - debug "order link was $OrderLink" - send_signed_request "$OrderLink" "" - CertData=$(json_get "$response" "certificate") - debug "CertData is at $CertData" - 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" - fi -} + # generate a file with the current variables for the dns-challenge + cat > "$TEMP_DIR/dns_verify/$d" <<- _EOF_ + token="${token}" + uri="${uri}" + keyauthorization="${keyauthorization}" + d="${d}" + primary_ns="${primary_ns}" + auth_key="${auth_key}" + _EOF_ -get_cr() { # get curl response - url="$1" - debug url "$url" - response=$(curl --user-agent "$CURL_USERAGENT" --silent "$url") - ret=$? - debug response "$response" - code=$(json_get "$response" status) - debug code "$code" - debug "get_cr return code $ret" - return $ret -} + else # set up the correct http token for verification + if [[ $API -eq 1 ]]; then + # get the token from the http component + token=$(json_get "$response" "token" "http-01") + # get the uri from the http component + uri=$(json_get "$response" "uri" "http-01") + debug uri "$uri" + else # APIv2 + 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") + # get the uri from the http component + uri=$(json_get "$response" "challenges" "type" "http-01" "url" | head -n1) + debug uri "$uri" + fi -get_os() { # function to get the current Operating System - uname_res=$(uname -s) - if [[ $(date -h 2>&1 | grep -ic busybox) -gt 0 ]]; then - os="busybox" - elif [[ ${uname_res} == "Linux" ]]; then - os="linux" - elif [[ ${uname_res} == "FreeBSD" ]]; then - os="bsd" - elif [[ ${uname_res} == "Darwin" ]]; then - os="mac" - elif [[ ${uname_res:0:6} == "CYGWIN" ]]; then - os="cygwin" - elif [[ ${uname_res:0:5} == "MINGW" ]]; then - os="mingw" - else - os="unknown" - fi - debug "detected os type = $os" -} + #create signed authorization key from token. + keyauthorization="$token.$thumbprint" -get_signing_params() { # get signing parameters from key - skey=$1 - if openssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key - pub_exp64=$(openssl rsa -in "${skey}" -noout -text \ - | grep publicExponent \ - | grep -oE "0x[a-f0-9]+" \ - | cut -d'x' -f2 \ - | hex2bin \ - | urlbase64) - pub_mod64=$(openssl rsa -in "${skey}" -noout -modulus \ - | cut -d'=' -f2 \ - | hex2bin \ - | urlbase64) + # save variable into temporary file + echo -n "$keyauthorization" > "$TEMP_DIR/$token" + chmod 644 "$TEMP_DIR/$token" - jwk='{"e":"'"${pub_exp64}"'","kty":"RSA","n":"'"${pub_mod64}"'"}' - jwkalg="RS256" - signalg="sha256" - elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. - crv="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')" - if [[ -z "$crv" ]]; then - gsp_keytype="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ - | grep "^ASN1 OID:" \ - | awk '{print $3}')" - case "$gsp_keytype" in - prime256v1) crv="P-256" ;; - secp384r1) crv="P-384" ;; - secp521r1) crv="P-521" ;; - *) error_exit "invalid curve algorithm type $gsp_keytype";; - esac - fi - case "$crv" in - P-256) jwkalg="ES256" ; signalg="sha256" ;; - P-384) jwkalg="ES384" ; signalg="sha384" ;; - P-521) jwkalg="ES512" ; signalg="sha512" ;; - *) error_exit "invalid curve algorithm type $crv";; - esac - pubtext="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ - | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \ - | tr -d ": \n\r")" - mid=$(( (${#pubtext} -2) / 2 + 2 )) - debug "pubtext = $pubtext" - x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64) - y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64) - jwk='{"crv":"'"$crv"'","kty":"EC","x":"'"$x64"'","y":"'"$y64"'"}' - debug "jwk $jwk" - else - error_exit "Invalid key file" - fi - thumbprint="$(printf "%s" "$jwk" | openssl dgst -sha256 -binary | urlbase64)" - debug "jwk alg = $jwkalg" - debug "jwk = $jwk" - debug "thumbprint $thumbprint" -} + # copy to token to acme challenge location + umask 0022 + IFS=\; read -r -a token_locations <<<"$DOMAIN_ACL" + for t_loc in "${token_locations[@]}"; do + debug "copying file from $TEMP_DIR/$token to ${t_loc}" + copy_file_to_location "challenge token" \ + "$TEMP_DIR/$token" \ + "${t_loc}/$token" + done + umask "$ORIG_UMASK" -graceful_exit() { # normal exit function. - clean_up - exit -} + wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}/.well-known/acme-challenge/$token" + debug wellknown_url "$wellknown_url" -help_message() { # print out the help message - cat <<- _EOF_ - $PROGNAME ver. $VERSION - Obtain SSL certificates from the letsencrypt.org ACME server + if [[ "$SKIP_HTTP_TOKEN_CHECK" == "true" ]]; then + info "SKIP_HTTP_TOKEN_CHECK=true so not checking that token is working correctly" + else + sleep "$HTTP_TOKEN_CHECK_WAIT" + # check that we can reach the challenge ourselves, if not, then error + if [[ ! "$(curl --user-agent "$CURL_USERAGENT" -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then + error_exit "for some reason could not reach $wellknown_url - please check it manually" + fi + fi - $(usage) + check_challenge_completion "$uri" "$d" "$keyauthorization" - Options: - -a, --all Check all certificates - -d, --debug Output debug information - -c, --create Create default config files - -f, --force Force renewal of cert (overrides expiry checks) - -h, --help Display this help message and exit - -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) - -Q, --mute Like -q, but also mute notification about successful upgrade - -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) - -u, --upgrade Upgrade getssl if a more recent version is available - -k, --keep "#" Maximum number of old getssl versions to keep when upgrading - -U, --nocheck Do not check if a more recent version is available - -w working_dir "Working directory" + debug "remove token from ${DOMAIN_ACL}" + IFS=\; read -r -a token_locations <<<"$DOMAIN_ACL" + for t_loc in "${token_locations[@]}"; do + if [[ "${t_loc:0:4}" == "ssh:" ]] ; then + sshhost=$(echo "${t_loc}"| awk -F: '{print $2}') + command="rm -f ${t_loc:(( ${#sshhost} + 5))}/${token:?}" + debug "running following command to remove token" + debug "ssh $SSH_OPTS $sshhost ${command}" + # shellcheck disable=SC2029 + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 + rm -f "${TEMP_DIR:?}/${token:?}" + elif [[ "${t_loc:0:4}" == "ftp:" ]] ; then + debug "using ftp to remove token file" + ftpuser=$(echo "${t_loc}"| awk -F: '{print $2}') + ftppass=$(echo "${t_loc}"| awk -F: '{print $3}') + ftphost=$(echo "${t_loc}"| awk -F: '{print $4}') + ftplocn=$(echo "${t_loc}"| awk -F: '{print $5}') + debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost location=$ftplocn" + ftp -n <<- EOF + open $ftphost + user $ftpuser $ftppass + cd $ftplocn + delete ${token:?} + EOF + else + rm -f "${t_loc:?}/${token:?}" + fi + done + fi + # increment domain-counter + ((dn++)) + fi +done # end of ... loop through domains for cert ( from SANS list) - _EOF_ -} +# perform validation if via DNS challenge +if [[ $VALIDATE_VIA_DNS == "true" ]]; then + # loop through dns-variable files to check if dns has been changed + for dnsfile in "$TEMP_DIR"/dns_verify/*; do + if [[ -e "$dnsfile" ]]; then + debug "loading DNSfile: $dnsfile" + # shellcheck source=/dev/null + . "$dnsfile" -hex2bin() { # Remove spaces, add leading zero, escape as hex string ensuring no trailing new line char -# printf -- "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')" - echo -e -n "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')" -} + # check for token at public dns server, waiting for a valid response. + for ns in $primary_ns; do + debug "checking dns at $ns" + ntries=0 + check_dns="fail" + while [[ "$check_dns" == "fail" ]]; do + if [[ "$os" == "cygwin" ]]; then + check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ + | grep ^_acme -A2\ + | grep '"'|awk -F'"' '{ print $2}') + elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ + | grep '300 IN TXT'|awk -F'"' '{ print $2}') + elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then + check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ + | grep 'descriptive text'|awk -F'"' '{ print $2}') + else + check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ + | grep 'text ='|awk -F'"' '{ print $2}') + fi + debug "expecting $auth_key" + debug "${ns} gave ... $check_result" -info() { # write out info as long as the quiet flag has not been set. - if [[ ${_QUIET} -eq 0 ]]; then - echo "$@" + if [[ "$check_result" == *"$auth_key"* ]]; then + check_dns="success" + else + if [[ $ntries -lt 100 ]]; then + ntries=$(( ntries + 1 )) + info "checking DNS at ${ns} for ${d}. Attempt $ntries/100 gave wrong result, "\ + "waiting $DNS_WAIT secs before checking again" + sleep $DNS_WAIT + else + debug "dns check failed - removing existing value" + error_exit "checking _acme-challenge.${d} gave $check_result not $auth_key" + fi + fi + done + done + fi + done + + if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then + info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns" + sleep "$DNS_EXTRA_WAIT" fi -} -json_awk() { # AWK json converter used for API2 - needs tidying up ;) -# shellcheck disable=SC2086 -echo "$1" | tr -d '\n' | awk ' -{ - tokenize($0) # while(get_token()) {print TOKEN} - if (0 == parse()) { - apply(JPATHS, NJPATHS) - } -} + # loop through dns-variable files to let the ACME server check the challenges + for dnsfile in "$TEMP_DIR"/dns_verify/*; do + if [[ -e "$dnsfile" ]]; then + debug "loading DNSfile: $dnsfile" + # shellcheck source=/dev/null + . "$dnsfile" -function apply (ary,size,i) { - for (i=1; i NTOKENS) to = NTOKENS - for (i = from; i < ITOKENS; i++) - context = context sprintf("%s ", TOKENS[i]) - context = context "<<" got ">> " - for (i = ITOKENS + 1; i <= to; i++) - context = context sprintf("%s ", TOKENS[i]) - scream("json_awk expected <" expected "> but got <" got "> at input token " ITOKENS "\n" context) -} - -function reset() { - TOKEN=""; delete TOKENS; NTOKENS=ITOKENS=0 - delete JPATHS; NJPATHS=0 - VALUE="" -} - -function scream(msg) { - FAILS[FILENAME] = FAILS[FILENAME] (FAILS[FILENAME]!="" ? "\n" : "") msg - msg = FILENAME ": " msg - print msg >"/dev/stderr" -} - -function tokenize(a1,pq,pb,ESCAPE,CHAR,STRING,NUMBER,KEYWORD,SPACE) { - SPACE="[ \t\n]+" - gsub(/"[^\001-\037"\\]*((\\[^u\001-\037]|\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])[^\001-\037"\\]*)*"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[ \t\n]+|./, "\n&", a1) - gsub("\n" SPACE, "\n", a1) - sub(/^\n/, "", a1) - ITOKENS=0 # get_token() helper - return NTOKENS = split(a1, TOKENS, /\n/) -}' -} +get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) + gad_d="$1" # domain name + gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER -json_get() { # get values from json - if [[ -z "$1" ]] || [[ "$1" == "null" ]]; then - echo "json was blank" + if [[ "$os" == "cygwin" ]]; then + all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ + | grep "primary name server" \ + | awk '{print $NF}') + if [[ -z "$all_auth_dns_servers" ]]; then + error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" + fi + primary_ns="$all_auth_dns_servers" return fi - if [[ $API = 1 ]]; then - # remove newlines, so it's a single chunk of JSON - json_data=$( echo "$1" | tr '\n' ' ') - # if $3 is defined, this is the section which the item is in. - if [[ -n "$3" ]]; then - jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}') - if [[ "$2" == "uri" ]]; then - jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}') - jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}') - else - jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') - fi + + if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + if [[ -z "$gad_s" ]]; then #checking for CNAMEs + res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d") else - jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d") fi - # check number of quotes - jg_q=${jg_result//[^\"]/} - # if 2 quotes, assume it's a quoted variable and just return the data within the quotes. - if [[ ${#jg_q} -eq 2 ]]; then - echo "$jg_result" | awk -F'"' '{print $2}' + if [[ -n "$res" ]]; then # domain is a CNAME so get main domain + gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') + fi + if [[ -z "$gad_s" ]]; then #checking for CNAMEs + res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d") else - echo "$jg_result" + res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d") fi - else - if [[ -n "$6" ]]; then - full=$(json_awk "$1") - section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') - echo "$full" | grep "^..${5}\",$section\]" | awk '{print $2}' | tr -d '"' - elif [[ -n "$5" ]]; then - full=$(json_awk "$1") - section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') - echo "$full" | grep "^..${2}\",$section" | grep "$5" | awk '{print $2}' | tr -d '"' - elif [[ -n "$3" ]]; then - json_awk "$1" | grep "^..${2}...${3}" | awk '{print $2}' | tr -d '"' - elif [[ -n "$2" ]]; then - json_awk "$1" | grep "^..${2}" | awk '{print $2}' | tr -d '"' + if [[ -z "$res" ]]; then + error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" else - json_awk "$1" + all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') fi + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return fi -} - -os_esed() { # Use different sed version for different os types (extended regex) - if [[ "$os" == "bsd" ]]; then # BSD requires -E flag for extended regex - sed -E "${@}" - elif [[ "$os" == "mac" ]]; then # MAC uses older BSD style sed. - sed -E "${@}" - else - sed -r "${@}" - fi -} -purge_archive() { # purge archive of old, invalid, certificates - arcdir="$1/archive" - debug "purging archives in ${arcdir}/" - for padir in "$arcdir"/????_??_??_??_??; do - # check each directory - if [[ -d "$padir" ]]; then - tstamp=$(basename "$padir"| awk -F"_" '{print $1"-"$2"-"$3" "$4":"$5}') - if [[ "$os" == "bsd" ]]; then - direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s) - elif [[ "$os" == "mac" ]]; then - direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s) - else - direpoc=$(date -d "$tstamp" +%s) - fi - current_epoc=$(date "+%s") - # as certs currently valid for 90 days, purge anything older than 100 - purgedate=$((current_epoc - 60*60*24*100)) - if [[ "$direpoc" -lt "$purgedate" ]]; then - echo "purge $padir" - rm -rf "${padir:?}" - fi + if [[ "$DNS_CHECK_FUNC" == "host" ]]; then + if [[ -z "$gad_s" ]]; then + res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server") + else + res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server") fi - done -} - -reload_service() { # Runs a command to reload services ( via ssh if needed) - if [[ -n "$RELOAD_CMD" ]]; then - info "reloading SSL services" - if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then - sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') - command=${RELOAD_CMD:(( ${#sshhost} + 5))} - debug "running following command to reload cert" - debug "ssh $SSH_OPTS $sshhost ${command}" - # shellcheck disable=SC2029 - # shellcheck disable=SC2086 - ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 - # allow 2 seconds for services to restart - sleep 2 + if [[ -z "$res" ]]; then + error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" else - debug "running reload command $RELOAD_CMD" - if ! eval "$RELOAD_CMD" ; then - error_exit "error running $RELOAD_CMD" - fi + all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ') + fi + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi + return fi -} -revoke_certificate() { # revoke a certificate - debug "revoking cert $REVOKE_CERT" - debug "using key $REVOKE_KEY" - ACCOUNT_KEY="$REVOKE_KEY" - # need to set the revoke key as "account_key" since it's used in send_signed_request. - get_signing_params "$REVOKE_KEY" - TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) - debug "revoking from $CA" - rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64) - send_signed_request "$URL_revoke" "{\"resource\": \"revoke-cert\", \"certificate\": \"$rcertdata\"}" - if [[ $code -eq "200" ]]; then - info "certificate revoked" + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) + + if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then + # this is a Non-authoritative server, need to check for an authoritative one. + gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') + if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + # if domain name doesn't exist, then find auth servers for next level up + gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + fi + fi + + if [[ -z "$gad_s" ]]; then + res=$(nslookup -debug -type=soa -type=ns "$gad_d") else - error_exit "Revocation failed: $(echo "$response" | grep "detail")" + res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}") fi -} -requires() { # check if required function is available - if [[ "$#" -gt 1 ]]; then # if more than 1 value, check list - for i in "$@"; do - if [[ "$i" == "${!#}" ]]; then # if on last variable then exit as not found - error_exit "this script requires one of: ${*:1:$(($#-1))}" - fi - res=$(command -v "$i" 2>/dev/null) - debug "checking for $i ... $res" - if [[ -n "$res" ]]; then # if function found, then set variable to function and return - debug "function $i found at $res - setting ${!#} to $i" - eval "${!#}=\$i" - return - fi - done - else # only one value, so check it. - result=$(command -v "$1" 2>/dev/null) - debug "checking for required $1 ... $result" - if [[ -z "$result" ]]; then - error_exit "This script requires $1 installed" - fi + if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then + gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') + elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') fi -} -set_server_type() { # uses SERVER_TYPE to set REMOTE_PORT and REMOTE_EXTRA - if [[ ${SERVER_TYPE} == "https" ]] || [[ ${SERVER_TYPE} == "webserver" ]]; then - REMOTE_PORT=443 - elif [[ ${SERVER_TYPE} == "ftp" ]]; then - REMOTE_PORT=21 - REMOTE_EXTRA="-starttls ftp" - elif [[ ${SERVER_TYPE} == "ftpi" ]]; then - REMOTE_PORT=990 - elif [[ ${SERVER_TYPE} == "imap" ]]; then - REMOTE_PORT=143 - REMOTE_EXTRA="-starttls imap" - elif [[ ${SERVER_TYPE} == "imaps" ]]; then - REMOTE_PORT=993 - elif [[ ${SERVER_TYPE} == "pop3" ]]; then - REMOTE_PORT=110 - REMOTE_EXTRA="-starttls pop3" - elif [[ ${SERVER_TYPE} == "pop3s" ]]; then - REMOTE_PORT=995 - elif [[ ${SERVER_TYPE} == "smtp" ]]; then - REMOTE_PORT=25 - REMOTE_EXTRA="-starttls smtp" - elif [[ ${SERVER_TYPE} == "smtps_deprecated" ]]; then - REMOTE_PORT=465 - elif [[ ${SERVER_TYPE} == "smtps" ]] || [[ ${SERVER_TYPE} == "smtp_submission" ]]; then - REMOTE_PORT=587 - REMOTE_EXTRA="-starttls smtp" - elif [[ ${SERVER_TYPE} == "xmpp" ]]; then - REMOTE_PORT=5222 - REMOTE_EXTRA="-starttls xmpp" - elif [[ ${SERVER_TYPE} == "xmpps" ]]; then - REMOTE_PORT=5269 - elif [[ ${SERVER_TYPE} == "ldaps" ]]; then - REMOTE_PORT=636 - elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then - REMOTE_PORT=${SERVER_TYPE} + all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \ + | awk ' $2 ~ "nameserver" {print $4}' \ + | sed 's/\.$//g'| tr '\n' ' ') + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" else - info "${DOMAIN}: unknown server type \"$SERVER_TYPE\" in SERVER_TYPE" - config_errors=true + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi } -send_signed_request() { # Sends a request to the ACME server, signed with your private key. - url=$1 - payload=$2 - needbase64=$3 - outfile=$4 # save response into this file (certificate data) +get_certificate() { # get certificate for csr, if all domains validated. + gc_csr=$1 # the csr file + gc_certfile=$2 # The filename for the certificate + gc_cafile=$3 # The filename for the CA certificate - debug url "$url" + der=$(openssl req -in "$gc_csr" -outform DER | urlbase64) + if [[ $API -eq 1 ]]; then + send_signed_request "$URL_new_cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" + # convert certificate information into correct format and save to file. + CertData=$(awk ' $1 ~ "^Location" {print $2}' "$CURL_HEADER" |tr -d '\r') + if [[ "$CertData" ]] ; then + echo -----BEGIN CERTIFICATE----- > "$gc_certfile" + curl --user-agent "$CURL_USERAGENT" --silent "$CertData" | openssl base64 -e >> "$gc_certfile" + echo -----END CERTIFICATE----- >> "$gc_certfile" + info "Certificate saved in $CERT_FILE" + fi - CURL_HEADER="$TEMP_DIR/curl.header" - dp="$TEMP_DIR/curl.dump" + # If certificate wasn't a valid certificate, error exit. + if [[ -z "$CertData" ]] ; then + response2=$(echo "$response" | fold -w64 |openssl base64 -d) + debug "response was $response" + error_exit "Sign failed: $(echo "$response2" | grep "detail")" + fi - CURL="curl " - # shellcheck disable=SC2072 - if [[ "$($CURL -V | head -1 | cut -d' ' -f2 )" > "7.33" ]]; then - CURL="$CURL --http1.1 " + # get a copy of the CA certificate. + IssuerData=$(grep -i '^Link' "$CURL_HEADER" \ + | cut -d " " -f 2\ + | cut -d ';' -f 1 \ + | sed 's///g') + if [[ "$IssuerData" ]] ; then + echo -----BEGIN CERTIFICATE----- > "$gc_cafile" + curl --user-agent "$CURL_USERAGENT" --silent "$IssuerData" | openssl base64 -e >> "$gc_cafile" + echo -----END CERTIFICATE----- >> "$gc_cafile" + info "The intermediate CA cert is in $gc_cafile" + fi + else # APIv2 + info "Requesting Finalize Link" + send_signed_request "$FinalizeLink" "{\"csr\": \"$der\"}" "needbase64" + info Requesting Order Link + debug "order link was $OrderLink" + send_signed_request "$OrderLink" "" + # if ACME response is processing (still creating certificates) then wait and try again. + while [[ "$response_status" == "processing" ]]; do + info "ACME server still Processing certificates" + sleep 5 + send_signed_request "$OrderLink" "" + done + info "Requesting certificate" + CertData=$(json_get "$response" "certificate") + send_signed_request "$CertData" "" "" "$FULL_CHAIN" + info "Full certificate saved in $FULL_CHAIN" + awk -v CERT_FILE="$gc_certfile" -v CA_CERT="$gc_cafile" '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 $gc_certfile" fi +} - CURL="$CURL --user-agent $CURL_USERAGENT --silent --dump-header $CURL_HEADER " +get_cr() { # get curl response + url="$1" + debug url "$url" + response=$(curl --user-agent "$CURL_USERAGENT" --silent "$url") + ret=$? + debug response "$response" + code=$(json_get "$response" status) + debug code "$code" + debug "get_cr return code $ret" + return $ret +} - if [[ ${_USE_DEBUG} -eq 1 ]]; then - CURL="$CURL --trace-ascii $dp " +get_os() { # function to get the current Operating System + uname_res=$(uname -s) + if [[ $(date -h 2>&1 | grep -ic busybox) -gt 0 ]]; then + os="busybox" + elif [[ ${uname_res} == "Linux" ]]; then + os="linux" + elif [[ ${uname_res} == "FreeBSD" ]]; then + os="bsd" + elif [[ ${uname_res} == "Darwin" ]]; then + os="mac" + elif [[ ${uname_res:0:6} == "CYGWIN" ]]; then + os="cygwin" + elif [[ ${uname_res:0:5} == "MINGW" ]]; then + os="mingw" + else + os="unknown" fi + debug "detected os type = $os" + if [[ -f /etc/issue ]]; then + debug "Running $(cat /etc/issue)" + fi +} - # convert payload to url base 64 - payload64="$(printf '%s' "${payload}" | urlbase64)" +get_signing_params() { # get signing parameters from key + skey=$1 + if openssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key + pub_exp64=$(openssl rsa -in "${skey}" -noout -text \ + | grep publicExponent \ + | grep -oE "0x[a-f0-9]+" \ + | cut -d'x' -f2 \ + | hex2bin \ + | urlbase64) + pub_mod64=$(openssl rsa -in "${skey}" -noout -modulus \ + | cut -d'=' -f2 \ + | hex2bin \ + | urlbase64) - # get nonce from ACME server - if [[ $API -eq 1 ]]; then - nonceurl="$CA/directory" - nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') - else # APIv2 - nonce=$($CURL -I "$URL_newNonce" | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') + jwk='{"e":"'"${pub_exp64}"'","kty":"RSA","n":"'"${pub_mod64}"'"}' + jwkalg="RS256" + signalg="sha256" + elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. + crv="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')" + if [[ -z "$crv" ]]; then + gsp_keytype="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ + | grep "^ASN1 OID:" \ + | awk '{print $3}')" + case "$gsp_keytype" in + prime256v1) crv="P-256" ;; + secp384r1) crv="P-384" ;; + secp521r1) crv="P-521" ;; + *) error_exit "invalid curve algorithm type $gsp_keytype";; + esac + fi + case "$crv" in + P-256) jwkalg="ES256" ; signalg="sha256" ;; + P-384) jwkalg="ES384" ; signalg="sha384" ;; + P-521) jwkalg="ES512" ; signalg="sha512" ;; + *) error_exit "invalid curve algorithm type $crv";; + esac + pubtext="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ + | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \ + | tr -d ": \n\r")" + mid=$(( (${#pubtext} -2) / 2 + 2 )) + x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64) + y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64) + jwk='{"crv":"'"$crv"'","kty":"EC","x":"'"$x64"'","y":"'"$y64"'"}' + else + error_exit "Invalid key file" fi + thumbprint="$(printf "%s" "$jwk" | openssl dgst -sha256 -binary | urlbase64)" + debug "jwk alg = $jwkalg" +} - nonceproblem="true" - while [[ "$nonceproblem" == "true" ]]; do +graceful_exit() { # normal exit function. + clean_up + exit +} - debug nonce "$nonce" +help_message() { # print out the help message + cat <<- _EOF_ + $PROGNAME ver. $VERSION + Obtain SSL certificates from the letsencrypt.org ACME server - # Build header with just our public key and algorithm information - header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}' + $(usage) - # 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)" - else # APIv2 - if [[ -z "$KID" ]]; then - debug "KID is blank, so using jwk" - protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' - protected64="$(printf '%s' "${protected}" | urlbase64)" - else - debug "using KID=${KID}" - protected="{\"alg\": \"$jwkalg\", \"kid\": \"$KID\",\"nonce\": \"${nonce}\", \"url\": \"${url}\"}" - protected64="$(printf '%s' "${protected}" | urlbase64)" - fi - fi - - # Sign header with nonce and our payload with our private key and encode signature as urlbase64 - sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg" - - # Send header + extended header + payload + signature to the acme-server - if [[ $API -eq 1 ]]; then - debug "header = $header" - debug "protected = $protected" - debug "payload = $payload" - body="{\"header\": ${header}," - body="${body}\"protected\": \"${protected64}\"," - body="${body}\"payload\": \"${payload64}\"," - body="${body}\"signature\": \"${signed64}\"}" - debug "header, payload and signature = $body" - else - debug "protected = $protected" - debug "payload = $payload" - body="{" - body="${body}\"protected\": \"${protected64}\"," - body="${body}\"payload\": \"${payload64}\"," - body="${body}\"signature\": \"${signed64}\"}" - debug "header, payload and signature = $body" - fi - - code="500" - loop_limit=5 - while [[ "$code" -eq 500 ]]; do - 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 - - 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 - response=$(urlbase64_decode "$response") - fi + Options: + -a, --all Check all certificates + -d, --debug Output debug information + -c, --create Create default config files + -f, --force Force renewal of cert (overrides expiry checks) + -h, --help Display this help message and exit + -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) + -Q, --mute Like -q, but also mute notification about successful upgrade + -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) + -u, --upgrade Upgrade getssl if a more recent version is available + -k, --keep "#" Maximum number of old getssl versions to keep when upgrading + -U, --nocheck Do not check if a more recent version is available + -w working_dir "Working directory" - debug responseHeaders "$responseHeaders" - debug response "$response" - code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) - debug code "$code" - if [[ $API -eq 1 ]]; then - response_status=$(json_get "$response" status \ - | head -1| awk -F'"' '{print $2}') - else # APIv2 - if [[ "$outfile" && "$response" ]]; then - debug "response written to $outfile" - elif [[ ${response##*()} == "{"* ]]; then - response_status=$(json_get "$response" status) - else - debug "response not in json format" - debug "$response" - fi - fi - debug "response status = $response_status" - if [[ "$code" -eq 500 ]]; then - info "error on acme server - trying again ...." - debug "loop_limit = $loop_limit" - sleep 5 - loop_limit=$((loop_limit - 1)) - if [[ $loop_limit -lt 1 ]]; then - 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" - nonce=$(echo "$responseHeaders" | grep -i "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') - debug "trying new nonce $nonce" - else - nonceproblem="false" - fi - done + _EOF_ } -sign_string() { # sign a string with a given key and algorithm and return urlbase64 - # sets the result in variable signed64 - str=$1 - key=$2 - signalg=$3 - - if openssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key - signed64="$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" | urlbase64)" - elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. - signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}') - debug "EC signature $signed" - if [[ "${signed:4:4}" == "0220" ]]; then #sha256 - R=$(echo "$signed" | cut -c 9-72) - part2=$(echo "$signed" | cut -c 73-) - elif [[ "${signed:4:4}" == "0221" ]]; then #sha256 - R=$(echo "$signed" | cut -c 11-74) - part2=$(echo "$signed" | cut -c 75-) - elif [[ "${signed:4:4}" == "0230" ]]; then #sha384 - R=$(echo "$signed" | cut -c 9-104) - part2=$(echo "$signed" | cut -c 105-) - elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 - R=$(echo "$signed" | cut -c 11-106) - part2=$(echo "$signed" | cut -c 107-) - elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 - R=$(echo "$signed" | cut -c 11-140) - part2=$(echo "$signed" | cut -c 141-) - elif [[ "${signed:6:4}" == "0242" ]]; then #sha512 - R=$(echo "$signed" | cut -c 11-142) - part2=$(echo "$signed" | cut -c 143-) - else - error_exit "error in EC signing couldn't get R from $signed" - fi - debug "R $R" - - if [[ "${part2:0:4}" == "0220" ]]; then #sha256 - S=$(echo "$part2" | cut -c 5-68) - elif [[ "${part2:0:4}" == "0221" ]]; then #sha256 - S=$(echo "$part2" | cut -c 7-70) - elif [[ "${part2:0:4}" == "0230" ]]; then #sha384 - S=$(echo "$part2" | cut -c 5-100) - elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 - S=$(echo "$part2" | cut -c 7-102) - elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 - S=$(echo "$part2" | cut -c 5-136) - elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 - S=$(echo "$part2" | cut -c 5-136) - else - error_exit "error in EC signing couldn't get S from $signed" - fi +hex2bin() { # Remove spaces, add leading zero, escape as hex string ensuring no trailing new line char +# printf -- "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')" + echo -e -n "$(cat | os_esed -e 's/[[:space:]]//g' -e 's/^(.(.{2})*)$/0\1/' -e 's/(.{2})/\\x\1/g')" +} - debug "S $S" - signed64=$(printf '%s' "${R}${S}" | hex2bin | urlbase64 ) - debug "encoded RS $signed64" +info() { # write out info as long as the quiet flag has not been set. + if [[ ${_QUIET} -eq 0 ]]; then + echo "$@" fi } -signal_exit() { # Handle trapped signals - case $1 in - INT) - error_exit "Program interrupted by user" ;; - TERM) - echo -e "\n$PROGNAME: Program terminated" >&2 - graceful_exit ;; - *) - error_exit "$PROGNAME: Terminating on unknown signal" ;; - esac +json_awk() { # AWK json converter used for API2 - needs tidying up ;) +# shellcheck disable=SC2086 +echo "$1" | tr -d '\n' | awk ' +{ + tokenize($0) # while(get_token()) {print TOKEN} + if (0 == parse()) { + apply(JPATHS, NJPATHS) + } } -urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' - openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:' +function apply (ary,size,i) { + for (i=1; i "$1" <<- _EOF_getssl_ - # This file is read first and is common to all domains - # - # Uncomment and modify any variables you need - # see https://github.com/srvrco/getssl/wiki/Config-variables for details - # - # The staging server is best for testing (hence set as default) - CA="https://acme-staging-v02.api.letsencrypt.org" - # This server issues full certificates, however has rate limits - #CA="https://acme-v02.api.letsencrypt.org" - - # The agreement that must be signed with the CA, if not defined the default agreement will be used - #AGREEMENT="$AGREEMENT" +function parse( ret) { + get_token() + if (ret = parse_value()) { + return ret + } + if (get_token()) { + report("EOF", TOKEN) + return 11 + } + return 0 +} - # Set an email address associated with your account - generally set at account level rather than domain. - #ACCOUNT_EMAIL="me@example.com" - ACCOUNT_KEY_LENGTH=4096 - ACCOUNT_KEY="$WORKING_DIR/account.key" +function report(expected, got, i,from,to,context) { + from = ITOKENS - 10; if (from < 1) from = 1 + to = ITOKENS + 10; if (to > NTOKENS) to = NTOKENS + for (i = from; i < ITOKENS; i++) + context = context sprintf("%s ", TOKENS[i]) + context = context "<<" got ">> " + for (i = ITOKENS + 1; i <= to; i++) + context = context sprintf("%s ", TOKENS[i]) + scream("json_awk expected <" expected "> but got <" got "> at input token " ITOKENS "\n" context) +} - # Account key and private key types - can be rsa, prime256v1, secp384r1 or secp521r1 - #ACCOUNT_KEY_TYPE="rsa" - PRIVATE_KEY_ALG="rsa" - #REUSE_PRIVATE_KEY="true" +function reset() { + TOKEN=""; delete TOKENS; NTOKENS=ITOKENS=0 + delete JPATHS; NJPATHS=0 + VALUE="" +} - # The command needed to reload apache / nginx or whatever you use - #RELOAD_CMD="" +function scream(msg) { + FAILS[FILENAME] = FAILS[FILENAME] (FAILS[FILENAME]!="" ? "\n" : "") msg + msg = FILENAME ": " msg + print msg >"/dev/stderr" +} - # The time period within which you want to allow renewal of a certificate - # this prevents hitting some of the rate limits. - # Creating a file called FORCE_RENEWAL in the domain directory allows one-off overrides - # of this setting - RENEW_ALLOW="30" +function tokenize(a1,pq,pb,ESCAPE,CHAR,STRING,NUMBER,KEYWORD,SPACE) { + SPACE="[ \t\n]+" + gsub(/"[^\001-\037"\\]*((\\[^u\001-\037]|\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])[^\001-\037"\\]*)*"|-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?|null|false|true|[ \t\n]+|./, "\n&", a1) + gsub("\n" SPACE, "\n", a1) + sub(/^\n/, "", a1) + ITOKENS=0 # get_token() helper + return NTOKENS = split(a1, TOKENS, /\n/) +}' +} - # 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" +json_get() { # get values from json + if [[ -z "$1" ]] || [[ "$1" == "null" ]]; then + echo "json was blank" + return + fi + if [[ $API = 1 ]]; then + # remove newlines, so it's a single chunk of JSON + json_data=$( echo "$1" | tr '\n' ' ') + # if $3 is defined, this is the section which the item is in. + if [[ -n "$3" ]]; then + jg_section=$(echo "$json_data" | awk -F"[}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${3}"'\"/){print $i}}}') + if [[ "$2" == "uri" ]]; then + jg_subsect=$(echo "$jg_section" | awk -F"[,]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i)}}}') + jg_result=$(echo "$jg_subsect" | awk -F'"' '{print $4}') + else + jg_result=$(echo "$jg_section" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + fi + else + jg_result=$(echo "$json_data" |awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/\"'"${2}"'\"/){print $(i+1)}}}') + fi + # check number of quotes + jg_q=${jg_result//[^\"]/} + # if 2 quotes, assume it's a quoted variable and just return the data within the quotes. + if [[ ${#jg_q} -eq 2 ]]; then + echo "$jg_result" | awk -F'"' '{print $2}' + else + echo "$jg_result" + fi + else + if [[ -n "$6" ]]; then + full=$(json_awk "$1") + section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') + echo "$full" | grep "^..${5}\",$section\]" | awk '{print $2}' | tr -d '"' + elif [[ -n "$5" ]]; then + full=$(json_awk "$1") + section=$(echo "$full" | grep "\"$2\"" | grep "\"$3\"" | grep "\"$4\"" | awk -F"," '{print $2}') + echo "$full" | grep "^..${2}\",$section" | grep "$5" | awk '{print $2}' | tr -d '"' + elif [[ -n "$3" ]]; then + json_awk "$1" | grep "^..${2}...${3}" | awk '{print $2}' | tr -d '"' + elif [[ -n "$2" ]]; then + json_awk "$1" | grep "^..${2}" | awk '{print $2}' | tr -d '"' + else + json_awk "$1" + fi + fi +} - # Use the following 3 variables if you want to validate via DNS - #VALIDATE_VIA_DNS="true" - #DNS_ADD_COMMAND= - #DNS_DEL_COMMAND= - _EOF_getssl_ +os_esed() { # Use different sed version for different os types (extended regex) + if [[ "$os" == "bsd" ]]; then # BSD requires -E flag for extended regex + sed -E "${@}" + elif [[ "$os" == "mac" ]]; then # MAC uses older BSD style sed. + sed -E "${@}" + else + sed -r "${@}" + fi } -write_openssl_conf() { # write out a minimal openssl conf - cat > "$1" <<- _EOF_openssl_conf_ - # minimal openssl.cnf file - distinguished_name = req_distinguished_name - [ req_distinguished_name ] - [v3_req] - [v3_ca] - _EOF_openssl_conf_ +purge_archive() { # purge archive of old, invalid, certificates + arcdir="$1/archive" + debug "purging archives in ${arcdir}/" + for padir in "$arcdir"/????_??_??_??_??; do + # check each directory + if [[ -d "$padir" ]]; then + tstamp=$(basename "$padir"| awk -F"_" '{print $1"-"$2"-"$3" "$4":"$5}') + if [[ "$os" == "bsd" ]]; then + direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s) + elif [[ "$os" == "mac" ]]; then + direpoc=$(date -j -f "%F %H:%M" "$tstamp" +%s) + else + direpoc=$(date -d "$tstamp" +%s) + fi + current_epoc=$(date "+%s") + # as certs currently valid for 90 days, purge anything older than 100 + purgedate=$((current_epoc - 60*60*24*100)) + if [[ "$direpoc" -lt "$purgedate" ]]; then + echo "purge $padir" + rm -rf "${padir:?}" + fi + fi + done } -# Trap signals -trap "signal_exit TERM" TERM HUP -trap "signal_exit INT" INT - -# Parse command-line -while [[ -n ${1+defined} ]]; do - case $1 in - -h | --help) - help_message; graceful_exit ;; - -d | --debug) - _USE_DEBUG=1 ;; - -c | --create) - _CREATE_CONFIG=1 ;; - -f | --force) - _FORCE_RENEW=1 ;; - -a | --all) - _CHECK_ALL=1 ;; - -k | --keep) - shift; _KEEP_VERSIONS="$1";; - -q | --quiet) - _QUIET=1 ;; - -Q | --mute) - _QUIET=1 - _MUTE=1 ;; - -r | --revoke) - _REVOKE=1 - shift - REVOKE_CERT="$1" - shift - REVOKE_KEY="$1" - shift - REVOKE_CA="$1" ;; - -u | --upgrade) - _UPGRADE=1 ;; - -U | --nocheck) - _UPGRADE_CHECK=0 ;; - -w) - shift; WORKING_DIR="$1" ;; - -*) - usage - error_exit "Unknown option $1" ;; - *) - if [[ -n $DOMAIN ]]; then - error_exit "invalid command line $DOMAIN - it appears to contain more than one domain" +reload_service() { # Runs a command to reload services ( via ssh if needed) + if [[ -n "$RELOAD_CMD" ]]; then + info "reloading SSL services" + if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then + sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') + command=${RELOAD_CMD:(( ${#sshhost} + 5))} + debug "running following command to reload cert" + debug "ssh $SSH_OPTS $sshhost ${command}" + # shellcheck disable=SC2029 + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 + # allow 2 seconds for services to restart + sleep 2 + else + debug "running reload command $RELOAD_CMD" + if ! eval "$RELOAD_CMD" ; then + error_exit "error running $RELOAD_CMD" fi - DOMAIN="$1" - if [[ -z $DOMAIN ]]; then - error_exit "invalid command line - it appears to contain a null variable" - fi ;; - esac - shift -done - -# Main logic -############ - -# Get the current OS, so the correct functions can be used for that OS. (sets the variable os) -get_os - -# check if "recent" version of bash. -#if [[ "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" -lt 42 ]]; then -# info "this script is designed for bash v4.2 or later - earlier version may give errors" -#fi - -#check if required applications are included - -requires which -requires openssl -requires curl -requires nslookup drill dig host DNS_CHECK_FUNC -requires awk -requires tr -requires date -requires grep -requires sed -requires sort -requires mktemp - -# Check if upgrades are available (unless they have specified -U to ignore Upgrade checks) -if [[ $_UPGRADE_CHECK -eq 1 ]]; then - check_getssl_upgrade -fi + fi + fi +} -# Revoke a certificate if requested -if [[ $_REVOKE -eq 1 ]]; then - if [[ -z $REVOKE_CA ]]; then - CA=$DEFAULT_REVOKE_CA - elif [[ "$REVOKE_CA" == "-d" ]]; then - _USE_DEBUG=1 - CA=$DEFAULT_REVOKE_CA +revoke_certificate() { # revoke a certificate + debug "revoking cert $REVOKE_CERT" + debug "using key $REVOKE_KEY" + ACCOUNT_KEY="$REVOKE_KEY" + # need to set the revoke key as "account_key" since it's used in send_signed_request. + get_signing_params "$REVOKE_KEY" + TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) + debug "revoking from $CA" + rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64) + send_signed_request "$URL_revoke" "{\"resource\": \"revoke-cert\", \"certificate\": \"$rcertdata\"}" + if [[ $code -eq "200" ]]; then + info "certificate revoked" else - CA=$REVOKE_CA + error_exit "Revocation failed: $(echo "$response" | grep "detail")" fi - URL_revoke=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null | grep "revoke-cert" | awk -F'"' '{print $4}') - revoke_certificate - graceful_exit -fi +} -# get latest agreement from CA (as default) -AGREEMENT=$(curl --user-agent "$CURL_USERAGENT" -I "${CA}/terms" 2>/dev/null | awk 'tolower($1) ~ "location:" {print $2}'|tr -d '\r') +requires() { # check if required function is available + if [[ "$#" -gt 1 ]]; then # if more than 1 value, check list + for i in "$@"; do + if [[ "$i" == "${!#}" ]]; then # if on last variable then exit as not found + error_exit "this script requires one of: ${*:1:$(($#-1))}" + fi + res=$(command -v "$i" 2>/dev/null) + debug "checking for $i ... $res" + if [[ -n "$res" ]]; then # if function found, then set variable to function and return + debug "function $i found at $res - setting ${!#} to $i" + eval "${!#}=\$i" + return + fi + done + else # only one value, so check it. + result=$(command -v "$1" 2>/dev/null) + debug "checking for required $1 ... $result" + if [[ -z "$result" ]]; then + error_exit "This script requires $1 installed" + fi + fi +} -# if nothing in command line, print help and exit. -if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then - help_message - graceful_exit -fi +set_server_type() { # uses SERVER_TYPE to set REMOTE_PORT and REMOTE_EXTRA + if [[ ${SERVER_TYPE} == "https" ]] || [[ ${SERVER_TYPE} == "webserver" ]]; then + REMOTE_PORT=443 + elif [[ ${SERVER_TYPE} == "ftp" ]]; then + REMOTE_PORT=21 + REMOTE_EXTRA="-starttls ftp" + elif [[ ${SERVER_TYPE} == "ftpi" ]]; then + REMOTE_PORT=990 + elif [[ ${SERVER_TYPE} == "imap" ]]; then + REMOTE_PORT=143 + REMOTE_EXTRA="-starttls imap" + elif [[ ${SERVER_TYPE} == "imaps" ]]; then + REMOTE_PORT=993 + elif [[ ${SERVER_TYPE} == "pop3" ]]; then + REMOTE_PORT=110 + REMOTE_EXTRA="-starttls pop3" + elif [[ ${SERVER_TYPE} == "pop3s" ]]; then + REMOTE_PORT=995 + elif [[ ${SERVER_TYPE} == "smtp" ]]; then + REMOTE_PORT=25 + REMOTE_EXTRA="-starttls smtp" + elif [[ ${SERVER_TYPE} == "smtps_deprecated" ]]; then + REMOTE_PORT=465 + elif [[ ${SERVER_TYPE} == "smtps" ]] || [[ ${SERVER_TYPE} == "smtp_submission" ]]; then + REMOTE_PORT=587 + REMOTE_EXTRA="-starttls smtp" + elif [[ ${SERVER_TYPE} == "xmpp" ]]; then + REMOTE_PORT=5222 + REMOTE_EXTRA="-starttls xmpp" + elif [[ ${SERVER_TYPE} == "xmpps" ]]; then + REMOTE_PORT=5269 + elif [[ ${SERVER_TYPE} == "ldaps" ]]; then + REMOTE_PORT=636 + elif [[ ${SERVER_TYPE} =~ ^[0-9]+$ ]]; then + REMOTE_PORT=${SERVER_TYPE} + else + info "${DOMAIN}: unknown server type \"$SERVER_TYPE\" in SERVER_TYPE" + config_errors=true + fi +} -# if the "working directory" doesn't exist, then create it. -if [[ ! -d "$WORKING_DIR" ]]; then - debug "Making working directory - $WORKING_DIR" - mkdir -p "$WORKING_DIR" -fi +send_signed_request() { # Sends a request to the ACME server, signed with your private key. + url=$1 + payload=$2 + needbase64=$3 + outfile=$4 # save response into this file (certificate data) -# read any variables from config in working directory -if [[ -s "$WORKING_DIR/getssl.cfg" ]]; then - debug "reading config from $WORKING_DIR/getssl.cfg" - # shellcheck source=/dev/null - . "$WORKING_DIR/getssl.cfg" -fi + debug url "$url" -# Define defaults for variables not set in the main config. -ACCOUNT_KEY="${ACCOUNT_KEY:=$WORKING_DIR/account.key}" -DOMAIN_STORAGE="${DOMAIN_STORAGE:=$WORKING_DIR}" -DOMAIN_DIR="$DOMAIN_STORAGE/$DOMAIN" -CERT_FILE="$DOMAIN_DIR/${DOMAIN}.crt" -FULL_CHAIN="$DOMAIN_DIR/fullchain.crt" -CA_CERT="$DOMAIN_DIR/chain.crt" -TEMP_DIR="$DOMAIN_DIR/tmp" -if [[ "$os" == "mingw" ]]; then - CSR_SUBJECT="//" -fi + CURL_HEADER="$TEMP_DIR/curl.header" + dp="$TEMP_DIR/curl.dump" -# Set the OPENSSL_CONF environment variable so openssl knows which config to use -export OPENSSL_CONF=$SSLCONF + CURL="curl " + # shellcheck disable=SC2072 + if [[ "$($CURL -V | head -1 | cut -d' ' -f2 )" > "7.33" ]]; then + CURL="$CURL --http1.1 " + fi -# if "-a" option then check other parameters and create run for each domain. -if [[ ${_CHECK_ALL} -eq 1 ]]; then - info "Check all certificates" + CURL="$CURL --user-agent $CURL_USERAGENT --silent --dump-header $CURL_HEADER " - if [[ ${_CREATE_CONFIG} -eq 1 ]]; then - error_exit "cannot combine -c|--create with -a|--all" + if [[ ${_USE_DEBUG} -eq 1 ]]; then + CURL="$CURL --trace-ascii $dp " fi - if [[ ${_FORCE_RENEW} -eq 1 ]]; then - error_exit "cannot combine -f|--force with -a|--all because of rate limits" - fi + # convert payload to url base 64 + payload64="$(printf '%s' "${payload}" | urlbase64)" - if [[ ! -d "$DOMAIN_STORAGE" ]]; then - error_exit "DOMAIN_STORAGE not found - $DOMAIN_STORAGE" + # get nonce from ACME server + if [[ $API -eq 1 ]]; then + nonceurl="$CA/directory" + nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') + else # APIv2 + nonce=$($CURL -I "$URL_newNonce" | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') fi - for dir in "${DOMAIN_STORAGE}"/*; do - if [[ -d "$dir" ]]; then - debug "Checking $dir" - cmd="$0 -U" # No update checks when calling recursively - if [[ ${_USE_DEBUG} -eq 1 ]]; then - cmd="$cmd -d" + nonceproblem="true" + while [[ "$nonceproblem" == "true" ]]; do + + # Build header with just our public key and algorithm information + header='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"'}' + + # 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)" + else # APIv2 + if [[ -z "$KID" ]]; then + debug "KID is blank, so using jwk" + protected='{"alg": "'"$jwkalg"'", "jwk": '"$jwk"', "nonce": "'"${nonce}"'", "url": "'"${url}"'"}' + protected64="$(printf '%s' "${protected}" | urlbase64)" + else + debug "using KID=${KID}" + protected="{\"alg\": \"$jwkalg\", \"kid\": \"$KID\",\"nonce\": \"${nonce}\", \"url\": \"${url}\"}" + protected64="$(printf '%s' "${protected}" | urlbase64)" fi - if [[ ${_QUIET} -eq 1 ]]; then - cmd="$cmd -q" + fi + + # Sign header with nonce and our payload with our private key and encode signature as urlbase64 + sign_string "$(printf '%s' "${protected64}.${payload64}")" "${ACCOUNT_KEY}" "$signalg" + + # Send header + extended header + payload + signature to the acme-server + debug "payload = $payload" + if [[ $API -eq 1 ]]; then + body="{\"header\": ${header}," + body="${body}\"protected\": \"${protected64}\"," + body="${body}\"payload\": \"${payload64}\"," + body="${body}\"signature\": \"${signed64}\"}" + else + body="{" + body="${body}\"protected\": \"${protected64}\"," + body="${body}\"payload\": \"${payload64}\"," + body="${body}\"signature\": \"${signed64}\"}" + fi + + code="500" + loop_limit=5 + while [[ "$code" -eq 500 ]]; do + 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 - # check if $dir is a directory with a getssl.cfg in it - if [[ -f "$dir/getssl.cfg" ]]; then - cmd="$cmd -w $WORKING_DIR $(basename "$dir")" - debug "CMD: $cmd" - eval "$cmd" + + 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 + response=$(urlbase64_decode "$response") + fi + + debug responseHeaders "$responseHeaders" + debug response "$response" + code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) + debug code "$code" + if [[ "$code" == 4* && $response != *"error:badNonce"* ]]; then + detail=$(echo "$response" | grep "detail") + error_exit "ACME server returned error: ${code}: ${detail}" + fi + + if [[ $API -eq 1 ]]; then + response_status=$(json_get "$response" status \ + | head -1| awk -F'"' '{print $2}') + else # APIv2 + if [[ "$outfile" && "$response" ]]; then + debug "response written to $outfile" + elif [[ ${response##*()} == "{"* ]]; then + response_status=$(json_get "$response" status) + else + debug "response not in json format" + debug "$response" + fi + fi + debug "response status = $response_status" + if [[ "$code" -eq 500 ]]; then + info "error on acme server - trying again ...." + debug "loop_limit = $loop_limit" + sleep 5 + loop_limit=$((loop_limit - 1)) + if [[ $loop_limit -lt 1 ]]; then + error_exit "500 error from ACME server: $response" + fi + fi + done + if [[ $response == *"error:badNonce"* ]]; then + debug "bad nonce" + nonce=$(echo "$responseHeaders" | grep -i "^replay-nonce:" | awk '{print $2}' | tr -d '\r\n ') + debug "trying new nonce $nonce" + else + nonceproblem="false" fi done +} - graceful_exit -fi -# end of "-a" option (looping through all domains) +sign_string() { # sign a string with a given key and algorithm and return urlbase64 + # sets the result in variable signed64 + str=$1 + key=$2 + signalg=$3 -# if "-c|--create" option used, then create config files. -if [[ ${_CREATE_CONFIG} -eq 1 ]]; then - # If main config file does not exists then create it. - if [[ ! -s "$WORKING_DIR/getssl.cfg" ]]; then - info "creating main config file $WORKING_DIR/getssl.cfg" - if [[ ! -s "$SSLCONF" ]]; then - SSLCONF="$WORKING_DIR/openssl.cnf" - write_openssl_conf "$SSLCONF" + if openssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key + signed64="$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" | urlbase64)" + elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. + signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}') + debug "EC signature $signed" + if [[ "${signed:4:4}" == "0220" ]]; then #sha256 + R=$(echo "$signed" | cut -c 9-72) + part2=$(echo "$signed" | cut -c 73-) + elif [[ "${signed:4:4}" == "0221" ]]; then #sha256 + R=$(echo "$signed" | cut -c 11-74) + part2=$(echo "$signed" | cut -c 75-) + elif [[ "${signed:4:4}" == "0230" ]]; then #sha384 + R=$(echo "$signed" | cut -c 9-104) + part2=$(echo "$signed" | cut -c 105-) + elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 + R=$(echo "$signed" | cut -c 11-106) + part2=$(echo "$signed" | cut -c 107-) + elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 + R=$(echo "$signed" | cut -c 11-140) + part2=$(echo "$signed" | cut -c 141-) + elif [[ "${signed:6:4}" == "0242" ]]; then #sha512 + R=$(echo "$signed" | cut -c 11-142) + part2=$(echo "$signed" | cut -c 143-) + else + error_exit "error in EC signing couldn't get R from $signed" fi - write_getssl_template "$WORKING_DIR/getssl.cfg" - fi - # If domain and domain config don't exist then create them. - if [[ ! -d "$DOMAIN_DIR" ]]; then - info "Making domain directory - $DOMAIN_DIR" - mkdir -p "$DOMAIN_DIR" - fi - if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then - info "domain config already exists $DOMAIN_DIR/getssl.cfg" - else - info "creating domain config file in $DOMAIN_DIR/getssl.cfg" - # if domain has an existing cert, copy from domain and use to create defaults. - EX_CERT=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null \ - | openssl x509 2>/dev/null) - EX_SANS="www.${DOMAIN}" - if [[ -n "${EX_CERT}" ]]; then - EX_SANS=$(echo "$EX_CERT" \ - | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ - | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) - EX_SANS=${EX_SANS//$'\n'/','} + debug "R $R" + + if [[ "${part2:0:4}" == "0220" ]]; then #sha256 + S=$(echo "$part2" | cut -c 5-68) + elif [[ "${part2:0:4}" == "0221" ]]; then #sha256 + S=$(echo "$part2" | cut -c 7-70) + elif [[ "${part2:0:4}" == "0230" ]]; then #sha384 + S=$(echo "$part2" | cut -c 5-100) + elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 + S=$(echo "$part2" | cut -c 7-102) + elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 + S=$(echo "$part2" | cut -c 5-136) + elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 + S=$(echo "$part2" | cut -c 5-136) + else + error_exit "error in EC signing couldn't get S from $signed" fi - write_domain_template "$DOMAIN_DIR/getssl.cfg" + + debug "S $S" + signed64=$(printf '%s' "${R}${S}" | hex2bin | urlbase64 ) + debug "encoded RS $signed64" fi - TEMP_DIR="$DOMAIN_DIR/tmp" - # end of "-c|--create" option, so exit - graceful_exit -fi -# end of "-c|--create" option to create config file. +} -# if domain directory doesn't exist, then create it. -if [[ ! -d "$DOMAIN_DIR" ]]; then - debug "Making working directory - $DOMAIN_DIR" - mkdir -p "$DOMAIN_DIR" -fi +signal_exit() { # Handle trapped signals + case $1 in + INT) + error_exit "Program interrupted by user" ;; + TERM) + echo -e "\n$PROGNAME: Program terminated" >&2 + graceful_exit ;; + *) + error_exit "$PROGNAME: Terminating on unknown signal" ;; + esac +} -# define a temporary directory, and if it doesn't exist, create it. -TEMP_DIR="$DOMAIN_DIR/tmp" -if [[ ! -d "${TEMP_DIR}" ]]; then - debug "Making temp directory - ${TEMP_DIR}" - mkdir -p "${TEMP_DIR}" -fi +urlbase64() { # urlbase64: base64 encoded string with '+' replaced with '-' and '/' replaced with '_' + openssl base64 -e | tr -d '\n\r' | os_esed -e 's:=*$::g' -e 'y:+/:-_:' +} -# read any variables from config in domain directory -if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then - debug "reading config from $DOMAIN_DIR/getssl.cfg" - # shellcheck source=/dev/null - . "$DOMAIN_DIR/getssl.cfg" -fi +# base64url decode +# From: https://gist.github.com/alvis/89007e96f7958f2686036d4276d28e47 +urlbase64_decode() { + INPUT=$1 # $(if [ -z "$1" ]; then echo -n $(cat -); else echo -n "$1"; fi) + MOD=$(($(echo -n "$INPUT" | wc -c) % 4)) + PADDING=$(if [ $MOD -eq 2 ]; then echo -n '=='; elif [ $MOD -eq 3 ]; then echo -n '=' ; fi) + echo -n "$INPUT$PADDING" | + sed s/-/+/g | + sed s/_/\\//g | + openssl base64 -d -A +} -# from SERVER_TYPE set REMOTE_PORT and REMOTE_EXTRA -set_server_type +usage() { # echos out the program usage + echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet]"\ + "[-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] domain" +} -# check config for typical errors. -check_config +write_domain_template() { # write out a template file for a domain. + cat > "$1" <<- _EOF_domain_ + # This file is read second (and per domain if running with the -a option) + # and overwrites any settings from the first file + # + # 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-v02.api.letsencrypt.org/" + # This server issues full certificates, however has rate limits + #CA="https://acme-v02.api.letsencrypt.org" -if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then - rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" - _FORCE_RENEW=1 - info "${DOMAIN}: forcing renewal (due to FORCE_RENEWAL file)" -fi + # Private key types - can be rsa, prime256v1, secp384r1 or secp521r1 + #PRIVATE_KEY_ALG="rsa" -# Obtain CA resource locations -ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) -debug "ca_all_loc from ${CA} gives $ca_all_loc" -# APIv1 -URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') -URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') -URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') -#API v2 -URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') -URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') -URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') -if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then - ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) - debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" - # APIv1 - URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') - URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') - URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') - #API v2 - URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') - URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') - URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') -fi + # 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="${EX_SANS}" -if [[ -n "$URL_new_reg" ]]; then - API=1 -elif [[ -n "$URL_newAccount" ]]; then - API=2 -else - info "unknown API version" - graceful_exit -fi -debug "Using API v$API" + # 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. + # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, + # password, host, port (explicitly needed even if using default port 443) and path on the server. + #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') -# Check if awk supports json_awk (required for ACMEv2) -if [[ $API -eq 2 ]]; then - json_awk_test=$(json_awk '{ "test": "1" }' 2>/dev/null) - if [[ "${json_awk_test}" == "" ]]; then - error_exit "Your version of awk does not work with json_awk (see http://github.com/step-/JSON.awk/issues/6), please install a newer version of mawk or gawk" - fi -fi + # Specify SSH options, e.g. non standard port in SSH_OPTS + # (Can also use SCP_OPTS and SFTP_OPTS) + # SSH_OPTS=-p 12345 -# 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 - debug "getting certificate for $DOMAIN from remote server" - # shellcheck disable=SC2086 - EX_CERT=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ - | openssl x509 2>/dev/null) - if [[ -n "$EX_CERT" ]]; then # if obtained a cert - if [[ -s "$CERT_FILE" ]]; then # if local exists - CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) - else # since local doesn't exist leave empty so that the domain validation will happen - CERT_LOCAL="" - fi - CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) - if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then - debug "certificate on server is same as the local cert" - else - # check if the certificate is for the right domain - EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -text \ - | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ - | sort -u | grep "^$DOMAIN\$") - if [[ "$EX_CERT_DOMAIN" == "$DOMAIN" ]]; then - # check renew-date on ex_cert and compare to local ( if local exists) - enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) - enddate_ex_s=$(date_epoc "$enddate_ex") - debug "external cert has enddate $enddate_ex ( $enddate_ex_s ) " - if [[ -s "$CERT_FILE" ]]; then # if local exists - enddate_lc=$(openssl x509 -noout -enddate < "$CERT_FILE" 2>/dev/null| cut -d= -f 2-) - enddate_lc_s=$(date_epoc "$enddate_lc") - debug "local cert has enddate $enddate_lc ( $enddate_lc_s ) " - else - enddate_lc_s=0 - debug "local cert doesn't exist" - fi - if [[ "$enddate_ex_s" -eq "$enddate_lc_s" ]]; then - debug "certificates expire at the same time" - elif [[ "$enddate_ex_s" -gt "$enddate_lc_s" ]]; then - # remote has longer to expiry date than local copy. - debug "remote cert has longer to run than local cert - ignoring" - else - info "${DOMAIN}: remote cert expires sooner than local, attempting to upload from local" - copy_file_to_location "domain certificate" \ - "$CERT_FILE" \ - "$DOMAIN_CERT_LOCATION" - copy_file_to_location "private key" \ - "$DOMAIN_DIR/${DOMAIN}.key" \ - "$DOMAIN_KEY_LOCATION" - copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" - cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem" - copy_file_to_location "full pem" \ - "$TEMP_DIR/${DOMAIN}_chain.pem" \ - "$DOMAIN_CHAIN_LOCATION" - cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" - copy_file_to_location "private key and domain cert pem" \ - "$TEMP_DIR/${DOMAIN}_K_C.pem" \ - "$DOMAIN_KEY_CERT_LOCATION" - cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" - copy_file_to_location "full pem" \ - "$TEMP_DIR/${DOMAIN}.pem" \ - "$DOMAIN_PEM_LOCATION" - reload_service - fi - else - info "${DOMAIN}: Certificate on remote domain does not match, ignoring remote certificate" + # 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/ssl/${DOMAIN}.crt" # this is domain cert + #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" # this is domain key + #CA_CERT_LOCATION="/etc/ssl/chain.crt" # this is CA cert + #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="" + + # Uncomment the following line to prevent non-interactive renewals of certificates + #PREVENT_NON_INTERACTIVE_RENEWAL="true" + + # 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" + #CHECK_REMOTE_WAIT="2" # wait 2 seconds before checking the remote server + _EOF_domain_ +} + +write_getssl_template() { # write out the main template file + cat > "$1" <<- _EOF_getssl_ + # This file is read first and is common to all domains + # + # Uncomment and modify any variables you need + # see https://github.com/srvrco/getssl/wiki/Config-variables for details + # + # The staging server is best for testing (hence set as default) + CA="https://acme-staging-v02.api.letsencrypt.org" + # This server issues full certificates, however has rate limits + #CA="https://acme-v02.api.letsencrypt.org" + + # The agreement that must be signed with the CA, if not defined the default agreement will be used + #AGREEMENT="$AGREEMENT" + + # Set an email address associated with your account - generally set at account level rather than domain. + #ACCOUNT_EMAIL="me@example.com" + ACCOUNT_KEY_LENGTH=4096 + ACCOUNT_KEY="$WORKING_DIR/account.key" + + # Account key and private key types - can be rsa, prime256v1, secp384r1 or secp521r1 + #ACCOUNT_KEY_TYPE="rsa" + PRIVATE_KEY_ALG="rsa" + #REUSE_PRIVATE_KEY="true" + + # The command needed to reload apache / nginx or whatever you use + #RELOAD_CMD="" + + # The time period within which you want to allow renewal of a certificate + # this prevents hitting some of the rate limits. + # Creating a file called FORCE_RENEWAL in the domain directory allows one-off overrides + # of this setting + RENEW_ALLOW="30" + + # 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" + + # Use the following 3 variables if you want to validate via DNS + #VALIDATE_VIA_DNS="true" + #DNS_ADD_COMMAND= + #DNS_DEL_COMMAND= + _EOF_getssl_ +} + +write_openssl_conf() { # write out a minimal openssl conf + cat > "$1" <<- _EOF_openssl_conf_ + # minimal openssl.cnf file + distinguished_name = req_distinguished_name + [ req_distinguished_name ] + [v3_req] + [v3_ca] + _EOF_openssl_conf_ +} + +# Trap signals +trap "signal_exit TERM" TERM HUP +trap "signal_exit INT" INT + +# Parse command-line +while [[ -n ${1+defined} ]]; do + case $1 in + -h | --help) + help_message; graceful_exit ;; + -d | --debug) + _USE_DEBUG=1 ;; + -c | --create) + _CREATE_CONFIG=1 ;; + -f | --force) + _FORCE_RENEW=1 ;; + -a | --all) + _CHECK_ALL=1 ;; + -k | --keep) + shift; _KEEP_VERSIONS="$1";; + -q | --quiet) + _QUIET=1 ;; + -Q | --mute) + _QUIET=1 + _MUTE=1 ;; + -r | --revoke) + _REVOKE=1 + shift + REVOKE_CERT="$1" + shift + REVOKE_KEY="$1" + shift + REVOKE_CA="$1" ;; + -u | --upgrade) + _UPGRADE=1 ;; + -U | --nocheck) + _UPGRADE_CHECK=0 ;; + -w) + shift; WORKING_DIR="$1" ;; + -*) + usage + error_exit "Unknown option $1" ;; + *) + if [[ -n $DOMAIN ]]; then + error_exit "invalid command line $DOMAIN - it appears to contain more than one domain" fi - fi - else - info "${DOMAIN}: no certificate obtained from host" - fi - # end of .... if obtained a cert + DOMAIN="$1" + if [[ -z $DOMAIN ]]; then + error_exit "invalid command line - it appears to contain a null variable" + fi ;; + esac + shift +done + +# Main logic +############ + +# Get the current OS, so the correct functions can be used for that OS. (sets the variable os) +get_os + +# check if "recent" version of bash. +#if [[ "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" -lt 42 ]]; then +# info "this script is designed for bash v4.2 or later - earlier version may give errors" +#fi + +#check if required applications are included + +requires which +requires openssl +requires curl +requires nslookup drill dig host DNS_CHECK_FUNC +requires awk +requires tr +requires date +requires grep +requires sed +requires sort +requires mktemp + +# Check if upgrades are available (unless they have specified -U to ignore Upgrade checks) +if [[ $_UPGRADE_CHECK -eq 1 ]]; then + check_getssl_upgrade fi -# end of .... check_remote is true then connect and obtain the current certificate -# if there is an existing certificate file, check details. -if [[ -s "$CERT_FILE" ]]; then - debug "certificate $CERT_FILE exists" - enddate=$(openssl x509 -in "$CERT_FILE" -noout -enddate 2>/dev/null| cut -d= -f 2-) - debug "local cert is valid until $enddate" - if [[ "$enddate" != "-" ]]; 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-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)" - # everything is OK, so exit. - graceful_exit - fi - else - debug "${DOMAIN}: certificate needs renewal" - fi +# Revoke a certificate if requested +if [[ $_REVOKE -eq 1 ]]; then + if [[ -z $REVOKE_CA ]]; then + CA=$DEFAULT_REVOKE_CA + elif [[ "$REVOKE_CA" == "-d" ]]; then + _USE_DEBUG=1 + CA=$DEFAULT_REVOKE_CA + else + CA=$REVOKE_CA fi + URL_revoke=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null | grep "revoke-cert" | awk -F'"' '{print $4}') + revoke_certificate + graceful_exit fi -# end of .... if there is an existing certificate file, check details. -if [[ ! -t 0 ]] && [[ "$PREVENT_NON_INTERACTIVE_RENEWAL" = "true" ]]; then - errmsg="$DOMAIN due for renewal," - errmsg="${errmsg} but not completed due to PREVENT_NON_INTERACTIVE_RENEWAL=true in config" - error_exit "$errmsg" -fi +# get latest agreement from CA (as default) +AGREEMENT=$(curl --user-agent "$CURL_USERAGENT" -I "${CA}/terms" 2>/dev/null | awk 'tolower($1) ~ "location:" {print $2}'|tr -d '\r') -# create account key if it doesn't exist. -if [[ -s "$ACCOUNT_KEY" ]]; then - debug "Account key exists at $ACCOUNT_KEY skipping generation" -else - info "creating account key $ACCOUNT_KEY" - create_key "$ACCOUNT_KEY_TYPE" "$ACCOUNT_KEY" "$ACCOUNT_KEY_LENGTH" +# if nothing in command line, print help and exit. +if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then + help_message + graceful_exit fi -# if not reusing private key, then remove the old keys -if [[ "$REUSE_PRIVATE_KEY" != "true" ]]; then - if [[ -s "$DOMAIN_DIR/${DOMAIN}.key" ]]; then - rm -f "$DOMAIN_DIR/${DOMAIN}.key" - fi - if [[ -s "$DOMAIN_DIR/${DOMAIN}.ec.key" ]]; then - rm -f "$DOMAIN_DIR/${DOMAIN}.ecs.key" - fi -fi -# create new domain keys if they don't already exist -if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then - create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH" -else - create_key "rsa" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH" - create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.ec.key" "$DOMAIN_KEY_LENGTH" +# if the "working directory" doesn't exist, then create it. +if [[ ! -d "$WORKING_DIR" ]]; then + debug "Making working directory - $WORKING_DIR" + mkdir -p "$WORKING_DIR" fi -# End of creating domain keys. -#create SAN -if [[ -z "$SANS" ]]; then - SANLIST="subjectAltName=DNS:${DOMAIN}" -elif [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - SANLIST="subjectAltName=DNS:${SANS//,/,DNS:}" -else - SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}" +# read any variables from config in working directory +if [[ -s "$WORKING_DIR/getssl.cfg" ]]; then + debug "reading config from $WORKING_DIR/getssl.cfg" + # shellcheck source=/dev/null + . "$WORKING_DIR/getssl.cfg" fi -debug "created SAN list = $SANLIST" -#create CSR's -if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then - create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key" -else - create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key" - create_csr "$DOMAIN_DIR/${DOMAIN}.ec.csr" "$DOMAIN_DIR/${DOMAIN}.ec.key" +# Define defaults for variables not set in the main config. +ACCOUNT_KEY="${ACCOUNT_KEY:=$WORKING_DIR/account.key}" +DOMAIN_STORAGE="${DOMAIN_STORAGE:=$WORKING_DIR}" +DOMAIN_DIR="$DOMAIN_STORAGE/$DOMAIN" +CERT_FILE="$DOMAIN_DIR/${DOMAIN}.crt" +FULL_CHAIN="$DOMAIN_DIR/fullchain.crt" +CA_CERT="$DOMAIN_DIR/chain.crt" +TEMP_DIR="$DOMAIN_DIR/tmp" +if [[ "$os" == "mingw" ]]; then + CSR_SUBJECT="//" fi -# use account key to register with CA -# currently the code registers every time, and gets an "already registered" back if it has been. -get_signing_params "$ACCOUNT_KEY" +# Set the OPENSSL_CONF environment variable so openssl knows which config to use +export OPENSSL_CONF=$SSLCONF -info "Registering account" -# send the request to the ACME server. -if [[ $API -eq 1 ]]; then - if [[ "$ACCOUNT_EMAIL" ]] ; then - regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' - else - regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' - fi - send_signed_request "$URL_new_reg" "$regjson" -elif [[ $API -eq 2 ]]; then - if [[ "$ACCOUNT_EMAIL" ]] ; then - regjson='{"termsOfServiceAgreed": true, "contact": ["mailto: '$ACCOUNT_EMAIL'"]}' - else - regjson='{"termsOfServiceAgreed": true}' - fi - send_signed_request "$URL_newAccount" "$regjson" -else - debug "cant determine account API" - graceful_exit -fi +# if "-a" option then check other parameters and create run for each domain. +if [[ ${_CHECK_ALL} -eq 1 ]]; then + info "Check all certificates" -if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then - info "Registered" - KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') - debug "KID=_$KID}_" - echo "$response" > "$TEMP_DIR/account.json" -elif [[ "$code" == '409' ]] ; then - KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') - debug responseHeaders "$responseHeaders" - debug "Already registered KID=$KID" -elif [[ "$code" == '200' ]] ; then - KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') - debug responseHeaders "$responseHeaders" - debug "Already registered account, KID=${KID}" -else - error_exit "Error registering account ...$responseHeaders ... $(json_get "$response" detail)" -fi -# end of registering account with CA + if [[ ${_CREATE_CONFIG} -eq 1 ]]; then + error_exit "cannot combine -c|--create with -a|--all" + fi -# verify each domain -info "Verify each domain" + if [[ ${_FORCE_RENEW} -eq 1 ]]; then + error_exit "cannot combine -f|--force with -a|--all because of rate limits" + fi -# loop through domains for cert ( from SANS list) -if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//,/ } -else - alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") -fi + if [[ ! -d "$DOMAIN_STORAGE" ]]; then + error_exit "DOMAIN_STORAGE not found - $DOMAIN_STORAGE" + fi -if [[ $API -eq 2 ]]; then - dstring="[" - for d in $alldomains; do - dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," - done - 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)\"" - request="{\"identifiers\": $dstring}" - send_signed_request "$URL_newOrder" "$request" - OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') - debug "Order link $OrderLink" - FinalizeLink=$(json_get "$response" "finalize") - debug "finalise link $FinalizeLink" - dn=0 - for d in $alldomains; do - # get authorizations link - AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") - debug "authorizations link for $d - ${AuthLink[$dn]}" - ((dn++)) + for dir in "${DOMAIN_STORAGE}"/*; do + if [[ -d "$dir" ]]; then + debug "Checking $dir" + cmd="$0 -U" # No update checks when calling recursively + if [[ ${_USE_DEBUG} -eq 1 ]]; then + cmd="$cmd -d" + fi + if [[ ${_QUIET} -eq 1 ]]; then + cmd="$cmd -q" + fi + # check if $dir is a directory with a getssl.cfg in it + if [[ -f "$dir/getssl.cfg" ]]; then + cmd="$cmd -w $WORKING_DIR $(basename "$dir")" + debug "CMD: $cmd" + eval "$cmd" + fi + fi done + + graceful_exit fi +# end of "-a" option (looping through all domains) -dn=0 -for d in $alldomains; do - # $d is domain in current loop, which is number $dn for ACL - info "Verifying $d" - if [[ "$USE_SINGLE_ACL" == "true" ]]; then - DOMAIN_ACL="${ACL[0]}" +# if "-c|--create" option used, then create config files. +if [[ ${_CREATE_CONFIG} -eq 1 ]]; then + # If main config file does not exists then create it. + if [[ ! -s "$WORKING_DIR/getssl.cfg" ]]; then + info "creating main config file $WORKING_DIR/getssl.cfg" + if [[ ! -s "$SSLCONF" ]]; then + SSLCONF="$WORKING_DIR/openssl.cnf" + write_openssl_conf "$SSLCONF" + fi + write_getssl_template "$WORKING_DIR/getssl.cfg" + fi + # If domain and domain config don't exist then create them. + if [[ ! -d "$DOMAIN_DIR" ]]; then + info "Making domain directory - $DOMAIN_DIR" + mkdir -p "$DOMAIN_DIR" + fi + if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then + info "domain config already exists $DOMAIN_DIR/getssl.cfg" else - DOMAIN_ACL="${ACL[$dn]}" + info "creating domain config file in $DOMAIN_DIR/getssl.cfg" + # if domain has an existing cert, copy from domain and use to create defaults. + EX_CERT=$(echo \ + | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null \ + | openssl x509 2>/dev/null) + EX_SANS="www.${DOMAIN}" + if [[ -n "${EX_CERT}" ]]; then + EX_SANS=$(echo "$EX_CERT" \ + | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ + | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) + EX_SANS=${EX_SANS//$'\n'/','} + fi + write_domain_template "$DOMAIN_DIR/getssl.cfg" fi + TEMP_DIR="$DOMAIN_DIR/tmp" + # end of "-c|--create" option, so exit + graceful_exit +fi +# end of "-c|--create" option to create config file. - # request a challenge token from ACME server - if [[ $API -eq 1 ]]; then - request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}" - send_signed_request "$URL_new_authz" "$request" - debug "completed send_signed_request" +# if domain directory doesn't exist, then create it. +if [[ ! -d "$DOMAIN_DIR" ]]; then + debug "Making working directory - $DOMAIN_DIR" + mkdir -p "$DOMAIN_DIR" +fi - # check if we got a valid response and token, if not then error exit - if [[ -n "$code" ]] && [[ ! "$code" == '201' ]] ; then - error_exit "new-authz error: $response" - fi - else - send_signed_request "${AuthLink[$dn]}" "" - fi +# define a temporary directory, and if it doesn't exist, create it. +TEMP_DIR="$DOMAIN_DIR/tmp" +if [[ ! -d "${TEMP_DIR}" ]]; then + debug "Making temp directory - ${TEMP_DIR}" + mkdir -p "${TEMP_DIR}" +fi - if [[ $response_status == "valid" ]]; then - info "$d is already validated" - if [[ "$DEACTIVATE_AUTH" == "true" ]]; then - deactivate_url="$(echo "$responseHeaders" | awk ' $1 ~ "^Location" {print $2}' | tr -d "\r")" - deactivate_url_list+=" $deactivate_url " - debug "url added to deactivate list ${deactivate_url}" - debug "deactivate list is now $deactivate_url_list" - fi - # increment domain-counter - ((dn++)) - else - PREVIOUSLY_VALIDATED="false" - if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification - if [[ $API -eq 1 ]]; then - # get the dns component of the ACME response - # get the token from the dns component - token=$(json_get "$response" "token" "dns-01") - debug token "$token" - # get the uri from the dns component - uri=$(json_get "$response" "uri" "dns-01") - debug uri "$uri" - else # APIv2 - debug "authlink response = $response" - # get the token from the http-01 component - token=$(json_get "$response" "challenges" "type" "dns-01" "token") - debug token "$token" - # get the uri from the http component - uri=$(json_get "$response" "challenges" "type" "dns-01" "url") - debug uri "$uri" - fi +# read any variables from config in domain directory +if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then + debug "reading config from $DOMAIN_DIR/getssl.cfg" + # shellcheck source=/dev/null + . "$DOMAIN_DIR/getssl.cfg" +fi - keyauthorization="$token.$thumbprint" - debug keyauthorization "$keyauthorization" +# from SERVER_TYPE set REMOTE_PORT and REMOTE_EXTRA +set_server_type - #create signed authorization key from token. - auth_key=$(printf '%s' "$keyauthorization" | openssl dgst -sha256 -binary \ - | openssl base64 -e \ - | tr -d '\n\r' \ - | sed -e 's:=*$::g' -e 'y:+/:-_:') - debug auth_key "$auth_key" +# check config for typical errors. +check_config - debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key" - if ! eval "$DNS_ADD_COMMAND" "$d" "$auth_key" ; then - error_exit "DNS_ADD_COMMAND failed for domain $d" - fi +if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then + rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" + _FORCE_RENEW=1 + info "${DOMAIN}: forcing renewal (due to FORCE_RENEWAL file)" +fi - # find a primary / authoritative DNS server for the domain - if [[ -z "$AUTH_DNS_SERVER" ]]; then - get_auth_dns "$d" - else - primary_ns="$AUTH_DNS_SERVER" - fi - debug primary_ns "$primary_ns" +# Obtain CA resource locations +ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) +debug "ca_all_loc from ${CA} gives $ca_all_loc" +# APIv1 +URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') +URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') +URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') +#API v2 +URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') +URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') +URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') +if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then + ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) + debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" + # APIv1 + URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') + URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') + URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') + #API v2 + URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') + URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') + URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') +fi - # make a directory to hold pending dns-challenges - if [[ ! -d "$TEMP_DIR/dns_verify" ]]; then - mkdir "$TEMP_DIR/dns_verify" - fi +if [[ -n "$URL_new_reg" ]]; then + API=1 +elif [[ -n "$URL_newAccount" ]]; then + API=2 +else + info "unknown API version" + graceful_exit +fi +debug "Using API v$API" - # generate a file with the current variables for the dns-challenge - cat > "$TEMP_DIR/dns_verify/$d" <<- _EOF_ - token="${token}" - uri="${uri}" - keyauthorization="${keyauthorization}" - d="${d}" - primary_ns="${primary_ns}" - auth_key="${auth_key}" - _EOF_ +# Check if awk supports json_awk (required for ACMEv2) +if [[ $API -eq 2 ]]; then + json_awk_test=$(json_awk '{ "test": "1" }' 2>/dev/null) + if [[ "${json_awk_test}" == "" ]]; then + error_exit "Your version of awk does not work with json_awk (see http://github.com/step-/JSON.awk/issues/6), please install a newer version of mawk or gawk" + fi +fi - else # set up the correct http token for verification - if [[ $API -eq 1 ]]; then - # get the token from the http component - token=$(json_get "$response" "token" "http-01") - debug token "$token" - # get the uri from the http component - uri=$(json_get "$response" "uri" "http-01") - debug uri "$uri" - else # APIv2 - 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") - debug token "$token" - # get the uri from the http component - uri=$(json_get "$response" "challenges" "type" "http-01" "url" | head -n1) - debug uri "$uri" +# 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 + debug "getting certificate for $DOMAIN from remote server" + # shellcheck disable=SC2086 + EX_CERT=$(echo \ + | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ + | openssl x509 2>/dev/null) + if [[ -n "$EX_CERT" ]]; then # if obtained a cert + if [[ -s "$CERT_FILE" ]]; then # if local exists + CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) + else # since local doesn't exist leave empty so that the domain validation will happen + CERT_LOCAL="" + fi + CERT_REMOTE=$(echo "$EX_CERT" | openssl x509 -noout -fingerprint 2>/dev/null) + if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then + debug "certificate on server is same as the local cert" + else + # check if the certificate is for the right domain + EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -text \ + | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ + | sort -u | grep "^$DOMAIN\$") + if [[ "$EX_CERT_DOMAIN" == "$DOMAIN" ]]; then + # check renew-date on ex_cert and compare to local ( if local exists) + enddate_ex=$(echo "$EX_CERT" | openssl x509 -noout -enddate 2>/dev/null| cut -d= -f 2-) + enddate_ex_s=$(date_epoc "$enddate_ex") + debug "external cert has enddate $enddate_ex ( $enddate_ex_s ) " + if [[ -s "$CERT_FILE" ]]; then # if local exists + enddate_lc=$(openssl x509 -noout -enddate < "$CERT_FILE" 2>/dev/null| cut -d= -f 2-) + enddate_lc_s=$(date_epoc "$enddate_lc") + debug "local cert has enddate $enddate_lc ( $enddate_lc_s ) " + else + enddate_lc_s=0 + debug "local cert doesn't exist" + fi + if [[ "$enddate_ex_s" -eq "$enddate_lc_s" ]]; then + debug "certificates expire at the same time" + elif [[ "$enddate_ex_s" -gt "$enddate_lc_s" ]]; then + # remote has longer to expiry date than local copy. + debug "remote cert has longer to run than local cert - ignoring" + else + info "${DOMAIN}: remote cert expires sooner than local, attempting to upload from local" + copy_file_to_location "domain certificate" \ + "$CERT_FILE" \ + "$DOMAIN_CERT_LOCATION" + copy_file_to_location "private key" \ + "$DOMAIN_DIR/${DOMAIN}.key" \ + "$DOMAIN_KEY_LOCATION" + copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" + cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem" + copy_file_to_location "full pem" \ + "$TEMP_DIR/${DOMAIN}_chain.pem" \ + "$DOMAIN_CHAIN_LOCATION" + cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" + copy_file_to_location "private key and domain cert pem" \ + "$TEMP_DIR/${DOMAIN}_K_C.pem" \ + "$DOMAIN_KEY_CERT_LOCATION" + cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" + copy_file_to_location "full pem" \ + "$TEMP_DIR/${DOMAIN}.pem" \ + "$DOMAIN_PEM_LOCATION" + reload_service + fi + else + info "${DOMAIN}: Certificate on remote domain does not match, ignoring remote certificate" fi + fi + else + info "${DOMAIN}: no certificate obtained from host" + fi + # end of .... if obtained a cert +fi +# end of .... check_remote is true then connect and obtain the current certificate - #create signed authorization key from token. - keyauthorization="$token.$thumbprint" - debug keyauthorization "$keyauthorization" - - # save variable into temporary file - echo -n "$keyauthorization" > "$TEMP_DIR/$token" - chmod 644 "$TEMP_DIR/$token" - - # copy to token to acme challenge location - umask 0022 - IFS=\; read -r -a token_locations <<<"$DOMAIN_ACL" - for t_loc in "${token_locations[@]}"; do - debug "copying file from $TEMP_DIR/$token to ${t_loc}" - copy_file_to_location "challenge token" \ - "$TEMP_DIR/$token" \ - "${t_loc}/$token" - done - umask "$ORIG_UMASK" - - wellknown_url="${CHALLENGE_CHECK_TYPE}://${d}/.well-known/acme-challenge/$token" - debug wellknown_url "$wellknown_url" - - if [[ "$SKIP_HTTP_TOKEN_CHECK" == "true" ]]; then - info "SKIP_HTTP_TOKEN_CHECK=true so not checking that token is working correctly" +# if there is an existing certificate file, check details. +if [[ -s "$CERT_FILE" ]]; then + debug "certificate $CERT_FILE exists" + enddate=$(openssl x509 -in "$CERT_FILE" -noout -enddate 2>/dev/null| cut -d= -f 2-) + debug "local cert is valid until $enddate" + if [[ "$enddate" != "-" ]]; 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-v02.api.letsencrypt.org" ]]; then + debug "upgrading from fake cert to real" else - sleep "$HTTP_TOKEN_CHECK_WAIT" - # check that we can reach the challenge ourselves, if not, then error - if [[ ! "$(curl --user-agent "$CURL_USERAGENT" -k --silent --location "$wellknown_url")" == "$keyauthorization" ]]; then - error_exit "for some reason could not reach $wellknown_url - please check it manually" - fi + info "${DOMAIN}: certificate is valid for more than $RENEW_ALLOW days (until $enddate)" + # everything is OK, so exit. + graceful_exit fi + else + debug "${DOMAIN}: certificate needs renewal" + fi + fi +fi +# end of .... if there is an existing certificate file, check details. - check_challenge_completion "$uri" "$d" "$keyauthorization" +if [[ ! -t 0 ]] && [[ "$PREVENT_NON_INTERACTIVE_RENEWAL" = "true" ]]; then + errmsg="$DOMAIN due for renewal," + errmsg="${errmsg} but not completed due to PREVENT_NON_INTERACTIVE_RENEWAL=true in config" + error_exit "$errmsg" +fi - debug "remove token from ${DOMAIN_ACL}" - IFS=\; read -r -a token_locations <<<"$DOMAIN_ACL" - for t_loc in "${token_locations[@]}"; do - if [[ "${t_loc:0:4}" == "ssh:" ]] ; then - sshhost=$(echo "${t_loc}"| awk -F: '{print $2}') - command="rm -f ${t_loc:(( ${#sshhost} + 5))}/${token:?}" - debug "running following command to remove token" - debug "ssh $SSH_OPTS $sshhost ${command}" - # shellcheck disable=SC2029 - # shellcheck disable=SC2086 - ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 - rm -f "${TEMP_DIR:?}/${token:?}" - elif [[ "${t_loc:0:4}" == "ftp:" ]] ; then - debug "using ftp to remove token file" - ftpuser=$(echo "${t_loc}"| awk -F: '{print $2}') - ftppass=$(echo "${t_loc}"| awk -F: '{print $3}') - ftphost=$(echo "${t_loc}"| awk -F: '{print $4}') - ftplocn=$(echo "${t_loc}"| awk -F: '{print $5}') - debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost location=$ftplocn" - ftp -n <<- EOF - open $ftphost - user $ftpuser $ftppass - cd $ftplocn - delete ${token:?} - EOF - else - rm -f "${t_loc:?}/${token:?}" - fi - done - fi - # increment domain-counter - ((dn++)) +# create account key if it doesn't exist. +if [[ -s "$ACCOUNT_KEY" ]]; then + debug "Account key exists at $ACCOUNT_KEY skipping generation" +else + info "creating account key $ACCOUNT_KEY" + create_key "$ACCOUNT_KEY_TYPE" "$ACCOUNT_KEY" "$ACCOUNT_KEY_LENGTH" +fi + +# if not reusing private key, then remove the old keys +if [[ "$REUSE_PRIVATE_KEY" != "true" ]]; then + if [[ -s "$DOMAIN_DIR/${DOMAIN}.key" ]]; then + rm -f "$DOMAIN_DIR/${DOMAIN}.key" fi -done # end of ... loop through domains for cert ( from SANS list) + if [[ -s "$DOMAIN_DIR/${DOMAIN}.ec.key" ]]; then + rm -f "$DOMAIN_DIR/${DOMAIN}.ec.key" + fi +fi +# create new domain keys if they don't already exist +if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then + create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH" +else + create_key "rsa" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LENGTH" + create_key "${PRIVATE_KEY_ALG}" "$DOMAIN_DIR/${DOMAIN}.ec.key" "$DOMAIN_KEY_LENGTH" +fi +# End of creating domain keys. -# perform validation if via DNS challenge -if [[ $VALIDATE_VIA_DNS == "true" ]]; then - # loop through dns-variable files to check if dns has been changed - for dnsfile in "$TEMP_DIR"/dns_verify/*; do - if [[ -e "$dnsfile" ]]; then - debug "loading DNSfile: $dnsfile" - # shellcheck source=/dev/null - . "$dnsfile" +#create SAN +if [[ -z "$SANS" ]]; then + SANLIST="subjectAltName=DNS:${DOMAIN}" +elif [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then + SANLIST="subjectAltName=DNS:${SANS//,/,DNS:}" +else + SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}" +fi +debug "created SAN list = $SANLIST" - # check for token at public dns server, waiting for a valid response. - for ns in $primary_ns; do - debug "checking dns at $ns" - ntries=0 - check_dns="fail" - while [[ "$check_dns" == "fail" ]]; do - if [[ "$os" == "cygwin" ]]; then - check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ - | grep ^_acme -A2\ - | grep '"'|awk -F'"' '{ print $2}') - elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ - | grep '300 IN TXT'|awk -F'"' '{ print $2}') - elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ - | grep 'descriptive text'|awk -F'"' '{ print $2}') - else - check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ - | grep 'text ='|awk -F'"' '{ print $2}') - fi - debug "expecting $auth_key" - debug "${ns} gave ... $check_result" +#create CSR's +if [[ "$DUAL_RSA_ECDSA" == "false" ]]; then + create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key" +else + create_csr "$DOMAIN_DIR/${DOMAIN}.csr" "$DOMAIN_DIR/${DOMAIN}.key" + create_csr "$DOMAIN_DIR/${DOMAIN}.ec.csr" "$DOMAIN_DIR/${DOMAIN}.ec.key" +fi - if [[ "$check_result" == *"$auth_key"* ]]; then - check_dns="success" - else - if [[ $ntries -lt 100 ]]; then - ntries=$(( ntries + 1 )) - info "checking DNS at ${ns} for ${d}. Attempt $ntries/100 gave wrong result, "\ - "waiting $DNS_WAIT secs before checking again" - sleep $DNS_WAIT - else - debug "dns check failed - removing existing value" - error_exit "checking _acme-challenge.${d} gave $check_result not $auth_key" - fi - fi - done - done - fi - done +# use account key to register with CA +# currently the code registers every time, and gets an "already registered" back if it has been. +get_signing_params "$ACCOUNT_KEY" - if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then - info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns" - sleep "$DNS_EXTRA_WAIT" +info "Registering account" +# send the request to the ACME server. +if [[ $API -eq 1 ]]; then + if [[ "$ACCOUNT_EMAIL" ]] ; then + regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}' + else + regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}' fi + send_signed_request "$URL_new_reg" "$regjson" +elif [[ $API -eq 2 ]]; then + if [[ "$ACCOUNT_EMAIL" ]] ; then + regjson='{"termsOfServiceAgreed": true, "contact": ["mailto: '$ACCOUNT_EMAIL'"]}' + else + regjson='{"termsOfServiceAgreed": true}' + fi + send_signed_request "$URL_newAccount" "$regjson" +else + debug "cant determine account API" + graceful_exit +fi - # loop through dns-variable files to let the ACME server check the challenges - for dnsfile in "$TEMP_DIR"/dns_verify/*; do - if [[ -e "$dnsfile" ]]; then - debug "loading DNSfile: $dnsfile" - # shellcheck source=/dev/null - . "$dnsfile" +if [[ "$code" == "" ]] || [[ "$code" == '201' ]] ; then + info "Registered" + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') + debug "KID=_$KID}_" + echo "$response" > "$TEMP_DIR/account.json" +elif [[ "$code" == '409' ]] ; then + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') + debug responseHeaders "$responseHeaders" + debug "Already registered KID=$KID" +elif [[ "$code" == '200' ]] ; then + KID=$(echo "$responseHeaders" | grep -i "^location" | awk '{print $2}'| tr -d '\r\n ') + debug responseHeaders "$responseHeaders" + debug "Already registered account, KID=${KID}" +else + error_exit "Error registering account ...$responseHeaders ... $(json_get "$response" detail)" +fi +# end of registering account with CA - check_challenge_completion "$uri" "$d" "$keyauthorization" +# verify each domain +info "Verify each domain" - debug "remove DNS entry" - eval "$DNS_DEL_COMMAND" "$d" "$auth_key" - # remove $dnsfile after each loop. - rm -f "$dnsfile" - fi - done +# loop through domains for cert ( from SANS list) +if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then + alldomains=${SANS//,/ } +else + alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") +fi + +if [[ $API -eq 2 ]]; then + create_order fi -# end of ... perform validation if via DNS challenge -#end of varify each domain. -# Verification has been completed for all SANS, so request certificate. +fulfill_challenges + +# Verification has been completed for all SANS, so request certificate. info "Verification completed, obtaining certificate." #obtain the certificate. @@ -2504,6 +2505,12 @@ get_certificate "$DOMAIN_DIR/${DOMAIN}.csr" \ "$CERT_FILE" \ "$CA_CERT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + info "Creating order for EC certificate" + if [[ $API -eq 2 ]]; then + create_order + fulfill_challenges + fi + info "obtaining EC certificate." get_certificate "$DOMAIN_DIR/${DOMAIN}.ec.csr" \ "${CERT_FILE%.*}.ec.crt" \ "${CA_CERT%.*}.ec.crt" @@ -2549,7 +2556,7 @@ if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" - copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}.ec" + copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location%.*}.ec.${to_location##*.}" fi fi # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. @@ -2563,7 +2570,7 @@ if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" - copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}.ec" + copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location%.*}.ec.${to_location##*.}" fi fi # if DOMAIN_PEM_LOCATION is not blank, then create and copy file. @@ -2577,7 +2584,7 @@ if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" - copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}.ec" + copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location%.*}.ec.${to_location##*.}" fi fi # end of copying certs. diff --git a/test/3-dual-rsa-ecdsa.bats b/test/3-dual-rsa-ecdsa.bats new file mode 100644 index 0000000..2dc9257 --- /dev/null +++ b/test/3-dual-rsa-ecdsa.bats @@ -0,0 +1,43 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create dual certificates using HTTP-01 verification" { + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of dual certificates using HTTP-01" { + #!FIXME test certificate has been updated + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success +} + +@test "Create dual certificates using DNS-01 verification" { + CONFIG_FILE="getssl-dns01-dual-rsa-ecdsa.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of dual certificates using DNS-01" { + #!FIXME test certificate has been updated + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + cleanup_environment +} diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index c4c88a1..95ebbac 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -10,9 +10,6 @@ WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private -# Prevent "Can't load /root/.rnd into RNG" error from openssl -# RUN touch /root/.rnd - # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core RUN git clone https://github.com/jasonkarns/bats-support /bats-support diff --git a/test/README.md b/test/README.md index 98ff929..7648f17 100644 --- a/test/README.md +++ b/test/README.md @@ -35,6 +35,6 @@ docker exec -it getssl-ubuntu18 /getssl/test/debug-test.sh getssl-http01.cfg ## TODO -1. Test RHEL6, Debian as well -2. Test SSH, SFTP -3. Test wildcards +1. Test wildcards +2. Test SSH, SFTP, SCP +3. Test change of key algorithm diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg new file mode 100644 index 0000000..042ed15 --- /dev/null +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -0,0 +1,37 @@ +# 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 +# +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" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg new file mode 100644 index 0000000..f6cfcb7 --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg @@ -0,0 +1,33 @@ +# 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 +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From b09e1807ba0a8d5c1bfc90f8af456842decb39bb Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 12 Feb 2020 14:22:30 +0000 Subject: [PATCH 094/337] Fix #424 - Sporadic "error in EC signing couldn't get R from ..." --- getssl | 29 ++++++++++----- test/5-secp384-http01.bats | 41 ++++++++++++++++++++++ test/test-config/getssl-http01-secp384.cfg | 32 +++++++++++++++++ test/test-config/getssl-http01-secp521.cfg | 32 +++++++++++++++++ 4 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 test/5-secp384-http01.bats create mode 100644 test/test-config/getssl-http01-secp384.cfg create mode 100644 test/test-config/getssl-http01-secp521.cfg diff --git a/getssl b/getssl index 0d593d7..5af09e1 100755 --- a/getssl +++ b/getssl @@ -1801,20 +1801,27 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}') debug "EC signature $signed" - if [[ "${signed:4:4}" == "0220" ]]; then #sha256 + if [[ "${signed:4:4}" == "021f" ]]; then #sha256 which needs padding + R=$(echo -n 00;echo "$signed" | cut -c 9-70) + part2=$(echo "$signed" | cut -c 71-) + elif [[ "${signed:4:4}" == "0220" ]]; then #sha256 R=$(echo "$signed" | cut -c 9-72) part2=$(echo "$signed" | cut -c 73-) - elif [[ "${signed:4:4}" == "0221" ]]; then #sha256 + elif [[ "${signed:4:4}" == "0221" ]]; then #sha256 which needs trimming R=$(echo "$signed" | cut -c 11-74) part2=$(echo "$signed" | cut -c 75-) + elif [[ "${signed:4:4}" == "022f" ]]; then #sha384 which needs padding + info "Padding sha384" + R=$(echo -n 00;echo "$signed" | cut -c 9-102) + part2=$(echo "$signed" | cut -c 103-) elif [[ "${signed:4:4}" == "0230" ]]; then #sha384 R=$(echo "$signed" | cut -c 9-104) part2=$(echo "$signed" | cut -c 105-) - elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 + elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 which needs trimming R=$(echo "$signed" | cut -c 11-106) part2=$(echo "$signed" | cut -c 107-) - elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 - R=$(echo "$signed" | cut -c 11-140) + elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 which needs padding + R=$(echo -n 00;echo "$signed" | cut -c 11-140) part2=$(echo "$signed" | cut -c 141-) elif [[ "${signed:6:4}" == "0242" ]]; then #sha512 R=$(echo "$signed" | cut -c 11-142) @@ -1824,18 +1831,22 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas fi debug "R $R" - if [[ "${part2:0:4}" == "0220" ]]; then #sha256 + if [[ "${part2:0:4}" == "021f" ]]; then #sha256 with padding + S=$(echo -n 00;echo "$part2" | cut -c 5-) + elif [[ "${part2:0:4}" == "0220" ]]; then #sha256 S=$(echo "$part2" | cut -c 5-68) elif [[ "${part2:0:4}" == "0221" ]]; then #sha256 S=$(echo "$part2" | cut -c 7-70) + elif [[ "${part2:0:4}" == "022f" ]]; then #sha384 with padding + S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0230" ]]; then #sha384 S=$(echo "$part2" | cut -c 5-100) elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 S=$(echo "$part2" | cut -c 7-102) - elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 - S=$(echo "$part2" | cut -c 5-136) + elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 with padding + S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 - S=$(echo "$part2" | cut -c 5-136) + S=$(echo "$part2" | cut -c 5-) else error_exit "error in EC signing couldn't get S from $signed" fi diff --git a/test/5-secp384-http01.bats b/test/5-secp384-http01.bats new file mode 100644 index 0000000..9010d58 --- /dev/null +++ b/test/5-secp384-http01.bats @@ -0,0 +1,41 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create new secp384r1 certificate using HTTP-01 verification" { + CONFIG_FILE="getssl-http01-secp384.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of secp384r1 certificate using HTTP-01" { + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success +} + + +@test "Create new secp521r1 certificate using HTTP-01 verification" { + CONFIG_FILE="getssl-http01-secp521.cfg" + setup_environment + init_getssl + create_certificate + assert_success +} + + +@test "Force renewal of secp521r1 certificate using HTTP-01" { + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success +} diff --git a/test/test-config/getssl-http01-secp384.cfg b/test/test-config/getssl-http01-secp384.cfg new file mode 100644 index 0000000..4fa3e82 --- /dev/null +++ b/test/test-config/getssl-http01-secp384.cfg @@ -0,0 +1,32 @@ +# 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 +# +CA="https://pebble:14000/dir" + +ACCOUNT_KEY_TYPE="secp384r1" +PRIVATE_KEY_ALG="secp384r1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01-secp521.cfg b/test/test-config/getssl-http01-secp521.cfg new file mode 100644 index 0000000..6068fbf --- /dev/null +++ b/test/test-config/getssl-http01-secp521.cfg @@ -0,0 +1,32 @@ +# 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 +# +CA="https://pebble:14000/dir" + +ACCOUNT_KEY_TYPE="secp521r1" +PRIVATE_KEY_ALG="secp521r1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From e0626f37c07fb80cc6f0416ce08a2922269cc7e3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 12 Feb 2020 16:01:35 +0000 Subject: [PATCH 095/337] Update version and revision history --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 5af09e1..7de661c 100755 --- a/getssl +++ b/getssl @@ -208,10 +208,12 @@ # 2020-02-05 Fix epoch_date for busybox # 2020-02-06 Bugfixes for json_awk and nslookup to support old awk versions (2.17) # 2020-02-11 Add SCP_OPTS and SFTP_OPTS +# 2020-02-12 Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502) +# 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.17" +VERSION="2.18" # defaults ACCOUNT_KEY_LENGTH=4096 From a9d0419c75fbfb57031a4a4410097d4256079b07 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 12 Feb 2020 20:50:38 +0000 Subject: [PATCH 096/337] Fix "Registration key already in use" error --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 7de661c..2de9612 100755 --- a/getssl +++ b/getssl @@ -210,10 +210,11 @@ # 2020-02-11 Add SCP_OPTS and SFTP_OPTS # 2020-02-12 Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502) # 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18) +# 2020-02-12 Fix "Registration key already in use" (2.19) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.18" +VERSION="2.19" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1753,7 +1754,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p debug response "$response" code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) debug code "$code" - if [[ "$code" == 4* && $response != *"error:badNonce"* ]]; then + if [[ "$code" == 4* && $response != *"error:badNonce"* && "$code" != 409 ]]; then detail=$(echo "$response" | grep "detail") error_exit "ACME server returned error: ${code}: ${detail}" fi From 74f4bbeac9ebe819f8f90f539e7944c3994ae10f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 14 Feb 2020 07:18:26 +0000 Subject: [PATCH 097/337] Fix bug #505 with DUAL_RSA_ECDSA and multiple locations --- getssl | 37 +++++++++++------ test/4-more-than-10-hosts.bats | 4 +- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 40 +++++++++++++++++++ test/debug-test.sh | 14 ++++++- test/run-all-tests.cmd | 5 +++ ...tssl-http01-dual-rsa-ecdsa-2-locations.cfg | 32 +++++++++++++++ test/test_helper.bash | 6 +-- 7 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 test/6-dual-rsa-ecdsa-copy-2-locations.bats create mode 100644 test/run-all-tests.cmd create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg diff --git a/getssl b/getssl index 2de9612..dbcd867 100755 --- a/getssl +++ b/getssl @@ -211,10 +211,11 @@ # 2020-02-12 Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502) # 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18) # 2020-02-12 Fix "Registration key already in use" (2.19) +# 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.19" +VERSION="2.20" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -549,8 +550,12 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. cert=$1 # descriptive name, just used for display from=$2 # current file location to=$3 # location to move file to. + suffix=$4 # (optional) optional suffix for DUAL_RSA_ECDSA, i.e. save to private.key becomes save to private.ec.key IFS=\; read -r -a copy_locations <<<"$3" for to in "${copy_locations[@]}"; do + if [[ -n "$suffix" ]]; then + to="${to%.*}.${suffix}.${to##*.}" + fi info "copying $cert to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then debug "using scp -q $SCP_OPTS $from ${to:4}" @@ -1823,6 +1828,9 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 which needs trimming R=$(echo "$signed" | cut -c 11-106) part2=$(echo "$signed" | cut -c 107-) + elif [[ "${signed:6:4}" == "0240" ]]; then #sha512 which needs padding + R=$(echo -n 00;echo "$signed" | cut -c 9-138) + part2=$(echo "$signed" | cut -c 141-) elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 which needs padding R=$(echo -n 00;echo "$signed" | cut -c 11-140) part2=$(echo "$signed" | cut -c 141-) @@ -1846,6 +1854,8 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas S=$(echo "$part2" | cut -c 5-100) elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 S=$(echo "$part2" | cut -c 7-102) + elif [[ "${part2:0:4}" == "0240" ]]; then #sha512 with padding + S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 with padding S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 @@ -2545,17 +2555,20 @@ if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then copy_file_to_location "ec domain certificate" \ "${CERT_FILE%.*}.ec.crt" \ - "${DOMAIN_CERT_LOCATION%.*}.ec.crt" + "${DOMAIN_CERT_LOCATION}" \ + "ec" fi if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then - copy_file_to_location "ec private key" \ - "$DOMAIN_DIR/${DOMAIN}.ec.key" \ - "${DOMAIN_KEY_LOCATION%.*}.ec.key" + copy_file_to_location "ec private key" \ + "$DOMAIN_DIR/${DOMAIN}.ec.key" \ + "${DOMAIN_KEY_LOCATION}" \ + "ec" fi if [[ -n "$CA_CERT_LOCATION" ]]; then - copy_file_to_location "ec CA certificate" \ - "${CA_CERT%.*}.ec.crt" \ - "${CA_CERT_LOCATION%.*}.ec.crt" + copy_file_to_location "ec CA certificate" \ + "${CA_CERT%.*}.ec.crt" \ + "${CA_CERT_LOCATION%.*}.crt" \ + "ec" fi fi @@ -2570,7 +2583,7 @@ if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" - copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}" "ec" fi fi # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. @@ -2583,8 +2596,8 @@ if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" - copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" + copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}" "ec" fi fi # if DOMAIN_PEM_LOCATION is not blank, then create and copy file. @@ -2598,7 +2611,7 @@ if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" - copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}" "ec" fi fi # end of copying certs. diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index 01e364d..ff61d52 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -17,7 +17,7 @@ setup() { # Add 11 hosts to DNS (also need to be added as aliases in docker-compose.yml) for prefix in a b c d e f g h i j k; do - curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a done init_getssl @@ -39,6 +39,6 @@ setup() { # Remove all the dns aliases cleanup_environment for prefix in a b c d e f g h i j k; do - curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a done } diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats new file mode 100644 index 0000000..4e64043 --- /dev/null +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -0,0 +1,40 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# These are run for every test, not once per file +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a +} + + +teardown() { + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a +} + + +@test "Create dual certificates and copy RSA and ECDSA chain and key to two locations" { + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations.cfg" + setup_environment + mkdir -p /root/a.${GETSSL_HOST} + + init_getssl + create_certificate + assert_success + + # Check that the RSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.crt" ] + assert [ -e "/etc/nginx/pki/private/server.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.key" ] + + # Check that the ECDSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.ec.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.ec.crt" ] + assert [ -e "/etc/nginx/pki/private/server.ec.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.ec.key" ] +} diff --git a/test/debug-test.sh b/test/debug-test.sh index 23d1983..ab00666 100644 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -3,12 +3,22 @@ # This runs getssl outside of the BATS framework for debugging, etc, against pebble # Usage: /getssl/test/debug-test.sh getssl-http01.cfg +DEBUG="" +if [ $# -eq 2 ]; then + DEBUG=$1 + shift +fi + CONFIG_FILE=$1 +if [ ! -e "$CONFIG_FILE" ]; then + CONFIG_FILE=${CODE_DIR}/test/test-config/${CONFIG_FILE} +fi source /getssl/test/test_helper.bash setup_environment 3>&1 export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt "${CODE_DIR}/getssl" -c "$GETSSL_HOST" 3>&1 -cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" -"${CODE_DIR}/getssl" -f "$GETSSL_HOST" 3>&1 +cp "${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" +# shellcheck disable=SC2086 +"${CODE_DIR}/getssl" ${DEBUG} -f "$GETSSL_HOST" 3>&1 diff --git a/test/run-all-tests.cmd b/test/run-all-tests.cmd new file mode 100644 index 0000000..16c6fd5 --- /dev/null +++ b/test/run-all-tests.cmd @@ -0,0 +1,5 @@ +docker exec -it getssl-alpine bats /getssl/test +docker exec -it getssl-centos6 bats /getssl/test +docker exec -it getssl-debian bats /getssl/test +docker exec -it getssl-ubuntu bats /getssl/test +docker exec -it getssl-ubuntu18 bats /getssl/test diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg new file mode 100644 index 0000000..80533ce --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg @@ -0,0 +1,32 @@ +# Test that more than one location can be specified for CERT and KEY locations and that the +# files are copied to both locations when both RSA and ECDSA certificates are created +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="true" + +# 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;/root/a.${GETSSL_HOST}/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test_helper.bash b/test/test_helper.bash index b33ee0b..554d60a 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -9,7 +9,7 @@ setup_environment() { fi if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then - wget --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/master/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 @@ -17,14 +17,14 @@ setup_environment() { cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt fi - curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" /getssl/test/restart-nginx } cleanup_environment() { - curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a } From 535909bd3b54944d6292ed29a2c1e245c081c1a8 Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 21 Feb 2020 01:06:23 +0300 Subject: [PATCH 098/337] ${options} must be without quotes --- dns_scripts/dns_add_nsupdate | 2 +- dns_scripts/dns_del_nsupdate | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 13b0fc9..7a2f300 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -23,7 +23,7 @@ fi # Note that blank line is a "send" command to nsupdate -nsupdate "${options}" -v < Date: Fri, 21 Feb 2020 01:30:55 +0300 Subject: [PATCH 099/337] indent correction --- dns_scripts/dns_add_nsupdate | 14 +++++++------- dns_scripts/dns_del_nsupdate | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 7a2f300..7e0a722 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -14,11 +14,11 @@ token="$2" if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then - if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then - exit $(( $? + 128 )) - fi + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi - options="-k ${DNS_NSUPDATE_KEYFILE}" + options="-k ${DNS_NSUPDATE_KEYFILE}" fi # Note that blank line is a "send" command to nsupdate @@ -31,9 +31,9 @@ EOF sts=$? if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then - if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'close' "${fulldomain}"; then - exit $(( sts + ( $? * 10 ) )) - fi + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'close' "${fulldomain}"; then + exit $(( sts + ( $? * 10 ) )) + fi fi exit ${sts} diff --git a/dns_scripts/dns_del_nsupdate b/dns_scripts/dns_del_nsupdate index d2ff048..8e0f253 100755 --- a/dns_scripts/dns_del_nsupdate +++ b/dns_scripts/dns_del_nsupdate @@ -14,11 +14,11 @@ token="$2" # 'open" / 'close' if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then - if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'open' "${fulldomain}" ; then - exit $(( $? + 128 )) - fi + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi - options="-k ${DNS_NSUPDATE_KEYFILE}" + options="-k ${DNS_NSUPDATE_KEYFILE}" fi # Note that blank line is a "send" command to nsupdate @@ -31,9 +31,9 @@ EOF sts=$? if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then - if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'close' "${fulldomain}" ; then - exit $(( sts + ( $? * 10 ) )) - fi + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! "${DNS_NSUPDATE_KEY_HOOK}" 'del' 'close' "${fulldomain}" ; then + exit $(( sts + ( $? * 10 ) )) + fi fi exit ${sts} From b869824b49cc9e24e4592f75452a8b3b3527bf6d Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 21 Feb 2020 02:28:25 +0300 Subject: [PATCH 100/337] nsupdate scripts: add support for custom nameserver and zone --- dns_scripts/dns_add_nsupdate | 10 ++++++---- dns_scripts/dns_del_nsupdate | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 7e0a722..98f5e7f 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -21,12 +21,14 @@ if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then options="-k ${DNS_NSUPDATE_KEYFILE}" fi -# Note that blank line is a "send" command to nsupdate +if [ -n "${DNS_SERVER}" ]; then + cmd+="server ${DNS_SERVER}\n" +fi -nsupdate ${options} -v < Date: Sat, 22 Feb 2020 18:02:21 +0000 Subject: [PATCH 101/337] Use openssl asn1parse in sign_string --- getssl | 80 ++++++++++++++++------------------------------------------ 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/getssl b/getssl index dbcd867..d64b276 100755 --- a/getssl +++ b/getssl @@ -339,7 +339,7 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # if ACME response is that their check gave an invalid response, error exit if [[ "$status" == "invalid" ]] ; then - err_detail=$(json_get "$response" detail) + err_detail=$(echo "$response" | grep "detail") error_exit "$domain:Verify error:$err_detail" fi @@ -347,7 +347,8 @@ check_challenge_completion() { # checks with the ACME server if our challenge is if [[ "$status" == "pending" ]] ; then info "Pending" else - error_exit "$domain:Verify error:$response" + err_detail=$(echo "$response" | grep "detail") + error_exit "$domain:Verify error:$status:$err_detail" fi debug "sleep 5 secs before testing verify again" sleep 5 @@ -431,7 +432,7 @@ check_config() { # check the config files for all obvious errors info "${DOMAIN}: ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg" config_errors=true fi - # check domain exist + # check domain exists if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "${d}")" -ge 1 ]]; then debug "found IP for ${d}" @@ -1807,65 +1808,28 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas if openssl rsa -in "${skey}" -noout 2>/dev/null ; then # RSA key signed64="$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" | urlbase64)" elif openssl ec -in "${skey}" -noout 2>/dev/null ; then # Elliptic curve key. - signed=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" -hex | awk '{print $2}') - debug "EC signature $signed" - if [[ "${signed:4:4}" == "021f" ]]; then #sha256 which needs padding - R=$(echo -n 00;echo "$signed" | cut -c 9-70) - part2=$(echo "$signed" | cut -c 71-) - elif [[ "${signed:4:4}" == "0220" ]]; then #sha256 - R=$(echo "$signed" | cut -c 9-72) - part2=$(echo "$signed" | cut -c 73-) - elif [[ "${signed:4:4}" == "0221" ]]; then #sha256 which needs trimming - R=$(echo "$signed" | cut -c 11-74) - part2=$(echo "$signed" | cut -c 75-) - elif [[ "${signed:4:4}" == "022f" ]]; then #sha384 which needs padding - info "Padding sha384" - R=$(echo -n 00;echo "$signed" | cut -c 9-102) - part2=$(echo "$signed" | cut -c 103-) - elif [[ "${signed:4:4}" == "0230" ]]; then #sha384 - R=$(echo "$signed" | cut -c 9-104) - part2=$(echo "$signed" | cut -c 105-) - elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 which needs trimming - R=$(echo "$signed" | cut -c 11-106) - part2=$(echo "$signed" | cut -c 107-) - elif [[ "${signed:6:4}" == "0240" ]]; then #sha512 which needs padding - R=$(echo -n 00;echo "$signed" | cut -c 9-138) - part2=$(echo "$signed" | cut -c 141-) - elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 which needs padding - R=$(echo -n 00;echo "$signed" | cut -c 11-140) - part2=$(echo "$signed" | cut -c 141-) - elif [[ "${signed:6:4}" == "0242" ]]; then #sha512 - R=$(echo "$signed" | cut -c 11-142) - part2=$(echo "$signed" | cut -c 143-) + # ECDSA signature width + # e.g. 521 bits requires 66 bytes to express, a signature consists of 2 integers so 132 bytes + # https://crypto.stackexchange.com/questions/12299/ecc-key-size-and-signature-size/ + if [ "$signalg" = "sha256" ]; then + w=64 + elif [ "$signalg" = "sha384" ]; then + w=96 + elif [ "$signalg" = "sha512" ]; then + w=132 else - error_exit "error in EC signing couldn't get R from $signed" + error_exit "Unknown signing algorithm $signalg" fi + asn1parse=$(printf '%s' "${str}" | openssl dgst -"$signalg" -sign "$key" | openssl asn1parse -inform DER) + #shellcheck disable=SC2086 + R=$(echo $asn1parse | awk '{ print $13 }' | cut -c2-) debug "R $R" - - if [[ "${part2:0:4}" == "021f" ]]; then #sha256 with padding - S=$(echo -n 00;echo "$part2" | cut -c 5-) - elif [[ "${part2:0:4}" == "0220" ]]; then #sha256 - S=$(echo "$part2" | cut -c 5-68) - elif [[ "${part2:0:4}" == "0221" ]]; then #sha256 - S=$(echo "$part2" | cut -c 7-70) - elif [[ "${part2:0:4}" == "022f" ]]; then #sha384 with padding - S=$(echo -n 00;echo "$part2" | cut -c 5-) - elif [[ "${part2:0:4}" == "0230" ]]; then #sha384 - S=$(echo "$part2" | cut -c 5-100) - elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 - S=$(echo "$part2" | cut -c 7-102) - elif [[ "${part2:0:4}" == "0240" ]]; then #sha512 with padding - S=$(echo -n 00;echo "$part2" | cut -c 5-) - elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 with padding - S=$(echo -n 00;echo "$part2" | cut -c 5-) - elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 - S=$(echo "$part2" | cut -c 5-) - else - error_exit "error in EC signing couldn't get S from $signed" - fi - + #shellcheck disable=SC2086 + S=$(echo $asn1parse | awk '{ print $20 }' | cut -c2-) debug "S $S" - signed64=$(printf '%s' "${R}${S}" | hex2bin | urlbase64 ) + + # pad R and S to the correct length for the signing algorithm + signed64=$(printf "%${w}s%${w}s" "${R}" "${S}" | tr ' ' '0' | hex2bin | urlbase64 ) debug "encoded RS $signed64" fi } From aa5eb90a6b8a54bf178c0b620b8c5ae8ebed4b91 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 22 Feb 2020 18:04:45 +0000 Subject: [PATCH 102/337] Add tests using staging server --- dns_scripts/dns_add_duckdns | 19 ++++ dns_scripts/dns_del_duckdns | 12 +++ docker-compose.yml | 19 +++- test/1-simple-http01.bats | 7 +- test/2-simple-dns01.bats | 8 +- test/3-dual-rsa-ecdsa.bats | 14 ++- test/4-more-than-10-hosts.bats | 7 +- test/5-secp384-http01.bats | 12 +++ test/6-dual-rsa-ecdsa-copy-2-locations.bats | 13 ++- test/7-duckdns-dns01.bats | 42 ++++++++ test/8-duckdns-ecdsa.bats | 103 ++++++++++++++++++++ test/debug-test.sh | 8 +- test/run-all-tests.cmd | 1 + test/run-all-tests.sh | 1 + test/test-config/getssl-duckdns01.cfg | 37 +++++++ 15 files changed, 293 insertions(+), 10 deletions(-) create mode 100644 dns_scripts/dns_add_duckdns create mode 100644 dns_scripts/dns_del_duckdns create mode 100644 test/7-duckdns-dns01.bats create mode 100644 test/8-duckdns-ecdsa.bats create mode 100644 test/test-config/getssl-duckdns01.cfg diff --git a/dns_scripts/dns_add_duckdns b/dns_scripts/dns_add_duckdns new file mode 100644 index 0000000..ef40efe --- /dev/null +++ b/dns_scripts/dns_add_duckdns @@ -0,0 +1,19 @@ +#!/bin/bash + +# need to add your Token for duckdns below +token=${DUCKDNS_TOKEN:-} + +if [ -z "$token" ]; then + echo "DUCKDNS_TOKEN not set" + exit 1 +fi + +domain="$1" +txtvalue="$2" + +response=$(curl --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=${txtvalue}") +if [ "$response" != "OK" ]; then + echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" + echo "Response: $response" + exit 1 +fi diff --git a/dns_scripts/dns_del_duckdns b/dns_scripts/dns_del_duckdns new file mode 100644 index 0000000..b9b9f9f --- /dev/null +++ b/dns_scripts/dns_del_duckdns @@ -0,0 +1,12 @@ +#!/bin/bash + +# need to add your Token for duckdns below +token=${DUCKDNS_TOKEN:-} +domain="$1" + +response=$(curl --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=&clear=true") +if [ "$response" != "OK" ]; then + echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" + echo "$response" + exit 1 +fi diff --git a/docker-compose.yml b/docker-compose.yml index 09a4264..8f9e4cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -156,7 +156,24 @@ services: - i.ubuntu18.getssl.test - j.ubuntu18.getssl.test - k.ubuntu18.getssl.test - + getssl-duckdns: + build: + context: . + dockerfile: test/Dockerfile-ubuntu + container_name: getssl-duckdns + volumes: + - .:/getssl + environment: + GETSSL_HOST: getssl.duckdns.org + GETSSL_IP: 10.30.50.15 + NGINX_CONFIG: /etc/nginx/sites-enabled/default + DUCKDNS_TOKEN: $DUCKDNS_TOKEN + STAGING: "true" + networks: + acmenet: + ipv4_address: 10.30.50.15 + aliases: + - getssl.duckdns.org networks: diff --git a/test/1-simple-http01.bats b/test/1-simple-http01.bats index 4c55304..fd96a8a 100644 --- a/test/1-simple-http01.bats +++ b/test/1-simple-http01.bats @@ -12,6 +12,9 @@ setup() { @test "Create new certificate using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl @@ -24,7 +27,9 @@ setup() { @test "Force renewal of certificate using HTTP-01" { - #!FIXME test certificate has been updated + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' diff --git a/test/2-simple-dns01.bats b/test/2-simple-dns01.bats index 9d9f44b..ffd0b9e 100644 --- a/test/2-simple-dns01.bats +++ b/test/2-simple-dns01.bats @@ -12,6 +12,10 @@ setup() { @test "Create new certificate using DNS-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl @@ -24,7 +28,9 @@ setup() { @test "Force renewal of certificate using DNS-01" { - #!FIXME test certificate has been updated + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' diff --git a/test/3-dual-rsa-ecdsa.bats b/test/3-dual-rsa-ecdsa.bats index 2dc9257..7820a96 100644 --- a/test/3-dual-rsa-ecdsa.bats +++ b/test/3-dual-rsa-ecdsa.bats @@ -12,6 +12,9 @@ setup() { @test "Create dual certificates using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01-dual-rsa-ecdsa.cfg" setup_environment init_getssl @@ -21,12 +24,17 @@ setup() { @test "Force renewal of dual certificates using HTTP-01" { - #!FIXME test certificate has been updated + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success } @test "Create dual certificates using DNS-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-dns01-dual-rsa-ecdsa.cfg" setup_environment init_getssl @@ -36,7 +44,9 @@ setup() { @test "Force renewal of dual certificates using DNS-01" { - #!FIXME test certificate has been updated + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success cleanup_environment diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index ff61d52..5bdfc2a 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -12,6 +12,9 @@ setup() { @test "Create certificates for more than 10 hosts using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01-10-hosts.cfg" setup_environment @@ -30,7 +33,9 @@ setup() { @test "Force renewal of more than 10 certificates using HTTP-01" { - #!FIXME test certificate has been updated + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' diff --git a/test/5-secp384-http01.bats b/test/5-secp384-http01.bats index 9010d58..29da2da 100644 --- a/test/5-secp384-http01.bats +++ b/test/5-secp384-http01.bats @@ -12,6 +12,9 @@ setup() { @test "Create new secp384r1 certificate using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01-secp384.cfg" setup_environment init_getssl @@ -21,12 +24,18 @@ setup() { @test "Force renewal of secp384r1 certificate using HTTP-01" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success } @test "Create new secp521r1 certificate using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01-secp521.cfg" setup_environment init_getssl @@ -36,6 +45,9 @@ setup() { @test "Force renewal of secp521r1 certificate using HTTP-01" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success } diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index 4e64043..aae21bb 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -7,17 +7,24 @@ load '/getssl/test/test_helper.bash' # These are run for every test, not once per file setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + fi } teardown() { - curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + if [ -z "$STAGING" ]; then + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + fi } @test "Create dual certificates and copy RSA and ECDSA chain and key to two locations" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations.cfg" setup_environment mkdir -p /root/a.${GETSSL_HOST} diff --git a/test/7-duckdns-dns01.bats b/test/7-duckdns-dns01.bats new file mode 100644 index 0000000..e81b414 --- /dev/null +++ b/test/7-duckdns-dns01.bats @@ -0,0 +1,42 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# These are run for every test, not once per file +setup() { + if [ -n "$STAGING" ]; then + export GETSSL_HOST=getssl.duckdns.org + fi +} + + +@test "Create new certificate using staging server and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-duckdns01.cfg" + + setup_environment + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + +@test "Force renewal of certificate using staging server and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + cleanup_environment + curl --silent -X POST -d '{"host":"getssl.duckdns.org", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a +} diff --git a/test/8-duckdns-ecdsa.bats b/test/8-duckdns-ecdsa.bats new file mode 100644 index 0000000..f50dd05 --- /dev/null +++ b/test/8-duckdns-ecdsa.bats @@ -0,0 +1,103 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# These are run for every test, not once per file +setup() { + if [ -n "$STAGING" ]; then + export GETSSL_HOST=getssl.duckdns.org + fi +} + + +@test "Create new certificate using staging server and prime256v1" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-duckdns01.cfg" + GETSSL_HOST=getssl.duckdns.org + + setup_environment + init_getssl + sed -e 's/rsa/prime256v1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + run ${CODE_DIR}/getssl "$GETSSL_HOST" + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + + +@test "Force renewal of certificate using staging server and prime256v1" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + cleanup_environment +} + + +@test "Create new certificate using staging server and secp384r1" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-duckdns01.cfg" + GETSSL_HOST=getssl.duckdns.org + + setup_environment + init_getssl + sed -e 's/rsa/secp384r1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + run ${CODE_DIR}/getssl "$GETSSL_HOST" + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + + +@test "Force renewal of certificate using staging server and secp384r1" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + cleanup_environment +} + + +@test "Create new certificate using staging server and secp521r1" { + skip "The staging server returns 'ECDSA curve P-521 not allowed'" + + CONFIG_FILE="getssl-duckdns01.cfg" + GETSSL_HOST=getssl.duckdns.org + + setup_environment + init_getssl + sed -e 's/rsa/secp521r1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + run ${CODE_DIR}/getssl "$GETSSL_HOST" + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + + +@test "Force renewal of certificate using staging server and secp521r1" { + skip "The staging server returns 'ECDSA curve P-521 not allowed'" + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + cleanup_environment +} diff --git a/test/debug-test.sh b/test/debug-test.sh index ab00666..1327fde 100644 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -13,10 +13,16 @@ CONFIG_FILE=$1 if [ ! -e "$CONFIG_FILE" ]; then CONFIG_FILE=${CODE_DIR}/test/test-config/${CONFIG_FILE} fi + +#shellcheck disable=SC1091 source /getssl/test/test_helper.bash setup_environment 3>&1 -export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + +# Only add the pebble CA to the cert bundle if using pebble +if [ "$(grep -q pebble "${CONFIG_FILE}")" = 0 ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +fi "${CODE_DIR}/getssl" -c "$GETSSL_HOST" 3>&1 cp "${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" diff --git a/test/run-all-tests.cmd b/test/run-all-tests.cmd index 16c6fd5..2c5ff6c 100644 --- a/test/run-all-tests.cmd +++ b/test/run-all-tests.cmd @@ -3,3 +3,4 @@ docker exec -it getssl-centos6 bats /getssl/test docker exec -it getssl-debian bats /getssl/test docker exec -it getssl-ubuntu bats /getssl/test docker exec -it getssl-ubuntu18 bats /getssl/test +docker exec -it getssl-duckdns bats /getssl/test diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh index b526c63..6a0eb8a 100644 --- a/test/run-all-tests.sh +++ b/test/run-all-tests.sh @@ -5,3 +5,4 @@ docker exec -it getssl-centos6 bats /getssl/test docker exec -it getssl-debian bats /getssl/test docker exec -it getssl-ubuntu bats /getssl/test docker exec -it getssl-ubuntu18 bats /getssl/test +docker exec -it getssl-duckdns bats /getssl/test diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-duckdns01.cfg new file mode 100644 index 0000000..4a37bcd --- /dev/null +++ b/test/test-config/getssl-duckdns01.cfg @@ -0,0 +1,37 @@ +# Test that the script works with external dns provider and staging server +# +CA="https://acme-staging-v02.api.letsencrypt.org/directory" + +VALIDATE_VIA_DNS=true +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" +AUTH_DNS_SERVER=1.1.1.1 +CHECK_ALL_AUTH_DNS=false +DNS_EXTRA_WAIT=20 + +ACCOUNT_KEY_TYPE="rsa" +PRIVATE_KEY_ALG="rsa" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed (using a custom port) +SERVER_TYPE="https" +CHECK_REMOTE="true" From 6fea6179baa512cc97b47aec37e1fb2385ea4f84 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 22 Feb 2020 18:08:42 +0000 Subject: [PATCH 103/337] Merge pull request #513 from 532910/nsupdate_options --- dns_scripts/dns_add_nsupdate | 24 +++++++++++++----------- dns_scripts/dns_del_nsupdate | 24 +++++++++++++----------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/dns_scripts/dns_add_nsupdate b/dns_scripts/dns_add_nsupdate index 13b0fc9..98f5e7f 100755 --- a/dns_scripts/dns_add_nsupdate +++ b/dns_scripts/dns_add_nsupdate @@ -14,26 +14,28 @@ token="$2" if [ -n "${DNS_NSUPDATE_KEYFILE}" ]; then - if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then - exit $(( $? + 128 )) - fi + if [ -n "${DNS_NSUPDATE_KEY_HOOK}" ] && ! ${DNS_NSUPDATE_KEY_HOOK} 'add' 'open' "${fulldomain}" ; then + exit $(( $? + 128 )) + fi - options="-k ${DNS_NSUPDATE_KEYFILE}" + options="-k ${DNS_NSUPDATE_KEYFILE}" fi -# Note that blank line is a "send" command to nsupdate +if [ -n "${DNS_SERVER}" ]; then + cmd+="server ${DNS_SERVER}\n" +fi -nsupdate "${options}" -v < Date: Sat, 22 Feb 2020 18:10:30 +0000 Subject: [PATCH 104/337] Update revision history --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index d64b276..f6e3972 100755 --- a/getssl +++ b/getssl @@ -212,6 +212,7 @@ # 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18) # 2020-02-12 Fix "Registration key already in use" (2.19) # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) +# 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From 2a9cc14f510156e640eaf61350e4d2d5336acb6c Mon Sep 17 00:00:00 2001 From: sergio Date: Sat, 22 Feb 2020 23:36:26 +0300 Subject: [PATCH 105/337] add vim lines for cfg files --- getssl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/getssl b/getssl index f6e3972..03a20d9 100755 --- a/getssl +++ b/getssl @@ -1870,6 +1870,8 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. cat > "$1" <<- _EOF_domain_ + # vim: filetype=sh + # # This file is read second (and per domain if running with the -a option) # and overwrites any settings from the first file # @@ -1938,6 +1940,8 @@ write_domain_template() { # write out a template file for a domain. write_getssl_template() { # write out the main template file cat > "$1" <<- _EOF_getssl_ + # vim: filetype=sh + # # This file is read first and is common to all domains # # Uncomment and modify any variables you need From 0278ae50494d64d15f06322e68143a90b3b85abb Mon Sep 17 00:00:00 2001 From: Scott Gustafson Date: Sun, 23 Feb 2020 09:32:13 -0700 Subject: [PATCH 106/337] Fix the merge to include changes on the same lines I ignored. Add dated comment line. --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index bd2f296..e807933 100755 --- a/getssl +++ b/getssl @@ -213,6 +213,7 @@ # 2020-02-12 Fix "Registration key already in use" (2.19) # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) +# 2020-02-23 Add dig to config check for systems without drill (ubuntu) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -433,9 +434,9 @@ check_config() { # check the config files for all obvious errors info "${DOMAIN}: ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg" config_errors=true fi - # check domain exist + # check domain exists if [[ "$DNS_CHECK_FUNC" == "drill" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" SOA|grep -c "^${d}")" -ge 1 ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "${d}")" -ge 1 ]]; then debug "found IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" From 5beb0f8b9c454025a2e51b1ac7ee5e52bd5eaaf3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 25 Feb 2020 17:05:22 +0000 Subject: [PATCH 107/337] Fix test breakage from using dig by default --- docker-compose.yml | 4 ++-- test/debug-test.sh | 2 +- test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg | 1 + test/test-config/getssl-dns01.cfg | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8f9e4cb..3eb81b7 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 -dnsserver 10.30.50.3:8053 + command: pebble -config /test/config/pebble-config.json -dnsserver 10.30.50.3:53 environment: # with Go 1.13.x which defaults TLS 1.3 to on GODEBUG: "tls13=1" @@ -15,7 +15,7 @@ services: ipv4_address: 10.30.50.2 challtestsrv: image: letsencrypt/pebble-challtestsrv:latest - command: pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 10.30.50.3 + command: pebble-challtestsrv -defaultIPv6 "" -defaultIPv4 10.30.50.3 -dns01 ":53" ports: - 8055:8055 # HTTP Management API networks: diff --git a/test/debug-test.sh b/test/debug-test.sh index 1327fde..8807670 100644 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -20,7 +20,7 @@ source /getssl/test/test_helper.bash setup_environment 3>&1 # Only add the pebble CA to the cert bundle if using pebble -if [ "$(grep -q pebble "${CONFIG_FILE}")" = 0 ]; then +if grep -q pebble "${CONFIG_FILE}"; then export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt fi diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 042ed15..543c201 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -7,6 +7,7 @@ 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 DUAL_RSA_ECDSA="true" ACCOUNT_KEY_TYPE="prime256v1" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 98637b0..7e26b98 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -7,6 +7,7 @@ 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 # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" From fef7454746f19ebf56fc834a50876d6846e38ce5 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 25 Feb 2020 17:06:00 +0000 Subject: [PATCH 108/337] Remove TTL from grep when parsing dig output --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index e807933..09da52f 100755 --- a/getssl +++ b/getssl @@ -998,7 +998,7 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ - | grep '300 IN TXT'|awk -F'"' '{ print $2}') + | grep 'IN TXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') From 9420d8fd565beb1c88e3d27a91d08e185b398f89 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 26 Feb 2020 11:22:53 +0000 Subject: [PATCH 109/337] Create stale2.yml --- .github/workflows/stale2.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/stale2.yml diff --git a/.github/workflows/stale2.yml b/.github/workflows/stale2.yml new file mode 100644 index 0000000..cd79668 --- /dev/null +++ b/.github/workflows/stale2.yml @@ -0,0 +1,20 @@ +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue will be closed as no response' + stale-issue-label: 'needs more information' + exempt-issue-label: 'enhancement' + days-before-stale: 60 + days-before-close: 30 From d68e317798d0021ffe41f666369dac16531f66b1 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 26 Feb 2020 11:24:18 +0000 Subject: [PATCH 110/337] Delete stale.yml --- .github/workflows/stale.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 8699ac5..0000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: "Close stale issues" -on: - schedule: - - cron: "0 0 * * *" - -jobs: - stale: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'This issue will be closed as no response' - stale-issue-label: 'needs more information' - exempt-issue-label: 'enhancement' - days-before-stale: 60 - days-before-close: 30 From 0b3bff9082752df6e7703c6e302eff8385e21351 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 26 Feb 2020 23:12:49 +0000 Subject: [PATCH 111/337] Test using nslookup and on ubuntu16 --- docker-compose.yml | 35 +++++++++++++-- ...ple-dns01.bats => 2-simple-dns01-dig.bats} | 14 +++--- test/2-simple-dns01-nslookup.bats | 34 +++++++++++++++ test/8-duckdns-ecdsa.bats | 43 ++++--------------- test/Dockerfile-ubuntu16 | 25 +++++++++++ test/debug-test.sh | 6 +-- test/run-all-tests.cmd | 9 ++++ test/test-config/getssl-duckdns01.cfg | 2 +- test/test_helper.bash | 3 +- 9 files changed, 122 insertions(+), 49 deletions(-) rename test/{2-simple-dns01.bats => 2-simple-dns01-dig.bats} (63%) create mode 100644 test/2-simple-dns01-nslookup.bats create mode 100644 test/Dockerfile-ubuntu16 diff --git a/docker-compose.yml b/docker-compose.yml index 3eb81b7..b493888 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -129,6 +129,33 @@ services: - i.ubuntu.getssl.test - j.ubuntu.getssl.test - k.ubuntu.getssl.test + getssl-ubuntu16: + build: + context: . + dockerfile: test/Dockerfile-ubuntu16 + container_name: getssl-ubuntu16 + volumes: + - .:/getssl + environment: + GETSSL_HOST: ubuntu16.getssl.test + GETSSL_IP: 10.30.50.14 + NGINX_CONFIG: /etc/nginx/sites-enabled/default + networks: + acmenet: + ipv4_address: 10.30.50.14 + aliases: + - ubuntu16.getssl.test + - a.ubuntu16.getssl.test + - b.ubuntu16.getssl.test + - c.ubuntu16.getssl.test + - d.ubuntu16.getssl.test + - e.ubuntu16.getssl.test + - f.ubuntu16.getssl.test + - g.ubuntu16.getssl.test + - h.ubuntu16.getssl.test + - i.ubuntu16.getssl.test + - j.ubuntu16.getssl.test + - k.ubuntu16.getssl.test getssl-ubuntu18: build: context: . @@ -138,11 +165,11 @@ services: - .:/getssl environment: GETSSL_HOST: ubuntu18.getssl.test - GETSSL_IP: 10.30.50.14 + GETSSL_IP: 10.30.50.15 NGINX_CONFIG: /etc/nginx/sites-enabled/default networks: acmenet: - ipv4_address: 10.30.50.14 + ipv4_address: 10.30.50.15 aliases: - ubuntu18.getssl.test - a.ubuntu18.getssl.test @@ -165,13 +192,13 @@ services: - .:/getssl environment: GETSSL_HOST: getssl.duckdns.org - GETSSL_IP: 10.30.50.15 + GETSSL_IP: 10.30.50.16 NGINX_CONFIG: /etc/nginx/sites-enabled/default DUCKDNS_TOKEN: $DUCKDNS_TOKEN STAGING: "true" networks: acmenet: - ipv4_address: 10.30.50.15 + ipv4_address: 10.30.50.16 aliases: - getssl.duckdns.org diff --git a/test/2-simple-dns01.bats b/test/2-simple-dns01-dig.bats similarity index 63% rename from test/2-simple-dns01.bats rename to test/2-simple-dns01-dig.bats index ffd0b9e..cbac598 100644 --- a/test/2-simple-dns01.bats +++ b/test/2-simple-dns01-dig.bats @@ -11,7 +11,7 @@ setup() { } -@test "Create new certificate using DNS-01 verification" { +@test "Create new certificate using DNS-01 verification (dig)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi @@ -19,22 +19,24 @@ setup() { CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl - create_certificate + create_certificate -d assert_success + assert_output --partial "dig" refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } -@test "Force renewal of certificate using DNS-01" { +@test "Force renewal of certificate using DNS-01 (dig)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - run ${CODE_DIR}/getssl -f $GETSSL_HOST + run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success + assert_output --partial "dig" refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment } diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats new file mode 100644 index 0000000..f92d817 --- /dev/null +++ b/test/2-simple-dns01-nslookup.bats @@ -0,0 +1,34 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + mv /usr/bin/dig /usr/bin/dig.getssl.bak +} + + +teardown() { + mv /usr/bin/dig.getssl.bak /usr/bin/dig +} + + +@test "Create new certificate using DNS-01 verification (nslookup)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + create_certificate -d + assert_success + assert_output --partial "nslookup" + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/8-duckdns-ecdsa.bats b/test/8-duckdns-ecdsa.bats index f50dd05..0950d96 100644 --- a/test/8-duckdns-ecdsa.bats +++ b/test/8-duckdns-ecdsa.bats @@ -23,10 +23,10 @@ setup() { setup_environment init_getssl sed -e 's/rsa/prime256v1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" - run ${CODE_DIR}/getssl "$GETSSL_HOST" + run ${CODE_DIR}/getssl -d "$GETSSL_HOST" assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } @@ -35,10 +35,10 @@ setup() { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - run ${CODE_DIR}/getssl -f $GETSSL_HOST + run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment } @@ -54,10 +54,10 @@ setup() { setup_environment init_getssl sed -e 's/rsa/secp384r1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" - run ${CODE_DIR}/getssl "$GETSSL_HOST" + run ${CODE_DIR}/getssl -d "$GETSSL_HOST" assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } @@ -66,38 +66,13 @@ setup() { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - run ${CODE_DIR}/getssl -f $GETSSL_HOST + run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment } -@test "Create new certificate using staging server and secp521r1" { - skip "The staging server returns 'ECDSA curve P-521 not allowed'" - - CONFIG_FILE="getssl-duckdns01.cfg" - GETSSL_HOST=getssl.duckdns.org - - setup_environment - init_getssl - sed -e 's/rsa/secp521r1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" - run ${CODE_DIR}/getssl "$GETSSL_HOST" - assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' -} - - -@test "Force renewal of certificate using staging server and secp521r1" { - skip "The staging server returns 'ECDSA curve P-521 not allowed'" - run ${CODE_DIR}/getssl -f $GETSSL_HOST - assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' - cleanup_environment -} +# Note letsencrypt doesn't support ECDSA curve P-521 as it's being deprecated diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 new file mode 100644 index 0000000..958bb6f --- /dev/null +++ b/test/Dockerfile-ubuntu16 @@ -0,0 +1,25 @@ +FROM ubuntu:xenial +# xenial = 16 + +# Note this image uses mawk + +# Update and install required software +RUN apt-get update --fix-missing +RUN apt-get install -y 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-no-ssl /etc/nginx/sites-enabled/default + +# Prevent "Can't load /root/.rnd into RNG" error from openssl +# RUN touch /root/.rnd + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/debug-test.sh b/test/debug-test.sh index 8807670..890366b 100644 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -9,14 +9,14 @@ if [ $# -eq 2 ]; then shift fi +#shellcheck disable=SC1091 +source /getssl/test/test_helper.bash + CONFIG_FILE=$1 if [ ! -e "$CONFIG_FILE" ]; then CONFIG_FILE=${CODE_DIR}/test/test-config/${CONFIG_FILE} fi -#shellcheck disable=SC1091 -source /getssl/test/test_helper.bash - setup_environment 3>&1 # Only add the pebble CA to the cert bundle if using pebble diff --git a/test/run-all-tests.cmd b/test/run-all-tests.cmd index 2c5ff6c..e887b6e 100644 --- a/test/run-all-tests.cmd +++ b/test/run-all-tests.cmd @@ -1,6 +1,15 @@ +echo %time% docker exec -it getssl-alpine bats /getssl/test +echo %time% docker exec -it getssl-centos6 bats /getssl/test +echo %time% docker exec -it getssl-debian bats /getssl/test +echo %time% docker exec -it getssl-ubuntu bats /getssl/test +echo %time% docker exec -it getssl-ubuntu18 bats /getssl/test +echo %time% +docker exec -it getssl-ubuntu16 bats /getssl/test +echo %time% docker exec -it getssl-duckdns bats /getssl/test +echo %time% diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-duckdns01.cfg index 4a37bcd..10ac366 100644 --- a/test/test-config/getssl-duckdns01.cfg +++ b/test/test-config/getssl-duckdns01.cfg @@ -7,7 +7,7 @@ DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" AUTH_DNS_SERVER=1.1.1.1 CHECK_ALL_AUTH_DNS=false -DNS_EXTRA_WAIT=20 +DNS_EXTRA_WAIT=30 ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" diff --git a/test/test_helper.bash b/test/test_helper.bash index 554d60a..0d106fa 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -39,5 +39,6 @@ init_getssl() { create_certificate() { # Create certificate cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" - run ${CODE_DIR}/getssl "$GETSSL_HOST" + # shellcheck disable=SC2086 + run ${CODE_DIR}/getssl $1 "$GETSSL_HOST" } From 4ed430562d9e2b7b94d65af699e2a635803b0c2f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 27 Feb 2020 09:21:12 +0000 Subject: [PATCH 112/337] Change to using gatsbyjs version (multiple exempt tags) --- .github/workflows/stale2.yml | 38 +++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/stale2.yml b/.github/workflows/stale2.yml index cd79668..14a3d06 100644 --- a/.github/workflows/stale2.yml +++ b/.github/workflows/stale2.yml @@ -1,20 +1,30 @@ -name: Mark stale issues and pull requests - on: schedule: - - cron: "0 0 * * *" + - cron: "0 0 * * *" + +name: Run Stale Bot on Issue Comments jobs: - stale: - + build: + name: stale runs-on: ubuntu-latest - steps: - - uses: actions/stale@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'This issue will be closed as no response' - stale-issue-label: 'needs more information' - exempt-issue-label: 'enhancement' - days-before-stale: 60 - days-before-close: 30 + - uses: actions/checkout@master + - name: stale + uses: gatsbyjs/stale@master + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRY_RUN: true + DAYS_BEFORE_STALE: 60 + DAYS_BEFORE_CLOSE: 30 + STALE_ISSUE_LABEL: 'stale' + STALE_PR_LABEL: 'stale' + OPERATIONS_PER_RUN: 30 + STALE_ISSUE_MESSAGE: 'This issue will be closed as no response' + CLOSE_MESSAGE: 'Closing stale issue' + EXEMPT_ISSUE_LABELS: | + enhancement + bug + feature + help wanted + documentation From f3de9a8cc474b214e049930f22c0a97cbedb881b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 27 Feb 2020 09:28:52 +0000 Subject: [PATCH 113/337] Add rfc to exempt tags --- .github/workflows/stale2.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale2.yml b/.github/workflows/stale2.yml index 14a3d06..4a676c6 100644 --- a/.github/workflows/stale2.yml +++ b/.github/workflows/stale2.yml @@ -23,8 +23,9 @@ jobs: STALE_ISSUE_MESSAGE: 'This issue will be closed as no response' CLOSE_MESSAGE: 'Closing stale issue' EXEMPT_ISSUE_LABELS: | - enhancement bug + documentation + enhancement feature help wanted - documentation + rfc From 3119ea986cd005d997bf18bcc40d3d9c648b417a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 27 Feb 2020 09:29:56 +0000 Subject: [PATCH 114/337] Better stale/close messages --- .github/workflows/stale2.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale2.yml b/.github/workflows/stale2.yml index 4a676c6..ce6f9c1 100644 --- a/.github/workflows/stale2.yml +++ b/.github/workflows/stale2.yml @@ -20,8 +20,8 @@ jobs: STALE_ISSUE_LABEL: 'stale' STALE_PR_LABEL: 'stale' OPERATIONS_PER_RUN: 30 - STALE_ISSUE_MESSAGE: 'This issue will be closed as no response' - CLOSE_MESSAGE: 'Closing stale issue' + STALE_ISSUE_MESSAGE: 'This issue will be closed as no updates for 60 days' + CLOSE_MESSAGE: 'Closing stale issue after 90 days of inactivity' EXEMPT_ISSUE_LABELS: | bug documentation From 1c8937d0542cc044fc8206e234012ec811f95b60 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 27 Feb 2020 14:39:56 +0000 Subject: [PATCH 115/337] Fix execution permissions --- dns_scripts/dns_add_challtestsrv | 0 dns_scripts/dns_add_duckdns | 0 dns_scripts/dns_del_challtestsrv | 0 dns_scripts/dns_del_duckdns | 0 test/restart-nginx | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dns_scripts/dns_add_challtestsrv mode change 100644 => 100755 dns_scripts/dns_add_duckdns mode change 100644 => 100755 dns_scripts/dns_del_challtestsrv mode change 100644 => 100755 dns_scripts/dns_del_duckdns mode change 100644 => 100755 test/restart-nginx diff --git a/dns_scripts/dns_add_challtestsrv b/dns_scripts/dns_add_challtestsrv old mode 100644 new mode 100755 diff --git a/dns_scripts/dns_add_duckdns b/dns_scripts/dns_add_duckdns old mode 100644 new mode 100755 diff --git a/dns_scripts/dns_del_challtestsrv b/dns_scripts/dns_del_challtestsrv old mode 100644 new mode 100755 diff --git a/dns_scripts/dns_del_duckdns b/dns_scripts/dns_del_duckdns old mode 100644 new mode 100755 diff --git a/test/restart-nginx b/test/restart-nginx old mode 100644 new mode 100755 From 1ab68d7c3b02a8fc0a84de7d0739f607d0ab96ec Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 27 Feb 2020 14:40:24 +0000 Subject: [PATCH 116/337] Run tests on PR and push --- .github/workflows/run-all-tests.yml | 17 +++++++++++++++++ test/run-all-tests.sh | 12 ++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/run-all-tests.yml mode change 100644 => 100755 test/run-all-tests.sh diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml new file mode 100644 index 0000000..0e43289 --- /dev/null +++ b/.github/workflows/run-all-tests.yml @@ -0,0 +1,17 @@ +name: Run all tests +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite + run: test/run-all-tests.sh diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh old mode 100644 new mode 100755 index 6a0eb8a..ee2f1db --- a/test/run-all-tests.sh +++ b/test/run-all-tests.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -docker exec -it getssl-alpine bats /getssl/test -docker exec -it getssl-centos6 bats /getssl/test -docker exec -it getssl-debian bats /getssl/test -docker exec -it getssl-ubuntu bats /getssl/test -docker exec -it getssl-ubuntu18 bats /getssl/test -docker exec -it getssl-duckdns bats /getssl/test +docker exec getssl-alpine bats /getssl/test +docker exec getssl-centos6 bats /getssl/test +docker exec getssl-debian bats /getssl/test +docker exec getssl-ubuntu bats /getssl/test +docker exec getssl-ubuntu18 bats /getssl/test +docker exec getssl-duckdns bats /getssl/test From 9b9784489b2c816e1fb4347d7c4398f7a352f84b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 4 Mar 2020 18:16:26 +0000 Subject: [PATCH 117/337] Parallelize the gitactions tests --- .github/workflows/run-all-tests.yml | 54 +++++- docker-compose.yml | 180 ------------------ test/4-more-than-10-hosts.bats | 2 +- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 2 +- test/7-duckdns-dns01.bats | 2 +- test/Dockerfile-alpine | 4 +- test/Dockerfile-duckdns | 25 +++ test/README-Testing.md | 35 ++++ test/README.md | 40 ---- test/restart-nginx | 2 +- test/run-all-tests.cmd | 15 -- test/run-all-tests.sh | 8 - test/run-test.cmd | 52 +++++ test/run-test.sh | 46 +++++ .../{ => test-config}/alpine-supervisord.conf | 28 +-- test/test-config/getssl-duckdns01.cfg | 2 +- test/test_helper.bash | 41 +++- 17 files changed, 261 insertions(+), 277 deletions(-) create mode 100644 test/Dockerfile-duckdns create mode 100644 test/README-Testing.md delete mode 100644 test/README.md delete mode 100644 test/run-all-tests.cmd delete mode 100755 test/run-all-tests.sh create mode 100644 test/run-test.cmd create mode 100644 test/run-test.sh rename test/{ => test-config}/alpine-supervisord.conf (80%) diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 0e43289..5a02eda 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -7,11 +7,59 @@ on: branches: - master jobs: - build: + test-alpine: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - - name: Run test suite - run: test/run-all-tests.sh + - name: Run test suite on Alpine + run: test/run-test.sh alpine + test-centos6: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on centos6 + run: test/run-test.sh centos6 + test-debian: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Debian + run: test/run-test.sh debian + test-duckdns: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu using DuckDNS + run: test/run-test.sh duckdns + test-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu + run: test/run-test.sh ubuntu + test-ubuntu16: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu16 + run: test/run-test.sh ubuntu16 + test-ubuntu18: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu18 + run: test/run-test.sh ubuntu18 diff --git a/docker-compose.yml b/docker-compose.yml index b493888..ec5c24a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,186 +21,6 @@ services: networks: acmenet: ipv4_address: 10.30.50.3 - getssl-alpine: - build: - context: . - dockerfile: test/Dockerfile-alpine - container_name: getssl-alpine - volumes: - - .:/getssl - environment: - GETSSL_HOST: alpine.getssl.test - GETSSL_IP: 10.30.50.10 - NGINX_CONFIG: /etc/nginx/conf.d/default.conf - networks: - acmenet: - ipv4_address: 10.30.50.10 - aliases: - - alpine.getssl.test - - a.alpine.getssl.test - - b.alpine.getssl.test - - c.alpine.getssl.test - - d.alpine.getssl.test - - e.alpine.getssl.test - - f.alpine.getssl.test - - g.alpine.getssl.test - - h.alpine.getssl.test - - i.alpine.getssl.test - - j.alpine.getssl.test - - k.alpine.getssl.test - getssl-centos6: - build: - context: . - dockerfile: test/Dockerfile-centos6 - container_name: getssl-centos6 - volumes: - - .:/getssl - environment: - GETSSL_HOST: centos6.getssl.test - GETSSL_IP: 10.30.50.11 - NGINX_CONFIG: /etc/nginx/conf.d/default.conf - networks: - acmenet: - ipv4_address: 10.30.50.11 - aliases: - - centos6.getssl.test - - a.centos6.getssl.test - - b.centos6.getssl.test - - c.centos6.getssl.test - - d.centos6.getssl.test - - e.centos6.getssl.test - - f.centos6.getssl.test - - g.centos6.getssl.test - - h.centos6.getssl.test - - i.centos6.getssl.test - - j.centos6.getssl.test - - k.centos6.getssl.test - getssl-debian: - build: - context: . - dockerfile: test/Dockerfile-debian - container_name: getssl-debian - volumes: - - .:/getssl - environment: - GETSSL_HOST: debian.getssl.test - GETSSL_IP: 10.30.50.12 - NGINX_CONFIG: /etc/nginx/sites-enabled/default - networks: - acmenet: - ipv4_address: 10.30.50.12 - aliases: - - debian.getssl.test - - a.debian.getssl.test - - b.debian.getssl.test - - c.debian.getssl.test - - d.debian.getssl.test - - e.debian.getssl.test - - f.debian.getssl.test - - g.debian.getssl.test - - h.debian.getssl.test - - i.debian.getssl.test - - j.debian.getssl.test - - k.debian.getssl.test - getssl-ubuntu: - build: - context: . - dockerfile: test/Dockerfile-ubuntu - container_name: getssl-ubuntu - volumes: - - .:/getssl - environment: - GETSSL_HOST: ubuntu.getssl.test - GETSSL_IP: 10.30.50.13 - NGINX_CONFIG: /etc/nginx/sites-enabled/default - networks: - acmenet: - ipv4_address: 10.30.50.13 - aliases: - - ubuntu.getssl.test - - a.ubuntu.getssl.test - - b.ubuntu.getssl.test - - c.ubuntu.getssl.test - - d.ubuntu.getssl.test - - e.ubuntu.getssl.test - - f.ubuntu.getssl.test - - g.ubuntu.getssl.test - - h.ubuntu.getssl.test - - i.ubuntu.getssl.test - - j.ubuntu.getssl.test - - k.ubuntu.getssl.test - getssl-ubuntu16: - build: - context: . - dockerfile: test/Dockerfile-ubuntu16 - container_name: getssl-ubuntu16 - volumes: - - .:/getssl - environment: - GETSSL_HOST: ubuntu16.getssl.test - GETSSL_IP: 10.30.50.14 - NGINX_CONFIG: /etc/nginx/sites-enabled/default - networks: - acmenet: - ipv4_address: 10.30.50.14 - aliases: - - ubuntu16.getssl.test - - a.ubuntu16.getssl.test - - b.ubuntu16.getssl.test - - c.ubuntu16.getssl.test - - d.ubuntu16.getssl.test - - e.ubuntu16.getssl.test - - f.ubuntu16.getssl.test - - g.ubuntu16.getssl.test - - h.ubuntu16.getssl.test - - i.ubuntu16.getssl.test - - j.ubuntu16.getssl.test - - k.ubuntu16.getssl.test - getssl-ubuntu18: - build: - context: . - dockerfile: test/Dockerfile-ubuntu18 - container_name: getssl-ubuntu18 - volumes: - - .:/getssl - environment: - GETSSL_HOST: ubuntu18.getssl.test - GETSSL_IP: 10.30.50.15 - NGINX_CONFIG: /etc/nginx/sites-enabled/default - networks: - acmenet: - ipv4_address: 10.30.50.15 - aliases: - - ubuntu18.getssl.test - - a.ubuntu18.getssl.test - - b.ubuntu18.getssl.test - - c.ubuntu18.getssl.test - - d.ubuntu18.getssl.test - - e.ubuntu18.getssl.test - - f.ubuntu18.getssl.test - - g.ubuntu18.getssl.test - - h.ubuntu18.getssl.test - - i.ubuntu18.getssl.test - - j.ubuntu18.getssl.test - - k.ubuntu18.getssl.test - getssl-duckdns: - build: - context: . - dockerfile: test/Dockerfile-ubuntu - container_name: getssl-duckdns - volumes: - - .:/getssl - environment: - GETSSL_HOST: getssl.duckdns.org - GETSSL_IP: 10.30.50.16 - NGINX_CONFIG: /etc/nginx/sites-enabled/default - DUCKDNS_TOKEN: $DUCKDNS_TOKEN - STAGING: "true" - networks: - acmenet: - ipv4_address: 10.30.50.16 - aliases: - - getssl.duckdns.org networks: diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index 5bdfc2a..bd93adc 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -44,6 +44,6 @@ setup() { # Remove all the dns aliases cleanup_environment for prefix in a b c d e f g h i j k; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a done } diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index aae21bb..73363ec 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -16,7 +16,7 @@ setup() { teardown() { if [ -z "$STAGING" ]; then - curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a fi } diff --git a/test/7-duckdns-dns01.bats b/test/7-duckdns-dns01.bats index e81b414..9466f05 100644 --- a/test/7-duckdns-dns01.bats +++ b/test/7-duckdns-dns01.bats @@ -38,5 +38,5 @@ setup() { refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment - curl --silent -X POST -d '{"host":"getssl.duckdns.org", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"getssl.duckdns.org"}' http://10.30.50.3:8055/clear-a } diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index ff69490..0c166cb 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -18,5 +18,5 @@ RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert RUN /bats-core/install.sh /usr/local # Use supervisord to run nginx in the background -COPY ./test/alpine-supervisord.conf /etc/supervisord.conf -ENTRYPOINT /usr/bin/supervisord -c /etc/supervisord.conf +COPY ./test/test-config/alpine-supervisord.conf /etc/supervisord.conf +CMD tail -f /dev/null diff --git a/test/Dockerfile-duckdns b/test/Dockerfile-duckdns new file mode 100644 index 0000000..0bdc1f8 --- /dev/null +++ b/test/Dockerfile-duckdns @@ -0,0 +1,25 @@ +FROM ubuntu:latest + +# Note this image uses mawk1.3 + +ENV staging "true" +ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb +# Update and install required software +RUN apt-get update --fix-missing +RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y vim dos2unix # for debugging +# TODO test with drill, dig, host + +WORKDIR /root + +# Prevent "Can't load /root/.rnd into RNG" error from openssl +RUN touch /root/.rnd + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/README-Testing.md b/test/README-Testing.md new file mode 100644 index 0000000..3cd4b2c --- /dev/null +++ b/test/README-Testing.md @@ -0,0 +1,35 @@ +# Testing + +## Continuous Integration + +For continuous integration testing we have the following: + +`gitactions` script which runs whenever a PR is pushed: + +1. Uses `docker-compose` to start `pebble` (letsencrypt test server) and `challtestsrv` (minimal dns client for pebble) +2. Then runs the `bats` test scripts (all the files with a ".bats" extension) for each OS (alpine, centos6, debian, ubuntu) +3. Runs the `bats` test script against the staging server (using nn ubuntu docker image and duckdns.org) + +## To run all the tests on a single OS + +1. Start `pebble` and `challtestsrv` using ```docker-compose up -d --build``` +2. Run the test suite ```run-test.sh []``` +3. eg. `run-test.sh ubuntu16` + +## To run a single bats test on a single OS + +1. Start `pebble` and `challtestsrv` using ```docker-compose up -d --build``` +2. ```run-test.sh bats ``` +3. e.g. `run-test.sh ubuntu bats /getssl/test/1-simple-http01.bats` + +## To debug a test + +1. Start `pebble` and `challtestsrv` using ```docker-compose up -d --build``` +2. ```run-test.sh /getssl/test/debug-test.sh ``` +3. e.g. `run-test.sh ubuntu /getssl/test/debug-test.sh -d /getssl/test/test-config/getssl-http01-cfg` + +## TODO + +1. Test wildcards +2. Test SSH, SFTP, SCP +3. Test change of key algorithm (should automatically delete and re-create account.key) diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 7648f17..0000000 --- a/test/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# 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 - -```sh -docker-compose -f "docker-compose.yml" up -d --build -``` - -Run the tests - -```sh -test/run-all-tests.sh -``` - -Run individual test - -```sh -docker exec -it getssl bats /getssl/test/ -``` - -Debug (uses helper script to set `CURL_CA_BUNDLE` as pebble uses a local certificate, -otherwise you get a "unknown API version" error) - -```sh -docker exec -it getssl- /getssl/test/debug-test.sh ` - -eg. - -```sh -docker exec -it getssl-ubuntu18 /getssl/test/debug-test.sh getssl-http01.cfg -``` - -## TODO - -1. Test wildcards -2. Test SSH, SFTP, SCP -3. Test change of key algorithm diff --git a/test/restart-nginx b/test/restart-nginx index d35f60f..f947d8d 100755 --- a/test/restart-nginx +++ b/test/restart-nginx @@ -4,5 +4,5 @@ if [ "$GETSSL_HOST" = "alpine.getssl.test" ]; then killall -HUP nginx >&3- sleep 5 else - service nginx restart >&3- + service nginx restart >/dev/null >&3- fi diff --git a/test/run-all-tests.cmd b/test/run-all-tests.cmd deleted file mode 100644 index e887b6e..0000000 --- a/test/run-all-tests.cmd +++ /dev/null @@ -1,15 +0,0 @@ -echo %time% -docker exec -it getssl-alpine bats /getssl/test -echo %time% -docker exec -it getssl-centos6 bats /getssl/test -echo %time% -docker exec -it getssl-debian bats /getssl/test -echo %time% -docker exec -it getssl-ubuntu bats /getssl/test -echo %time% -docker exec -it getssl-ubuntu18 bats /getssl/test -echo %time% -docker exec -it getssl-ubuntu16 bats /getssl/test -echo %time% -docker exec -it getssl-duckdns bats /getssl/test -echo %time% diff --git a/test/run-all-tests.sh b/test/run-all-tests.sh deleted file mode 100755 index ee2f1db..0000000 --- a/test/run-all-tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -docker exec getssl-alpine bats /getssl/test -docker exec getssl-centos6 bats /getssl/test -docker exec getssl-debian bats /getssl/test -docker exec getssl-ubuntu bats /getssl/test -docker exec getssl-ubuntu18 bats /getssl/test -docker exec getssl-duckdns bats /getssl/test diff --git a/test/run-test.cmd b/test/run-test.cmd new file mode 100644 index 0000000..e951c44 --- /dev/null +++ b/test/run-test.cmd @@ -0,0 +1,52 @@ +@echo off +IF %1.==. GOTO NoOS +set OS=%1 + +:CheckCommand +IF %2.==. GOTO NoCmd +set COMMAND=%2 %3 + +:CheckAlias +IF %OS%==duckdns GOTO duckdns +set ALIAS=%OS%.getssl.test +set STAGING= +GOTO Run + +:NoOS +set OS=ubuntu +GOTO CheckCommand + +:NoCmd +REM set COMMAND=/getssl/test/run-bats.sh +set COMMAND=bats /getssl/test +GOTO CheckAlias + +:duckdns +set ALIAS=getssl.duckdns.org +set STAGING=--env STAGING=true + +:Run +for %%I in (.) do set CurrDirName=%%~nxI + +docker build --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . +@echo on +docker run -it ^ + --env GETSSL_HOST=%ALIAS% %STAGING% ^ + -v %cd%:/getssl ^ + --rm ^ + --network %CurrDirName%_acmenet ^ + --network-alias %ALIAS% ^ + --network-alias a.%OS%.getssl.test ^ + --network-alias b.%OS%.getssl.test ^ + --network-alias c.%OS%.getssl.test ^ + --network-alias d.%OS%.getssl.test ^ + --network-alias e.%OS%.getssl.test ^ + --network-alias f.%OS%.getssl.test ^ + --network-alias g.%OS%.getssl.test ^ + --network-alias h.%OS%.getssl.test ^ + --network-alias i.%OS%.getssl.test ^ + --network-alias j.%OS%.getssl.test ^ + --network-alias k.%OS%.getssl.test ^ + --name getssl-%OS% ^ + getssl-%OS% ^ + %COMMAND% diff --git a/test/run-test.sh b/test/run-test.sh new file mode 100644 index 0000000..d85730f --- /dev/null +++ b/test/run-test.sh @@ -0,0 +1,46 @@ +#! /usr/bin/env bash + +if [ $# -eq 0 ]; then + echo "Usage: $(basename "$0") []" + echo "e.g. $(basename "$0") alpine bats /getssl/test" + exit 1 +fi +OS=$1 + +if [ $# -gt 1 ]; then + shift + COMMAND=$* +else + COMMAND="bats /getssl/test" +fi + +if [ "$OS" == "duckdns" ]; then + ALIAS="getssl.duckdns.org" + STAGING="--env STAGING=true" +else + ALIAS="$OS.getssl.test" + STAGING="" +fi + +docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . +# shellcheck disable=SC2086 +docker run \ + --env GETSSL_HOST="$OS.getssl.test" $STAGING \ + -v "$(pwd)":/getssl \ + --rm \ + --network ${PWD##*/}_acmenet \ + --network-alias $ALIAS \ + --network-alias "a.$OS.getssl.test" \ + --network-alias "b.$OS.getssl.test" \ + --network-alias "c.$OS.getssl.test" \ + --network-alias "d.$OS.getssl.test" \ + --network-alias "e.$OS.getssl.test" \ + --network-alias "f.$OS.getssl.test" \ + --network-alias "g.$OS.getssl.test" \ + --network-alias "h.$OS.getssl.test" \ + --network-alias "i.$OS.getssl.test" \ + --network-alias "j.$OS.getssl.test" \ + --network-alias "k.$OS.getssl.test" \ + --name "getssl-$OS" \ + "getssl-$OS" \ + $COMMAND diff --git a/test/alpine-supervisord.conf b/test/test-config/alpine-supervisord.conf similarity index 80% rename from test/alpine-supervisord.conf rename to test/test-config/alpine-supervisord.conf index 8eec585..9759570 100644 --- a/test/alpine-supervisord.conf +++ b/test/test-config/alpine-supervisord.conf @@ -1,14 +1,14 @@ -[supervisord] -nodaemon=true -logfile=/tmp/supervisord.log -childlogdir=/tmp -pidfile = /tmp/supervisord.pid - -[program:nginx] -command=nginx -g 'daemon off;' -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -autorestart=false -startretries=0 +[supervisord] +nodaemon=false +logfile=/tmp/supervisord.log +childlogdir=/tmp +pidfile = /tmp/supervisord.pid + +[program:nginx] +command=nginx +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +autorestart=false +startretries=0 diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-duckdns01.cfg index 10ac366..517aaeb 100644 --- a/test/test-config/getssl-duckdns01.cfg +++ b/test/test-config/getssl-duckdns01.cfg @@ -7,7 +7,7 @@ DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" AUTH_DNS_SERVER=1.1.1.1 CHECK_ALL_AUTH_DNS=false -DNS_EXTRA_WAIT=30 +DNS_EXTRA_WAIT=60 ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" diff --git a/test/test_helper.bash b/test/test_helper.bash index 0d106fa..d151d5a 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -8,15 +8,6 @@ setup_environment() { rm -r ${INSTALL_DIR}/.getssl fi - if [ ! -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 - CERT_FILE=/etc/ssl/certs/ca-certificates.crt - if [ ! -f $CERT_FILE ]; then - CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt - fi - cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt - fi - curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" /getssl/test/restart-nginx @@ -24,7 +15,7 @@ setup_environment() { cleanup_environment() { - curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'"}' http://10.30.50.3:8055/clear-a } @@ -42,3 +33,33 @@ create_certificate() { # shellcheck disable=SC2086 run ${CODE_DIR}/getssl $1 "$GETSSL_HOST" } + +# start nginx in background on alpine via supervisord +if [[ -f /usr/bin/supervisord && -f /etc/supervisord.conf ]]; then + if [[ ! $(pgrep supervisord) ]]; then + /usr/bin/supervisord -c /etc/supervisord.conf >&3- + fi +fi + +# Find NGINX configuration directory for HTTP-01 testing (need to add SSL to config) +if [[ -f /etc/nginx/conf.d/default.conf ]]; then + export NGINX_CONFIG=/etc/nginx/conf.d/default.conf +elif [[ -f /etc/nginx/sites-enabled/default ]]; then + export NGINX_CONFIG=/etc/nginx/sites-enabled/default +else + echo "Can't find NGINX directory" + exit 1 +fi + +# Find IP address +GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') +export GETSSL_IP + +if [ ! -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 + CERT_FILE=/etc/ssl/certs/ca-certificates.crt + if [ ! -f $CERT_FILE ]; then + CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt + fi + cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt +fi From a67dc5db09215d3b5ad6612b50a3cc1c43fbe3de Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 4 Mar 2020 18:18:46 +0000 Subject: [PATCH 118/337] Add +x for scripts used in testing --- dns_scripts/dns_freedns.sh | 0 test/debug-test.sh | 0 test/run-test.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dns_scripts/dns_freedns.sh mode change 100644 => 100755 test/debug-test.sh mode change 100644 => 100755 test/run-test.sh diff --git a/dns_scripts/dns_freedns.sh b/dns_scripts/dns_freedns.sh old mode 100644 new mode 100755 diff --git a/test/debug-test.sh b/test/debug-test.sh old mode 100644 new mode 100755 diff --git a/test/run-test.sh b/test/run-test.sh old mode 100644 new mode 100755 From 95f292ce058af360c23aaa3776e58502fcd92f2f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 5 Mar 2020 07:09:16 +0000 Subject: [PATCH 119/337] Add test status badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1d0d3fc..d0aa22c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) # getssl Obtain SSL certificates from the letsencrypt.org ACME server. Suitable From 5ad1f96ee1e49a59ab80eeade75075101129c22c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 5 Mar 2020 13:52:35 +0000 Subject: [PATCH 120/337] Create shellcheck.yml --- .github/workflows/shellcheck.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/shellcheck.yml diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..d5adbf5 --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,17 @@ +name: shellcheck + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Lint check + uses: azohra/shell-linter@v0.2.0 + with: + path: "getssl" From f51fa14c40839fd4e23aacee025f34058008837d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 5 Mar 2020 13:54:27 +0000 Subject: [PATCH 121/337] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0aa22c..c359239 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) +![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) ![shellcheck](https://github.com/srvrco/getssl/workflows/shellcheck/badge.svg) # getssl Obtain SSL certificates from the letsencrypt.org ACME server. Suitable From 1dec15f1b5f626f5c2f3038817228b29527250b7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 6 Mar 2020 13:50:24 +0000 Subject: [PATCH 122/337] Test on centos7 against staging server --- .github/workflows/run-all-tests.yml | 20 +++++++--- test/7-duckdns-dns01.bats | 8 ---- test/8-duckdns-ecdsa.bats | 8 ---- test/Dockerfile-centos7-duckdns | 28 ++++++++++++++ ...file-duckdns => Dockerfile-ubuntu-duckdns} | 0 test/restart-nginx | 5 ++- test/run-test.cmd | 6 ++- test/run-test.sh | 5 ++- test/test-config/nginx-centos7.conf | 37 +++++++++++++++++++ test/test_helper.bash | 16 +++++++- 10 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 test/Dockerfile-centos7-duckdns rename test/{Dockerfile-duckdns => Dockerfile-ubuntu-duckdns} (100%) create mode 100644 test/test-config/nginx-centos7.conf diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 5a02eda..e6d80ed 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -23,22 +23,22 @@ jobs: run: docker-compose up -d --build - name: Run test suite on centos6 run: test/run-test.sh centos6 - test-debian: + test-centos7-duckdns: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - - name: Run test suite on Debian - run: test/run-test.sh debian - test-duckdns: + - name: Run test suite on CentOS7 against Staging using DuckDNS + run: test/run-test.sh centos7-duckdns + test-debian: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - - name: Run test suite on Ubuntu using DuckDNS - run: test/run-test.sh duckdns + - name: Run test suite on Debian + run: test/run-test.sh debian test-ubuntu: runs-on: ubuntu-latest steps: @@ -63,3 +63,11 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Ubuntu18 run: test/run-test.sh ubuntu18 + test-ubuntu-duckdns: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu against Staging using DuckDNS + run: test/run-test.sh ubuntu-duckdns diff --git a/test/7-duckdns-dns01.bats b/test/7-duckdns-dns01.bats index 9466f05..0c680ea 100644 --- a/test/7-duckdns-dns01.bats +++ b/test/7-duckdns-dns01.bats @@ -5,13 +5,6 @@ load '/bats-assert/load.bash' load '/getssl/test/test_helper.bash' -# These are run for every test, not once per file -setup() { - if [ -n "$STAGING" ]; then - export GETSSL_HOST=getssl.duckdns.org - fi -} - @test "Create new certificate using staging server and DuckDNS" { if [ -z "$STAGING" ]; then @@ -38,5 +31,4 @@ setup() { refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' cleanup_environment - curl --silent -X POST -d '{"host":"getssl.duckdns.org"}' http://10.30.50.3:8055/clear-a } diff --git a/test/8-duckdns-ecdsa.bats b/test/8-duckdns-ecdsa.bats index 0950d96..dfe84fe 100644 --- a/test/8-duckdns-ecdsa.bats +++ b/test/8-duckdns-ecdsa.bats @@ -5,12 +5,6 @@ load '/bats-assert/load.bash' load '/getssl/test/test_helper.bash' -# These are run for every test, not once per file -setup() { - if [ -n "$STAGING" ]; then - export GETSSL_HOST=getssl.duckdns.org - fi -} @test "Create new certificate using staging server and prime256v1" { @@ -18,7 +12,6 @@ setup() { skip "Running internal tests, skipping external test" fi CONFIG_FILE="getssl-duckdns01.cfg" - GETSSL_HOST=getssl.duckdns.org setup_environment init_getssl @@ -49,7 +42,6 @@ setup() { skip "Running internal tests, skipping external test" fi CONFIG_FILE="getssl-duckdns01.cfg" - GETSSL_HOST=getssl.duckdns.org setup_environment init_getssl diff --git a/test/Dockerfile-centos7-duckdns b/test/Dockerfile-centos7-duckdns new file mode 100644 index 0000000..839ff76 --- /dev/null +++ b/test/Dockerfile-centos7-duckdns @@ -0,0 +1,28 @@ +FROM centos:centos7 + +# Note this image uses gawk + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl bind-utils wget which nginx + +ENV staging "true" +ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf +COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local + +EXPOSE 80 443 + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/Dockerfile-duckdns b/test/Dockerfile-ubuntu-duckdns similarity index 100% rename from test/Dockerfile-duckdns rename to test/Dockerfile-ubuntu-duckdns diff --git a/test/restart-nginx b/test/restart-nginx index f947d8d..e62433d 100755 --- a/test/restart-nginx +++ b/test/restart-nginx @@ -1,8 +1,11 @@ #!/usr/bin/env bash -if [ "$GETSSL_HOST" = "alpine.getssl.test" ]; then +if [ "$GETSSL_OS" = "alpine" ]; then killall -HUP nginx >&3- sleep 5 +elif [ "$GETSSL_OS" == "centos7" ]; then + pgrep nginx | head -1 | xargs kill -HUP + sleep 5 else service nginx restart >/dev/null >&3- fi diff --git a/test/run-test.cmd b/test/run-test.cmd index e951c44..d1cf263 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -7,7 +7,8 @@ IF %2.==. GOTO NoCmd set COMMAND=%2 %3 :CheckAlias -IF %OS%==duckdns GOTO duckdns +REM check if OS *contains* duckdns +IF NOT x%OS:duckdns=%==x%OS% GOTO duckdns set ALIAS=%OS%.getssl.test set STAGING= GOTO Run @@ -22,7 +23,7 @@ set COMMAND=bats /getssl/test GOTO CheckAlias :duckdns -set ALIAS=getssl.duckdns.org +set ALIAS=%OS:-duckdns=%-getssl.duckdns.org set STAGING=--env STAGING=true :Run @@ -32,6 +33,7 @@ docker build --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . @echo on docker run -it ^ --env GETSSL_HOST=%ALIAS% %STAGING% ^ + --env GETSSL_OS=%OS:-duckdns=% ^ -v %cd%:/getssl ^ --rm ^ --network %CurrDirName%_acmenet ^ diff --git a/test/run-test.sh b/test/run-test.sh index d85730f..07a1a49 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -14,8 +14,8 @@ else COMMAND="bats /getssl/test" fi -if [ "$OS" == "duckdns" ]; then - ALIAS="getssl.duckdns.org" +if [[ "$OS" == *"duckdns"* ]]; then + ALIAS="${OS%-duckdns}-getssl.duckdns.org" STAGING="--env STAGING=true" else ALIAS="$OS.getssl.test" @@ -26,6 +26,7 @@ docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . # shellcheck disable=SC2086 docker run \ --env GETSSL_HOST="$OS.getssl.test" $STAGING \ + --env GETSSL_OS=${OS%-duckdns} \ -v "$(pwd)":/getssl \ --rm \ --network ${PWD##*/}_acmenet \ diff --git a/test/test-config/nginx-centos7.conf b/test/test-config/nginx-centos7.conf new file mode 100644 index 0000000..2327039 --- /dev/null +++ b/test/test-config/nginx-centos7.conf @@ -0,0 +1,37 @@ +# For more information on configuration, see: +# * Official English Documentation: http://nginx.org/en/docs/ +# * Official Russian Documentation: http://nginx.org/ru/docs/ + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. +include /usr/share/nginx/modules/*.conf; + +events { + worker_connections 1024; +} + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Load modular configuration files from the /etc/nginx/conf.d directory. + # See http://nginx.org/en/docs/ngx_core_module.html#include + # for more information. + include /etc/nginx/conf.d/*.conf; +} diff --git a/test/test_helper.bash b/test/test_helper.bash index d151d5a..3ffcf51 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -35,10 +35,15 @@ create_certificate() { } # start nginx in background on alpine via supervisord +# shellcheck disable=SC2153 # Ignore GETSSL_OS looks like typo of GETSSL_IP if [[ -f /usr/bin/supervisord && -f /etc/supervisord.conf ]]; then if [[ ! $(pgrep supervisord) ]]; then /usr/bin/supervisord -c /etc/supervisord.conf >&3- fi +elif [ "$GETSSL_OS" == "centos7" ]; then + if [ -z "$(pgrep nginx)" ]; then + nginx >&3- + fi fi # Find NGINX configuration directory for HTTP-01 testing (need to add SSL to config) @@ -52,7 +57,16 @@ else fi # Find IP address -GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') +if [[ -n "$(command -v ip)" ]]; then + IP=$(ip address) +elif [[ -n "$(command -v hostname)" ]]; then + IP=$(hostname -I) +else + echo "Cannot find IP address" + exit 1 +fi + +GETSSL_IP=$(echo "$IP" | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') export GETSSL_IP if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then From 99bd342a5901326f7854a9ed6f3c79812ca297eb Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 6 Mar 2020 14:04:19 +0000 Subject: [PATCH 123/337] Update for multiple duckdns domains --- test/run-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/run-test.sh b/test/run-test.sh index 07a1a49..97842a5 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -25,7 +25,7 @@ fi docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . # shellcheck disable=SC2086 docker run \ - --env GETSSL_HOST="$OS.getssl.test" $STAGING \ + --env GETSSL_HOST=$ALIAS $STAGING \ --env GETSSL_OS=${OS%-duckdns} \ -v "$(pwd)":/getssl \ --rm \ From e724424df8e62eafd5993d71f79f0f9f23c21a7c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 7 Mar 2020 12:21:37 +0000 Subject: [PATCH 124/337] Allow any amount of whitespace in dig/drill output --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 96f7423..c23adea 100755 --- a/getssl +++ b/getssl @@ -997,8 +997,9 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then | grep ^_acme -A2\ | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${d}" "@${ns}" check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ - | grep 'IN TXT'|awk -F'"' '{ print $2}') + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') From 0ee5f69783558e9f7080d3d9b3a5ac56190c02cb Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 8 Mar 2020 15:03:46 +0000 Subject: [PATCH 125/337] Fix dig CNAME lookup in get_auth_dns --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index c23adea..d801b26 100755 --- a/getssl +++ b/getssl @@ -1069,10 +1069,10 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d") + if [[ -z "$gad_s" ]]; then #checking for CNAMEs (grep for CNAME required because if no CNAME then dig returns SOA record) + res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) else - res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d") + res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) fi if [[ -n "$res" ]]; then # domain is a CNAME so get main domain gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') From d0c19f2df45ac0ef0d2f08fd7a0940de2cb36b21 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 10 Mar 2020 15:24:27 +0000 Subject: [PATCH 126/337] Use dig +trace to find primary_ns and fix dig NS parsing (used as fallback) --- getssl | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/getssl b/getssl index d801b26..c222bd2 100755 --- a/getssl +++ b/getssl @@ -1069,18 +1069,35 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs (grep for CNAME required because if no CNAME then dig returns SOA record) - res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) + # Use SOA +trace to find the name server + if [[ -z "$gad_s" ]]; then + res=$($DNS_CHECK_FUNC SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1) else - res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) - fi - if [[ -n "$res" ]]; then # domain is a CNAME so get main domain - gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') + res=$($DNS_CHECK_FUNC SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1) fi - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d") - else - res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d") + + # fallback to existing code + if [[ -z "$res" ]]; then + if [[ -z "$gad_s" ]]; then #checking for CNAMEs + res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d" ) + else + res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" ) + fi + if [[ -n "$res" ]]; then # domain is a CNAME so get main domain + gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') + fi + # If gad_d is an A record then this returns the SOA for the root domain, e.g. without the www + # dig NS ubuntu.getssl.text + # > getssl.test. IN SOA ns1.duckdns.org + # If gad_d is a CNAME record then this returns the NS for the domain pointed to by $gad_d + # dig NS www.getssl.text + # > www.getssl.test. IN CNAME getssl.test + # > getssl.test. IN NS ns1.duckdns.org + if [[ -z "$gad_s" ]]; then + res=$($DNS_CHECK_FUNC NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1) + else + res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1) + fi fi if [[ -z "$res" ]]; then error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" From 0d9b0f402465855457a8581e384b2d06995db635 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 10 Mar 2020 15:26:07 +0000 Subject: [PATCH 127/337] Add centos7 test --- .github/workflows/run-all-tests.yml | 10 +++++++++- test/2-simple-dns01-nslookup.bats | 8 ++++++-- test/Dockerfile-centos7 | 20 ++++++++++++++++++++ test/debug-test.sh | 2 +- test/test_helper.bash | 5 ++--- 5 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 test/Dockerfile-centos7 diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index e6d80ed..1ff795d 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -21,8 +21,16 @@ jobs: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - - name: Run test suite on centos6 + - name: Run test suite on CentOS6 run: test/run-test.sh centos6 + test-centos7: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 + run: test/run-test.sh centos7 test-centos7-duckdns: runs-on: ubuntu-latest steps: diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index f92d817..c8d5cc6 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -8,12 +8,16 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - mv /usr/bin/dig /usr/bin/dig.getssl.bak + if [ -f /usr/bin/dig ]; then + mv /usr/bin/dig /usr/bin/dig.getssl.bak + fi } teardown() { - mv /usr/bin/dig.getssl.bak /usr/bin/dig + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi } diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 new file mode 100644 index 0000000..8a34bc5 --- /dev/null +++ b/test/Dockerfile-centos7 @@ -0,0 +1,20 @@ +FROM centos:centos7 + +# Note this image uses drill, does not have dig or nslookup installed + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl ldns wget which nginx + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf +COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local diff --git a/test/debug-test.sh b/test/debug-test.sh index 890366b..ac94b53 100755 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -10,7 +10,7 @@ if [ $# -eq 2 ]; then fi #shellcheck disable=SC1091 -source /getssl/test/test_helper.bash +source /getssl/test/test_helper.bash 3>&1 CONFIG_FILE=$1 if [ ! -e "$CONFIG_FILE" ]; then diff --git a/test/test_helper.bash b/test/test_helper.bash index 3ffcf51..f311b18 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -58,15 +58,14 @@ fi # Find IP address if [[ -n "$(command -v ip)" ]]; then - IP=$(ip address) + GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') elif [[ -n "$(command -v hostname)" ]]; then - IP=$(hostname -I) + GETSSL_IP=$(hostname -I | sed -e 's/[[:space:]]*$//') else echo "Cannot find IP address" exit 1 fi -GETSSL_IP=$(echo "$IP" | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') export GETSSL_IP if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then From a5393a58d778ea272b44095de58f2c5f477c1134 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 10 Mar 2020 21:04:55 +0000 Subject: [PATCH 128/337] Added instructions on how to upgrade from v01 to v02 --- README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c359239..3882b50 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,16 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. +## Upgrading from ACME v01 to ACME v02 + +Find the following line in your `getssl.cfg` file: + +```CA="https://acme-v01.api.letsencrypt.org"``` + +and change it to: + +```CA="https://acme-v02.api.letsencrypt.org"``` + ## Features * **Bash** - It runs on virtually all unix machines, including BSD, most @@ -177,9 +187,9 @@ simple bash file containing variables, an example of which is: ```getssl # Uncomment and modify any variables you need # The staging server is best for testing (hence set as default) -CA="https://acme-staging.api.letsencrypt.org" +CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits -#CA="https://acme-v01.api.letsencrypt.org" +#CA="https://acme-v02.api.letsencrypt.org" AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf" @@ -206,9 +216,9 @@ config file (again called `getssl.cfg`). An example of which is: # 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" +#CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits -#CA="https://acme-v01.api.letsencrypt.org" +#CA="https://acme-v02.api.letsencrypt.org" #AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" @@ -273,9 +283,9 @@ same server would be: ```getssl # uncomment and modify any variables you need # The staging server is best for testing -CA="https://acme-staging.api.letsencrypt.org" +CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits -#CA="https://acme-v01.api.letsencrypt.org" +#CA="https://acme-v02.api.letsencrypt.org" # additional domains - this could be multiple domains / subdomains in a comma separated list SANS="www.example.com" From 080a9ad941904e8572675bfc53029ee3e84af98b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 11 Mar 2020 10:31:51 +0000 Subject: [PATCH 129/337] Update revision history --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index c222bd2..a73a601 100755 --- a/getssl +++ b/getssl @@ -214,10 +214,11 @@ # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # 2020-02-23 Add dig to config check for systems without drill (ubuntu) +# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME (2.21) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.20" +VERSION="2.21" # defaults ACCOUNT_KEY_LENGTH=4096 From 953bfd25c2a07bc52db2326397fc5144724cda88 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 12 Mar 2020 09:33:59 +0000 Subject: [PATCH 130/337] Fix bug with DNS-01 and multiple domains --- dns_scripts/dns_add_challtestsrv | 2 +- getssl | 49 ++++++++++++++----- test/9-multiple-domains-dns01.bats | 47 ++++++++++++++++++ .../getssl-multiple-domains-dns01.cfg | 37 ++++++++++++++ 4 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 test/9-multiple-domains-dns01.bats create mode 100644 test/test-config/getssl-multiple-domains-dns01.cfg diff --git a/dns_scripts/dns_add_challtestsrv b/dns_scripts/dns_add_challtestsrv index 601bcfc..98444b5 100755 --- a/dns_scripts/dns_add_challtestsrv +++ b/dns_scripts/dns_add_challtestsrv @@ -4,4 +4,4 @@ fulldomain="${1}" token="${2}" -curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt +curl --silent -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt diff --git a/getssl b/getssl index a73a601..93ff02d 100755 --- a/getssl +++ b/getssl @@ -750,13 +750,37 @@ create_order() { OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') debug "Order link $OrderLink" FinalizeLink=$(json_get "$response" "finalize") - dn=0 - for d in $alldomains; do - # get authorizations link - AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") - debug "authorizations link for $d - ${AuthLink[$dn]}" - ((dn++)) - done + + if [[ $API -eq 1 ]]; then + dn=0 + for d in $alldomains; do + # get authorizations link + AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") + debug "authorizations link for $d - ${AuthLink[$dn]}" + ((dn++)) + done + else + # Authorization links are unsorted, so fetch the authorization link, find the domain, save response in the correct array position + AuthLinks=$(json_get "$response" "authorizations") + AuthLinkResponse=() + AuthLinkResponseHeader=() + for l in $AuthLinks; do + debug "Requesting authorizations link for $l" + send_signed_request "$l" "" + # Get domain from response + authdomain=$(json_get "$response" "identifier" "value") + # find array position (This is O(n2) but that doubt we'll see performance issues) + dn=0 + for d in $alldomains; do + if [ "$d" == "$authdomain" ]; then + debug "Saving authorization response for $authdomain for domain alldomains[$dn]" + AuthLinkResponse[$dn]=$response + AuthLinkResponseHeader[$dn]=$responseHeaders + fi + ((dn++)) + done + done + fi } date_epoc() { # convert the date into epoch time @@ -823,7 +847,9 @@ for d in $alldomains; do error_exit "new-authz error: $response" fi else - send_signed_request "${AuthLink[$dn]}" "" + response=${AuthLinkResponse[$dn]} + responseHeaders=${AuthLinkResponseHeader[$dn]} + response_status=$(json_get "$response" status) fi if [[ $response_status == "valid" ]]; then @@ -841,16 +867,14 @@ for d in $alldomains; do if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification if [[ $API -eq 1 ]]; then # get the dns component of the ACME response - # get the token from the dns component + # get the token and uri from the dns component token=$(json_get "$response" "token" "dns-01") - # get the uri from the dns component uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 debug "authlink response = $response" - # get the token from the http-01 component + # get the token and uri from the dns-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") - # get the uri from the http component uri=$(json_get "$response" "challenges" "type" "dns-01" "url") debug uri "$uri" fi @@ -901,7 +925,6 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - 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") diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats new file mode 100644 index 0000000..1b2cd9e --- /dev/null +++ b/test/9-multiple-domains-dns01.bats @@ -0,0 +1,47 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificates for multi-level domains using DNS-01 verification" { + # This tests we can create a certificate for .getssl.test and getssl.test (in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-multiple-domains-dns01.cfg" + setup_environment + + # Add top level domain from SANS to DNS + curl --silent -X POST -d '{"host":"getssl.test", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + + +@test "Force renewal of multi-level domains using DNS-01" { + # This tests we can renew a certificate for .getssl.test and getssl.test (in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + # Remove all the dns aliases + cleanup_environment + curl --silent -X POST -d '{"host":"getssl.tst"}' http://10.30.50.3:8055/clear-a +} diff --git a/test/test-config/getssl-multiple-domains-dns01.cfg b/test/test-config/getssl-multiple-domains-dns01.cfg new file mode 100644 index 0000000..e0d596c --- /dev/null +++ b/test/test-config/getssl-multiple-domains-dns01.cfg @@ -0,0 +1,37 @@ +# 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 +# +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 + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="getssl.test" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=( + '/var/www/html/.well-known/acme-challenge' + '/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From ba59e4452b74195a518f4a7d5b52e1106178105a Mon Sep 17 00:00:00 2001 From: Heiko Date: Mon, 23 Mar 2020 22:52:34 +0800 Subject: [PATCH 131/337] Fix staging server URL in domain template The trailing slash of the Let's Encrypt staging server URL broke API discovery --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 96f7423..eb41c9d 100755 --- a/getssl +++ b/getssl @@ -1890,7 +1890,7 @@ write_domain_template() { # write out a template file for a domain. # see https://github.com/srvrco/getssl/wiki/Example-config-files for example configs # # The staging server is best for testing - #CA="https://acme-staging-v02.api.letsencrypt.org/" + #CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits #CA="https://acme-v02.api.letsencrypt.org" From 6fe3119c90ec99c0c39085f9ea7ef43fe353de73 Mon Sep 17 00:00:00 2001 From: Heiko Date: Mon, 23 Mar 2020 22:56:53 +0800 Subject: [PATCH 132/337] Bump version number and change log --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index eb41c9d..b870e68 100755 --- a/getssl +++ b/getssl @@ -214,10 +214,11 @@ # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # 2020-02-23 Add dig to config check for systems without drill (ubuntu) +# 2020-03-23 Fix staging server URL in domain template # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.20" +VERSION="2.21" # defaults ACCOUNT_KEY_LENGTH=4096 From 665f72550fa665f6a3d8ed03fc004a329333c4f6 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 24 Mar 2020 21:54:36 +0000 Subject: [PATCH 133/337] Find primary ns using all dns utils (dig, host, nslookup) --- getssl | 169 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 59 deletions(-) diff --git a/getssl b/getssl index 93ff02d..010b755 100755 --- a/getssl +++ b/getssl @@ -214,7 +214,8 @@ # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # 2020-02-23 Add dig to config check for systems without drill (ubuntu) -# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME (2.21) +# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME +# 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup) (2.21) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -234,7 +235,7 @@ CSR_SUBJECT="/" CURL_USERAGENT="${PROGNAME}/${VERSION}" DEACTIVATE_AUTH="false" DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" -DNS_EXTRA_WAIT="" +DNS_EXTRA_WAIT=60 DNS_WAIT=10 DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" @@ -825,6 +826,29 @@ error_exit() { # give error message on error exit exit 1 } +find_dns_utils() { + HAS_NSLOOKUP=false + HAS_DIG_OR_DRILL="" + HAS_HOST=false + if [[ -n "$(command -v nslookup)" ]]; then + debug "HAS NSLOOKUP=true" + HAS_NSLOOKUP=true + fi + + if [[ -n "$(command -v drill)" ]]; then + debug "HAS DIG_OR_DRILL=drill" + HAS_DIG_OR_DRILL="drill" + elif [[ -n "$(command -v dig)" ]]; then + debug "HAS DIG_OR_DRILL=dig" + HAS_DIG_OR_DRILL="dig" + fi + + if [[ -n "$(command -v host)" ]]; then + debug "HAS HOST=true" + HAS_HOST=true + fi +} + fulfill_challenges() { dn=0 for d in $alldomains; do @@ -1078,10 +1102,11 @@ fi } get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) - gad_d="$1" # domain name + orig_gad_d="$1" # domain name gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER if [[ "$os" == "cygwin" ]]; then + gad_d="$orig_gad_d" all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ | grep "primary name server" \ | awk '{print $NF}') @@ -1092,23 +1117,27 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n return fi - if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + if [[ -n "$HAS_DIG_OR_DRILL" ]]; then + gad_d="$orig_gad_d" + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver # Use SOA +trace to find the name server if [[ -z "$gad_s" ]]; then - res=$($DNS_CHECK_FUNC SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1) + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1) else - res=$($DNS_CHECK_FUNC SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1) + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1) fi # fallback to existing code if [[ -z "$res" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d" ) + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s" + if [[ -z "$gad_s" ]]; then #checking for CNAMEs (need grep as dig 9.11 sometimes returns everything not just CNAME entries) + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) else - res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" ) + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) fi if [[ -n "$res" ]]; then # domain is a CNAME so get main domain gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') + debug Domain is a CNAME, actual domain is "$gad_d" fi # If gad_d is an A record then this returns the SOA for the root domain, e.g. without the www # dig NS ubuntu.getssl.text @@ -1117,77 +1146,96 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n # dig NS www.getssl.text # > www.getssl.test. IN CNAME getssl.test # > getssl.test. IN NS ns1.duckdns.org + debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver if [[ -z "$gad_s" ]]; then - res=$($DNS_CHECK_FUNC NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1) + res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1) else - res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1) + res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1) fi fi - if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else + if [[ -n "$res" ]]; then all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') - fi - return fi - if [[ "$DNS_CHECK_FUNC" == "host" ]]; then + if [[ "$HAS_HOST" == true ]]; then + gad_d="$orig_gad_d" + debug Using "host -t NS" to find primary name server for "$gad_d" if [[ -z "$gad_s" ]]; then - res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server") + res=$(host -t NS "$gad_d"| grep "name server") else - res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server") + res=$(host -t NS "$gad_d" "$gad_s"| grep "name server") fi - if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else + if [[ -n "$res" ]]; then all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ') + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') - fi - return fi - res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) + if [[ "$HAS_NSLOOKUP" == true ]]; then + gad_d="$orig_gad_d" + debug Using "nslookup -debug -type=soa -type=ns $gad_d $gad_s" to find primary name server + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) + + if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then + # this is a Non-authoritative server, need to check for an authoritative one. + gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') + if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + # if domain name doesn't exist, then find auth servers for next level up + gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + # handle scenario where awk returns nothing + if [[ -z "$gad_d" ]]; then + gad_d="$orig_gad_d" + fi + fi - if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then - # this is a Non-authoritative server, need to check for an authoritative one. - gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') - if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - # if domain name doesn't exist, then find auth servers for next level up - gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + # shellcheck disable=SC2086 + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) fi - fi - if [[ -z "$gad_s" ]]; then - res=$(nslookup -debug -type=soa -type=ns "$gad_d") - else - res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}") - fi + if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then + gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') + elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') + # handle scenario where awk returns nothing + if [[ -z "$gad_d" ]]; then + gad_d="$orig_gad_d" + fi + fi - if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then - gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') - elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') - fi + # shellcheck disable=SC2086 + # not quoting gad_s fixes the nslookup: couldn't get address for '': not found warning (#332) + all_auth_dns_servers=$(nslookup -debug -type=soa -type=ns "$gad_d" $gad_s \ + | awk '$1 ~ "nameserver" {print $3}' \ + | sed 's/\.$//g'| tr '\n' ' ') - all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \ - | awk ' $2 ~ "nameserver" {print $4}' \ - | sed 's/\.$//g'| tr '\n' ' ') - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + if [[ -n "$all_auth_dns_servers" ]]; then + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return + fi fi + + # nslookup on alpine/ubuntu containers doesn't support -debug, print a warning in this case + # This means getssl cannot check that the DNS record has been updated on the primary name server + info "Warning: Couldn't find primary DNS server - please set PUBLIC_DNS_SERVER or AUTH_DNS_SERVER in config" + info "This means getssl cannot check the DNS entry has been updated" } get_certificate() { # get certificate for csr, if all domains validated. @@ -2289,6 +2337,9 @@ set_server_type # check config for typical errors. check_config +# check what dns utils are installed +find_dns_utils + if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" _FORCE_RENEW=1 From 8a8fe446e7f3e527b85642f0396a80aedf0237a9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 24 Mar 2020 21:55:24 +0000 Subject: [PATCH 134/337] Changes to ensure get_auth_dns is tested --- test/2-simple-dns01-nslookup.bats | 3 +- ...ns-dns01.bats => 7-duckdns-dns01-dig.bats} | 4 +- test/7-duckdns-dns01-nslookup.bats | 49 +++++++++++++++++++ test/8-duckdns-ecdsa.bats | 8 +-- .../getssl-dns01-dual-rsa-ecdsa.cfg | 3 +- test/test-config/getssl-dns01.cfg | 3 +- test/test-config/getssl-duckdns01.cfg | 4 +- .../getssl-multiple-domains-dns01.cfg | 3 +- 8 files changed, 65 insertions(+), 12 deletions(-) rename test/{7-duckdns-dns01.bats => 7-duckdns-dns01-dig.bats} (85%) create mode 100644 test/7-duckdns-dns01-nslookup.bats diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index c8d5cc6..482be2a 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -34,5 +34,6 @@ teardown() { assert_output --partial "nslookup" refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' } diff --git a/test/7-duckdns-dns01.bats b/test/7-duckdns-dns01-dig.bats similarity index 85% rename from test/7-duckdns-dns01.bats rename to test/7-duckdns-dns01-dig.bats index 0c680ea..c6d8f56 100644 --- a/test/7-duckdns-dns01.bats +++ b/test/7-duckdns-dns01-dig.bats @@ -6,7 +6,7 @@ load '/getssl/test/test_helper.bash' -@test "Create new certificate using staging server and DuckDNS" { +@test "Create new certificate using staging server, dig and DuckDNS" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi @@ -21,7 +21,7 @@ load '/getssl/test/test_helper.bash' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } -@test "Force renewal of certificate using staging server and DuckDNS" { +@test "Force renewal of certificate using staging server, dig and DuckDNS" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi diff --git a/test/7-duckdns-dns01-nslookup.bats b/test/7-duckdns-dns01-nslookup.bats new file mode 100644 index 0000000..81c921e --- /dev/null +++ b/test/7-duckdns-dns01-nslookup.bats @@ -0,0 +1,49 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/dig ]; then + mv /usr/bin/dig /usr/bin/dig.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi +} + + +@test "Create new certificate using staging server, nslookup and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-duckdns01.cfg" + + setup_environment + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings +} + + +@test "Force renewal of certificate using staging server, nslookup and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + cleanup_environment +} diff --git a/test/8-duckdns-ecdsa.bats b/test/8-duckdns-ecdsa.bats index dfe84fe..2e10512 100644 --- a/test/8-duckdns-ecdsa.bats +++ b/test/8-duckdns-ecdsa.bats @@ -20,7 +20,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings } @@ -32,7 +32,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings cleanup_environment } @@ -50,7 +50,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' } @@ -62,7 +62,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' cleanup_environment } diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 543c201..8f29088 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -7,7 +7,8 @@ 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 +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" DUAL_RSA_ECDSA="true" ACCOUNT_KEY_TYPE="prime256v1" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 7e26b98..0b816b1 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -7,7 +7,8 @@ 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 +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-duckdns01.cfg index 517aaeb..8c12ee1 100644 --- a/test/test-config/getssl-duckdns01.cfg +++ b/test/test-config/getssl-duckdns01.cfg @@ -5,8 +5,8 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -AUTH_DNS_SERVER=1.1.1.1 -CHECK_ALL_AUTH_DNS=false +PUBLIC_DNS_SERVER=ns2.duckdns.org +CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=60 ACCOUNT_KEY_TYPE="rsa" diff --git a/test/test-config/getssl-multiple-domains-dns01.cfg b/test/test-config/getssl-multiple-domains-dns01.cfg index e0d596c..8754677 100644 --- a/test/test-config/getssl-multiple-domains-dns01.cfg +++ b/test/test-config/getssl-multiple-domains-dns01.cfg @@ -7,7 +7,8 @@ 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 +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="getssl.test" From 125d9e25e857727c9fde890cdf90349be21d0374 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 24 Mar 2020 21:58:53 +0000 Subject: [PATCH 135/337] Add test for IGNORE_DIRECTORY_DOMAIN --- test/9-multiple-domains-dns01.bats | 19 +++++++++ .../getssl-ignore-directory-domain.cfg | 39 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 test/test-config/getssl-ignore-directory-domain.cfg diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats index 1b2cd9e..2a9344f 100644 --- a/test/9-multiple-domains-dns01.bats +++ b/test/9-multiple-domains-dns01.bats @@ -45,3 +45,22 @@ setup() { cleanup_environment curl --silent -X POST -d '{"host":"getssl.tst"}' http://10.30.50.3:8055/clear-a } + +@test "Test IGNORE_DIRECTORY_DOMAIN using DNS-01 verification" { + # This tests we can create a certificate for getssl.test and .getssl.test (*both* in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-ignore-directory-domain.cfg" + setup_environment + + # Add top level domain from SANS to DNS + curl --silent -X POST -d '{"host":"getssl.test", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/test-config/getssl-ignore-directory-domain.cfg b/test/test-config/getssl-ignore-directory-domain.cfg new file mode 100644 index 0000000..9777891 --- /dev/null +++ b/test/test-config/getssl-ignore-directory-domain.cfg @@ -0,0 +1,39 @@ +# 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 +# +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list +IGNORE_DIRECTORY_DOMAIN="true" +SANS="getssl.test,$GETSSL_HOST" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=( + '/var/www/html/.well-known/acme-challenge' + '/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From 804c71ffece2aabb3d7c3b53bbb6fd8691234569 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 25 Mar 2020 21:26:45 +0000 Subject: [PATCH 136/337] Add test for IGNORE_DIRECTORY_DOMAIN --- getssl | 1 + test/9-multiple-domains-dns01.bats | 19 +++++++++ .../getssl-ignore-directory-domain.cfg | 39 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 test/test-config/getssl-ignore-directory-domain.cfg diff --git a/getssl b/getssl index 010b755..fc4924c 100755 --- a/getssl +++ b/getssl @@ -215,6 +215,7 @@ # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # 2020-02-23 Add dig to config check for systems without drill (ubuntu) # 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME +# 2020-03-12 Fix bug with DNS validation and multiple domains (#524) # 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup) (2.21) # ---------------------------------------------------------------------------------------- diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats index 1b2cd9e..2a9344f 100644 --- a/test/9-multiple-domains-dns01.bats +++ b/test/9-multiple-domains-dns01.bats @@ -45,3 +45,22 @@ setup() { cleanup_environment curl --silent -X POST -d '{"host":"getssl.tst"}' http://10.30.50.3:8055/clear-a } + +@test "Test IGNORE_DIRECTORY_DOMAIN using DNS-01 verification" { + # This tests we can create a certificate for getssl.test and .getssl.test (*both* in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-ignore-directory-domain.cfg" + setup_environment + + # Add top level domain from SANS to DNS + curl --silent -X POST -d '{"host":"getssl.test", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/test-config/getssl-ignore-directory-domain.cfg b/test/test-config/getssl-ignore-directory-domain.cfg new file mode 100644 index 0000000..9777891 --- /dev/null +++ b/test/test-config/getssl-ignore-directory-domain.cfg @@ -0,0 +1,39 @@ +# 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 +# +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list +IGNORE_DIRECTORY_DOMAIN="true" +SANS="getssl.test,$GETSSL_HOST" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=( + '/var/www/html/.well-known/acme-challenge' + '/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From e7a67f39e609940a9169efc2f46743630ee05114 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Mar 2020 22:01:59 +0100 Subject: [PATCH 137/337] Fix problem with domain in mixed case --- getssl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 010b755..72d3f36 100755 --- a/getssl +++ b/getssl @@ -773,6 +773,9 @@ create_order() { # find array position (This is O(n2) but that doubt we'll see performance issues) dn=0 for d in $alldomains; do + # Convert domain to lowercase as response from server will be in lowercase + # shellcheck disable=SC2018,SC2019 + d=$(echo "$d" | tr A-Z a-z) if [ "$d" == "$authdomain" ]; then debug "Saving authorization response for $authdomain for domain alldomains[$dn]" AuthLinkResponse[$dn]=$response @@ -913,8 +916,10 @@ for d in $alldomains; do | sed -e 's:=*$::g' -e 'y:+/:-_:') debug auth_key "$auth_key" - debug "adding dns via command: $DNS_ADD_COMMAND $d $auth_key" - if ! eval "$DNS_ADD_COMMAND" "$d" "$auth_key" ; then + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "$d" | tr A-Z a-z) + debug "adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" fi @@ -1091,7 +1096,9 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then check_challenge_completion "$uri" "$d" "$keyauthorization" debug "remove DNS entry" - eval "$DNS_DEL_COMMAND" "$d" "$auth_key" + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "$d" | tr A-Z a-z) + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" # remove $dnsfile after each loop. rm -f "$dnsfile" fi From 0dfda4f64dd262c5dd5c2af10b00b7c1b518f903 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Mar 2020 22:02:57 +0100 Subject: [PATCH 138/337] Make "check domain exists" case insensitive --- getssl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index 72d3f36..41663da 100755 --- a/getssl +++ b/getssl @@ -438,29 +438,29 @@ check_config() { # check the config files for all obvious errors fi # check domain exists if [[ "$DNS_CHECK_FUNC" == "drill" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "${d}")" -ge 1 ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c -i "${d}")" -ge 1 ]]; then debug "found IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" config_errors=true fi elif [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" -t SOA|grep -c "^${d}")" -ge 1 ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" -t SOA|grep -c -i "^${d}")" -ge 1 ]]; then debug "found SOA IP for ${d}" - elif [[ "$($DNS_CHECK_FUNC "${d}" -t A|grep -c "^${d}")" -ge 1 ]]; then + elif [[ "$($DNS_CHECK_FUNC "${d}" -t A|grep -c -i "^${d}")" -ge 1 ]]; then debug "found A IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" config_errors=true fi elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c "^${d}")" -ge 1 ]]; then + if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then debug "found IP for ${d}" else info "${DOMAIN}: DNS lookup failed for ${d}" config_errors=true fi - elif [[ "$(nslookup -query=AAAA "${d}"|grep -c "^${d}.*has AAAA address")" -ge 1 ]]; then + elif [[ "$(nslookup -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then debug "found IPv6 record for ${d}" elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then debug "found IPv4 record for ${d}" From ad28d693716d0e707b146912c509c53c20ea4431 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Mar 2020 22:04:29 +0100 Subject: [PATCH 139/337] Fix error messages in find_dns_utils from older versions of "command" --- getssl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index 41663da..73ca097 100755 --- a/getssl +++ b/getssl @@ -833,20 +833,20 @@ find_dns_utils() { HAS_NSLOOKUP=false HAS_DIG_OR_DRILL="" HAS_HOST=false - if [[ -n "$(command -v nslookup)" ]]; then + if [[ -n "$(command -v nslookup 2>/dev/null)" ]]; then debug "HAS NSLOOKUP=true" HAS_NSLOOKUP=true fi - if [[ -n "$(command -v drill)" ]]; then + if [[ -n "$(command -v drill 2>/dev/null)" ]]; then debug "HAS DIG_OR_DRILL=drill" HAS_DIG_OR_DRILL="drill" - elif [[ -n "$(command -v dig)" ]]; then + elif [[ -n "$(command -v dig 2>/dev/null)" ]]; then debug "HAS DIG_OR_DRILL=dig" HAS_DIG_OR_DRILL="dig" fi - if [[ -n "$(command -v host)" ]]; then + if [[ -n "$(command -v host 2>/dev/null)" ]]; then debug "HAS HOST=true" HAS_HOST=true fi From 125fabdc33708eaf31aade340ab4e6ddd5bb5552 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 30 Mar 2020 22:31:33 +0100 Subject: [PATCH 140/337] Add mixed case tests, rename duckdns tests --- .github/workflows/run-all-tests.yml | 8 ++-- test/10-mixed-case-staging.bats | 24 ++++++++++ test/10-mixed-case.bats | 45 +++++++++++++++++++ ...ns01-dig.bats => 7-staging-dns01-dig.bats} | 2 +- ...kup.bats => 7-staging-dns01-nslookup.bats} | 2 +- ...uckdns-ecdsa.bats => 8-staging-ecdsa.bats} | 4 +- test/9-test--all.bats | 34 ++++++++++++++ ...os7-duckdns => Dockerfile-centos7-staging} | 0 ...untu-duckdns => Dockerfile-ubuntu-staging} | 0 test/run-test.cmd | 10 ++--- test/run-test.sh | 6 +-- ...duckdns01.cfg => getssl-staging-dns01.cfg} | 0 test/test_helper.bash | 9 ++-- 13 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 test/10-mixed-case-staging.bats create mode 100644 test/10-mixed-case.bats rename test/{7-duckdns-dns01-dig.bats => 7-staging-dns01-dig.bats} (95%) rename test/{7-duckdns-dns01-nslookup.bats => 7-staging-dns01-nslookup.bats} (96%) rename test/{8-duckdns-ecdsa.bats => 8-staging-ecdsa.bats} (96%) create mode 100644 test/9-test--all.bats rename test/{Dockerfile-centos7-duckdns => Dockerfile-centos7-staging} (100%) rename test/{Dockerfile-ubuntu-duckdns => Dockerfile-ubuntu-staging} (100%) rename test/test-config/{getssl-duckdns01.cfg => getssl-staging-dns01.cfg} (100%) diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 1ff795d..ff0e121 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -31,14 +31,14 @@ jobs: run: docker-compose up -d --build - name: Run test suite on CentOS7 run: test/run-test.sh centos7 - test-centos7-duckdns: + test-centos7-staging: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - name: Run test suite on CentOS7 against Staging using DuckDNS - run: test/run-test.sh centos7-duckdns + run: test/run-test.sh centos7-staging test-debian: runs-on: ubuntu-latest steps: @@ -71,11 +71,11 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Ubuntu18 run: test/run-test.sh ubuntu18 - test-ubuntu-duckdns: + test-ubuntu-staging: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - name: Run test suite on Ubuntu against Staging using DuckDNS - run: test/run-test.sh ubuntu-duckdns + run: test/run-test.sh ubuntu-staging diff --git a/test/10-mixed-case-staging.bats b/test/10-mixed-case-staging.bats new file mode 100644 index 0000000..c1bac0d --- /dev/null +++ b/test/10-mixed-case-staging.bats @@ -0,0 +1,24 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +@test "Check can create certificate if domain is not lowercase using staging server and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + + CONFIG_FILE="getssl-staging-dns01.cfg" + GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) + + setup_environment + init_getssl + create_certificate + + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/10-mixed-case.bats b/test/10-mixed-case.bats new file mode 100644 index 0000000..2a4d6f3 --- /dev/null +++ b/test/10-mixed-case.bats @@ -0,0 +1,45 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + +@test "Check that HTTP-01 verification works if the domain is not lowercase" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + CONFIG_FILE="getssl-http01.cfg" + GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) + + setup_environment + init_getssl + create_certificate + + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + +@test "Check that DNS-01 verification works if the domain is not lowercase" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-dns01.cfg" + GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) + setup_environment + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/7-duckdns-dns01-dig.bats b/test/7-staging-dns01-dig.bats similarity index 95% rename from test/7-duckdns-dns01-dig.bats rename to test/7-staging-dns01-dig.bats index c6d8f56..8c0d7f1 100644 --- a/test/7-duckdns-dns01-dig.bats +++ b/test/7-staging-dns01-dig.bats @@ -10,7 +10,7 @@ load '/getssl/test/test_helper.bash' if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-duckdns01.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl diff --git a/test/7-duckdns-dns01-nslookup.bats b/test/7-staging-dns01-nslookup.bats similarity index 96% rename from test/7-duckdns-dns01-nslookup.bats rename to test/7-staging-dns01-nslookup.bats index 81c921e..027a210 100644 --- a/test/7-duckdns-dns01-nslookup.bats +++ b/test/7-staging-dns01-nslookup.bats @@ -24,7 +24,7 @@ teardown() { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-duckdns01.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl diff --git a/test/8-duckdns-ecdsa.bats b/test/8-staging-ecdsa.bats similarity index 96% rename from test/8-duckdns-ecdsa.bats rename to test/8-staging-ecdsa.bats index 2e10512..92c694a 100644 --- a/test/8-duckdns-ecdsa.bats +++ b/test/8-staging-ecdsa.bats @@ -11,7 +11,7 @@ load '/getssl/test/test_helper.bash' if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-duckdns01.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl @@ -41,7 +41,7 @@ load '/getssl/test/test_helper.bash' if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-duckdns01.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl diff --git a/test/9-test--all.bats b/test/9-test--all.bats new file mode 100644 index 0000000..94a2c28 --- /dev/null +++ b/test/9-test--all.bats @@ -0,0 +1,34 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + export PATH=$PATH:/getssl +} + + +@test "Create new certificate using --all" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + # Setup + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + + # Run test + run ${CODE_DIR}/getssl --all + + # Check success conditions + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/Dockerfile-centos7-duckdns b/test/Dockerfile-centos7-staging similarity index 100% rename from test/Dockerfile-centos7-duckdns rename to test/Dockerfile-centos7-staging diff --git a/test/Dockerfile-ubuntu-duckdns b/test/Dockerfile-ubuntu-staging similarity index 100% rename from test/Dockerfile-ubuntu-duckdns rename to test/Dockerfile-ubuntu-staging diff --git a/test/run-test.cmd b/test/run-test.cmd index d1cf263..43c4e40 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -7,8 +7,8 @@ IF %2.==. GOTO NoCmd set COMMAND=%2 %3 :CheckAlias -REM check if OS *contains* duckdns -IF NOT x%OS:duckdns=%==x%OS% GOTO duckdns +REM check if OS *contains* staging +IF NOT x%OS:staging=%==x%OS% GOTO staging set ALIAS=%OS%.getssl.test set STAGING= GOTO Run @@ -22,8 +22,8 @@ REM set COMMAND=/getssl/test/run-bats.sh set COMMAND=bats /getssl/test GOTO CheckAlias -:duckdns -set ALIAS=%OS:-duckdns=%-getssl.duckdns.org +:staging +set ALIAS=%OS:-staging=%-getssl.duckdns.org set STAGING=--env STAGING=true :Run @@ -33,7 +33,7 @@ docker build --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . @echo on docker run -it ^ --env GETSSL_HOST=%ALIAS% %STAGING% ^ - --env GETSSL_OS=%OS:-duckdns=% ^ + --env GETSSL_OS=%OS:-staging=% ^ -v %cd%:/getssl ^ --rm ^ --network %CurrDirName%_acmenet ^ diff --git a/test/run-test.sh b/test/run-test.sh index 97842a5..d99d5a3 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -14,8 +14,8 @@ else COMMAND="bats /getssl/test" fi -if [[ "$OS" == *"duckdns"* ]]; then - ALIAS="${OS%-duckdns}-getssl.duckdns.org" +if [[ "$OS" == *"staging"* ]]; then + ALIAS="${OS%-staging}-getssl.duckdns.org" STAGING="--env STAGING=true" else ALIAS="$OS.getssl.test" @@ -26,7 +26,7 @@ docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . # shellcheck disable=SC2086 docker run \ --env GETSSL_HOST=$ALIAS $STAGING \ - --env GETSSL_OS=${OS%-duckdns} \ + --env GETSSL_OS=${OS%-staging} \ -v "$(pwd)":/getssl \ --rm \ --network ${PWD##*/}_acmenet \ diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-staging-dns01.cfg similarity index 100% rename from test/test-config/getssl-duckdns01.cfg rename to test/test-config/getssl-staging-dns01.cfg diff --git a/test/test_helper.bash b/test/test_helper.bash index f311b18..f4c62af 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -21,7 +21,7 @@ cleanup_environment() { init_getssl() { # Run initialisation (create account key, etc) - run ${CODE_DIR}/getssl -c "$GETSSL_HOST" + run ${CODE_DIR}/getssl -c "$GETSSL_CMD_HOST" assert_success [ -d "$INSTALL_DIR/.getssl" ] } @@ -29,9 +29,9 @@ init_getssl() { create_certificate() { # Create certificate - cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" # shellcheck disable=SC2086 - run ${CODE_DIR}/getssl $1 "$GETSSL_HOST" + run ${CODE_DIR}/getssl $1 "$GETSSL_CMD_HOST" } # start nginx in background on alpine via supervisord @@ -68,6 +68,9 @@ fi export GETSSL_IP +GETSSL_CMD_HOST=$GETSSL_HOST +export GETSSL_CMD_HOST + if [ ! -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 CERT_FILE=/etc/ssl/certs/ca-certificates.crt From e50362501d3bee59fb092891cdd1a84efe6aedb8 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 8 Apr 2020 18:59:16 +0100 Subject: [PATCH 141/337] Update revision history and version number --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 45d63cd..a0c530c 100755 --- a/getssl +++ b/getssl @@ -218,10 +218,12 @@ # 2020-03-12 Fix bug with DNS validation and multiple domains (#524) # 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup) # 2020-03-23 Fix staging server URL in domain template (2.21) +# 2020-03-30 Fix error message find_dns_utils from over version of "command" +# 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.21" +VERSION="2.22" # defaults ACCOUNT_KEY_LENGTH=4096 From a73848c60eb3a675ff5f1b11c2f9d1b8db8050ad Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 8 Apr 2020 21:21:15 +0100 Subject: [PATCH 142/337] Check domain exists using all DNS utilities --- getssl | 63 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/getssl b/getssl index a0c530c..4c83b53 100755 --- a/getssl +++ b/getssl @@ -440,39 +440,42 @@ check_config() { # check the config files for all obvious errors info "${DOMAIN}: ACL location not specified for domain $d in $DOMAIN_DIR/getssl.cfg" config_errors=true fi - # check domain exists - if [[ "$DNS_CHECK_FUNC" == "drill" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c -i "${d}")" -ge 1 ]]; then - debug "found IP for ${d}" - else - info "${DOMAIN}: DNS lookup failed for ${d}" - config_errors=true + + # check domain exists using all DNS utilities + found_ip=false + if [[ -n "$HAS_DIG_OR_DRILL" ]]; then + debug "DNS lookup using $HAS_DIG_OR_DRILL ${d}" + if [[ "$($HAS_DIG_OR_DRILL -t SOA "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then + found_ip=true + elif [[ "$($HAS_DIG_OR_DRILL -t A "${d}"|grep -c -i "^${d}")" -ge 1 ]]; then + found_ip=true + elif [[ "$($HAS_DIG_OR_DRILL -t AAAA "${d}"|grep -c -i "^${d}")" -ge 1 ]]; then + found_ip=true fi - elif [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" -t SOA|grep -c -i "^${d}")" -ge 1 ]]; then - debug "found SOA IP for ${d}" - elif [[ "$($DNS_CHECK_FUNC "${d}" -t A|grep -c -i "^${d}")" -ge 1 ]]; then - debug "found A IP for ${d}" - else - info "${DOMAIN}: DNS lookup failed for ${d}" - config_errors=true + fi + + if [[ -n "$HAS_HOST" ]]; then + debug "DNS lookup using host ${d}" + if [[ "$(host "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then + found_ip=true fi - elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - if [[ "$($DNS_CHECK_FUNC "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then - debug "found IP for ${d}" - else - info "${DOMAIN}: DNS lookup failed for ${d}" - config_errors=true + fi + + if [[ -n "$HAS_NSLOOKUP" ]]; then + debug "DNS lookup using nslookup -query AAAA ${d}" + if [[ "$(nslookup -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then + debug "found IPv6 record for ${d}" + found_ip=true + elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then + debug "found IPv4 record for ${d}" fi - elif [[ "$(nslookup -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then - debug "found IPv6 record for ${d}" - elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then - debug "found IPv4 record for ${d}" - else + fi + + if [[ "$found_ip" == "false" ]]; then info "${DOMAIN}: DNS lookup failed for $d" config_errors=true fi - fi # end using http-01 challenge + fi # end using dns-01 challenge ((dn++)) done @@ -2345,12 +2348,12 @@ fi # from SERVER_TYPE set REMOTE_PORT and REMOTE_EXTRA set_server_type -# check config for typical errors. -check_config - # check what dns utils are installed find_dns_utils +# check config for typical errors. +check_config + if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" _FORCE_RENEW=1 From 17203b1ec1e9962d1fcbe88fa87a5efac73707a9 Mon Sep 17 00:00:00 2001 From: Juan Javier Baca Date: Thu, 16 Apr 2020 04:41:44 +0200 Subject: [PATCH 143/337] Add alternative working dirs Despite changing working dir from command line covers most usage cases, others defaults are also usefull like /etc/getssl, SCRIPTDIR/conf or SCRIPTDIR/.getssl. Last candidate (~/.getssl) is used if no config file was found in previous paths. --- getssl | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index a0c530c..a465376 100755 --- a/getssl +++ b/getssl @@ -220,6 +220,7 @@ # 2020-03-23 Fix staging server URL in domain template (2.21) # 2020-03-30 Fix error message find_dns_utils from over version of "command" # 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) +# 2020-04-16 Add alternative working dirs '/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -261,7 +262,7 @@ TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" VALIDATE_VIA_DNS="" -WORKING_DIR=~/.getssl +WORKING_DIR_CANDIDATES=('/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' '~/.getssl') _CHECK_ALL=0 _CREATE_CONFIG=0 _FORCE_RENEW=0 @@ -2179,6 +2180,7 @@ requires which requires openssl requires curl requires dig nslookup drill host DNS_CHECK_FUNC +requires dirname requires awk requires tr requires date @@ -2216,6 +2218,22 @@ if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then graceful_exit fi +# Test working directory candidates if unset. Last candidate defaults (~/getssl/) +if [[ -z "${WORKING_DIR}" ]] +then + SCRIPTDIR="$(cd "$(dirname "$0")"; pwd -P;)" + for WDCC in $(seq 0 $((${#WORKING_DIR_CANDIDATES[@]}-1)) ) + do + WORKING_DIR="$(eval echo "${WORKING_DIR_CANDIDATES[$WDCC]}")" + + debug "Testing working dir location '${WORKING_DIR}'" + if [[ -s "$WORKING_DIR/getssl.cfg" ]] + then + break + fi + done +fi + # if the "working directory" doesn't exist, then create it. if [[ ! -d "$WORKING_DIR" ]]; then debug "Making working directory - $WORKING_DIR" From 9dfff30b9d53e8271e1bc4908ad0704ac593ec5d Mon Sep 17 00:00:00 2001 From: Juan Javier Baca Date: Thu, 16 Apr 2020 04:44:16 +0200 Subject: [PATCH 144/337] Add -i|--install command line option While testing or setting up getssl the installation of certificates could fail. Option -i allows to copy and reload service quicker. --- getssl | 155 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/getssl b/getssl index a465376..8251911 100755 --- a/getssl +++ b/getssl @@ -221,6 +221,7 @@ # 2020-03-30 Fix error message find_dns_utils from over version of "command" # 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) # 2020-04-16 Add alternative working dirs '/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' +# 2020-04-16 Add -i|--install command line option # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -305,6 +306,79 @@ cert_archive() { # Archive certificate file by copying files to dated archive d purge_archive "$DOMAIN_DIR" } +cert_install() { # copy certs to the correct location (creating concatenated files as required) + umask 077 + + copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" + copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" + copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then + copy_file_to_location "ec domain certificate" \ + "${CERT_FILE%.*}.ec.crt" \ + "${DOMAIN_CERT_LOCATION}" \ + "ec" + fi + if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then + copy_file_to_location "ec private key" \ + "$DOMAIN_DIR/${DOMAIN}.ec.key" \ + "${DOMAIN_KEY_LOCATION}" \ + "ec" + fi + if [[ -n "$CA_CERT_LOCATION" ]]; then + copy_file_to_location "ec CA certificate" \ + "${CA_CERT%.*}.ec.crt" \ + "${CA_CERT_LOCATION%.*}.crt" \ + "ec" + fi + fi + + # if DOMAIN_CHAIN_LOCATION is not blank, then create and copy file. + if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then + if [[ "$(dirname "$DOMAIN_CHAIN_LOCATION")" == "." ]]; then + to_location="${DOMAIN_DIR}/${DOMAIN_CHAIN_LOCATION}" + else + to_location="${DOMAIN_CHAIN_LOCATION}" + fi + cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem" + copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" + copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}" "ec" + fi + fi + # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. + if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then + if [[ "$(dirname "$DOMAIN_KEY_CERT_LOCATION")" == "." ]]; then + to_location="${DOMAIN_DIR}/${DOMAIN_KEY_CERT_LOCATION}" + else + to_location="${DOMAIN_KEY_CERT_LOCATION}" + fi + cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" + copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" + copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}" "ec" + fi + fi + # if DOMAIN_PEM_LOCATION is not blank, then create and copy file. + if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then + if [[ "$(dirname "$DOMAIN_PEM_LOCATION")" == "." ]]; then + to_location="${DOMAIN_DIR}/${DOMAIN_PEM_LOCATION}" + else + to_location="${DOMAIN_PEM_LOCATION}" + fi + cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" + copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" + copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}" "ec" + fi + fi + # end of copying certs. + umask "$ORIG_UMASK" +} + check_challenge_completion() { # checks with the ACME server if our challenge is OK uri=$1 domain=$2 @@ -1410,6 +1484,7 @@ help_message() { # print out the help message -c, --create Create default config files -f, --force Force renewal of cert (overrides expiry checks) -h, --help Display this help message and exit + -i, --install Install certificates and reload service -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) -Q, --mute Like -q, but also mute notification about successful upgrade -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) @@ -2146,6 +2221,8 @@ while [[ -n ${1+defined} ]]; do _UPGRADE=1 ;; -U | --nocheck) _UPGRADE_CHECK=0 ;; + -i | --install) + _CERT_INSTALL=1 ;; -w) shift; WORKING_DIR="$1" ;; -*) @@ -2369,6 +2446,14 @@ check_config # check what dns utils are installed find_dns_utils +# if -i|--install install certs, reload and exit +if [ "0${_CERT_INSTALL}" -eq 1 ] +then + cert_install + reload_service + graceful_exit +fi + if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" _FORCE_RENEW=1 @@ -2647,76 +2732,8 @@ cert_archive debug "Certificates obtained and archived locally, will now copy to specified locations" # copy certs to the correct location (creating concatenated files as required) -umask 077 - -copy_file_to_location "domain certificate" "$CERT_FILE" "$DOMAIN_CERT_LOCATION" -copy_file_to_location "private key" "$DOMAIN_DIR/${DOMAIN}.key" "$DOMAIN_KEY_LOCATION" -copy_file_to_location "CA certificate" "$CA_CERT" "$CA_CERT_LOCATION" -if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then - copy_file_to_location "ec domain certificate" \ - "${CERT_FILE%.*}.ec.crt" \ - "${DOMAIN_CERT_LOCATION}" \ - "ec" - fi - if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then - copy_file_to_location "ec private key" \ - "$DOMAIN_DIR/${DOMAIN}.ec.key" \ - "${DOMAIN_KEY_LOCATION}" \ - "ec" - fi - if [[ -n "$CA_CERT_LOCATION" ]]; then - copy_file_to_location "ec CA certificate" \ - "${CA_CERT%.*}.ec.crt" \ - "${CA_CERT_LOCATION%.*}.crt" \ - "ec" - fi -fi +cert_install -# if DOMAIN_CHAIN_LOCATION is not blank, then create and copy file. -if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then - if [[ "$(dirname "$DOMAIN_CHAIN_LOCATION")" == "." ]]; then - to_location="${DOMAIN_DIR}/${DOMAIN_CHAIN_LOCATION}" - else - to_location="${DOMAIN_CHAIN_LOCATION}" - fi - cat "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}_chain.pem" - copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" - if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" - copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}" "ec" - fi -fi -# if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. -if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then - if [[ "$(dirname "$DOMAIN_KEY_CERT_LOCATION")" == "." ]]; then - to_location="${DOMAIN_DIR}/${DOMAIN_KEY_CERT_LOCATION}" - else - to_location="${DOMAIN_KEY_CERT_LOCATION}" - fi - cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" - copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" - if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" - copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}" "ec" - fi -fi -# if DOMAIN_PEM_LOCATION is not blank, then create and copy file. -if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then - if [[ "$(dirname "$DOMAIN_PEM_LOCATION")" == "." ]]; then - to_location="${DOMAIN_DIR}/${DOMAIN_PEM_LOCATION}" - else - to_location="${DOMAIN_PEM_LOCATION}" - fi - cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" "$CA_CERT" > "$TEMP_DIR/${DOMAIN}.pem" - copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" - if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" - copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}" "ec" - fi -fi -# end of copying certs. -umask "$ORIG_UMASK" # Run reload command to restart apache / nginx or whatever system reload_service From 17b13facdad9ed3cd74ee6da84dd3fb93501c009 Mon Sep 17 00:00:00 2001 From: Juan Javier Baca Date: Thu, 16 Apr 2020 04:45:08 +0200 Subject: [PATCH 145/337] Update revision history and version number --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 8251911..b278193 100755 --- a/getssl +++ b/getssl @@ -221,11 +221,11 @@ # 2020-03-30 Fix error message find_dns_utils from over version of "command" # 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) # 2020-04-16 Add alternative working dirs '/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' -# 2020-04-16 Add -i|--install command line option +# 2020-04-16 Add -i|--install command line option (2.23) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.22" +VERSION="2.23" # defaults ACCOUNT_KEY_LENGTH=4096 From 7e575fb030bf29a544c4650400667f2be0df384e Mon Sep 17 00:00:00 2001 From: Juan Javier Baca Moreno-Torres Date: Sat, 18 Apr 2020 01:46:02 +0200 Subject: [PATCH 146/337] Update shell-linter to 0.3.0 --- .github/workflows/shellcheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index d5adbf5..37b9cad 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -12,6 +12,6 @@ jobs: steps: - uses: actions/checkout@v1 - name: Lint check - uses: azohra/shell-linter@v0.2.0 + uses: azohra/shell-linter@v0.3.0 with: path: "getssl" From adcb9752065880efc03da23ea03efa4acb176304 Mon Sep 17 00:00:00 2001 From: Juan Javier Baca Moreno-Torres Date: Sat, 18 Apr 2020 02:10:09 +0200 Subject: [PATCH 147/337] Fix shellcheck warning and failures Clean code, avoid indirect variables and single quotes for delayed expansion. --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index b278193..bcaa37f 100755 --- a/getssl +++ b/getssl @@ -220,11 +220,12 @@ # 2020-03-23 Fix staging server URL in domain template (2.21) # 2020-03-30 Fix error message find_dns_utils from over version of "command" # 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) -# 2020-04-16 Add alternative working dirs '/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' +# 2020-04-16 Add alternative working dirs '/etc/getssl/' '${PROGDIR}/conf' '${PROGDIR}/.getssl' # 2020-04-16 Add -i|--install command line option (2.23) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} +PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" VERSION="2.23" # defaults @@ -263,7 +264,7 @@ TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" VALIDATE_VIA_DNS="" -WORKING_DIR_CANDIDATES=('/etc/getssl/' '${SCRIPTDIR}/conf' '${SCRIPTDIR}/.getssl' '~/.getssl') +WORKING_DIR_CANDIDATES=("/etc/getssl/" "${PROGDIR}/conf" "${PROGDIR}/.getssl" "${HOME}/.getssl") _CHECK_ALL=0 _CREATE_CONFIG=0 _FORCE_RENEW=0 @@ -2298,7 +2299,6 @@ fi # Test working directory candidates if unset. Last candidate defaults (~/getssl/) if [[ -z "${WORKING_DIR}" ]] then - SCRIPTDIR="$(cd "$(dirname "$0")"; pwd -P;)" for WDCC in $(seq 0 $((${#WORKING_DIR_CANDIDATES[@]}-1)) ) do WORKING_DIR="$(eval echo "${WORKING_DIR_CANDIDATES[$WDCC]}")" From 008a95dba778b2947c408444d93fb121a74621d3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 18 Apr 2020 14:36:33 +0100 Subject: [PATCH 148/337] Make ubuntu-staging depend on centos7-staging so they don't run at same time causing spurious errors --- .github/workflows/run-all-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index ff0e121..08fb41b 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -72,6 +72,7 @@ jobs: - name: Run test suite on Ubuntu18 run: test/run-test.sh ubuntu18 test-ubuntu-staging: + needs: test-centos7-staging runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 From 3341f674d422bdbc1ce7b5012278ce62b58596c3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 18 Apr 2020 14:36:59 +0100 Subject: [PATCH 149/337] Add tests for /etc/getssl and --install --- test/11-test--install.bats | 69 ++++++++++++++++++++++++ test/test-config/getssl-etc-template.cfg | 45 ++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 test/11-test--install.bats create mode 100644 test/test-config/getssl-etc-template.cfg diff --git a/test/11-test--install.bats b/test/11-test--install.bats new file mode 100644 index 0000000..0b5bbc7 --- /dev/null +++ b/test/11-test--install.bats @@ -0,0 +1,69 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + +@test "Check that config files in /etc/getssl works" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + + # Create /etc/getssl/$DOMAIN + rm -rf /etc/getssl + mkdir -p /etc/getssl/${GETSSL_CMD_HOST} + + # Copy the config file to /etc/getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "/etc/getssl/${GETSSL_CMD_HOST}/getssl.cfg" + cp "${CODE_DIR}/test/test-config/getssl-etc-template.cfg" "/etc/getssl/getssl.cfg" + + # Run getssl + run ${CODE_DIR}/getssl "$GETSSL_CMD_HOST" + + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + assert_line 'Verification completed, obtaining certificate.' + assert_line 'Requesting certificate' + refute [ -d '$HOME/.getssl' ] +} + + +@test "Check that --install doesn't call the ACME server" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + CONFIG_FILE="getssl-http01.cfg" + #setup_environment + + # Create /etc/getssl/$DOMAIN + #mkdir -p /etc/getssl/${GETSSL_CMD_HOST} + + # Copy the config file to /etc/getssl + #cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "/etc/getssl/${GETSSL_CMD_HOST}/getssl.cfg" + #cp "${CODE_DIR}/test/test-config/getssl-etc-template.cfg" "/etc/getssl/getssl.cfg" + + # Run getssl + run ${CODE_DIR}/getssl --install "$GETSSL_CMD_HOST" + + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_line 'Verification completed, obtaining certificate.' + refute_line 'Requesting certificate' + assert_line --partial 'copying domain certificate to' + assert_line --partial 'copying private key to' + assert_line --partial 'copying CA certificate to' +} diff --git a/test/test-config/getssl-etc-template.cfg b/test/test-config/getssl-etc-template.cfg new file mode 100644 index 0000000..6bfc8fd --- /dev/null +++ b/test/test-config/getssl-etc-template.cfg @@ -0,0 +1,45 @@ +# vim: filetype=sh +# +# This file is read first and is common to all domains +# +# Uncomment and modify any variables you need +# see https://github.com/srvrco/getssl/wiki/Config-variables for details +# +# The staging server is best for testing (hence set as default) +CA="https://acme-staging-v02.api.letsencrypt.org" +# This server issues full certificates, however has rate limits +#CA="https://acme-v02.api.letsencrypt.org" + +# The agreement that must be signed with the CA, if not defined the default agreement will be used +#AGREEMENT="" + +# Set an email address associated with your account - generally set at account level rather than domain. +#ACCOUNT_EMAIL="me@example.com" +ACCOUNT_KEY_LENGTH=4096 +ACCOUNT_KEY="/etc/getssl/account.key" + +# Account key and private key types - can be rsa, prime256v1, secp384r1 or secp521r1 +#ACCOUNT_KEY_TYPE="rsa" +PRIVATE_KEY_ALG="rsa" +#REUSE_PRIVATE_KEY="true" + +# The command needed to reload apache / nginx or whatever you use +#RELOAD_CMD="" + +# The time period within which you want to allow renewal of a certificate +# this prevents hitting some of the rate limits. +# Creating a file called FORCE_RENEWAL in the domain directory allows one-off overrides +# of this setting +RENEW_ALLOW="30" + +# 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" + +# Use the following 3 variables if you want to validate via DNS +#VALIDATE_VIA_DNS="true" +#DNS_ADD_COMMAND= +#DNS_DEL_COMMAND= From 857ad87b4f21ca7a8f2c78fe727f620c9ad8c7f1 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 18 Apr 2020 14:37:33 +0100 Subject: [PATCH 150/337] Change bats-assert and bats-support to use bats-core repo --- test/Dockerfile-alpine | 4 ++-- test/Dockerfile-centos6 | 4 ++-- test/Dockerfile-centos7 | 4 ++-- test/Dockerfile-centos7-staging | 4 ++-- test/Dockerfile-debian | 4 ++-- test/Dockerfile-ubuntu | 4 ++-- test/Dockerfile-ubuntu-staging | 4 ++-- test/Dockerfile-ubuntu16 | 4 ++-- test/Dockerfile-ubuntu18 | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index 0c166cb..caad22a 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -13,8 +13,8 @@ RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Use supervisord to run nginx in the background diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 9149dad..61c8b6b 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -14,8 +14,8 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index 8a34bc5..02fbcb7 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -15,6 +15,6 @@ COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 839ff76..899bf9b 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -18,8 +18,8 @@ COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index 95ebbac..b5da5dd 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -12,8 +12,8 @@ RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 290100d..720f0b0 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -15,8 +15,8 @@ RUN touch /root/.rnd # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 0bdc1f8..84022ca 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -17,8 +17,8 @@ RUN touch /root/.rnd # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 958bb6f..030d03a 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -17,8 +17,8 @@ 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 /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index ebe7607..1d68cd3 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -17,8 +17,8 @@ RUN touch /root/.rnd # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/jasonkarns/bats-support /bats-support -RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 From ac5ef3301107a6e42ebfc105dac405d8bfaf7a52 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 18 Apr 2020 15:12:40 +0100 Subject: [PATCH 151/337] Cleanup after test succeeds --- test/11-test--install.bats | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/11-test--install.bats b/test/11-test--install.bats index 0b5bbc7..6949d25 100644 --- a/test/11-test--install.bats +++ b/test/11-test--install.bats @@ -18,8 +18,10 @@ setup() { CONFIG_FILE="getssl-http01.cfg" setup_environment + # Fail if not running in docker and /etc/getssl already exists + refute [ -d /etc/getssl ] + # Create /etc/getssl/$DOMAIN - rm -rf /etc/getssl mkdir -p /etc/getssl/${GETSSL_CMD_HOST} # Copy the config file to /etc/getssl @@ -40,19 +42,12 @@ setup() { @test "Check that --install doesn't call the ACME server" { + # NOTE that this test depends on the previous test! if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi CONFIG_FILE="getssl-http01.cfg" - #setup_environment - - # Create /etc/getssl/$DOMAIN - #mkdir -p /etc/getssl/${GETSSL_CMD_HOST} - - # Copy the config file to /etc/getssl - #cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "/etc/getssl/${GETSSL_CMD_HOST}/getssl.cfg" - #cp "${CODE_DIR}/test/test-config/getssl-etc-template.cfg" "/etc/getssl/getssl.cfg" # Run getssl run ${CODE_DIR}/getssl --install "$GETSSL_CMD_HOST" @@ -66,4 +61,7 @@ setup() { assert_line --partial 'copying domain certificate to' assert_line --partial 'copying private key to' assert_line --partial 'copying CA certificate to' + + # Cleanup previous test + rm -rf /etc/getssl } From 43b82e4fc39f024d05a30abe36df62a9c10a430d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 18 Apr 2020 15:31:48 +0100 Subject: [PATCH 152/337] Fix cut&paste error --- test/Dockerfile-ubuntu-staging | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 84022ca..552f096 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -17,7 +17,7 @@ RUN touch /root/.rnd # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local From d8bf2fa14936912071b6069705daf0d02066aabc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 19 Apr 2020 14:13:29 +0100 Subject: [PATCH 153/337] Remove dependency on seq, ensure clean_up doesn't try to delete /tmp --- getssl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index bcaa37f..0fd4431 100755 --- a/getssl +++ b/getssl @@ -222,11 +222,12 @@ # 2020-03-30 Fix problems if domain name isn't in lowercase (2.22) # 2020-04-16 Add alternative working dirs '/etc/getssl/' '${PROGDIR}/conf' '${PROGDIR}/.getssl' # 2020-04-16 Add -i|--install command line option (2.23) +# 2020-04-19 Remove dependency on seq, ensure clean_up doesn't try to delete /tmp (2.24) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.23" +VERSION="2.24" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -633,7 +634,11 @@ clean_up() { # Perform pre-exit housekeeping shopt -u nullglob fi if [[ -n "$DOMAIN_DIR" ]]; then - rm -rf "${TEMP_DIR:?}" + if [ "${TEMP_DIR}" -ef "/tmp" ]; then + info "Not going to delete TEMP_DIR ${TEMP_DIR} as it appears to be /tmp" + else + rm -rf "${TEMP_DIR:?}" + fi fi if [[ -n "$TEMP_UPGRADE_FILE" ]] && [[ -f "$TEMP_UPGRADE_FILE" ]]; then rm -f "$TEMP_UPGRADE_FILE" @@ -2299,10 +2304,8 @@ fi # Test working directory candidates if unset. Last candidate defaults (~/getssl/) if [[ -z "${WORKING_DIR}" ]] then - for WDCC in $(seq 0 $((${#WORKING_DIR_CANDIDATES[@]}-1)) ) + for WORKING_DIR in "${WORKING_DIR_CANDIDATES[@]}" do - WORKING_DIR="$(eval echo "${WORKING_DIR_CANDIDATES[$WDCC]}")" - debug "Testing working dir location '${WORKING_DIR}'" if [[ -s "$WORKING_DIR/getssl.cfg" ]] then From 462573c8ba17d82730ac69de9b201b377eb7f96d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 19 Apr 2020 14:13:55 +0100 Subject: [PATCH 154/337] Test if DOMAIN_STORAGE is "/" clean_up doesn't delete /tmp --- test/11-test-no-domain-storage.bats | 19 ++++++++++++ .../getssl-http01-no-domain-storage.cfg | 31 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/11-test-no-domain-storage.bats create mode 100644 test/test-config/getssl-http01-no-domain-storage.cfg diff --git a/test/11-test-no-domain-storage.bats b/test/11-test-no-domain-storage.bats new file mode 100644 index 0000000..cefac3f --- /dev/null +++ b/test/11-test-no-domain-storage.bats @@ -0,0 +1,19 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +@test "Check that if domain storage isn't set getssl doesn't try to delete /tmp" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-no-domain-storage.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -a + assert_success + assert_line 'Not going to delete TEMP_DIR ///tmp as it appears to be /tmp' +} diff --git a/test/test-config/getssl-http01-no-domain-storage.cfg b/test/test-config/getssl-http01-no-domain-storage.cfg new file mode 100644 index 0000000..efa5318 --- /dev/null +++ b/test/test-config/getssl-http01-no-domain-storage.cfg @@ -0,0 +1,31 @@ +# 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 +# +CA="https://pebble:14000/dir" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" + +DOMAIN_STORAGE="/" From f7c196602d2067e5da7171b6f9be2bef01456f39 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 20 Apr 2020 21:26:04 +0100 Subject: [PATCH 155/337] Update revision history and version --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 2c0c794..35cfbe2 100755 --- a/getssl +++ b/getssl @@ -223,11 +223,12 @@ # 2020-04-16 Add alternative working dirs '/etc/getssl/' '${PROGDIR}/conf' '${PROGDIR}/.getssl' # 2020-04-16 Add -i|--install command line option (2.23) # 2020-04-19 Remove dependency on seq, ensure clean_up doesn't try to delete /tmp (2.24) +# 2020-04-20 Check for domain using all DNS utilities (2.25) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.24" +VERSION="2.25" # defaults ACCOUNT_KEY_LENGTH=4096 From a0c6ddf0797334e302405a0ef55deaa3a82ac64a Mon Sep 17 00:00:00 2001 From: Robert Wolf Date: Wed, 22 Apr 2020 09:12:38 +0200 Subject: [PATCH 156/337] fix HAS_HOST and HAS_NSLOOKUP condition --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 35cfbe2..a6fc1dc 100755 --- a/getssl +++ b/getssl @@ -532,14 +532,14 @@ check_config() { # check the config files for all obvious errors fi fi - if [[ -n "$HAS_HOST" ]]; then + if [[ "$HAS_HOST" == true ]]; then debug "DNS lookup using host ${d}" if [[ "$(host "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then found_ip=true fi fi - if [[ -n "$HAS_NSLOOKUP" ]]; then + if [[ "$HAS_NSLOOKUP" == true ]]; then debug "DNS lookup using nslookup -query AAAA ${d}" if [[ "$(nslookup -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then debug "found IPv6 record for ${d}" From 23d4d6ae65574a83e9cc9369260c80d4c556045f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 22 Apr 2020 17:55:25 +0100 Subject: [PATCH 157/337] Move test for failed,warning,error to test_helper, add test for "not found" --- test/1-simple-http01.bats | 8 +--- test/10-mixed-case-staging.bats | 4 +- test/10-mixed-case.bats | 8 +--- test/11-test--install.bats | 8 +--- test/11-test-no-domain-storage.bats | 1 + test/2-simple-dns01-dig.bats | 8 +--- test/2-simple-dns01-nslookup.bats | 5 +-- test/3-dual-rsa-ecdsa.bats | 4 ++ test/4-more-than-10-hosts.bats | 8 +--- test/5-secp384-http01.bats | 4 ++ test/6-dual-rsa-ecdsa-copy-2-locations.bats | 1 + test/7-staging-dns01-dig.bats | 8 +--- test/7-staging-dns01-nslookup.bats | 8 +--- test/8-staging-ecdsa.bats | 16 ++------ test/9-multiple-domains-dns01.bats | 12 ++---- test/9-test--all.bats | 4 +- test/test_helper.bash | 41 +++++++++++++-------- 17 files changed, 60 insertions(+), 88 deletions(-) diff --git a/test/1-simple-http01.bats b/test/1-simple-http01.bats index fd96a8a..6b37f86 100644 --- a/test/1-simple-http01.bats +++ b/test/1-simple-http01.bats @@ -20,9 +20,7 @@ setup() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } @@ -32,8 +30,6 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors cleanup_environment } diff --git a/test/10-mixed-case-staging.bats b/test/10-mixed-case-staging.bats index c1bac0d..ea622f7 100644 --- a/test/10-mixed-case-staging.bats +++ b/test/10-mixed-case-staging.bats @@ -18,7 +18,5 @@ load '/getssl/test/test_helper.bash' create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } diff --git a/test/10-mixed-case.bats b/test/10-mixed-case.bats index 2a4d6f3..b1d8f07 100644 --- a/test/10-mixed-case.bats +++ b/test/10-mixed-case.bats @@ -23,9 +23,7 @@ setup() { create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } @test "Check that DNS-01 verification works if the domain is not lowercase" { @@ -39,7 +37,5 @@ setup() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } diff --git a/test/11-test--install.bats b/test/11-test--install.bats index 6949d25..e034326 100644 --- a/test/11-test--install.bats +++ b/test/11-test--install.bats @@ -32,9 +32,7 @@ setup() { run ${CODE_DIR}/getssl "$GETSSL_CMD_HOST" assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors assert_line 'Verification completed, obtaining certificate.' assert_line 'Requesting certificate' refute [ -d '$HOME/.getssl' ] @@ -53,9 +51,7 @@ setup() { run ${CODE_DIR}/getssl --install "$GETSSL_CMD_HOST" assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors refute_line 'Verification completed, obtaining certificate.' refute_line 'Requesting certificate' assert_line --partial 'copying domain certificate to' diff --git a/test/11-test-no-domain-storage.bats b/test/11-test-no-domain-storage.bats index cefac3f..3be0be5 100644 --- a/test/11-test-no-domain-storage.bats +++ b/test/11-test-no-domain-storage.bats @@ -15,5 +15,6 @@ load '/getssl/test/test_helper.bash' cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" run ${CODE_DIR}/getssl -a assert_success + check_output_for_errors assert_line 'Not going to delete TEMP_DIR ///tmp as it appears to be /tmp' } diff --git a/test/2-simple-dns01-dig.bats b/test/2-simple-dns01-dig.bats index cbac598..6803f15 100644 --- a/test/2-simple-dns01-dig.bats +++ b/test/2-simple-dns01-dig.bats @@ -22,9 +22,7 @@ setup() { create_certificate -d assert_success assert_output --partial "dig" - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors "debug" } @@ -35,8 +33,6 @@ setup() { run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success assert_output --partial "dig" - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors "debug" cleanup_environment } diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index 482be2a..7e675a8 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -32,8 +32,5 @@ teardown() { create_certificate -d assert_success assert_output --partial "nslookup" - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce - # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' + check_output_for_errors "debug" } diff --git a/test/3-dual-rsa-ecdsa.bats b/test/3-dual-rsa-ecdsa.bats index 7820a96..d5c58f7 100644 --- a/test/3-dual-rsa-ecdsa.bats +++ b/test/3-dual-rsa-ecdsa.bats @@ -20,6 +20,7 @@ setup() { init_getssl create_certificate assert_success + check_output_for_errors } @@ -29,6 +30,7 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + check_output_for_errors } @test "Create dual certificates using DNS-01 verification" { @@ -40,6 +42,7 @@ setup() { init_getssl create_certificate assert_success + check_output_for_errors } @@ -49,5 +52,6 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + check_output_for_errors cleanup_environment } diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index bd93adc..f4eb95c 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -26,9 +26,7 @@ setup() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } @@ -38,9 +36,7 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors # Remove all the dns aliases cleanup_environment for prefix in a b c d e f g h i j k; do diff --git a/test/5-secp384-http01.bats b/test/5-secp384-http01.bats index 29da2da..3d05159 100644 --- a/test/5-secp384-http01.bats +++ b/test/5-secp384-http01.bats @@ -20,6 +20,7 @@ setup() { init_getssl create_certificate assert_success + check_output_for_errors } @@ -29,6 +30,7 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + check_output_for_errors } @@ -41,6 +43,7 @@ setup() { init_getssl create_certificate assert_success + check_output_for_errors } @@ -50,4 +53,5 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success + check_output_for_errors } diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index 73363ec..394e8d6 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -32,6 +32,7 @@ teardown() { init_getssl create_certificate assert_success + check_output_for_errors # Check that the RSA chain and key have been copied to both locations assert [ -e "/etc/nginx/pki/domain-chain.crt" ] diff --git a/test/7-staging-dns01-dig.bats b/test/7-staging-dns01-dig.bats index 8c0d7f1..7e21124 100644 --- a/test/7-staging-dns01-dig.bats +++ b/test/7-staging-dns01-dig.bats @@ -16,9 +16,7 @@ load '/getssl/test/test_helper.bash' init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } @test "Force renewal of certificate using staging server, dig and DuckDNS" { @@ -27,8 +25,6 @@ load '/getssl/test/test_helper.bash' fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors cleanup_environment } diff --git a/test/7-staging-dns01-nslookup.bats b/test/7-staging-dns01-nslookup.bats index 027a210..bd8d9da 100644 --- a/test/7-staging-dns01-nslookup.bats +++ b/test/7-staging-dns01-nslookup.bats @@ -30,9 +30,7 @@ teardown() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + check_output_for_errors "debug" } @@ -42,8 +40,6 @@ teardown() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + check_output_for_errors "debug" cleanup_environment } diff --git a/test/8-staging-ecdsa.bats b/test/8-staging-ecdsa.bats index 92c694a..127e989 100644 --- a/test/8-staging-ecdsa.bats +++ b/test/8-staging-ecdsa.bats @@ -18,9 +18,7 @@ load '/getssl/test/test_helper.bash' sed -e 's/rsa/prime256v1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" run ${CODE_DIR}/getssl -d "$GETSSL_HOST" assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + check_output_for_errors "debug" } @@ -30,9 +28,7 @@ load '/getssl/test/test_helper.bash' fi run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + check_output_for_errors "debug" cleanup_environment } @@ -48,9 +44,7 @@ load '/getssl/test/test_helper.bash' sed -e 's/rsa/secp384r1/g' < "${CODE_DIR}/test/test-config/${CONFIG_FILE}" > "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" run ${CODE_DIR}/getssl -d "$GETSSL_HOST" assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' + check_output_for_errors "debug" } @@ -60,9 +54,7 @@ load '/getssl/test/test_helper.bash' fi run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' + check_output_for_errors "debug" cleanup_environment } diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats index 2a9344f..c1de91c 100644 --- a/test/9-multiple-domains-dns01.bats +++ b/test/9-multiple-domains-dns01.bats @@ -25,9 +25,7 @@ setup() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } @@ -38,9 +36,7 @@ setup() { fi run ${CODE_DIR}/getssl -f $GETSSL_HOST assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors # Remove all the dns aliases cleanup_environment curl --silent -X POST -d '{"host":"getssl.tst"}' http://10.30.50.3:8055/clear-a @@ -60,7 +56,5 @@ setup() { init_getssl create_certificate assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } diff --git a/test/9-test--all.bats b/test/9-test--all.bats index 94a2c28..cb043a5 100644 --- a/test/9-test--all.bats +++ b/test/9-test--all.bats @@ -28,7 +28,5 @@ setup() { # Check success conditions assert_success - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + check_output_for_errors } diff --git a/test/test_helper.bash b/test/test_helper.bash index f4c62af..4af19dd 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -1,23 +1,31 @@ INSTALL_DIR=/root CODE_DIR=/getssl - -setup_environment() { - # One-off test setup - if [[ -d ${INSTALL_DIR}/.getssl ]]; then - rm -r ${INSTALL_DIR}/.getssl +check_output_for_errors() { + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + # less strict tests if running with debug output + if [ -n "$1" ]; then + # don't fail for :error:badNonce + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' + # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' + else + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' fi - - curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a - cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" - /getssl/test/restart-nginx + refute_output --partial 'not found' } - cleanup_environment() { curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'"}' http://10.30.50.3:8055/clear-a } +create_certificate() { + # Create certificate + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + # shellcheck disable=SC2086 + run ${CODE_DIR}/getssl $1 "$GETSSL_CMD_HOST" +} init_getssl() { # Run initialisation (create account key, etc) @@ -26,12 +34,15 @@ init_getssl() { [ -d "$INSTALL_DIR/.getssl" ] } +setup_environment() { + # One-off test setup + if [[ -d ${INSTALL_DIR}/.getssl ]]; then + rm -r ${INSTALL_DIR}/.getssl + fi -create_certificate() { - # Create certificate - cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" - # shellcheck disable=SC2086 - run ${CODE_DIR}/getssl $1 "$GETSSL_CMD_HOST" + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a + cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" + /getssl/test/restart-nginx } # start nginx in background on alpine via supervisord From 065356bcc8670df944d79855046b13d8532caaa9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 22 Apr 2020 20:25:05 +0100 Subject: [PATCH 158/337] Tweak the 'not found' check --- test/test_helper.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.bash b/test/test_helper.bash index 4af19dd..0aafa93 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -13,7 +13,7 @@ check_output_for_errors() { refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' fi - refute_output --partial 'not found' + refute_line --partial 'command not found' } cleanup_environment() { From 135fcece9253466f297bda627ace79042d8464d9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 22 Apr 2020 20:29:31 +0100 Subject: [PATCH 159/337] Fix domain case conversion for different locales --- getssl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/getssl b/getssl index a6fc1dc..f877ed5 100755 --- a/getssl +++ b/getssl @@ -224,11 +224,13 @@ # 2020-04-16 Add -i|--install command line option (2.23) # 2020-04-19 Remove dependency on seq, ensure clean_up doesn't try to delete /tmp (2.24) # 2020-04-20 Check for domain using all DNS utilities (2.25) +# 2020-04-22 Fix HAS_HOST and HAS_NSLOOKUP checks - wolfaba +# 2020-04-22 Fix domain case conversion for different locales (2.26) - glynge # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.25" +VERSION="2.26" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -532,14 +534,14 @@ check_config() { # check the config files for all obvious errors fi fi - if [[ "$HAS_HOST" == true ]]; then + if [[ "$HAS_HOST" == "true" ]]; then debug "DNS lookup using host ${d}" if [[ "$(host "${d}" |grep -c -i "^${d}")" -ge 1 ]]; then found_ip=true fi fi - if [[ "$HAS_NSLOOKUP" == true ]]; then + if [[ "$HAS_NSLOOKUP" == "true" ]]; then debug "DNS lookup using nslookup -query AAAA ${d}" if [[ "$(nslookup -query=AAAA "${d}"|grep -c -i "^${d}.*has AAAA address")" -ge 1 ]]; then debug "found IPv6 record for ${d}" @@ -863,8 +865,7 @@ create_order() { dn=0 for d in $alldomains; do # Convert domain to lowercase as response from server will be in lowercase - # shellcheck disable=SC2018,SC2019 - d=$(echo "$d" | tr A-Z a-z) + d=$(echo "$d" | tr "[:upper:]" "[:lower:]") if [ "$d" == "$authdomain" ]; then debug "Saving authorization response for $authdomain for domain alldomains[$dn]" AuthLinkResponse[$dn]=$response @@ -1260,7 +1261,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi fi - if [[ "$HAS_HOST" == true ]]; then + if [[ "$HAS_HOST" == "true" ]]; then gad_d="$orig_gad_d" debug Using "host -t NS" to find primary name server for "$gad_d" if [[ -z "$gad_s" ]]; then @@ -1279,7 +1280,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi fi - if [[ "$HAS_NSLOOKUP" == true ]]; then + if [[ "$HAS_NSLOOKUP" == "true" ]]; then gad_d="$orig_gad_d" debug Using "nslookup -debug -type=soa -type=ns $gad_d $gad_s" to find primary name server res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) From 472ea2312fdbe6766c8b7953d42a7c0ceb70a979 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 25 Apr 2020 12:37:24 +0100 Subject: [PATCH 160/337] Set noninteractive so tests don't hang --- test/Dockerfile-ubuntu | 3 +++ test/Dockerfile-ubuntu-staging | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 720f0b0..66d7a35 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -2,6 +2,9 @@ FROM ubuntu:latest # Note this image uses mawk1.3 +# Set noninteractive otherwise tzdata hangs +ENV DEBIAN_FRONTEND noninteractive + # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils wget nginx-light diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 552f096..58762d0 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -2,8 +2,13 @@ FROM ubuntu:latest # Note this image uses mawk1.3 +# Set noninteractive otherwise tzdata hangs +ENV DEBIAN_FRONTEND noninteractive + +# Ensure tests in this image use the staging server ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb + # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils wget nginx-light From 62e7cfce0fc6b28ad62675d0b916832c41b0a866 Mon Sep 17 00:00:00 2001 From: rhinoduck Date: Sun, 26 Apr 2020 09:57:41 +0000 Subject: [PATCH 161/337] Fix changelog formatting --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index f877ed5..54c95b1 100755 --- a/getssl +++ b/getssl @@ -225,7 +225,7 @@ # 2020-04-19 Remove dependency on seq, ensure clean_up doesn't try to delete /tmp (2.24) # 2020-04-20 Check for domain using all DNS utilities (2.25) # 2020-04-22 Fix HAS_HOST and HAS_NSLOOKUP checks - wolfaba -# 2020-04-22 Fix domain case conversion for different locales (2.26) - glynge +# 2020-04-22 Fix domain case conversion for different locales - glynge (2.26) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From 7ec1e8648c2561263b2781cc8902cc0b67866f05 Mon Sep 17 00:00:00 2001 From: Harald Heigl Date: Sun, 26 Apr 2020 15:01:14 +0200 Subject: [PATCH 162/337] Fixed ipv4 confirmation with nslookup --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 54c95b1..86943ee 100755 --- a/getssl +++ b/getssl @@ -548,6 +548,7 @@ check_config() { # check the config files for all obvious errors found_ip=true elif [[ "$(nslookup "${d}"| grep -c ^Name)" -ge 1 ]]; then debug "found IPv4 record for ${d}" + found_ip=true fi fi From 61fe4647ad2379b2eef6befc1fb4172253e2144c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 26 Apr 2020 20:21:45 +0100 Subject: [PATCH 163/337] Add tests for #553 --- test/1-simple-http01-dig.bats | 40 ++++++++++++++++++++++++++++++ test/1-simple-http01-nslookup.bats | 40 ++++++++++++++++++++++++++++++ test/1-simple-http01.bats | 4 +-- test/2-simple-dns01-dig.bats | 17 ++++++++++++- test/2-simple-dns01-nslookup.bats | 6 +++++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 test/1-simple-http01-dig.bats create mode 100644 test/1-simple-http01-nslookup.bats diff --git a/test/1-simple-http01-dig.bats b/test/1-simple-http01-dig.bats new file mode 100644 index 0000000..be01f27 --- /dev/null +++ b/test/1-simple-http01-dig.bats @@ -0,0 +1,40 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/host ]; then + mv /usr/bin/host /usr/bin/host.getssl.bak + fi + if [ -f /usr/bin/nslookup ]; then + mv /usr/bin/nslookup /usr/bin/nslookup.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi + if [ -f /usr/bin/nslookup.getssl.bak ]; then + mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup + fi +} + + +@test "Create new certificate using HTTP-01 verification (dig)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} diff --git a/test/1-simple-http01-nslookup.bats b/test/1-simple-http01-nslookup.bats new file mode 100644 index 0000000..78d175a --- /dev/null +++ b/test/1-simple-http01-nslookup.bats @@ -0,0 +1,40 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/dig ]; then + mv /usr/bin/dig /usr/bin/dig.getssl.bak + fi + if [ -f /usr/bin/host ]; then + mv /usr/bin/host /usr/bin/host.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi +} + + +@test "Create new certificate using HTTP-01 verification (nslookup)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} diff --git a/test/1-simple-http01.bats b/test/1-simple-http01.bats index 6b37f86..a57010b 100644 --- a/test/1-simple-http01.bats +++ b/test/1-simple-http01.bats @@ -11,7 +11,7 @@ setup() { } -@test "Create new certificate using HTTP-01 verification" { +@test "Create new certificate using HTTP-01 verification (any dns tool)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi @@ -24,7 +24,7 @@ setup() { } -@test "Force renewal of certificate using HTTP-01" { +@test "Force renewal of certificate using HTTP-01 (any dns tool)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi diff --git a/test/2-simple-dns01-dig.bats b/test/2-simple-dns01-dig.bats index 6803f15..0a54684 100644 --- a/test/2-simple-dns01-dig.bats +++ b/test/2-simple-dns01-dig.bats @@ -5,9 +5,24 @@ load '/bats-assert/load.bash' load '/getssl/test/test_helper.bash' -# This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/host ]; then + mv /usr/bin/host /usr/bin/host.getssl.bak + fi + if [ -f /usr/bin/nslookup ]; then + mv /usr/bin/nslookup /usr/bin/nslookup.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi + if [ -f /usr/bin/nslookup.getssl.bak ]; then + mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup + fi } diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index 7e675a8..dc6f2f5 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -11,6 +11,9 @@ setup() { if [ -f /usr/bin/dig ]; then mv /usr/bin/dig /usr/bin/dig.getssl.bak fi + if [ -f /usr/bin/host ]; then + mv /usr/bin/host /usr/bin/host.getssl.bak + fi } @@ -18,6 +21,9 @@ teardown() { if [ -f /usr/bin/dig.getssl.bak ]; then mv /usr/bin/dig.getssl.bak /usr/bin/dig fi + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi } From 39ccc6bb0e9790a0264cd181644ed59da4ae3c42 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 29 Apr 2020 19:53:37 +0100 Subject: [PATCH 164/337] Fix ftp/sftp problems if challenge starts with a dash --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 86943ee..109d96d 100755 --- a/getssl +++ b/getssl @@ -700,7 +700,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. user $ftpuser $ftppass cd $ftpdirn lcd $fromdir - put $fromfile + put ./$fromfile _EOF elif [[ "${to:0:5}" == "sftp:" ]] ; then debug "using sftp to copy the file from $from" @@ -718,7 +718,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. sshpass -p "$ftppass" sftp $SFTP_OPTS "$ftpuser@$ftphost" <<- _EOF cd $ftpdirn lcd $fromdir - put $fromfile + put ./$fromfile _EOF elif [[ "${to:0:5}" == "davs:" ]] ; then debug "using davs to copy the file from $from" From b5a9a4280ee6d758668877bfc576ac085a46c8af Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 29 Apr 2020 20:56:40 +0100 Subject: [PATCH 165/337] Update revision history --- getssl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/getssl b/getssl index 109d96d..5c044d4 100755 --- a/getssl +++ b/getssl @@ -226,6 +226,8 @@ # 2020-04-20 Check for domain using all DNS utilities (2.25) # 2020-04-22 Fix HAS_HOST and HAS_NSLOOKUP checks - wolfaba # 2020-04-22 Fix domain case conversion for different locales - glynge (2.26) +# 2020-04-26 Fixed ipv4 confirmation with nslookup - Cyber1000 +# 2020-04-29 Fix ftp/sftp problems if challenge starts with a dash # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From 50a9865e716ec433f878836d6fca30c80cbae0c8 Mon Sep 17 00:00:00 2001 From: Scott Gustafson Date: Sat, 2 May 2020 23:39:50 -0600 Subject: [PATCH 166/337] Fix route53 script for python 3. --- dns_scripts/dns_route53.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns_scripts/dns_route53.py b/dns_scripts/dns_route53.py index a972dfa..f2011bf 100755 --- a/dns_scripts/dns_route53.py +++ b/dns_scripts/dns_route53.py @@ -31,7 +31,7 @@ for zone in response['HostedZones']: if not zone['Config']['PrivateZone']: zone_list[zone['Name']] = zone['Id'] -for key in sorted(zone_list.iterkeys(), key=len, reverse=True): +for key in sorted(zone_list.keys(), key=len, reverse=True): if ".{z}".format(z=key) in ".{z}.".format(z=fqdn): zone_id = zone_list[key] From 919aed2cdb26b148cf1daeeb7d0a4871b4cb1a09 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 6 May 2020 16:04:11 +0100 Subject: [PATCH 167/337] Fix missing fullchain.ec.crt when creating dual certificates --- getssl | 18 ++++++++++-------- test/3-dual-rsa-ecdsa.bats | 8 ++++++++ test/test_helper.bash | 7 +++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index 5c044d4..a92f09b 100755 --- a/getssl +++ b/getssl @@ -1342,6 +1342,7 @@ get_certificate() { # get certificate for csr, if all domains validated. gc_csr=$1 # the csr file gc_certfile=$2 # The filename for the certificate gc_cafile=$3 # The filename for the CA certificate + gc_fullchain=$4 # The filename for the fullchain der=$(openssl req -in "$gc_csr" -outform DER | urlbase64) if [[ $API -eq 1 ]]; then @@ -1388,9 +1389,9 @@ get_certificate() { # get certificate for csr, if all domains validated. done info "Requesting certificate" CertData=$(json_get "$response" "certificate") - send_signed_request "$CertData" "" "" "$FULL_CHAIN" - info "Full certificate saved in $FULL_CHAIN" - awk -v CERT_FILE="$gc_certfile" -v CA_CERT="$gc_cafile" 'BEGIN {outfile=CERT_FILE} split_after==1 {outfile=CA_CERT;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > outfile}' "$FULL_CHAIN" + send_signed_request "$CertData" "" "" "$gc_fullchain" + info "Full certificate saved in $gc_fullchain" + awk -v CERT_FILE="$gc_certfile" -v CA_CERT="$gc_cafile" 'BEGIN {outfile=CERT_FILE} split_after==1 {outfile=CA_CERT;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > outfile}' "$gc_fullchain" info "Certificate saved in $gc_certfile" fi } @@ -1892,7 +1893,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p # get nonce from ACME server if [[ $API -eq 1 ]]; then nonceurl="$CA/directory" - nonce=$($CURL -I $nonceurl | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') + nonce=$($CURL -I "$nonceurl" | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') else # APIv2 nonce=$($CURL -I "$URL_newNonce" | grep "^Replay-Nonce:" | awk '{print $2}' | tr -d '\r\n ') fi @@ -2458,8 +2459,7 @@ find_dns_utils check_config # if -i|--install install certs, reload and exit -if [ "0${_CERT_INSTALL}" -eq 1 ] -then +if [ "0${_CERT_INSTALL}" -eq 1 ]; then cert_install reload_service graceful_exit @@ -2724,7 +2724,8 @@ info "Verification completed, obtaining certificate." #obtain the certificate. get_certificate "$DOMAIN_DIR/${DOMAIN}.csr" \ "$CERT_FILE" \ - "$CA_CERT" + "$CA_CERT" \ + "$FULL_CHAIN" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then info "Creating order for EC certificate" if [[ $API -eq 2 ]]; then @@ -2734,7 +2735,8 @@ if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then info "obtaining EC certificate." get_certificate "$DOMAIN_DIR/${DOMAIN}.ec.csr" \ "${CERT_FILE%.*}.ec.crt" \ - "${CA_CERT%.*}.ec.crt" + "${CA_CERT%.*}.ec.crt" \ + "${FULL_CHAIN%.*}.ec.crt" fi # create Archive of new certs and keys. diff --git a/test/3-dual-rsa-ecdsa.bats b/test/3-dual-rsa-ecdsa.bats index d5c58f7..486fa13 100644 --- a/test/3-dual-rsa-ecdsa.bats +++ b/test/3-dual-rsa-ecdsa.bats @@ -21,6 +21,10 @@ setup() { create_certificate assert_success check_output_for_errors + check_certificates + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] } @@ -43,6 +47,10 @@ setup() { create_certificate assert_success check_output_for_errors + check_certificates + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] } diff --git a/test/test_helper.bash b/test/test_helper.bash index 0aafa93..1e65189 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -1,6 +1,13 @@ INSTALL_DIR=/root CODE_DIR=/getssl +check_certificates() +{ + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" ] +} + check_output_for_errors() { refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' # less strict tests if running with debug output From 9f19977238bd3618192e457e3bfe825ea498aeba Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 10 May 2020 12:05:32 +0100 Subject: [PATCH 168/337] Update revision history and version --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index a92f09b..76714b7 100755 --- a/getssl +++ b/getssl @@ -228,11 +228,12 @@ # 2020-04-22 Fix domain case conversion for different locales - glynge (2.26) # 2020-04-26 Fixed ipv4 confirmation with nslookup - Cyber1000 # 2020-04-29 Fix ftp/sftp problems if challenge starts with a dash +# 2020-05-06 Fix missing fullchain.ec.crt when creating dual certificates (2.27) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.26" +VERSION="2.27" # defaults ACCOUNT_KEY_LENGTH=4096 From d39f60540a1c6e7b32fdb73b942510e609242516 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 10 May 2020 12:23:04 +0100 Subject: [PATCH 169/337] Change CA to v02, fix shellcheck warning and indentation --- getssl | 123 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/getssl b/getssl index a3f5eb9..cbb2559 100755 --- a/getssl +++ b/getssl @@ -2072,72 +2072,77 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then - export DOMAIN="$DOMAIN" - export EX_SANS="$EX_SANS" - envsubst < $WORKING_DIR/getssl_default.cfg > "$1" + export DOMAIN="$DOMAIN" + export EX_SANS="$EX_SANS" + envsubst < "$WORKING_DIR/getssl_default.cfg" > "$1" else cat > "$1" <<- _EOF_domain_ - # Uncomment and modify any variables you need + # vim: filetype=sh + # + # This file is read second (and per domain if running with the -a option) + # and overwrites any settings from the first file + # + # 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" + #CA="https://acme-staging-v02.api.letsencrypt.org" # This server issues full certificates, however has rate limits - #CA="https://acme-v01.api.letsencrypt.org" - - # Private key types - can be rsa, prime256v1, secp384r1 or secp521r1 - #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="${EX_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. - # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, - # password, host, port (explicitly needed even if using default port 443) and path on the server. - #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' - # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' - # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') - - # Specify SSH options, e.g. non standard port in SSH_OPTS - # (Can also use SCP_OPTS and SFTP_OPTS) - # SSH_OPTS=-p 12345 - - # 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/ssl/${DOMAIN}.crt" # this is domain cert - #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" # this is domain key - #CA_CERT_LOCATION="/etc/ssl/chain.crt" # this is CA cert - #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="" - - # Uncomment the following line to prevent non-interactive renewals of certificates - #PREVENT_NON_INTERACTIVE_RENEWAL="true" - - # 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" - #CHECK_REMOTE_WAIT="2" # wait 2 seconds before checking the remote server - _EOF_domain_ + #CA="https://acme-v02.api.letsencrypt.org" + + # Private key types - can be rsa, prime256v1, secp384r1 or secp521r1 + #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="${EX_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. + # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, + # password, host, port (explicitly needed even if using default port 443) and path on the server. + #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' + # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') + + # Specify SSH options, e.g. non standard port in SSH_OPTS + # (Can also use SCP_OPTS and SFTP_OPTS) + # SSH_OPTS=-p 12345 + + # 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/ssl/${DOMAIN}.crt" # this is domain cert + #DOMAIN_KEY_LOCATION="/etc/ssl/${DOMAIN}.key" # this is domain key + #CA_CERT_LOCATION="/etc/ssl/chain.crt" # this is CA cert + #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="" + + # Uncomment the following line to prevent non-interactive renewals of certificates + #PREVENT_NON_INTERACTIVE_RENEWAL="true" + + # 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" + #CHECK_REMOTE_WAIT="2" # wait 2 seconds before checking the remote server + _EOF_domain_ fi } From 89ca12a72889d1befb70a66a61f6adfa0f161fad Mon Sep 17 00:00:00 2001 From: aws02 user Date: Mon, 11 May 2020 10:53:13 +1000 Subject: [PATCH 170/337] Remove .idea files --- .idea/.gitignore | 2 -- .idea/getssl.iml | 8 -------- .idea/misc.xml | 6 ------ .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ 5 files changed, 30 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/getssl.iml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index e7e9d11..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Default ignored files -/workspace.xml diff --git a/.idea/getssl.iml b/.idea/getssl.iml deleted file mode 100644 index c956989..0000000 --- a/.idea/getssl.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 28a804d..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 5f2e056..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From ee5e1e8cac509b63f76630ebb9cfaf40ec696403 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 13 May 2020 17:12:27 +0100 Subject: [PATCH 171/337] Add --check-config and auto upgrade CA v02 url --- getssl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/getssl b/getssl index a501f78..1a14ac8 100755 --- a/getssl +++ b/getssl @@ -283,6 +283,7 @@ _REVOKE=0 _UPGRADE=0 _UPGRADE_CHECK=1 _USE_DEBUG=0 +_ONLY_CHECK_CONFIG=0 config_errors="false" LANG=C API=1 @@ -292,6 +293,18 @@ ORIGCMD="$0 $*" # Define all functions (in alphabetical order) +auto_upgrade_v2() { # Automatically update clients to v2 + if [[ "${CA}" == *"acme-v01."* ]] || [[ "${CA}" == *"acme-staging."* ]]; then + OLDCA=${CA} + # shellcheck disable=SC2001 + CA=$(echo "${OLDCA}" | sed "s/v01/v02/g") + # shellcheck disable=SC2001 + CA=$(echo "${CA}" | sed "s/staging/staging-v02/g") + info "Upgraded to v2 (changed ${OLDCA} to ${CA})" + fi + debug "Using certificate issuer: ${CA}" +} + cert_archive() { # Archive certificate file by copying files to dated archive dir. debug "creating an archive copy of current new certs" date_time=$(date +%Y_%m_%d_%H_%M) @@ -2245,6 +2258,8 @@ while [[ -n ${1+defined} ]]; do _UPGRADE_CHECK=0 ;; -i | --install) _CERT_INSTALL=1 ;; + --check-config) + _ONLY_CHECK_CONFIG=1 ;; -w) shift; WORKING_DIR="$1" ;; -*) @@ -2462,9 +2477,18 @@ set_server_type # check what dns utils are installed find_dns_utils +# auto upgrade clients to v2 +auto_upgrade_v2 + # check config for typical errors. check_config +# exit if just checking config (used for testing) +if [ "${_ONLY_CHECK_CONFIG}" -eq 1 ]; then + info "Configuration check successful" + graceful_exit +fi + # if -i|--install install certs, reload and exit if [ "0${_CERT_INSTALL}" -eq 1 ]; then cert_install From dfba3304e4260654d665fb724c8dbf9e0fbc8256 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 13 May 2020 17:20:24 +0100 Subject: [PATCH 172/337] Add testing for the auto-update to v02 feature --- test/12-auto-upgrade-v1.bats | 75 +++++++++++++++++++ .../getssl-upgrade-test-pebble.cfg | 8 ++ .../getssl-upgrade-test-v1-prod.cfg | 7 ++ .../getssl-upgrade-test-v1-staging.cfg | 7 ++ .../getssl-upgrade-test-v2-prod.cfg | 7 ++ .../getssl-upgrade-test-v2-staging.cfg | 7 ++ 6 files changed, 111 insertions(+) create mode 100644 test/12-auto-upgrade-v1.bats create mode 100644 test/test-config/getssl-upgrade-test-pebble.cfg create mode 100644 test/test-config/getssl-upgrade-test-v1-prod.cfg create mode 100644 test/test-config/getssl-upgrade-test-v1-staging.cfg create mode 100644 test/test-config/getssl-upgrade-test-v2-prod.cfg create mode 100644 test/test-config/getssl-upgrade-test-v2-staging.cfg diff --git a/test/12-auto-upgrade-v1.bats b/test/12-auto-upgrade-v1.bats new file mode 100644 index 0000000..4165cd3 --- /dev/null +++ b/test/12-auto-upgrade-v1.bats @@ -0,0 +1,75 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +@test "Check that auto upgrade to v2 doesn't change pebble url" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-upgrade-test-pebble.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -d --check-config "$GETSSL_CMD_HOST" + assert_success + assert_line 'Using certificate issuer: https://pebble:14000/dir' +} + + +@test "Check that auto upgrade to v2 doesn't change v2 staging url" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-upgrade-test-v2-staging.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -d --check-config "$GETSSL_CMD_HOST" + assert_success + assert_line 'Using certificate issuer: https://acme-staging-v02.api.letsencrypt.org/directory' +} + + +@test "Check that auto upgrade to v2 doesn't change v2 prod url" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-upgrade-test-v2-prod.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -d --check-config "$GETSSL_CMD_HOST" + assert_success + assert_line 'Using certificate issuer: https://acme-v02.api.letsencrypt.org/directory' +} + + +@test "Check that auto upgrade to v2 changes v1 staging to v2 staging url" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-upgrade-test-v1-staging.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -d --check-config "$GETSSL_CMD_HOST" + assert_success + assert_line 'Using certificate issuer: https://acme-staging-v02.api.letsencrypt.org/directory' +} + + +@test "Check that auto upgrade to v2 changes v1 prod to v2 prod url" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-upgrade-test-v1-prod.cfg" + setup_environment + mkdir ${INSTALL_DIR}/.getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/getssl.cfg" + run ${CODE_DIR}/getssl -d --check-config "$GETSSL_CMD_HOST" + assert_success + assert_line 'Using certificate issuer: https://acme-v02.api.letsencrypt.org/directory' +} diff --git a/test/test-config/getssl-upgrade-test-pebble.cfg b/test/test-config/getssl-upgrade-test-pebble.cfg new file mode 100644 index 0000000..f5f8b3b --- /dev/null +++ b/test/test-config/getssl-upgrade-test-pebble.cfg @@ -0,0 +1,8 @@ +# +# Test that auto-upgrade to v2 doesn't change pebble url +# +CA="https://pebble:14000/dir" + + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') diff --git a/test/test-config/getssl-upgrade-test-v1-prod.cfg b/test/test-config/getssl-upgrade-test-v1-prod.cfg new file mode 100644 index 0000000..41f0176 --- /dev/null +++ b/test/test-config/getssl-upgrade-test-v1-prod.cfg @@ -0,0 +1,7 @@ +# +# Test that auto-upgrade to v2 changes v1 prod to v2 prod +# +CA="https://acme-v01.api.letsencrypt.org/directory" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') diff --git a/test/test-config/getssl-upgrade-test-v1-staging.cfg b/test/test-config/getssl-upgrade-test-v1-staging.cfg new file mode 100644 index 0000000..215f246 --- /dev/null +++ b/test/test-config/getssl-upgrade-test-v1-staging.cfg @@ -0,0 +1,7 @@ +# +# Test that auto-upgrade to v2 changes v1 staging to v2 staging +# +CA="https://acme-staging.api.letsencrypt.org/directory" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') diff --git a/test/test-config/getssl-upgrade-test-v2-prod.cfg b/test/test-config/getssl-upgrade-test-v2-prod.cfg new file mode 100644 index 0000000..9b9c009 --- /dev/null +++ b/test/test-config/getssl-upgrade-test-v2-prod.cfg @@ -0,0 +1,7 @@ +# +# Test that auto-upgrade to v2 doesn't change v2 prod url +# +CA="https://acme-v02.api.letsencrypt.org/directory" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') diff --git a/test/test-config/getssl-upgrade-test-v2-staging.cfg b/test/test-config/getssl-upgrade-test-v2-staging.cfg new file mode 100644 index 0000000..98bfd17 --- /dev/null +++ b/test/test-config/getssl-upgrade-test-v2-staging.cfg @@ -0,0 +1,7 @@ +# +# Test that auto-upgrade to v2 doesn't change v2 staging url +# +CA="https://acme-staging-v02.api.letsencrypt.org/directory" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') From aae47d2c260f6025617ed0cd9e38447fdd9f407e Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 14 May 2020 17:05:20 +0100 Subject: [PATCH 173/337] Add --notify-valid --- getssl | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/getssl b/getssl index 1a14ac8..1807249 100755 --- a/getssl +++ b/getssl @@ -277,6 +277,7 @@ _CREATE_CONFIG=0 _FORCE_RENEW=0 _KEEP_VERSIONS="" _MUTE=0 +_NOTIFY_VALID=0 _QUIET=0 _RECREATE_CSR=0 _REVOKE=0 @@ -1496,8 +1497,10 @@ get_signing_params() { # get signing parameters from key } graceful_exit() { # normal exit function. + exit_code=$1 clean_up - exit + # shellcheck disable=SC2086 + exit $exit_code } help_message() { # print out the help message @@ -2230,30 +2233,33 @@ while [[ -n ${1+defined} ]]; do -h | --help) help_message; graceful_exit ;; -d | --debug) - _USE_DEBUG=1 ;; + _USE_DEBUG=1 ;; -c | --create) - _CREATE_CONFIG=1 ;; + _CREATE_CONFIG=1 ;; -f | --force) - _FORCE_RENEW=1 ;; + _FORCE_RENEW=1 ;; + --notify-valid) + # Exit 2 if certificate is valid and doesn't need renewing + _NOTIFY_VALID=2 ;; -a | --all) - _CHECK_ALL=1 ;; + _CHECK_ALL=1 ;; -k | --keep) - shift; _KEEP_VERSIONS="$1";; + shift; _KEEP_VERSIONS="$1";; -q | --quiet) - _QUIET=1 ;; + _QUIET=1 ;; -Q | --mute) - _QUIET=1 - _MUTE=1 ;; + _QUIET=1 + _MUTE=1 ;; -r | --revoke) - _REVOKE=1 - shift - REVOKE_CERT="$1" - shift - REVOKE_KEY="$1" - shift - REVOKE_CA="$1" ;; + _REVOKE=1 + shift + REVOKE_CERT="$1" + shift + REVOKE_KEY="$1" + shift + REVOKE_CA="$1" ;; -u | --upgrade) - _UPGRADE=1 ;; + _UPGRADE=1 ;; -U | --nocheck) _UPGRADE_CHECK=0 ;; -i | --install) @@ -2630,8 +2636,8 @@ if [[ -s "$CERT_FILE" ]]; then debug "upgrading from fake cert to real" else info "${DOMAIN}: certificate is valid for more than $RENEW_ALLOW days (until $enddate)" - # everything is OK, so exit. - graceful_exit + # everything is OK, so exit, if requested with the --notify-valid, exit with code 2 + graceful_exit $_NOTIFY_VALID fi else debug "${DOMAIN}: certificate needs renewal" From 2287057e386996666184b07049d93e1dec9d2aa5 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 14 May 2020 17:05:55 +0100 Subject: [PATCH 174/337] Add test for --notify-valid --- test/13-notify-valid.bats | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/13-notify-valid.bats diff --git a/test/13-notify-valid.bats b/test/13-notify-valid.bats new file mode 100644 index 0000000..ac1a50c --- /dev/null +++ b/test/13-notify-valid.bats @@ -0,0 +1,45 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificate to check valid exit code" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check no-renewal needed exits with normal exit code" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl $GETSSL_HOST + assert_success + check_output_for_errors +} + + +@test "Check no-renewal needed returns 2 if requested" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl --notify-valid $GETSSL_HOST + assert [ $status == 2 ] + check_output_for_errors + cleanup_environment +} From db316d0d16b998a470a45bd18f0bb410b793a643 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 23 May 2020 20:58:44 +0100 Subject: [PATCH 175/337] Fix --revoke --- getssl | 93 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/getssl b/getssl index 1807249..710fba4 100755 --- a/getssl +++ b/getssl @@ -1743,6 +1743,45 @@ json_get() { # get values from json fi } +obtain_ca_resource_locations() +{ + # Obtain CA resource locations + ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) + debug "ca_all_loc from ${CA} gives $ca_all_loc" + # APIv1 + URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') + URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') + URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') + #API v2 + URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') + URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') + URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') + URL_revoke=$(echo "$ca_all_loc" | grep "revokeCert" | awk -F'"' '{print $4}') + + if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then + ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) + debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" + # APIv1 + URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') + URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') + URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') + #API v2 + URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') + URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') + URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') + fi + + if [[ -n "$URL_new_reg" ]]; then + API=1 + elif [[ -n "$URL_newAccount" ]]; then + API=2 + else + info "unknown API version" + graceful_exit + fi + debug "Using API v$API" +} + os_esed() { # Use different sed version for different os types (extended regex) if [[ "$os" == "bsd" ]]; then # BSD requires -E flag for extended regex sed -E "${@}" @@ -1807,9 +1846,9 @@ revoke_certificate() { # revoke a certificate # need to set the revoke key as "account_key" since it's used in send_signed_request. get_signing_params "$REVOKE_KEY" TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) - debug "revoking from $CA" - rcertdata=$(openssl x509 -in "$REVOKE_CERT" -inform PEM -outform DER | urlbase64) - send_signed_request "$URL_revoke" "{\"resource\": \"revoke-cert\", \"certificate\": \"$rcertdata\"}" + debug "revoking from $URL_revoke" + rcertdata=$(sed '1d;$d' "$REVOKE_CERT" | tr -d "\r\n" | tr '/+' '_-' | tr -d '= ') + send_signed_request "$URL_revoke" "{\"certificate\": \"$rcertdata\",\"reason\": $REVOKE_REASON}" if [[ $code -eq "200" ]]; then info "certificate revoked" else @@ -1959,15 +1998,18 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p while [[ "$code" -eq 500 ]]; do if [[ "$outfile" ]] ; then $CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" > "$outfile" + errcode=$? response=$(cat "$outfile") elif [[ "$needbase64" ]] ; then response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url" | urlbase64) + errcode=$? else response=$($CURL -X POST -H "Content-Type: application/jose+json" --data "$body" "$url") + errcode=$? fi - if [[ "$response" == "" ]]; then - error_exit "ERROR curl \"$url\" returned nothing" + if [[ $errcode -gt 0 || ( "$response" == "" && $url != *"revoke"* ) ]]; then + error_exit "ERROR curl \"$url\" failed with $errcode and returned $response" fi responseHeaders=$(cat "$CURL_HEADER") @@ -2257,7 +2299,9 @@ while [[ -n ${1+defined} ]]; do shift REVOKE_KEY="$1" shift - REVOKE_CA="$1" ;; + CA="$1" + REVOKE_CA="$1" + REVOKE_REASON=0 ;; -u | --upgrade) _UPGRADE=1 ;; -U | --nocheck) @@ -2324,7 +2368,8 @@ if [[ $_REVOKE -eq 1 ]]; then else CA=$REVOKE_CA fi - URL_revoke=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null | grep "revoke-cert" | awk -F'"' '{print $4}') + + obtain_ca_resource_locations revoke_certificate graceful_exit fi @@ -2508,39 +2553,7 @@ if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then info "${DOMAIN}: forcing renewal (due to FORCE_RENEWAL file)" fi -# Obtain CA resource locations -ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) -debug "ca_all_loc from ${CA} gives $ca_all_loc" -# APIv1 -URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') -URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') -URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') -#API v2 -URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') -URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') -URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') -if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then - ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) - debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" - # APIv1 - URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') - URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') - URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') - #API v2 - URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') - URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') - URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') -fi - -if [[ -n "$URL_new_reg" ]]; then - API=1 -elif [[ -n "$URL_newAccount" ]]; then - API=2 -else - info "unknown API version" - graceful_exit -fi -debug "Using API v$API" +obtain_ca_resource_locations # Check if awk supports json_awk (required for ACMEv2) if [[ $API -eq 2 ]]; then From fc4add8747d46be2398734ffbe616e93d7c2ad88 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 23 May 2020 21:00:04 +0100 Subject: [PATCH 176/337] Test --revoke --- test/14-test-revoke.bats | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/14-test-revoke.bats diff --git a/test/14-test-revoke.bats b/test/14-test-revoke.bats new file mode 100644 index 0000000..ac056c2 --- /dev/null +++ b/test/14-test-revoke.bats @@ -0,0 +1,37 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificate to check revoke" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check we can revoke a certificate" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt + KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key + CA="https://pebble:14000/dir" + run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA + assert_success + check_output_for_errors +} From d568e15563ea7353173a940374ccc4470f1e5b32 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 23 May 2020 21:28:32 +0100 Subject: [PATCH 177/337] Update test to test using staging server --- test/14-test-revoke.bats | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/14-test-revoke.bats b/test/14-test-revoke.bats index ac056c2..2e95e0d 100644 --- a/test/14-test-revoke.bats +++ b/test/14-test-revoke.bats @@ -13,9 +13,11 @@ setup() { @test "Create certificate to check revoke" { if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-http01.cfg" fi - CONFIG_FILE="getssl-http01.cfg" + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" setup_environment init_getssl create_certificate @@ -26,11 +28,14 @@ setup() { @test "Check we can revoke a certificate" { if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-http01.cfg" fi + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key - CA="https://pebble:14000/dir" + run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA assert_success check_output_for_errors From 86b365b2c35508cd4421763adc5d96ecffc38418 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 24 May 2020 09:46:13 +0100 Subject: [PATCH 178/337] Update revision history and version number --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 710fba4..371906f 100755 --- a/getssl +++ b/getssl @@ -229,11 +229,13 @@ # 2020-04-26 Fixed ipv4 confirmation with nslookup - Cyber1000 # 2020-04-29 Fix ftp/sftp problems if challenge starts with a dash # 2020-05-06 Fix missing fullchain.ec.crt when creating dual certificates (2.27) +# 2020-05-14 Add --notify-valid option (exit 2 if certificate is valid) +# 2020-05-23 Fix --revoke (didn't work with ACMEv02) (2.28) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.27" +VERSION="2.28" # defaults ACCOUNT_KEY_LENGTH=4096 From fec8905c1c2ecac816c96a6eafb31a70d6e09ddd Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 6 Jun 2020 14:26:18 +0100 Subject: [PATCH 179/337] Fix missing URL_revoke definition when no CA directory suffix --- getssl | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/getssl b/getssl index 371906f..5668571 100755 --- a/getssl +++ b/getssl @@ -1747,22 +1747,11 @@ json_get() { # get values from json obtain_ca_resource_locations() { - # Obtain CA resource locations - ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}" 2>/dev/null) - debug "ca_all_loc from ${CA} gives $ca_all_loc" - # APIv1 - URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') - URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') - URL_new_cert=$(echo "$ca_all_loc" | grep "new-cert" | awk -F'"' '{print $4}') - #API v2 - URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') - URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') - URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') - URL_revoke=$(echo "$ca_all_loc" | grep "revokeCert" | awk -F'"' '{print $4}') - - if [[ -z "$URL_new_reg" ]] && [[ -z "$URL_newAccount" ]]; then - ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}/directory" 2>/dev/null) - debug "ca_all_loc from ${CA}/directory gives $ca_all_loc" + for suffix in "" "/directory" "/dir"; + do + # Obtain CA resource locations + ca_all_loc=$(curl --user-agent "$CURL_USERAGENT" "${CA}${suffix}" 2>/dev/null) + debug "ca_all_loc from ${CA}${suffix} gives $ca_all_loc" # APIv1 URL_new_reg=$(echo "$ca_all_loc" | grep "new-reg" | awk -F'"' '{print $4}') URL_new_authz=$(echo "$ca_all_loc" | grep "new-authz" | awk -F'"' '{print $4}') @@ -1771,15 +1760,19 @@ obtain_ca_resource_locations() URL_newAccount=$(echo "$ca_all_loc" | grep "newAccount" | awk -F'"' '{print $4}') URL_newNonce=$(echo "$ca_all_loc" | grep "newNonce" | awk -F'"' '{print $4}') URL_newOrder=$(echo "$ca_all_loc" | grep "newOrder" | awk -F'"' '{print $4}') - fi + URL_revoke=$(echo "$ca_all_loc" | grep "revokeCert" | awk -F'"' '{print $4}') + + if [[ -n "$URL_new_reg" ]] || [[ -n "$URL_newAccount" ]]; then + break + fi + done if [[ -n "$URL_new_reg" ]]; then API=1 elif [[ -n "$URL_newAccount" ]]; then API=2 else - info "unknown API version" - graceful_exit + error_exit "unknown API version" fi debug "Using API v$API" } From 913b7fcbb8ee8e40fd64d3fad8934bdbcbd318ae Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 6 Jun 2020 14:27:09 +0100 Subject: [PATCH 180/337] Add tests for obtain_ca_resource_locations and no CA suffix --- test/15-test-revoke-no-suffix.bats | 42 +++++++++++++++++++ test/test-config/getssl-http01-no-suffix.cfg | 30 +++++++++++++ .../getssl-staging-dns01-no-suffix.cfg | 37 ++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 test/15-test-revoke-no-suffix.bats create mode 100644 test/test-config/getssl-http01-no-suffix.cfg create mode 100644 test/test-config/getssl-staging-dns01-no-suffix.cfg diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats new file mode 100644 index 0000000..ce97ddd --- /dev/null +++ b/test/15-test-revoke-no-suffix.bats @@ -0,0 +1,42 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificate to check revoke" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01-no-suffix.cfg" + else + CONFIG_FILE="getssl-http01-no-suffix.cfg" + fi + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check we can revoke a certificate" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-http01.cfg" + fi + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" + CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt + KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key + + run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA + assert_success + check_output_for_errors +} diff --git a/test/test-config/getssl-http01-no-suffix.cfg b/test/test-config/getssl-http01-no-suffix.cfg new file mode 100644 index 0000000..a1295dd --- /dev/null +++ b/test/test-config/getssl-http01-no-suffix.cfg @@ -0,0 +1,30 @@ +# 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 +# +CA="https://pebble:14000/dir" +CA="https://pebble:14000" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg new file mode 100644 index 0000000..32fdda3 --- /dev/null +++ b/test/test-config/getssl-staging-dns01-no-suffix.cfg @@ -0,0 +1,37 @@ +# Test that the script works with external dns provider and staging server +# +CA="https://acme-staging-v02.api.letsencrypt.org" + +VALIDATE_VIA_DNS=true +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" +PUBLIC_DNS_SERVER=ns2.duckdns.org +CHECK_ALL_AUTH_DNS=true +DNS_EXTRA_WAIT=60 + +ACCOUNT_KEY_TYPE="rsa" +PRIVATE_KEY_ALG="rsa" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed (using a custom port) +SERVER_TYPE="https" +CHECK_REMOTE="true" From 48298c86da2dadb6c3b12db3bee012200ac2f1ef Mon Sep 17 00:00:00 2001 From: Jaroslav Beran Date: Sat, 13 Jun 2020 12:20:18 +0200 Subject: [PATCH 181/337] .gitattributes added --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..217326a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto + +# Make all text files lf formatted +* text eol=lf From 59ea8bfe4aae303ece5eb8b29f8750963c339bcc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 15 Jun 2020 21:52:40 +0100 Subject: [PATCH 182/337] Update nginx config to test both certificates --- .../getssl-dns01-dual-rsa-ecdsa.cfg | 2 +- .../getssl-http01-dual-rsa-ecdsa.cfg | 2 +- test/test-config/nginx-ubuntu-dual-certs | 95 +++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 test/test-config/nginx-ubuntu-dual-certs diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 8f29088..7b031c8 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -32,7 +32,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="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx" # Define the server type and confirm correct certificate is installed SERVER_TYPE="https" diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg index f6cfcb7..9e348fc 100644 --- a/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa.cfg @@ -26,7 +26,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="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx" # Define the server type and confirm correct certificate is installed SERVER_TYPE="https" diff --git a/test/test-config/nginx-ubuntu-dual-certs b/test/test-config/nginx-ubuntu-dual-certs new file mode 100644 index 0000000..6346cde --- /dev/null +++ b/test/test-config/nginx-ubuntu-dual-certs @@ -0,0 +1,95 @@ +## +# 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; + + ssl_certificate /etc/nginx/pki/server.ec.crt; + ssl_certificate_key /etc/nginx/pki/private/server.ec.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 d24782088d6227d1c9399a3e7885a56622bb504c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 16 Jun 2020 21:42:51 +0100 Subject: [PATCH 183/337] Add --retry to curl to fix occasional "Bad Gateway" errors --- dns_scripts/dns_add_duckdns | 2 +- dns_scripts/dns_del_duckdns | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dns_scripts/dns_add_duckdns b/dns_scripts/dns_add_duckdns index ef40efe..9f61c8c 100755 --- a/dns_scripts/dns_add_duckdns +++ b/dns_scripts/dns_add_duckdns @@ -11,7 +11,7 @@ fi domain="$1" txtvalue="$2" -response=$(curl --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=${txtvalue}") +response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=${txtvalue}") if [ "$response" != "OK" ]; then echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" echo "Response: $response" diff --git a/dns_scripts/dns_del_duckdns b/dns_scripts/dns_del_duckdns index b9b9f9f..f20c664 100755 --- a/dns_scripts/dns_del_duckdns +++ b/dns_scripts/dns_del_duckdns @@ -4,7 +4,7 @@ token=${DUCKDNS_TOKEN:-} domain="$1" -response=$(curl --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=&clear=true") +response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=&clear=true") if [ "$response" != "OK" ]; then echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" echo "$response" From 89036c126b4f317f39051b0f6fc3b11e7d2490af Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Jun 2020 17:19:36 +0100 Subject: [PATCH 184/337] Fix CHECK_REMOTE for DUAL_RSA_ECDSA --- getssl | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/getssl b/getssl index 5668571..4a1a43c 100755 --- a/getssl +++ b/getssl @@ -2561,9 +2561,14 @@ fi # 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 debug "getting certificate for $DOMAIN from remote server" +if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + CIPHER="-cipher RSA" +else + CIPHER="" +fi # shellcheck disable=SC2086 EX_CERT=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ + | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${CIPHER} 2>/dev/null \ | openssl x509 2>/dev/null) if [[ -n "$EX_CERT" ]]; then # if obtained a cert if [[ -s "$CERT_FILE" ]]; then # if local exists @@ -2817,16 +2822,30 @@ fi # Check if the certificate is installed correctly if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" - # shellcheck disable=SC2086 - CERT_REMOTE=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} 2>/dev/null \ - | openssl x509 -noout -fingerprint 2>/dev/null) - CERT_LOCAL=$(openssl x509 -noout -fingerprint < "$CERT_FILE" 2>/dev/null) - if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then - info "${DOMAIN} - certificate installed OK on server" + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + PARAMS=("-cipher RSA" "-cipher ECDSA") + CERTS=("$CERT_FILE" "${CERT_FILE%.*}.ec.crt") + TYPES=("rsa" "$PRIVATE_KEY_ALG") else - error_exit "${DOMAIN} - certificate obtained but certificate on server is different from the new certificate" + PARAMS=("") + CERTS=("$CERT_FILE") + TYPES=("$PRIVATE_KEY_ALG") fi + + for ((i=0; i<${#PARAMS[@]};++i)); do + debug "Checking ${CERTS[i]}" + # shellcheck disable=SC2086 + CERT_REMOTE=$(echo \ + | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} 2>/dev/null \ + | openssl x509 -noout -fingerprint 2>/dev/null) + CERT_LOCAL=$(openssl x509 -noout -fingerprint < "${CERTS[i]}" 2>/dev/null) + if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then + info "${DOMAIN} - ${TYPES[i]} certificate installed OK on server" + else + info "${CERTS[i]} didn't match server" + error_exit "${DOMAIN} - ${TYPES[i]} certificate obtained but certificate on server is different from the new certificate" + fi + done fi # end of Check if the certificate is installed correctly From 19369723cac07784e68e20e67a401d186fdf9468 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Jun 2020 17:21:36 +0100 Subject: [PATCH 185/337] Add/fix tests for CHECK_REMOTE and DUAL_RSA_ECDSA --- test/3-dual-rsa-ecdsa.bats | 35 ++++++++++++++++- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 9 ++++- test/Dockerfile-centos6 | 3 ++ .../getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg | 39 +++++++++++++++++++ ...1-dual-rsa-ecdsa-2-locations-old-nginx.cfg | 32 +++++++++++++++ ...tssl-http01-dual-rsa-ecdsa-2-locations.cfg | 2 +- ...getssl-http01-dual-rsa-ecdsa-old-nginx.cfg | 33 ++++++++++++++++ test/test-config/nginx-ubuntu-dual-certs | 2 + test/test_helper.bash | 14 +++++++ 9 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-old-nginx.cfg create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg diff --git a/test/3-dual-rsa-ecdsa.bats b/test/3-dual-rsa-ecdsa.bats index 486fa13..87527c2 100644 --- a/test/3-dual-rsa-ecdsa.bats +++ b/test/3-dual-rsa-ecdsa.bats @@ -15,7 +15,14 @@ setup() { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - CONFIG_FILE="getssl-http01-dual-rsa-ecdsa.cfg" + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa.cfg" + else + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-old-nginx.cfg" + fi + setup_environment init_getssl create_certificate @@ -28,6 +35,22 @@ setup() { } +@test "Check renewal test works for dual certificates using HTTP-01" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + check_nginx + run ${CODE_DIR}/getssl -d $GETSSL_HOST + + if [ "$OLD_NGINX" = "false" ]; then + assert_line "certificate on server is same as the local cert" + else + assert_line --partial "certificate is valid for more than 30 days" + fi + assert_success +} + + @test "Force renewal of dual certificates using HTTP-01" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" @@ -37,11 +60,19 @@ setup() { check_output_for_errors } + @test "Create dual certificates using DNS-01 verification" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - CONFIG_FILE="getssl-dns01-dual-rsa-ecdsa.cfg" + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + CONFIG_FILE="getssl-dns01-dual-rsa-ecdsa.cfg" + else + CONFIG_FILE="getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg" + fi + setup_environment init_getssl create_certificate diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index 394e8d6..a8e7653 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -25,7 +25,14 @@ teardown() { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations.cfg" + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations.cfg" + else + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations-old-nginx.cfg" + fi + setup_environment mkdir -p /root/a.${GETSSL_HOST} diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 61c8b6b..5ffc91c 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -1,6 +1,9 @@ FROM centos:centos6 # Note this image uses gawk +# Note if you are running this using WSL2 you need to put the following lines in %userprofile%\.wslconfig +# [wsl2] +# kernelCommandLine = vsyscall=emulate # Update and install required software RUN yum -y update diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg new file mode 100644 index 0000000..062a47c --- /dev/null +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg @@ -0,0 +1,39 @@ +# 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 +# +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=('/var/www/html/.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.ec.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.ec.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="false" diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-old-nginx.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-old-nginx.cfg new file mode 100644 index 0000000..50c3970 --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-old-nginx.cfg @@ -0,0 +1,32 @@ +# Test that more than one location can be specified for CERT and KEY locations and that the +# files are copied to both locations when both RSA and ECDSA certificates are created +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="true" + +# 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;/root/a.${GETSSL_HOST}/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="false" diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg index 80533ce..96e4d4d 100644 --- a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg @@ -25,7 +25,7 @@ DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/do 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx" # Define the server type and confirm correct certificate is installed SERVER_TYPE="https" diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg new file mode 100644 index 0000000..41581ae --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg @@ -0,0 +1,33 @@ +# 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 +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.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.ec.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.ec.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="false" diff --git a/test/test-config/nginx-ubuntu-dual-certs b/test/test-config/nginx-ubuntu-dual-certs index 6346cde..ce1fbbf 100644 --- a/test/test-config/nginx-ubuntu-dual-certs +++ b/test/test-config/nginx-ubuntu-dual-certs @@ -42,6 +42,8 @@ server { # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + server_name _; ssl_certificate /etc/nginx/pki/server.crt; ssl_certificate_key /etc/nginx/pki/private/server.key; diff --git a/test/test_helper.bash b/test/test_helper.bash index 1e65189..fafad5c 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -8,6 +8,20 @@ check_certificates() assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" ] } +# Only nginx > 1.11.0 support dual certificates in a single configuration file +# https://unix.stackexchange.com/questions/285924/how-to-compare-a-programs-version-in-a-shell-script +check_nginx() { + requiredver="1.11.0" + currentver="$(nginx -v)" + if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then + export OLD_NGINX="false" + else + echo "INFO: Running nginx version $currentver which doesn't support dual certificates" >&3 + echo "INFO: not checking that certificate is installed correctly" >&3 + export OLD_NGINX="true" + fi +} + check_output_for_errors() { refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' # less strict tests if running with debug output From 98ee28cd52079d9099e366f09b0ab341fb43119b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Jun 2020 17:22:27 +0100 Subject: [PATCH 186/337] Merge pebble dns and staging dns tests --- test/2-simple-dns01-dig.bats | 7 ++----- test/2-simple-dns01-nslookup.bats | 4 ++-- test/7-staging-dns01-dig.bats | 2 ++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/2-simple-dns01-dig.bats b/test/2-simple-dns01-dig.bats index 0a54684..6e1eca3 100644 --- a/test/2-simple-dns01-dig.bats +++ b/test/2-simple-dns01-dig.bats @@ -27,11 +27,11 @@ teardown() { @test "Create new certificate using DNS-01 verification (dig)" { + CONFIG_FILE="getssl-dns01.cfg" if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" + CONFIG_FILE="getssl-staging-dns01.cfg" fi - CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl create_certificate -d @@ -42,9 +42,6 @@ teardown() { @test "Force renewal of certificate using DNS-01 (dig)" { - if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" - fi run ${CODE_DIR}/getssl -d -f $GETSSL_HOST assert_success assert_output --partial "dig" diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index dc6f2f5..39c8bae 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -28,11 +28,11 @@ teardown() { @test "Create new certificate using DNS-01 verification (nslookup)" { + CONFIG_FILE="getssl-dns01.cfg" if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" + CONFIG_FILE="getssl-dns01.cfg" fi - CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl create_certificate -d diff --git a/test/7-staging-dns01-dig.bats b/test/7-staging-dns01-dig.bats index 7e21124..3d70ce5 100644 --- a/test/7-staging-dns01-dig.bats +++ b/test/7-staging-dns01-dig.bats @@ -7,6 +7,7 @@ load '/getssl/test/test_helper.bash' @test "Create new certificate using staging server, dig and DuckDNS" { + skip if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi @@ -20,6 +21,7 @@ load '/getssl/test/test_helper.bash' } @test "Force renewal of certificate using staging server, dig and DuckDNS" { + skip if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi From 0f039c0dcf694e53ef60abe7875a2cb826164cb3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Jun 2020 18:14:27 +0100 Subject: [PATCH 187/337] Update README.md Remove manual ACMEv1 -> ACMEv2 upgrade information --- README.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/README.md b/README.md index 3882b50..12eaf66 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,6 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. -## Upgrading from ACME v01 to ACME v02 - -Find the following line in your `getssl.cfg` file: - -```CA="https://acme-v01.api.letsencrypt.org"``` - -and change it to: - -```CA="https://acme-v02.api.letsencrypt.org"``` - ## Features * **Bash** - It runs on virtually all unix machines, including BSD, most @@ -43,7 +33,7 @@ and change it to: debug information is available. * **Reload services** - After a new certificate is obtained then the relevant services (e.g. apache/nginx/postfix) can be reloaded. -* **ACME v1 and V2** - Supports both ACME versions 1 and 2 +* **ACME v1 and V2** - Supports both ACME versions 1 and 2 (note ACMEv1 is deprecated and clients will automatically use v2) ## Installation From 84996161286c36d1503e4eba691bace27e9db27b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 14 Jul 2020 21:40:30 +0100 Subject: [PATCH 188/337] Support centos8 --- test/Dockerfile-centos8 | 20 ++++++++++++++++++++ test/restart-nginx | 2 +- test/test_helper.bash | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/Dockerfile-centos8 diff --git a/test/Dockerfile-centos8 b/test/Dockerfile-centos8 new file mode 100644 index 0000000..15c14d6 --- /dev/null +++ b/test/Dockerfile-centos8 @@ -0,0 +1,20 @@ +FROM centos:centos8 + +# Note this image uses drill, does not have dig or nslookup installed + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl bind-utils wget which nginx + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf +COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local diff --git a/test/restart-nginx b/test/restart-nginx index e62433d..4dc8af0 100755 --- a/test/restart-nginx +++ b/test/restart-nginx @@ -3,7 +3,7 @@ if [ "$GETSSL_OS" = "alpine" ]; then killall -HUP nginx >&3- sleep 5 -elif [ "$GETSSL_OS" == "centos7" ]; then +elif [[ "$GETSSL_OS" == "centos"[78] ]]; then pgrep nginx | head -1 | xargs kill -HUP sleep 5 else diff --git a/test/test_helper.bash b/test/test_helper.bash index fafad5c..9358619 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -72,7 +72,7 @@ if [[ -f /usr/bin/supervisord && -f /etc/supervisord.conf ]]; then if [[ ! $(pgrep supervisord) ]]; then /usr/bin/supervisord -c /etc/supervisord.conf >&3- fi -elif [ "$GETSSL_OS" == "centos7" ]; then +elif [[ "$GETSSL_OS" == "centos"[78] ]]; then if [ -z "$(pgrep nginx)" ]; then nginx >&3- fi From 31958a27f3395e9256ef579dc91e5f1a6c29609e Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 14 Jul 2020 21:40:51 +0100 Subject: [PATCH 189/337] Check error message shown if ACL has a leading space --- test/16-test-bad-acl.bats | 23 +++++++++++++++++ test/test-config/getssl-http01-bad-acl.cfg | 29 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/16-test-bad-acl.bats create mode 100644 test/test-config/getssl-http01-bad-acl.cfg diff --git a/test/16-test-bad-acl.bats b/test/16-test-bad-acl.bats new file mode 100644 index 0000000..d953951 --- /dev/null +++ b/test/16-test-bad-acl.bats @@ -0,0 +1,23 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Test behaviour if ACL= line has a space" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-bad-acl.cfg" + setup_environment + init_getssl + create_certificate + assert_failure +} diff --git a/test/test-config/getssl-http01-bad-acl.cfg b/test/test-config/getssl-http01-bad-acl.cfg new file mode 100644 index 0000000..461d3a3 --- /dev/null +++ b/test/test-config/getssl-http01-bad-acl.cfg @@ -0,0 +1,29 @@ +# 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 +# +CA="https://pebble:14000/dir" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL= ('/var/www/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From 13f3a8b8def1510f8d5f362952e32cc001101c76 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 14 Jul 2020 21:41:21 +0100 Subject: [PATCH 190/337] Support space delimited SANS --- getssl | 15 ++-- test/15-test-revoke-no-suffix.bats | 4 +- test/17-test-spaces-in-sans.bats | 75 +++++++++++++++++++ ...tp01-spaces-sans-and-ignore-dir-domain.cfg | 29 +++++++ .../test-config/getssl-http01-spaces-sans.cfg | 28 +++++++ 5 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 test/17-test-spaces-in-sans.bats create mode 100644 test/test-config/getssl-http01-spaces-sans-and-ignore-dir-domain.cfg create mode 100644 test/test-config/getssl-http01-spaces-sans.cfg diff --git a/getssl b/getssl index 4a1a43c..a5e72cf 100755 --- a/getssl +++ b/getssl @@ -231,11 +231,14 @@ # 2020-05-06 Fix missing fullchain.ec.crt when creating dual certificates (2.27) # 2020-05-14 Add --notify-valid option (exit 2 if certificate is valid) # 2020-05-23 Fix --revoke (didn't work with ACMEv02) (2.28) +# 2020-06-06 Fix missing URL_revoke definition when no CA directory suffix (#566) +# 2020-06-18 Fix CHECK_REMOTE for DUAL_RSA_ECDSA (#570) +# 2020-07-14 Support space separated SANS (#574) (2.29) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.28" +VERSION="2.29" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -497,7 +500,7 @@ check_config() { # check the config files for all obvious errors # get all domains if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//,/ } + alldomains=${SANS//[, ]/ } else alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") fi @@ -784,7 +787,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then alldomains=$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u) else - alldomains=$(echo "$DOMAIN,$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u) + alldomains=$(echo "$DOMAIN,$SANS" | sed -e 's/,/ /g; s/ $//; y/ /\n/' | sort -u) fi domains_in_csr=$(openssl req -text -noout -in "$csr_file" \ | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ @@ -2695,9 +2698,9 @@ fi if [[ -z "$SANS" ]]; then SANLIST="subjectAltName=DNS:${DOMAIN}" elif [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - SANLIST="subjectAltName=DNS:${SANS//,/,DNS:}" + SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" else - SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//,/,DNS:}" + SANLIST="subjectAltName=DNS:${DOMAIN},DNS:${SANS//[, ]/,DNS:}" fi debug "created SAN list = $SANLIST" @@ -2757,7 +2760,7 @@ info "Verify each domain" # loop through domains for cert ( from SANS list) if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//,/ } + alldomains=${SANS//[, ]/ } else alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") fi diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index ce97ddd..7b19c5a 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -11,7 +11,7 @@ setup() { } -@test "Create certificate to check revoke" { +@test "Create certificate to check revoke (no suffix)" { if [ -n "$STAGING" ]; then CONFIG_FILE="getssl-staging-dns01-no-suffix.cfg" else @@ -26,7 +26,7 @@ setup() { } -@test "Check we can revoke a certificate" { +@test "Check we can revoke a certificate (no suffix)" { if [ -n "$STAGING" ]; then CONFIG_FILE="getssl-staging-dns01.cfg" else diff --git a/test/17-test-spaces-in-sans.bats b/test/17-test-spaces-in-sans.bats new file mode 100644 index 0000000..01e7090 --- /dev/null +++ b/test/17-test-spaces-in-sans.bats @@ -0,0 +1,75 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Test behaviour if SANS line is space separated instead of comma separated" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-spaces-sans.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Test renewal if SANS line is space separated instead of comma separated" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + check_output_for_errors + cleanup_environment +} + + +@test "Test behaviour if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-spaces-sans-and-ignore-dir-domain.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Test renewal if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + check_output_for_errors + cleanup_environment + + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a + done +} diff --git a/test/test-config/getssl-http01-spaces-sans-and-ignore-dir-domain.cfg b/test/test-config/getssl-http01-spaces-sans-and-ignore-dir-domain.cfg new file mode 100644 index 0000000..1b3cdca --- /dev/null +++ b/test/test-config/getssl-http01-spaces-sans-and-ignore-dir-domain.cfg @@ -0,0 +1,29 @@ +# 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 + +CA="https://pebble:14000/dir" + +# Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list +IGNORE_DIRECTORY_DOMAIN="true" +SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01-spaces-sans.cfg b/test/test-config/getssl-http01-spaces-sans.cfg new file mode 100644 index 0000000..c93b4f7 --- /dev/null +++ b/test/test-config/getssl-http01-spaces-sans.cfg @@ -0,0 +1,28 @@ +# 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 + +CA="https://pebble:14000/dir" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From ed880a39feeb459f83473e13440cb5c14ab3a4ed Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 15 Jul 2020 14:34:49 +0100 Subject: [PATCH 191/337] Add centos8 --- .github/workflows/run-all-tests.yml | 172 +++++++++++++++------------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index 08fb41b..7bb05dc 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -1,82 +1,90 @@ -name: Run all tests -on: - push: - branches: - - master - pull_request: - branches: - - master -jobs: - test-alpine: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Alpine - run: test/run-test.sh alpine - test-centos6: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on CentOS6 - run: test/run-test.sh centos6 - test-centos7: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on CentOS7 - run: test/run-test.sh centos7 - test-centos7-staging: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on CentOS7 against Staging using DuckDNS - run: test/run-test.sh centos7-staging - test-debian: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Debian - run: test/run-test.sh debian - test-ubuntu: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Ubuntu - run: test/run-test.sh ubuntu - test-ubuntu16: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Ubuntu16 - run: test/run-test.sh ubuntu16 - test-ubuntu18: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Ubuntu18 - run: test/run-test.sh ubuntu18 - test-ubuntu-staging: - needs: test-centos7-staging - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Ubuntu against Staging using DuckDNS - run: test/run-test.sh ubuntu-staging +name: Run all tests +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test-alpine: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Alpine + run: test/run-test.sh alpine + test-centos6: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS6 + run: test/run-test.sh centos6 + test-centos7: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 + run: test/run-test.sh centos7 + test-centos7-staging: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 against Staging using DuckDNS + run: test/run-test.sh centos7-staging + test-centos8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS8 + run: test/run-test.sh centos8 + test-debian: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Debian + run: test/run-test.sh debian + test-ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu + run: test/run-test.sh ubuntu + test-ubuntu16: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu16 + run: test/run-test.sh ubuntu16 + test-ubuntu18: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu18 + run: test/run-test.sh ubuntu18 + test-ubuntu-staging: + needs: test-centos7-staging + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu against Staging using DuckDNS + run: test/run-test.sh ubuntu-staging From 5c66f2956d85e87078958b5519fb64c512fe6a7a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 16 Jul 2020 11:37:02 +0100 Subject: [PATCH 192/337] Test space delimited SANS using DNS-01 authentication --- test/17-test-spaces-in-sans-dns01.bats | 75 +++++++++++++++++++ ...ats => 17-test-spaces-in-sans-http01.bats} | 8 +- test/9-multiple-domains-dns01.bats | 4 +- ... getssl-dns01-ignore-directory-domain.cfg} | 0 ....cfg => getssl-dns01-multiple-domains.cfg} | 0 ...ns01-spaces-sans-and-ignore-dir-domain.cfg | 35 +++++++++ test/test-config/getssl-dns01-spaces-sans.cfg | 34 +++++++++ 7 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 test/17-test-spaces-in-sans-dns01.bats rename test/{17-test-spaces-in-sans.bats => 17-test-spaces-in-sans-http01.bats} (94%) rename test/test-config/{getssl-ignore-directory-domain.cfg => getssl-dns01-ignore-directory-domain.cfg} (100%) rename test/test-config/{getssl-multiple-domains-dns01.cfg => getssl-dns01-multiple-domains.cfg} (100%) create mode 100644 test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg create mode 100644 test/test-config/getssl-dns01-spaces-sans.cfg diff --git a/test/17-test-spaces-in-sans-dns01.bats b/test/17-test-spaces-in-sans-dns01.bats new file mode 100644 index 0000000..c54de7b --- /dev/null +++ b/test/17-test-spaces-in-sans-dns01.bats @@ -0,0 +1,75 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Test behaviour if SANS line is space separated instead of comma separated (dns01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-dns01-spaces-sans.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Test renewal if SANS line is space separated instead of comma separated (dns01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + check_output_for_errors + cleanup_environment +} + + +@test "Test behaviour if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN (dns01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Test renewal if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN (dns01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + check_output_for_errors + cleanup_environment + + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a + done +} diff --git a/test/17-test-spaces-in-sans.bats b/test/17-test-spaces-in-sans-http01.bats similarity index 94% rename from test/17-test-spaces-in-sans.bats rename to test/17-test-spaces-in-sans-http01.bats index 01e7090..2d2d667 100644 --- a/test/17-test-spaces-in-sans.bats +++ b/test/17-test-spaces-in-sans-http01.bats @@ -11,7 +11,7 @@ setup() { } -@test "Test behaviour if SANS line is space separated instead of comma separated" { +@test "Test behaviour if SANS line is space separated instead of comma separated (http01)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi @@ -30,7 +30,7 @@ setup() { } -@test "Test renewal if SANS line is space separated instead of comma separated" { +@test "Test renewal if SANS line is space separated instead of comma separated (http01)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi @@ -41,7 +41,7 @@ setup() { } -@test "Test behaviour if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN" { +@test "Test behaviour if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN (http01)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi @@ -60,7 +60,7 @@ setup() { } -@test "Test renewal if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN" { +@test "Test renewal if SANS line is space separated and IGNORE_DIRECTORY_DOMAIN (http01)" { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats index c1de91c..65eab09 100644 --- a/test/9-multiple-domains-dns01.bats +++ b/test/9-multiple-domains-dns01.bats @@ -16,7 +16,7 @@ setup() { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - CONFIG_FILE="getssl-multiple-domains-dns01.cfg" + CONFIG_FILE="getssl-dns01-multiple-domains.cfg" setup_environment # Add top level domain from SANS to DNS @@ -47,7 +47,7 @@ setup() { if [ -n "$STAGING" ]; then skip "Using staging server, skipping internal test" fi - CONFIG_FILE="getssl-ignore-directory-domain.cfg" + CONFIG_FILE="getssl-dns01-ignore-directory-domain.cfg" setup_environment # Add top level domain from SANS to DNS diff --git a/test/test-config/getssl-ignore-directory-domain.cfg b/test/test-config/getssl-dns01-ignore-directory-domain.cfg similarity index 100% rename from test/test-config/getssl-ignore-directory-domain.cfg rename to test/test-config/getssl-dns01-ignore-directory-domain.cfg diff --git a/test/test-config/getssl-multiple-domains-dns01.cfg b/test/test-config/getssl-dns01-multiple-domains.cfg similarity index 100% rename from test/test-config/getssl-multiple-domains-dns01.cfg rename to test/test-config/getssl-dns01-multiple-domains.cfg diff --git a/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg new file mode 100644 index 0000000..453d046 --- /dev/null +++ b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg @@ -0,0 +1,35 @@ +# 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 + +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list +IGNORE_DIRECTORY_DOMAIN="true" +SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-dns01-spaces-sans.cfg b/test/test-config/getssl-dns01-spaces-sans.cfg new file mode 100644 index 0000000..2b7e02b --- /dev/null +++ b/test/test-config/getssl-dns01-spaces-sans.cfg @@ -0,0 +1,34 @@ +# 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 + +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From e6336b9c8b2605895e824a6e6fee25dbb8a86c10 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 4 Aug 2020 21:23:13 +0100 Subject: [PATCH 193/337] Add some debug statements to CHECK_REMOTE --- getssl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/getssl b/getssl index a5e72cf..56fc765 100755 --- a/getssl +++ b/getssl @@ -2842,6 +2842,8 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} 2>/dev/null \ | openssl x509 -noout -fingerprint 2>/dev/null) CERT_LOCAL=$(openssl x509 -noout -fingerprint < "${CERTS[i]}" 2>/dev/null) + debug CERT_LOCAL="${CERT_LOCAL}" + debug CERT_REMOTE="${CERT_REMOTE}" if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then info "${DOMAIN} - ${TYPES[i]} certificate installed OK on server" else From 1bed21bff574fe552bdf94b40741c5e8be9d02bc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 4 Aug 2020 21:23:45 +0100 Subject: [PATCH 194/337] Fix bug detecting the nginx version --- test/test_helper.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_helper.bash b/test/test_helper.bash index 9358619..d498f93 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -12,7 +12,7 @@ check_certificates() # https://unix.stackexchange.com/questions/285924/how-to-compare-a-programs-version-in-a-shell-script check_nginx() { requiredver="1.11.0" - currentver="$(nginx -v)" + currentver=$(nginx -v 2>&1) if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then export OLD_NGINX="false" else From 73a7939f4fe8b19c8cabc91750b158869c516f61 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 4 Aug 2020 21:24:22 +0100 Subject: [PATCH 195/337] Fix DOMAIN_CERT and DOMAIN_KEY --- test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg index 41581ae..9cf155f 100644 --- a/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-old-nginx.cfg @@ -19,8 +19,8 @@ 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.ec.crt" -DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.ec.key" +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 From d188e9ec7d07d817c8c25183516040be3e39385b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 5 Aug 2020 20:48:14 +0100 Subject: [PATCH 196/337] Use -sigalgs instead of -cipher to check certificate installed --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 56fc765..385c3bf 100755 --- a/getssl +++ b/getssl @@ -2826,7 +2826,7 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - PARAMS=("-cipher RSA" "-cipher ECDSA") + PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA512") CERTS=("$CERT_FILE" "${CERT_FILE%.*}.ec.crt") TYPES=("rsa" "$PRIVATE_KEY_ALG") else From ac4f848dac14f53c6e576d35254f5ac0083d4e6a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 5 Aug 2020 20:49:14 +0100 Subject: [PATCH 197/337] Include tls1.3 in the tests --- test/test-config/nginx-ubuntu-dual-certs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-config/nginx-ubuntu-dual-certs b/test/test-config/nginx-ubuntu-dual-certs index ce1fbbf..1eceecd 100644 --- a/test/test-config/nginx-ubuntu-dual-certs +++ b/test/test-config/nginx-ubuntu-dual-certs @@ -42,7 +42,7 @@ server { # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; server_name _; ssl_certificate /etc/nginx/pki/server.crt; From 68f04db61ffc8b5cc667aaa5d01ad147c088c401 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 6 Aug 2020 14:49:20 +0100 Subject: [PATCH 198/337] Add ECDSA+SHA384 to -sigalgs --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 385c3bf..b22c83b 100755 --- a/getssl +++ b/getssl @@ -234,6 +234,7 @@ # 2020-06-06 Fix missing URL_revoke definition when no CA directory suffix (#566) # 2020-06-18 Fix CHECK_REMOTE for DUAL_RSA_ECDSA (#570) # 2020-07-14 Support space separated SANS (#574) (2.29) +# 2020-08-06 Use -sigalgs instead of -cipher when checking remote for tls1.3 (#570) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -2565,7 +2566,7 @@ fi if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then debug "getting certificate for $DOMAIN from remote server" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - CIPHER="-cipher RSA" + CIPHER="-sigalgs RSA-PSS+SHA256" else CIPHER="" fi @@ -2826,7 +2827,7 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA512") + PARAMS=("-sigalgs RSA-PSS+SHA256" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") CERTS=("$CERT_FILE" "${CERT_FILE%.*}.ec.crt") TYPES=("rsa" "$PRIVATE_KEY_ALG") else From 0b4a8e505ad3c1af058b65890149a98098d5056c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 20 Aug 2020 18:55:04 +0100 Subject: [PATCH 199/337] Add test for certificate created but not installed --- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 24 ++++++++++++++ ...dual-rsa-ecdsa-2-locations-wrong-nginx.cfg | 32 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-wrong-nginx.cfg diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index a8e7653..593d770 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -40,6 +40,8 @@ teardown() { create_certificate assert_success check_output_for_errors + assert_line --partial "rsa certificate installed OK on server" + assert_line --partial "prime256v1 certificate installed OK on server" # Check that the RSA chain and key have been copied to both locations assert [ -e "/etc/nginx/pki/domain-chain.crt" ] @@ -53,3 +55,25 @@ teardown() { assert [ -e "/etc/nginx/pki/private/server.ec.key" ] assert [ -e "/root/a.${GETSSL_HOST}/server.ec.key" ] } + + +@test "Create dual certificates and copy to two locations but not returned by server" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations-wrong-nginx.cfg" + else + skip "Skipping as old nginx servers cannot return both certificates" + fi + + setup_environment + mkdir -p /root/a.${GETSSL_HOST} + + init_getssl + create_certificate + assert_failure + assert_line --partial "prime256v1 certificate obtained but not installed on server" +} diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-wrong-nginx.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-wrong-nginx.cfg new file mode 100644 index 0000000..80533ce --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations-wrong-nginx.cfg @@ -0,0 +1,32 @@ +# Test that more than one location can be specified for CERT and KEY locations and that the +# files are copied to both locations when both RSA and ECDSA certificates are created +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="true" + +# 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;/root/a.${GETSSL_HOST}/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From e9912e790db735633eee65b94bcea078fbc561d5 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 20 Aug 2020 18:58:25 +0100 Subject: [PATCH 200/337] Update sigalgs, different error if certificate not installed vs different --- getssl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index b22c83b..6e7fb80 100755 --- a/getssl +++ b/getssl @@ -2565,11 +2565,11 @@ fi # 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 debug "getting certificate for $DOMAIN from remote server" -if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - CIPHER="-sigalgs RSA-PSS+SHA256" -else + if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then + CIPHER="-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" + else CIPHER="" -fi + fi # shellcheck disable=SC2086 EX_CERT=$(echo \ | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${CIPHER} 2>/dev/null \ @@ -2827,7 +2827,7 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - PARAMS=("-sigalgs RSA-PSS+SHA256" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") + PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") CERTS=("$CERT_FILE" "${CERT_FILE%.*}.ec.crt") TYPES=("rsa" "$PRIVATE_KEY_ALG") else @@ -2847,6 +2847,9 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then debug CERT_REMOTE="${CERT_REMOTE}" if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then info "${DOMAIN} - ${TYPES[i]} certificate installed OK on server" + elif [[ "$CERT_REMOTE" == "" ]]; then + info "${CERTS[i]} not returned by server" + error_exit "${DOMAIN} - ${TYPES[i]} certificate obtained but not installed on server" else info "${CERTS[i]} didn't match server" error_exit "${DOMAIN} - ${TYPES[i]} certificate obtained but certificate on server is different from the new certificate" From 82fa60a81f072777168de2fd0d526879b8bc9999 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 24 Aug 2020 18:34:58 +0100 Subject: [PATCH 201/337] Fix tests for old nginx --- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 6 ++++-- test/test_helper.bash | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats index 593d770..6f75f25 100644 --- a/test/6-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -40,8 +40,10 @@ teardown() { create_certificate assert_success check_output_for_errors - assert_line --partial "rsa certificate installed OK on server" - assert_line --partial "prime256v1 certificate installed OK on server" + if [ "$OLD_NGINX" = "false" ]; then + assert_line --partial "rsa certificate installed OK on server" + assert_line --partial "prime256v1 certificate installed OK on server" + fi # Check that the RSA chain and key have been copied to both locations assert [ -e "/etc/nginx/pki/domain-chain.crt" ] diff --git a/test/test_helper.bash b/test/test_helper.bash index d498f93..db79ea4 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -12,7 +12,7 @@ check_certificates() # https://unix.stackexchange.com/questions/285924/how-to-compare-a-programs-version-in-a-shell-script check_nginx() { requiredver="1.11.0" - currentver=$(nginx -v 2>&1) + currentver=$(nginx -v 2>&1 | awk -F"/" '{print $2}') if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then export OLD_NGINX="false" else From 68b05d7c6e051d35bd585379f60e04c7bf0c4f35 Mon Sep 17 00:00:00 2001 From: Paul Slootman Date: Mon, 31 Aug 2020 13:06:42 +0200 Subject: [PATCH 202/337] Fix slow fork bomb when directory containing getssl isn't writeable (#440) getssl updets itself by: - checking for a new version - if so: - download the new one into a tmp location - rename the current one to name with version appended - rename the tmp file to the current location - run the new version If the renaming fails, the old version gets run, which again downloads, etc. Now exit if the rename (install command) fails. --- getssl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index a5e72cf..734f176 100755 --- a/getssl +++ b/getssl @@ -234,6 +234,7 @@ # 2020-06-06 Fix missing URL_revoke definition when no CA directory suffix (#566) # 2020-06-18 Fix CHECK_REMOTE for DUAL_RSA_ECDSA (#570) # 2020-07-14 Support space separated SANS (#574) (2.29) +# 2020-08-31 Fix slow fork bomb when directory containing getssl isn't writeable (#440) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -608,8 +609,12 @@ check_getssl_upgrade() { # check if a more recent version of code is available a # use a default of 0 for cases where the latest code has not been obtained. if [[ "${latestvdec:-0}" -gt "$localvdec" ]]; then if [[ ${_UPGRADE} -eq 1 ]]; then - install "$0" "${0}.v${VERSION}" - install -m 700 "$TEMP_UPGRADE_FILE" "$0" + if ! install "$0" "${0}.v${VERSION}"; then + error_exit "problem renaming old version while updating, check permissions" + fi + if ! install -m 700 "$TEMP_UPGRADE_FILE" "$0"; then + error_exit "problem installing new version while updating, check permissions" + fi if [[ ${_MUTE} -eq 0 ]]; then echo "Updated getssl from v${VERSION} to v${latestversion}" echo "these update notification can be turned off using the -Q option" From 47b3962c139d799f9c1b19a0d5a4eb4ecde2b665 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 2 Sep 2020 08:54:39 +0100 Subject: [PATCH 203/337] Support older versions of openssl which don't support RSA-PSS --- getssl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 6e7fb80..5e4285d 100755 --- a/getssl +++ b/getssl @@ -2566,7 +2566,13 @@ fi if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then debug "getting certificate for $DOMAIN from remote server" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - CIPHER="-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" + # shellcheck disable=SC2086 + # check if openssl supports RSA-PSS + if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS 2>/dev/null) ]]; then + CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA512" + else + CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" + fi else CIPHER="" fi @@ -2827,7 +2833,14 @@ fi if [[ ${CHECK_REMOTE} == "true" ]]; then sleep "$CHECK_REMOTE_WAIT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") + # shellcheck disable=SC2086 + # check if openssl supports RSA-PSS + if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS 2>/dev/null) ]]; then + PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") + else + PARAMS=("-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") + fi + CERTS=("$CERT_FILE" "${CERT_FILE%.*}.ec.crt") TYPES=("rsa" "$PRIVATE_KEY_ALG") else From 693c37d4db4225508675e5a7f2fa5fc81f906675 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 2 Sep 2020 08:55:26 +0100 Subject: [PATCH 204/337] Fix some test fragility --- test/14-test-revoke.bats | 2 +- test/15-test-revoke-no-suffix.bats | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/14-test-revoke.bats b/test/14-test-revoke.bats index 2e95e0d..e624552 100644 --- a/test/14-test-revoke.bats +++ b/test/14-test-revoke.bats @@ -38,5 +38,5 @@ setup() { run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA assert_success - check_output_for_errors + check_output_for_errors "debug" } diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 7b19c5a..26f5f3f 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -38,5 +38,5 @@ setup() { run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA assert_success - check_output_for_errors + check_output_for_errors "debug" } From 50310a722ad707bc974613c45645572c22c9dc55 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 2 Sep 2020 08:56:46 +0100 Subject: [PATCH 205/337] Fix bug when SANS is separated by commas and spaces --- getssl | 4 +++ test/17-test-spaces-in-sans-dns01.bats | 19 +++++++++++ test/17-test-spaces-in-sans-http01.bats | 19 +++++++++++ .../getssl-dns01-spaces-and-commas-sans.cfg | 34 +++++++++++++++++++ .../getssl-http01-spaces-and-commas-sans.cfg | 28 +++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 test/test-config/getssl-dns01-spaces-and-commas-sans.cfg create mode 100644 test/test-config/getssl-http01-spaces-and-commas-sans.cfg diff --git a/getssl b/getssl index 6e7fb80..c317d3d 100755 --- a/getssl +++ b/getssl @@ -2521,6 +2521,10 @@ if [[ -s "$DOMAIN_DIR/getssl.cfg" ]]; then . "$DOMAIN_DIR/getssl.cfg" fi +# Ensure SANS is comma separated by replacing any number of commas or spaces with a single comma +# shellcheck disable=SC2001 +SANS=$(echo "$SANS" | sed 's/[, ]\+/,/g') + # from SERVER_TYPE set REMOTE_PORT and REMOTE_EXTRA set_server_type diff --git a/test/17-test-spaces-in-sans-dns01.bats b/test/17-test-spaces-in-sans-dns01.bats index c54de7b..75dce4b 100644 --- a/test/17-test-spaces-in-sans-dns01.bats +++ b/test/17-test-spaces-in-sans-dns01.bats @@ -73,3 +73,22 @@ setup() { curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a done } + + +@test "Test behaviour if SANS line is comma and space separated (dns01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-dns01-spaces-and-commas-sans.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} diff --git a/test/17-test-spaces-in-sans-http01.bats b/test/17-test-spaces-in-sans-http01.bats index 2d2d667..f3ba482 100644 --- a/test/17-test-spaces-in-sans-http01.bats +++ b/test/17-test-spaces-in-sans-http01.bats @@ -73,3 +73,22 @@ setup() { curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a done } + + +@test "Test behaviour if SANS line is comma and space separated (http01)" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-spaces-and-commas-sans.cfg" + setup_environment + + # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + done + + init_getssl + create_certificate + assert_success + check_output_for_errors +} diff --git a/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg new file mode 100644 index 0000000..fe38df9 --- /dev/null +++ b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg @@ -0,0 +1,34 @@ +# 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 + +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" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}, b.${GETSSL_HOST}, c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-http01-spaces-and-commas-sans.cfg b/test/test-config/getssl-http01-spaces-and-commas-sans.cfg new file mode 100644 index 0000000..c4f02c7 --- /dev/null +++ b/test/test-config/getssl-http01-spaces-and-commas-sans.cfg @@ -0,0 +1,28 @@ +# 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 + +CA="https://pebble:14000/dir" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}, b.${GETSSL_HOST}, c.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +# Use a single ACL for all checks +USE_SINGLE_ACL="true" + +# Location for all your certs, these can either be on the server (full path name) +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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From d95b3e61b79c79369ab4c43c9c1c4a64ae9db5b7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 2 Sep 2020 15:25:59 +0100 Subject: [PATCH 206/337] Fix openssl RSA-PSS check --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 5e4285d..2737f1a 100755 --- a/getssl +++ b/getssl @@ -2568,7 +2568,7 @@ if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then # shellcheck disable=SC2086 # check if openssl supports RSA-PSS - if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS 2>/dev/null) ]]; then + if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA512" else CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" @@ -2835,7 +2835,7 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then # shellcheck disable=SC2086 # check if openssl supports RSA-PSS - if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS 2>/dev/null) ]]; then + if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") else PARAMS=("-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") From 07bf481a591ac722f7e28017379d689ac04a550b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 3 Sep 2020 20:41:51 +0100 Subject: [PATCH 207/337] Increase DNS_EXTRA_WAIT to allow dns changes extra time to propagate --- test/test-config/getssl-staging-dns01-no-suffix.cfg | 2 +- test/test-config/getssl-staging-dns01.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg index 32fdda3..47d2cec 100644 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ b/test/test-config/getssl-staging-dns01-no-suffix.cfg @@ -7,7 +7,7 @@ DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true -DNS_EXTRA_WAIT=60 +DNS_EXTRA_WAIT=120 ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 8c12ee1..348cabf 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -7,7 +7,7 @@ DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true -DNS_EXTRA_WAIT=60 +DNS_EXTRA_WAIT=120 ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" From 65befd7db5c44ed89ab246bd262d7631272e36c1 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 6 Sep 2020 21:24:38 +0100 Subject: [PATCH 208/337] Update revision history and version number --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index b1b7e16..35a6a73 100755 --- a/getssl +++ b/getssl @@ -237,11 +237,12 @@ # 2020-08-06 Use -sigalgs instead of -cipher when checking remote for tls1.3 (#570) # 2020-08-31 Fix slow fork bomb when directory containing getssl isn't writeable (#440) # 2020-09-01 Use RSA-PSS when checking remote for DUAL_RSA_ECDSA (#570) +# 2020-09-02 Fix issue when SANS is space and comma separated (#579) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.29" +VERSION="2.30" # defaults ACCOUNT_KEY_LENGTH=4096 From c1346a1be8c078b62464dc4b5f0b913dfd7dd240 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 10 Sep 2020 09:45:27 +0100 Subject: [PATCH 209/337] Merge staging server tests into normal tests --- test/10-mixed-case-staging.bats | 22 --------------- test/10-mixed-case.bats | 6 ++-- test/2-simple-dns01-nslookup.bats | 2 +- test/7-staging-dns01-dig.bats | 32 --------------------- test/7-staging-dns01-nslookup.bats | 45 ------------------------------ 5 files changed, 5 insertions(+), 102 deletions(-) delete mode 100644 test/10-mixed-case-staging.bats delete mode 100644 test/7-staging-dns01-dig.bats delete mode 100644 test/7-staging-dns01-nslookup.bats diff --git a/test/10-mixed-case-staging.bats b/test/10-mixed-case-staging.bats deleted file mode 100644 index ea622f7..0000000 --- a/test/10-mixed-case-staging.bats +++ /dev/null @@ -1,22 +0,0 @@ -#! /usr/bin/env bats - -load '/bats-support/load.bash' -load '/bats-assert/load.bash' -load '/getssl/test/test_helper.bash' - - -@test "Check can create certificate if domain is not lowercase using staging server and DuckDNS" { - if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" - fi - - CONFIG_FILE="getssl-staging-dns01.cfg" - GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) - - setup_environment - init_getssl - create_certificate - - assert_success - check_output_for_errors -} diff --git a/test/10-mixed-case.bats b/test/10-mixed-case.bats index b1d8f07..12b787c 100644 --- a/test/10-mixed-case.bats +++ b/test/10-mixed-case.bats @@ -28,9 +28,11 @@ setup() { @test "Check that DNS-01 verification works if the domain is not lowercase" { if [ -n "$STAGING" ]; then - skip "Using staging server, skipping internal test" + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" fi - CONFIG_FILE="getssl-dns01.cfg" + GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) setup_environment diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index 39c8bae..d13b318 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -30,7 +30,7 @@ teardown() { @test "Create new certificate using DNS-01 verification (nslookup)" { CONFIG_FILE="getssl-dns01.cfg" if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-dns01.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" fi setup_environment diff --git a/test/7-staging-dns01-dig.bats b/test/7-staging-dns01-dig.bats deleted file mode 100644 index 3d70ce5..0000000 --- a/test/7-staging-dns01-dig.bats +++ /dev/null @@ -1,32 +0,0 @@ -#! /usr/bin/env bats - -load '/bats-support/load.bash' -load '/bats-assert/load.bash' -load '/getssl/test/test_helper.bash' - - - -@test "Create new certificate using staging server, dig and DuckDNS" { - skip - if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" - fi - CONFIG_FILE="getssl-staging-dns01.cfg" - - setup_environment - init_getssl - create_certificate - assert_success - check_output_for_errors -} - -@test "Force renewal of certificate using staging server, dig and DuckDNS" { - skip - if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" - fi - run ${CODE_DIR}/getssl -f $GETSSL_HOST - assert_success - check_output_for_errors - cleanup_environment -} diff --git a/test/7-staging-dns01-nslookup.bats b/test/7-staging-dns01-nslookup.bats deleted file mode 100644 index bd8d9da..0000000 --- a/test/7-staging-dns01-nslookup.bats +++ /dev/null @@ -1,45 +0,0 @@ -#! /usr/bin/env bats - -load '/bats-support/load.bash' -load '/bats-assert/load.bash' -load '/getssl/test/test_helper.bash' - -# This is run for every test -setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - if [ -f /usr/bin/dig ]; then - mv /usr/bin/dig /usr/bin/dig.getssl.bak - fi -} - - -teardown() { - if [ -f /usr/bin/dig.getssl.bak ]; then - mv /usr/bin/dig.getssl.bak /usr/bin/dig - fi -} - - -@test "Create new certificate using staging server, nslookup and DuckDNS" { - if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" - fi - CONFIG_FILE="getssl-staging-dns01.cfg" - - setup_environment - init_getssl - create_certificate - assert_success - check_output_for_errors "debug" -} - - -@test "Force renewal of certificate using staging server, nslookup and DuckDNS" { - if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" - fi - run ${CODE_DIR}/getssl -f $GETSSL_HOST - assert_success - check_output_for_errors "debug" - cleanup_environment -} From fb1823e356c999b8397d248c572996eab29a2e1c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 10 Sep 2020 09:46:57 +0100 Subject: [PATCH 210/337] Retry DNS_ADD_COMMAND if dns isn't updated after waiting 10 times --- getssl | 27 +++++++++++++-- test/18-staging-retry-dns-add.bats | 20 +++++++++++ test/dns_fail_add_duckdns | 19 +++++++++++ .../getssl-staging-dns01-fail-dns-add.cfg | 33 +++++++++++++++++++ 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 test/18-staging-retry-dns-add.bats create mode 100644 test/dns_fail_add_duckdns create mode 100644 test/test-config/getssl-staging-dns01-fail-dns-add.cfg diff --git a/getssl b/getssl index 35a6a73..7ae6ae5 100755 --- a/getssl +++ b/getssl @@ -258,8 +258,10 @@ CSR_SUBJECT="/" CURL_USERAGENT="${PROGNAME}/${VERSION}" DEACTIVATE_AUTH="false" DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" -DNS_EXTRA_WAIT=60 -DNS_WAIT=10 +DNS_EXTRA_WAIT=60 # How long to wait after the DNS has updated before telling the ACME server to check. +DNS_WAIT_RETRY_ADD="false" # Try the dns_add_command again if the DNS record hasn't updated +DNS_WAIT=10 # How long to wait before checking the DNS record again +DNS_WAIT_COUNT=100 # How many times to wait for the DNS record to update DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" GETSSL_IGNORE_CP_PRESERVE="false" @@ -454,6 +456,7 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # if ACME response is that their check gave an invalid response, error exit if [[ "$status" == "invalid" ]] ; then err_detail=$(echo "$response" | grep "detail") + #! FIXME need to check for "DNS problem: SERVFAIL looking up CAA ..." and retry error_exit "$domain:Verify error:$err_detail" fi @@ -1187,13 +1190,29 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then if [[ "$check_result" == *"$auth_key"* ]]; then check_dns="success" else - if [[ $ntries -lt 100 ]]; then + if [[ $ntries -lt $DNS_WAIT_COUNT ]]; then ntries=$(( ntries + 1 )) + + if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 == 0 )) ]]; then + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "$d" | tr A-Z a-z) + debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then + error_exit "DNS_ADD_COMMAND failed for domain $d" + fi + + fi info "checking DNS at ${ns} for ${d}. Attempt $ntries/100 gave wrong result, "\ "waiting $DNS_WAIT secs before checking again" sleep $DNS_WAIT else debug "dns check failed - removing existing value" + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "$d" | tr A-Z a-z) + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" + # remove $dnsfile after each loop. + rm -f "$dnsfile" + error_exit "checking _acme-challenge.${d} gave $check_result not $auth_key" fi fi @@ -1235,6 +1254,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ "$os" == "cygwin" ]]; then gad_d="$orig_gad_d" + # shellcheck disable=SC2086 all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ | grep "primary name server" \ | awk '{print $NF}') @@ -1314,6 +1334,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ "$HAS_NSLOOKUP" == "true" ]]; then gad_d="$orig_gad_d" debug Using "nslookup -debug -type=soa -type=ns $gad_d $gad_s" to find primary name server + # shellcheck disable=SC2086 res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then diff --git a/test/18-staging-retry-dns-add.bats b/test/18-staging-retry-dns-add.bats new file mode 100644 index 0000000..8b636d6 --- /dev/null +++ b/test/18-staging-retry-dns-add.bats @@ -0,0 +1,20 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + + +@test "Check retry add dns command if dns isn't updated (DuckDNS)" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-staging-dns01-fail-dns-add.cfg" + + setup_environment + init_getssl + create_certificate -d + assert_failure + assert_line --partial "Retrying adding dns via command" +} diff --git a/test/dns_fail_add_duckdns b/test/dns_fail_add_duckdns new file mode 100644 index 0000000..03df89f --- /dev/null +++ b/test/dns_fail_add_duckdns @@ -0,0 +1,19 @@ +#!/bin/bash + +# Special test script which will always fail to update dns + +token=${DUCKDNS_TOKEN:-} + +if [ -z "$token" ]; then + echo "DUCKDNS_TOKEN not set" + exit 1 +fi + +domain="$1" + +response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=FAIL") +if [ "$response" != "OK" ]; then + echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" + echo "Response: $response" + exit 1 +fi diff --git a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg new file mode 100644 index 0000000..2985d32 --- /dev/null +++ b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg @@ -0,0 +1,33 @@ +# Special config to test that the retry dns_add_command logic works +# +CA="https://acme-staging-v02.api.letsencrypt.org/directory" + +# Generic staging config +VALIDATE_VIA_DNS=true +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" +PUBLIC_DNS_SERVER=ns2.duckdns.org +CHECK_ALL_AUTH_DNS=true + +# Test that the retry works (dns_add_command will always fail) +DNS_WAIT_RETRY_ADD="true" +DNS_ADD_COMMAND="/getssl/test/dns_fail_add_duckdns" + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 + +# Standard config +ACCOUNT_KEY_TYPE="rsa" +PRIVATE_KEY_ALG="rsa" +SANS="" +ACL=('/var/www/html/.well-known/acme-challenge') +USE_SINGLE_ACL="false" +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 +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" +SERVER_TYPE="https" +CHECK_REMOTE="true" From 8c3210e4d998ed0842583e6e782a8548e66b4f7f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 13 Sep 2020 15:31:22 +0100 Subject: [PATCH 211/337] Add tests for multiple ACL --- test/1-simple-http01-two-acl.bats | 26 +++++++++++++++++++ test/test-config/getssl-http01-two-acl.cfg | 29 ++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 test/1-simple-http01-two-acl.bats create mode 100644 test/test-config/getssl-http01-two-acl.cfg diff --git a/test/1-simple-http01-two-acl.bats b/test/1-simple-http01-two-acl.bats new file mode 100644 index 0000000..0169bd0 --- /dev/null +++ b/test/1-simple-http01-two-acl.bats @@ -0,0 +1,26 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Check that can install challenge token to multiple locations when using HTTP-01 verification" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-http01-two-acl.cfg" + setup_environment + init_getssl + create_certificate -d + assert_success + assert_output --partial "to /var/www/html/.well-known/acme-challenge" + assert_output --partial "to /var/webroot/html/.well-known/acme-challenge" + check_output_for_errors "debug" +} diff --git a/test/test-config/getssl-http01-two-acl.cfg b/test/test-config/getssl-http01-two-acl.cfg new file mode 100644 index 0000000..dcf28fd --- /dev/null +++ b/test/test-config/getssl-http01-two-acl.cfg @@ -0,0 +1,29 @@ +# 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 +# +CA="https://pebble:14000/dir" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge;/var/webroot/html/.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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" From f644f97ab685ea541785f56916837fde15cfecd8 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 15 Sep 2020 08:42:32 +0100 Subject: [PATCH 212/337] Remove ACL from cfg for dns tests --- .../getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg | 6 ------ test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg | 6 ------ .../test-config/getssl-dns01-ignore-directory-domain.cfg | 9 --------- test/test-config/getssl-dns01-multiple-domains.cfg | 9 --------- test/test-config/getssl-dns01-spaces-and-commas-sans.cfg | 6 ------ .../getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg | 6 ------ test/test-config/getssl-dns01-spaces-sans.cfg | 6 ------ test/test-config/getssl-dns01.cfg | 6 ------ 8 files changed, 54 deletions(-) diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg index 062a47c..235e1da 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg @@ -17,12 +17,6 @@ PRIVATE_KEY_ALG="prime256v1" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=('/var/www/html/.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.ec.crt" diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 7b031c8..6bbcc44 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -17,12 +17,6 @@ PRIVATE_KEY_ALG="prime256v1" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=('/var/www/html/.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" diff --git a/test/test-config/getssl-dns01-ignore-directory-domain.cfg b/test/test-config/getssl-dns01-ignore-directory-domain.cfg index 9777891..4bbd766 100644 --- a/test/test-config/getssl-dns01-ignore-directory-domain.cfg +++ b/test/test-config/getssl-dns01-ignore-directory-domain.cfg @@ -14,15 +14,6 @@ DNS_EXTRA_WAIT="" IGNORE_DIRECTORY_DOMAIN="true" SANS="getssl.test,$GETSSL_HOST" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=( - '/var/www/html/.well-known/acme-challenge' - '/var/www/html/.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" diff --git a/test/test-config/getssl-dns01-multiple-domains.cfg b/test/test-config/getssl-dns01-multiple-domains.cfg index 8754677..f0fae04 100644 --- a/test/test-config/getssl-dns01-multiple-domains.cfg +++ b/test/test-config/getssl-dns01-multiple-domains.cfg @@ -13,15 +13,6 @@ DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="getssl.test" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=( - '/var/www/html/.well-known/acme-challenge' - '/var/www/html/.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" diff --git a/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg index fe38df9..2660a9d 100644 --- a/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg +++ b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg @@ -13,12 +13,6 @@ DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="a.${GETSSL_HOST}, b.${GETSSL_HOST}, c.${GETSSL_HOST}" -# Acme Challenge Location. -ACL=('/var/www/html/.well-known/acme-challenge') - -# Use a single ACL for all checks -USE_SINGLE_ACL="true" - # Location for all your certs, these can either be on the server (full path name) DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" diff --git a/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg index 453d046..6b87010 100644 --- a/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg +++ b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg @@ -14,12 +14,6 @@ DNS_EXTRA_WAIT="" IGNORE_DIRECTORY_DOMAIN="true" SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" -# Acme Challenge Location. -ACL=('/var/www/html/.well-known/acme-challenge') - -# Use a single ACL for all checks -USE_SINGLE_ACL="true" - # Location for all your certs, these can either be on the server (full path name) DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" diff --git a/test/test-config/getssl-dns01-spaces-sans.cfg b/test/test-config/getssl-dns01-spaces-sans.cfg index 2b7e02b..8438228 100644 --- a/test/test-config/getssl-dns01-spaces-sans.cfg +++ b/test/test-config/getssl-dns01-spaces-sans.cfg @@ -13,12 +13,6 @@ DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" -# Acme Challenge Location. -ACL=('/var/www/html/.well-known/acme-challenge') - -# Use a single ACL for all checks -USE_SINGLE_ACL="true" - # Location for all your certs, these can either be on the server (full path name) DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 0b816b1..883f29e 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -13,12 +13,6 @@ DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=('/var/www/html/.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" From 2d6d4719332478f767fd8bf80bebe8fc6666bd7b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 15 Sep 2020 09:42:43 +0100 Subject: [PATCH 213/337] make executable --- test/dns_fail_add_duckdns | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/dns_fail_add_duckdns diff --git a/test/dns_fail_add_duckdns b/test/dns_fail_add_duckdns old mode 100644 new mode 100755 From 90a418801d5b3669e10ef627886d893e9a749bd9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 15 Sep 2020 15:04:49 +0100 Subject: [PATCH 214/337] Add retry logic for "Bad Gateway" duckdns error --- dns_scripts/dns_add_duckdns | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dns_scripts/dns_add_duckdns b/dns_scripts/dns_add_duckdns index 9f61c8c..9d1776a 100755 --- a/dns_scripts/dns_add_duckdns +++ b/dns_scripts/dns_add_duckdns @@ -10,8 +10,17 @@ fi domain="$1" txtvalue="$2" +i=1 response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=${txtvalue}") + +while [[ "${response}" == *"502 Bad Gateway"* ]] && [ $i -le 5 ]; do + echo "Retrying Bad Gateway response (attempt $i of 5)" + sleep 5 + i=$((i+1)) + response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=${txtvalue}") +done + if [ "$response" != "OK" ]; then echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" echo "Response: $response" From ad097a5b29e068debc14f8439abe78441638e0b6 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 15 Sep 2020 15:37:41 +0100 Subject: [PATCH 215/337] Add bad gateway retry logic to duckdns del command --- dns_scripts/dns_del_duckdns | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dns_scripts/dns_del_duckdns b/dns_scripts/dns_del_duckdns index f20c664..bdb653c 100755 --- a/dns_scripts/dns_del_duckdns +++ b/dns_scripts/dns_del_duckdns @@ -3,8 +3,17 @@ # need to add your Token for duckdns below token=${DUCKDNS_TOKEN:-} domain="$1" +i=1 response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=&clear=true") + +while [[ "${response}" == *"502 Bad Gateway"* ]] && [ $i -le 5 ]; do + echo "Retrying Bad Gateway response (attempt $i of 5)" + sleep 5 + i=$((i+1)) + response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=&clear=true") +done + if [ "$response" != "OK" ]; then echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" echo "$response" From 75d06beb5adca32c2172e14763c3494b337b24c4 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 17 Sep 2020 13:50:50 +0100 Subject: [PATCH 216/337] Fix get_auth_dns to return multiple nameservers (broken in v2.21) --- getssl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index 7ae6ae5..c92733f 100755 --- a/getssl +++ b/getssl @@ -1222,7 +1222,7 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then done if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then - info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME-server to check the dns" + info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME server to check the dns" sleep "$DNS_EXTRA_WAIT" fi @@ -1267,20 +1267,22 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -n "$HAS_DIG_OR_DRILL" ]]; then gad_d="$orig_gad_d" - debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver # Use SOA +trace to find the name server if [[ -z "$gad_s" ]]; then - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1) + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d" to find primary nameserver + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W") else - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1) + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W") fi # fallback to existing code if [[ -z "$res" ]]; then - debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s" if [[ -z "$gad_s" ]]; then #checking for CNAMEs (need grep as dig 9.11 sometimes returns everything not just CNAME entries) + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d" res=$($HAS_DIG_OR_DRILL CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) else + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s" res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) fi if [[ -n "$res" ]]; then # domain is a CNAME so get main domain @@ -1294,11 +1296,12 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n # dig NS www.getssl.text # > www.getssl.test. IN CNAME getssl.test # > getssl.test. IN NS ns1.duckdns.org - debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver if [[ -z "$gad_s" ]]; then - res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1) + debug Using "$HAS_DIG_OR_DRILL NS $gad_d" to find primary nameserver + res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W") else - res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1) + debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver + res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W") fi fi if [[ -n "$res" ]]; then @@ -2194,6 +2197,7 @@ write_domain_template() { # write out a template file for a domain. # where "/path/to/your/website/folder/" is the path, on your web server, to the web root for your domain. # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, # password, host, port (explicitly needed even if using default port 443) and path on the server. + # Multiple locations can be defined for a file by separating the locations with a semi-colon. #ACL=('/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' From 36c92d150e954390fd4f8603cce1997a98bcbdf8 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 23 Sep 2020 22:34:40 +0100 Subject: [PATCH 217/337] Fixes and changes to test get_auth_dns --- getssl | 98 +++++++++++------ test/19-test-get_auth_dns-dig.bats | 166 +++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 33 deletions(-) create mode 100644 test/19-test-get_auth_dns-dig.bats diff --git a/getssl b/getssl index c92733f..05b2f2f 100755 --- a/getssl +++ b/getssl @@ -292,6 +292,9 @@ _NOTIFY_VALID=0 _QUIET=0 _RECREATE_CSR=0 _REVOKE=0 +_RUNNING_TEST=0 +_TEST_SKIP_CNAME_CALL=0 +_TEST_SKIP_SOA_CALL=0 _UPGRADE=0 _UPGRADE_CHECK=1 _USE_DEBUG=0 @@ -942,8 +945,19 @@ date_renew() { # calculates the renewal time in epoch debug() { # write out debug info if the debug flag has been set if [[ ${_USE_DEBUG} -eq 1 ]]; then - echo " " - echo "$@" + # If running tests then output in TAP format (for debugging tests) + if [[ -n "$_RUNNING_TEST" ]]; then + echo "#" "$@" >&3 + else + echo " " + echo "$@" + fi + fi +} + +test_output() { # write out debug output for testing + if [[ ${_RUNNING_TEST} -eq 1 ]]; then + echo "#" "$@" fi } @@ -1251,6 +1265,9 @@ fi get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) orig_gad_d="$1" # domain name gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER + if [[ -n "$gad_s" ]]; then + gad_s="@$gad_s" + fi if [[ "$os" == "cygwin" ]]; then gad_d="$orig_gad_d" @@ -1268,49 +1285,62 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -n "$HAS_DIG_OR_DRILL" ]]; then gad_d="$orig_gad_d" # Use SOA +trace to find the name server - if [[ -z "$gad_s" ]]; then - debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d" to find primary nameserver - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W") - else - debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W") + if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver + test_output "Using $HAS_DIG_OR_DRILL SOA" + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") fi - # fallback to existing code + # Check if domain is a CNAME if [[ -z "$res" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs (need grep as dig 9.11 sometimes returns everything not just CNAME entries) - debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d" - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) + test_output "Using $HAS_DIG_OR_DRILL CNAME" + + # Two options here; either dig CNAME will return the CNAME and the NS or just the CNAME + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d $gad_s" + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "$gad_s"| grep "^$gad_d") + cname=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') + + if [[ $_TEST_SKIP_CNAME_CALL == 0 ]]; then + debug Checking if CNAME result contains NS records + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "$gad_s"| grep -E "IN\W(NS|SOA)\W") else - debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s" - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) - fi - if [[ -n "$res" ]]; then # domain is a CNAME so get main domain - gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') - debug Domain is a CNAME, actual domain is "$gad_d" + res="" fi - # If gad_d is an A record then this returns the SOA for the root domain, e.g. without the www - # dig NS ubuntu.getssl.text - # > getssl.test. IN SOA ns1.duckdns.org - # If gad_d is a CNAME record then this returns the NS for the domain pointed to by $gad_d - # dig NS www.getssl.text - # > www.getssl.test. IN CNAME getssl.test - # > getssl.test. IN NS ns1.duckdns.org - if [[ -z "$gad_s" ]]; then - debug Using "$HAS_DIG_OR_DRILL NS $gad_d" to find primary nameserver - res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W") - else - debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver - res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W") + + if [[ -n "$cname" ]]; then # domain is a CNAME so get main domain + debug Domain is a CNAME, actual domain is "$cname" fi fi + + # Query for NS records + if [[ -z "$res" ]]; then + test_output "Using $HAS_DIG_OR_DRILL NS" + debug Using "$HAS_DIG_OR_DRILL NS $gad_d $gad_s" to find primary nameserver + res=$($HAS_DIG_OR_DRILL NS "$gad_d" $gad_s | grep -E "IN\W(NS|SOA)\W") + fi + if [[ -n "$res" ]]; then - all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') + # Convert dig output into an array of nameservers + IFS=$'\n' read -r -d '' -a ns_servers < <(echo "$res" | awk '$4 ~ "(NS|SOA)" {print $5}' | sed 's/\.$//g') + + # Nameservers from SOA +trace includes root and all intermediate servers, so just use all the ones with the same domain as the last name server + # i.e. if we have root, google, duckdns1, duckdns2 then return all the duckdns servers + ns_domain=${ns_servers[${#ns_servers[@]} -1 ]#*.} + all_auth_dns_servers="" + for i in "${ns_servers[@]}"; do + if [[ $i =~ $ns_domain ]]; then + all_auth_dns_servers="$all_auth_dns_servers $i" + fi + done + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then primary_ns="$all_auth_dns_servers" else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print " " $1}') fi + + debug set primary_ns = "$primary_ns" + test_output set primary_ns ="$primary_ns" return fi fi @@ -2343,6 +2373,8 @@ while [[ -n ${1+defined} ]]; do _ONLY_CHECK_CONFIG=1 ;; -w) shift; WORKING_DIR="$1" ;; + --source) + return ;; -*) usage error_exit "Unknown option $1" ;; diff --git a/test/19-test-get_auth_dns-dig.bats b/test/19-test-get_auth_dns-dig.bats new file mode 100644 index 0000000..a726442 --- /dev/null +++ b/test/19-test-get_auth_dns-dig.bats @@ -0,0 +1,166 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + . /getssl/getssl --source + find_dns_utils + _RUNNING_TEST=1 + _USE_DEBUG=0 + echo " " >&3 + echo " " >&3 + + if [ -f /usr/bin/host ]; then + mv /usr/bin/host /usr/bin/host.getssl.bak + fi + if [ -f /usr/bin/nslookup ]; then + mv /usr/bin/nslookup /usr/bin/nslookup.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi + if [ -f /usr/bin/nslookup.getssl.bak ]; then + mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup + fi +} + + + @test "Check get_auth_dns using dig NS" { + # Test that get_auth_dns() handles scenario where NS query returns Authority section + # + # ************** EXAMPLE DIG OUTPUT ************** + # + # ;; ANSWER SECTION: + # ubuntu-getssl.duckdns.org. 60 IN A 54.89.252.137 + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN NS ns2.duckdns.org. + # duckdns.org. 600 IN NS ns3.duckdns.org. + # duckdns.org. 600 IN NS ns1.duckdns.org. + # + # ;; ADDITIONAL SECTION: + # ns2.duckdns.org. 600 IN A 54.191.117.119 + # ns3.duckdns.org. 600 IN A 52.26.169.94 + # ns1.duckdns.org. 600 IN A 54.187.92.222 + + # Disable CNAME check + _TEST_SKIP_CNAME_CALL=1 + + PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns ubuntu-getssl.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + # Assert that we had to use dig NS + assert_line --partial 'Using dig NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} + + +@test "Check get_auth_dns using dig SOA" { + # Test that get_auth_dns() handles scenario where SOA query returns Authority section + # + # ************** EXAMPLE DIG OUTPUT ************** + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN SOA ns3.duckdns.org. hostmaster.duckdns.org. 2019170803 6000 120 2419200 600 + + # DuckDNS server returns nothing for SOA, so use public dns instead + PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns ubuntu-getssl.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + + # Assert that we had to use dig NS + assert_line --partial 'Using dig SOA' + refute_line --partial 'Using dig NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} + + +@test "Check get_auth_dns using dig CNAME (public dns)" { + # Test that get_auth_dns() handles scenario where CNAME query returns just a CNAME record + # + # ************** EXAMPLE DIG OUTPUT ************** + # + # ;; ANSWER SECTION: + # www.duckdns.org. 600 IN CNAME DuckDNSAppELB-570522007.us-west-2.elb.amazonaws.com. + + # Disable SOA check + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns www.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' + + # Assert that we found a CNAME and use dig NS + assert_line --partial 'Using dig CNAME' + assert_line --partial 'Using dig NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=false + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' +} + + +@test "Check get_auth_dns using dig CNAME (duckdns)" { + # Test that get_auth_dns() handles scenario where CNAME query returns authority section containing NS records + # + # ************** EXAMPLE DIG OUTPUT ************** + # + # ;; ANSWER SECTION: + # www.duckdns.org. 600 IN CNAME DuckDNSAppELB-570522007.us-west-2.elb.amazonaws.com. + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN NS ns1.duckdns.org. + # duckdns.org. 600 IN NS ns2.duckdns.org. + # duckdns.org. 600 IN NS ns3.duckdns.org. + # + # ;; ADDITIONAL SECTION: + # ns1.duckdns.org. 600 IN A 54.187.92.222 + # ns2.duckdns.org. 600 IN A 54.191.117.119 + # ns3.duckdns.org. 600 IN A 52.26.169.94 + + PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns www.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + + # Assert that we found a CNAME but didn't use dig NS + assert_line --partial 'Using dig CNAME' + refute_line --partial 'Using dig NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} From 6e25567397a77f6101adfd6093015f97b6f76e71 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Sep 2020 10:15:08 +0100 Subject: [PATCH 218/337] Fix dns check for upper case domain names --- getssl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/getssl b/getssl index 05b2f2f..5fbabca 100755 --- a/getssl +++ b/getssl @@ -946,7 +946,7 @@ date_renew() { # calculates the renewal time in epoch debug() { # write out debug info if the debug flag has been set if [[ ${_USE_DEBUG} -eq 1 ]]; then # If running tests then output in TAP format (for debugging tests) - if [[ -n "$_RUNNING_TEST" ]]; then + if [[ ${_RUNNING_TEST} -eq 1 ]]; then echo "#" "$@" >&3 else echo " " @@ -1141,8 +1141,7 @@ for d in $alldomains; do command="rm -f ${t_loc:(( ${#sshhost} + 5))}/${token:?}" debug "running following command to remove token" debug "ssh $SSH_OPTS $sshhost ${command}" - # shellcheck disable=SC2029 - # shellcheck disable=SC2086 + # shellcheck disable=SC2029 disable=SC2086 ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 rm -f "${TEMP_DIR:?}/${token:?}" elif [[ "${t_loc:0:4}" == "ftp:" ]] ; then @@ -1177,6 +1176,10 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then # shellcheck source=/dev/null . "$dnsfile" + # Always use lowercase domain name when querying DNS servers + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "$d" | tr A-Z a-z) + # check for token at public dns server, waiting for a valid response. for ns in $primary_ns; do debug "checking dns at $ns" @@ -1184,18 +1187,18 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then check_dns="fail" while [[ "$check_dns" == "fail" ]]; do if [[ "$os" == "cygwin" ]]; then - check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ + check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ | grep ^_acme -A2\ | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${d}" "@${ns}" - check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ + debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" + check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ + check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') else - check_result=$(nslookup -type=txt "_acme-challenge.${d}" "${ns}" \ + check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ | grep 'text ='|awk -F'"' '{ print $2}') fi debug "expecting $auth_key" @@ -1208,26 +1211,22 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then ntries=$(( ntries + 1 )) if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 == 0 )) ]]; then - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "$d" | tr A-Z a-z) debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" fi fi - info "checking DNS at ${ns} for ${d}. Attempt $ntries/100 gave wrong result, "\ + info "checking DNS at ${ns} for ${lower_d}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ "waiting $DNS_WAIT secs before checking again" sleep $DNS_WAIT else debug "dns check failed - removing existing value" - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "$d" | tr A-Z a-z) eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" # remove $dnsfile after each loop. rm -f "$dnsfile" - error_exit "checking _acme-challenge.${d} gave $check_result not $auth_key" + error_exit "checking _acme-challenge.${lower_d} gave $check_result not $auth_key" fi fi done From cfd6562e9b9df4e341da7f61ca94620d0c918306 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Sep 2020 10:18:49 +0100 Subject: [PATCH 219/337] Fix the dns test config files --- .../test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg | 4 ++-- test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg | 4 ++-- test/test-config/getssl-dns01-ignore-directory-domain.cfg | 4 ++-- test/test-config/getssl-dns01-multiple-domains.cfg | 4 ++-- test/test-config/getssl-dns01-spaces-and-commas-sans.cfg | 4 ++-- .../getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg | 4 ++-- test/test-config/getssl-dns01-spaces-sans.cfg | 4 ++-- test/test-config/getssl-dns01.cfg | 8 ++++++-- test/test-config/getssl-staging-dns01-fail-dns-add.cfg | 2 +- test/test-config/getssl-staging-dns01-no-suffix.cfg | 2 +- test/test-config/getssl-staging-dns01.cfg | 2 +- 11 files changed, 23 insertions(+), 19 deletions(-) diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg index 235e1da..15b4108 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa-old-nginx.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 DUAL_RSA_ECDSA="true" ACCOUNT_KEY_TYPE="prime256v1" diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 6bbcc44..4059dd4 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 DUAL_RSA_ECDSA="true" ACCOUNT_KEY_TYPE="prime256v1" diff --git a/test/test-config/getssl-dns01-ignore-directory-domain.cfg b/test/test-config/getssl-dns01-ignore-directory-domain.cfg index 4bbd766..e55fbe0 100644 --- a/test/test-config/getssl-dns01-ignore-directory-domain.cfg +++ b/test/test-config/getssl-dns01-ignore-directory-domain.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 # Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list IGNORE_DIRECTORY_DOMAIN="true" diff --git a/test/test-config/getssl-dns01-multiple-domains.cfg b/test/test-config/getssl-dns01-multiple-domains.cfg index f0fae04..82497ad 100644 --- a/test/test-config/getssl-dns01-multiple-domains.cfg +++ b/test/test-config/getssl-dns01-multiple-domains.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="getssl.test" diff --git a/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg index 2660a9d..204d0bf 100644 --- a/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg +++ b/test/test-config/getssl-dns01-spaces-and-commas-sans.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="a.${GETSSL_HOST}, b.${GETSSL_HOST}, c.${GETSSL_HOST}" diff --git a/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg index 6b87010..75e7304 100644 --- a/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg +++ b/test/test-config/getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 # Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list IGNORE_DIRECTORY_DOMAIN="true" diff --git a/test/test-config/getssl-dns01-spaces-sans.cfg b/test/test-config/getssl-dns01-spaces-sans.cfg index 8438228..e954fa0 100644 --- a/test/test-config/getssl-dns01-spaces-sans.cfg +++ b/test/test-config/getssl-dns01-spaces-sans.cfg @@ -7,8 +7,8 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT=0 # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="a.${GETSSL_HOST} b.${GETSSL_HOST} c.${GETSSL_HOST}" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 883f29e..e995c80 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -7,8 +7,12 @@ 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" -PUBLIC_DNS_SERVER=10.30.50.3 -DNS_EXTRA_WAIT="" +AUTH_DNS_SERVER=10.30.50.3 + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" diff --git a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg index 2985d32..125cfef 100644 --- a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg +++ b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" # Generic staging config VALIDATE_VIA_DNS=true DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org +AUTH_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true # Test that the retry works (dns_add_command will always fail) diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg index 47d2cec..6b764f3 100644 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ b/test/test-config/getssl-staging-dns01-no-suffix.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org +AUTH_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 348cabf..655f1c6 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org +AUTH_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 From b0debe01b0e8c7518ef1d11d3c9d36e4de39e86b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 1 Oct 2020 08:58:58 +0100 Subject: [PATCH 220/337] Use different args for drill in SOA check --- getssl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 5fbabca..92c01a3 100755 --- a/getssl +++ b/getssl @@ -1285,9 +1285,15 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n gad_d="$orig_gad_d" # Use SOA +trace to find the name server if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then - debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver - test_output "Using $HAS_DIG_OR_DRILL SOA" - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") + if [[ "$HAS_DIG_OR_DRILL" == "dig" ]]; then + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver + test_output "Using $HAS_DIG_OR_DRILL SOA" + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") + else + debug Using "$HAS_DIG_OR_DRILL -T $gad_d $gad_s" to find primary nameserver + test_output "Using $HAS_DIG_OR_DRILL SOA" + res=$($HAS_DIG_OR_DRILL -T SOA "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") + fi fi # Check if domain is a CNAME From 55ea0642c7bca86365b687e5d585379d79ec0d84 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 2 Oct 2020 15:09:23 +0100 Subject: [PATCH 221/337] Add unit test for get_auth_dns using drill --- ...dig.bats => u1-test-get_auth_dns-dig.bats} | 28 ++- test/u2-test-get_auth_dns-drill.bats | 201 ++++++++++++++++++ 2 files changed, 214 insertions(+), 15 deletions(-) rename test/{19-test-get_auth_dns-dig.bats => u1-test-get_auth_dns-dig.bats} (91%) create mode 100644 test/u2-test-get_auth_dns-drill.bats diff --git a/test/19-test-get_auth_dns-dig.bats b/test/u1-test-get_auth_dns-dig.bats similarity index 91% rename from test/19-test-get_auth_dns-dig.bats rename to test/u1-test-get_auth_dns-dig.bats index a726442..356c5c5 100644 --- a/test/19-test-get_auth_dns-dig.bats +++ b/test/u1-test-get_auth_dns-dig.bats @@ -7,29 +7,27 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { + for app in drill host nslookup + do + if [ -f /usr/bin/${app} ]; then + mv /usr/bin/${app} /usr/bin/${app}.getssl.bak + fi + done + . /getssl/getssl --source find_dns_utils _RUNNING_TEST=1 _USE_DEBUG=0 - echo " " >&3 - echo " " >&3 - - if [ -f /usr/bin/host ]; then - mv /usr/bin/host /usr/bin/host.getssl.bak - fi - if [ -f /usr/bin/nslookup ]; then - mv /usr/bin/nslookup /usr/bin/nslookup.getssl.bak - fi } teardown() { - if [ -f /usr/bin/host.getssl.bak ]; then - mv /usr/bin/host.getssl.bak /usr/bin/host - fi - if [ -f /usr/bin/nslookup.getssl.bak ]; then - mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup - fi + for app in drill host nslookup + do + if [ -f /usr/bin/${app}.getssl.bak ]; then + mv /usr/bin/${app}.getssl.bak /usr/bin/${app} + fi + done } diff --git a/test/u2-test-get_auth_dns-drill.bats b/test/u2-test-get_auth_dns-drill.bats new file mode 100644 index 0000000..a0d977c --- /dev/null +++ b/test/u2-test-get_auth_dns-drill.bats @@ -0,0 +1,201 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + for app in dig host nslookup + do + if [ -f /usr/bin/${app} ]; then + mv /usr/bin/${app} /usr/bin/${app}.getssl.bak + fi + done + + . /getssl/getssl --source + find_dns_utils + _RUNNING_TEST=1 + _USE_DEBUG=0 +} + + +teardown() { + for app in dig host nslookup + do + if [ -f /usr/bin/${app}.getssl.bak ]; then + mv /usr/bin/${app}.getssl.bak /usr/bin/${app} + fi + done +} + + +teardown() { + if [ -f /usr/bin/host.getssl.bak ]; then + mv /usr/bin/host.getssl.bak /usr/bin/host + fi + if [ -f /usr/bin/nslookup.getssl.bak ]; then + mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup + fi + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi +} + + +@test "Check get_auth_dns using drill NS" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where NS query returns Authority section + # + # ************** EXAMPLE DRILL OUTPUT ************** + # + # ;; ANSWER SECTION: + # ubuntu-getssl.duckdns.org. 60 IN A 54.89.252.137 + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN NS ns2.duckdns.org. + # duckdns.org. 600 IN NS ns3.duckdns.org. + # duckdns.org. 600 IN NS ns1.duckdns.org. + # + # ;; ADDITIONAL SECTION: + # ns2.duckdns.org. 600 IN A 54.191.117.119 + # ns3.duckdns.org. 600 IN A 52.26.169.94 + # ns1.duckdns.org. 600 IN A 54.187.92.222 + + # Disable SOA and CNAME check + _TEST_SKIP_CNAME_CALL=1 + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns ubuntu-getssl.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + # Assert that we had to use drill NS + assert_line --partial 'Using drill NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} + + +@test "Check get_auth_dns using drill SOA" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where SOA query returns Authority section + # + # ************** EXAMPLE DRILL OUTPUT ************** + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN SOA ns3.duckdns.org. hostmaster.duckdns.org. 2019170803 6000 120 2419200 600 + + # DuckDNS server returns nothing for SOA, so use public dns instead + PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns ubuntu-getssl.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + + # Assert that we had to use drill NS + assert_line --partial 'Using drill SOA' + refute_line --partial 'Using drill NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} + + +@test "Check get_auth_dns using drill CNAME (public dns)" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where CNAME query returns just a CNAME record + # + # ************** EXAMPLE drill OUTPUT ************** + # + # ;; ANSWER SECTION: + # www.duckdns.org. 600 IN CNAME DuckDNSAppELB-570522007.us-west-2.elb.amazonaws.com. + + # Disable SOA check + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns www.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' + + # Assert that we found a CNAME and use drill NS + assert_line --partial 'Using drill CNAME' + assert_line --partial 'Using drill NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=false + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' +} + + +@test "Check get_auth_dns using drill CNAME (duckdns)" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where CNAME query returns authority section containing NS records + # + # ************** EXAMPLE drill OUTPUT ************** + # + # ;; ANSWER SECTION: + # www.duckdns.org. 600 IN CNAME DuckDNSAppELB-570522007.us-west-2.elb.amazonaws.com. + # + # ;; AUTHORITY SECTION: + # duckdns.org. 600 IN NS ns1.duckdns.org. + # duckdns.org. 600 IN NS ns2.duckdns.org. + # duckdns.org. 600 IN NS ns3.duckdns.org. + # + # ;; ADDITIONAL SECTION: + # ns1.duckdns.org. 600 IN A 54.187.92.222 + # ns2.duckdns.org. 600 IN A 54.191.117.119 + # ns3.duckdns.org. 600 IN A 52.26.169.94 + + # Disable SOA check + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_ALL_AUTH_DNS=false + + run get_auth_dns www.duckdns.org + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + + # Assert that we found a CNAME but didn't use drill NS + assert_line --partial 'Using drill CNAME' + refute_line --partial 'Using drill NS' + + # Check all Authoritive DNS servers are returned if requested + CHECK_ALL_AUTH_DNS=true + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' +} From 0f422225f47bdcc194266548b06cfc92815e1bbd Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 2 Oct 2020 15:09:50 +0100 Subject: [PATCH 222/337] Update all docker images to include drill (for unit tests) --- test/Dockerfile-alpine | 2 +- test/Dockerfile-centos6 | 2 +- test/Dockerfile-centos7 | 4 +--- test/Dockerfile-centos7-staging | 2 +- test/Dockerfile-debian | 2 +- test/Dockerfile-ubuntu | 2 +- test/Dockerfile-ubuntu-staging | 3 +-- test/Dockerfile-ubuntu16 | 5 +---- test/Dockerfile-ubuntu18 | 2 +- 9 files changed, 9 insertions(+), 15 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index caad22a..e0f3a6c 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -2,7 +2,7 @@ FROM alpine:latest # Note this image uses busybox awk instead of gawk -RUN apk --no-cache add supervisor openssl git curl bind-tools wget nginx bash +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash WORKDIR /root diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 5ffc91c..862e680 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -8,7 +8,7 @@ FROM centos:centos6 # Update and install required software RUN yum -y update RUN yum -y install epel-release -RUN yum -y install git curl dnsutils wget nginx +RUN yum -y install git curl dnsutils ldns wget nginx WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index 02fbcb7..afbcf23 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -1,11 +1,9 @@ FROM centos:centos7 -# Note this image uses drill, does not have dig or nslookup installed - # Update and install required software RUN yum -y update RUN yum -y install epel-release -RUN yum -y install git curl ldns wget which nginx +RUN yum -y install git curl ldns bind-utils wget which nginx WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 899bf9b..a539cac 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -5,7 +5,7 @@ FROM centos:centos7 # Update and install required software RUN yum -y update RUN yum -y install epel-release -RUN yum -y install git curl bind-utils wget which nginx +RUN yum -y install git curl bind-utils ldns wget which nginx ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index b5da5dd..d066813 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -4,7 +4,7 @@ FROM debian:latest # Update and install required software RUN apt-get update --fix-missing -RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 66d7a35..2ef9e25 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -7,7 +7,7 @@ ENV DEBIAN_FRONTEND noninteractive # Update and install required software RUN apt-get update --fix-missing -RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light RUN apt-get install -y vim dos2unix # for debugging # TODO test with drill, dig, host diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 58762d0..05f0471 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -11,9 +11,8 @@ ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb # Update and install required software RUN apt-get update --fix-missing -RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light RUN apt-get install -y vim dos2unix # for debugging -# TODO test with drill, dig, host WORKDIR /root diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 030d03a..ba6164c 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -5,16 +5,13 @@ FROM ubuntu:xenial # Update and install required software RUN apt-get update --fix-missing -RUN apt-get install -y git curl dnsutils wget nginx-light +RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default -# Prevent "Can't load /root/.rnd into RNG" error from openssl -# RUN touch /root/.rnd - # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 1d68cd3..1735646 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -5,7 +5,7 @@ FROM ubuntu:bionic # Update and install required software RUN apt-get update --fix-missing -RUN apt-get install -y git curl dnsutils wget gawk nginx-light +RUN apt-get install -y git curl dnsutils ldnsutils wget gawk nginx-light WORKDIR /root RUN mkdir /etc/nginx/pki From 44f0ce1a536243c32253bf2cd697beae1544b5e0 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 2 Oct 2020 15:16:23 +0100 Subject: [PATCH 223/337] Update revision history --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 92c01a3..e6500e8 100755 --- a/getssl +++ b/getssl @@ -237,7 +237,8 @@ # 2020-08-06 Use -sigalgs instead of -cipher when checking remote for tls1.3 (#570) # 2020-08-31 Fix slow fork bomb when directory containing getssl isn't writeable (#440) # 2020-09-01 Use RSA-PSS when checking remote for DUAL_RSA_ECDSA (#570) -# 2020-09-02 Fix issue when SANS is space and comma separated (#579) +# 2020-09-02 Fix issue when SANS is space and comma separated (#579) (2.30) +# 2020-10-02 Various fixes to get_auth_dns and changes to support unit tests (#308) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} From cb5941307afc25e81bf0518a354548c88eec0ba9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 3 Oct 2020 19:27:28 +0100 Subject: [PATCH 224/337] Change staging tests to use all auth dns servers --- test/test-config/getssl-staging-dns01-fail-dns-add.cfg | 2 +- test/test-config/getssl-staging-dns01-no-suffix.cfg | 2 +- test/test-config/getssl-staging-dns01.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg index 125cfef..2985d32 100644 --- a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg +++ b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" # Generic staging config VALIDATE_VIA_DNS=true DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -AUTH_DNS_SERVER=ns2.duckdns.org +PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true # Test that the retry works (dns_add_command will always fail) diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg index 6b764f3..47d2cec 100644 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ b/test/test-config/getssl-staging-dns01-no-suffix.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -AUTH_DNS_SERVER=ns2.duckdns.org +PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 655f1c6..348cabf 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -AUTH_DNS_SERVER=ns2.duckdns.org +PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 From 96d987c07bec39f5a941527975bc05461b350bb3 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 4 Oct 2020 20:01:17 +0100 Subject: [PATCH 225/337] Add CHECK_PUBLIC_DNS_SERVER --- getssl | 21 ++++++++++++++++++++- test/u1-test-get_auth_dns-dig.bats | 18 ++++++++++++++++-- test/u2-test-get_auth_dns-drill.bats | 16 +++++++++++++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index e6500e8..a6fa355 100755 --- a/getssl +++ b/getssl @@ -239,6 +239,7 @@ # 2020-09-01 Use RSA-PSS when checking remote for DUAL_RSA_ECDSA (#570) # 2020-09-02 Fix issue when SANS is space and comma separated (#579) (2.30) # 2020-10-02 Various fixes to get_auth_dns and changes to support unit tests (#308) +# 2020-10-04 Add CHECK_PUBLIC_DNS_SERVER to check the DNS challenge has been updated there # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -272,6 +273,7 @@ ORIG_UMASK=$(umask) PREVIOUSLY_VALIDATED="true" PRIVATE_KEY_ALG="rsa" PUBLIC_DNS_SERVER="" +CHECK_PUBLIC_DNS_SERVER="true" RELOAD_CMD="" RENEW_ALLOW="30" REUSE_PRIVATE_KEY="true" @@ -1278,7 +1280,11 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -z "$all_auth_dns_servers" ]]; then error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" fi - primary_ns="$all_auth_dns_servers" + if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$all_auth_dns_servers $PUBLIC_DNS_SERVER" + else + primary_ns="$all_auth_dns_servers" + fi return fi @@ -1345,6 +1351,10 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n primary_ns=$(echo "$all_auth_dns_servers" | awk '{print " " $1}') fi + if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$primary_ns $PUBLIC_DNS_SERVER" + fi + debug set primary_ns = "$primary_ns" test_output set primary_ns ="$primary_ns" return @@ -1366,6 +1376,11 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n else primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi + + if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$primary_ns $PUBLIC_DNS_SERVER" + fi + return fi fi @@ -1416,6 +1431,10 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n else primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi + + if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$primary_ns $PUBLIC_DNS_SERVER" + fi return fi fi diff --git a/test/u1-test-get_auth_dns-dig.bats b/test/u1-test-get_auth_dns-dig.bats index 356c5c5..d13cc56 100644 --- a/test/u1-test-get_auth_dns-dig.bats +++ b/test/u1-test-get_auth_dns-dig.bats @@ -31,7 +31,7 @@ teardown() { } - @test "Check get_auth_dns using dig NS" { +@test "Check get_auth_dns using dig NS" { # Test that get_auth_dns() handles scenario where NS query returns Authority section # # ************** EXAMPLE DIG OUTPUT ************** @@ -53,6 +53,7 @@ teardown() { _TEST_SKIP_CNAME_CALL=1 PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns ubuntu-getssl.duckdns.org @@ -79,6 +80,7 @@ teardown() { # DuckDNS server returns nothing for SOA, so use public dns instead PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns ubuntu-getssl.duckdns.org @@ -94,6 +96,11 @@ teardown() { CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + + # Check that we also check the public DNS server if requested + CHECK_PUBLIC_DNS_SERVER=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org 1\.0\.0\.1' } @@ -109,6 +116,7 @@ teardown() { _TEST_SKIP_SOA_CALL=1 PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns www.duckdns.org @@ -121,9 +129,14 @@ teardown() { assert_line --partial 'Using dig NS' # Check all Authoritive DNS servers are returned if requested - CHECK_ALL_AUTH_DNS=false + CHECK_ALL_AUTH_DNS=true run get_auth_dns www.duckdns.org assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' + + # Check that we also check the public DNS server if requested + CHECK_PUBLIC_DNS_SERVER=true + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com 1\.0\.0\.1' } @@ -146,6 +159,7 @@ teardown() { # ns3.duckdns.org. 600 IN A 52.26.169.94 PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns www.duckdns.org diff --git a/test/u2-test-get_auth_dns-drill.bats b/test/u2-test-get_auth_dns-drill.bats index a0d977c..1db1011 100644 --- a/test/u2-test-get_auth_dns-drill.bats +++ b/test/u2-test-get_auth_dns-drill.bats @@ -72,6 +72,7 @@ teardown() { _TEST_SKIP_SOA_CALL=1 PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns ubuntu-getssl.duckdns.org @@ -103,6 +104,7 @@ teardown() { # DuckDNS server returns nothing for SOA, so use public dns instead PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns ubuntu-getssl.duckdns.org @@ -118,6 +120,11 @@ teardown() { CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + + # Check that we also check the public DNS server if requested + CHECK_PUBLIC_DNS_SERVER=true + run get_auth_dns ubuntu-getssl.duckdns.org + assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org 1\.0\.0\.1' } @@ -138,6 +145,7 @@ teardown() { _TEST_SKIP_SOA_CALL=1 PUBLIC_DNS_SERVER=1.0.0.1 + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns www.duckdns.org @@ -150,9 +158,14 @@ teardown() { assert_line --partial 'Using drill NS' # Check all Authoritive DNS servers are returned if requested - CHECK_ALL_AUTH_DNS=false + CHECK_ALL_AUTH_DNS=true run get_auth_dns www.duckdns.org assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com' + + # Check that we also check the public DNS server if requested + CHECK_PUBLIC_DNS_SERVER=true + run get_auth_dns www.duckdns.org + assert_output --regexp 'set primary_ns = ns.*\.awsdns.*\.com 1\.0\.0\.1' } @@ -183,6 +196,7 @@ teardown() { _TEST_SKIP_SOA_CALL=1 PUBLIC_DNS_SERVER=ns1.duckdns.org + CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=false run get_auth_dns www.duckdns.org From d195235a7ed78a83f6f93257b9aa8971ba93d8bf Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 4 Oct 2020 20:12:41 +0100 Subject: [PATCH 226/337] Document the settings used for DNS verification --- getssl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index a6fa355..f99c53e 100755 --- a/getssl +++ b/getssl @@ -252,7 +252,6 @@ ACCOUNT_KEY_TYPE="rsa" CA="https://acme-staging-v02.api.letsencrypt.org/directory" CA_CERT_LOCATION="" CHALLENGE_CHECK_TYPE="http" -CHECK_ALL_AUTH_DNS="false" CHECK_REMOTE="true" CHECK_REMOTE_WAIT=0 CODE_LOCATION="https://raw.githubusercontent.com/srvrco/getssl/master/getssl" @@ -260,10 +259,6 @@ CSR_SUBJECT="/" CURL_USERAGENT="${PROGNAME}/${VERSION}" DEACTIVATE_AUTH="false" DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" -DNS_EXTRA_WAIT=60 # How long to wait after the DNS has updated before telling the ACME server to check. -DNS_WAIT_RETRY_ADD="false" # Try the dns_add_command again if the DNS record hasn't updated -DNS_WAIT=10 # How long to wait before checking the DNS record again -DNS_WAIT_COUNT=100 # How many times to wait for the DNS record to update DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" GETSSL_IGNORE_CP_PRESERVE="false" @@ -272,8 +267,6 @@ IGNORE_DIRECTORY_DOMAIN="false" ORIG_UMASK=$(umask) PREVIOUSLY_VALIDATED="true" PRIVATE_KEY_ALG="rsa" -PUBLIC_DNS_SERVER="" -CHECK_PUBLIC_DNS_SERVER="true" RELOAD_CMD="" RENEW_ALLOW="30" REUSE_PRIVATE_KEY="true" @@ -284,8 +277,22 @@ OCSP_MUST_STAPLE="false" TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" -VALIDATE_VIA_DNS="" WORKING_DIR_CANDIDATES=("/etc/getssl/" "${PROGDIR}/conf" "${PROGDIR}/.getssl" "${HOME}/.getssl") + +# Variables used when validating using a DNS entry +VALIDATE_VIA_DNS="" # Set this to "true" to enable DNS validation +AUTH_DNS_SERVER="" # Use this DNS server to check the challenge token has been set +PUBLIC_DNS_SERVER="" # Use this DNS server to find the authoritative DNS servers for the domain +CHECK_ALL_AUTH_DNS="false" # Check the challenge token has been set on all authoritative DNS servers +CHECK_PUBLIC_DNS_SERVER="true" # Check the public DNS server as well as the authoritative DNS servers +DNS_ADD_COMMAND="" # Use this command/script to add the challenge token to the DNS entries for the domain +DNS_DEL_COMMAND="" # Use this command/script to remove the challenge token from the DNS entries for the domain +DNS_WAIT_COUNT=100 # How many times to wait for the DNS record to update +DNS_WAIT=10 # How long to wait before checking the DNS record again +DNS_EXTRA_WAIT=60 # How long to wait after the DNS entries are visible to us before telling the ACME server to check. +DNS_WAIT_RETRY_ADD="false" # Try the dns_add_command again if the DNS record hasn't updated + +# Private variables _CHECK_ALL=0 _CREATE_CONFIG=0 _FORCE_RENEW=0 From 7be325dbadd4993b9b68dd1b38328a42082d9b79 Mon Sep 17 00:00:00 2001 From: Mesar Hameed Date: Tue, 13 Oct 2020 00:19:47 +0100 Subject: [PATCH 227/337] bugfix: strip comments in drill/dig output when checking txt in dns. The dns query is often being reprinted as a comment as part of the drill/dig output this caused multiple hits for the grep, making the comparison invalid. Make sure we strip out comments before we look for the challenge text in the txt record. --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index f99c53e..e6541aa 100755 --- a/getssl +++ b/getssl @@ -1203,6 +1203,7 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ + | grep -i "^_acme-challenge.${lower_d}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ From f2bcce5ab2acf39d0b1a37261e11a46706f1d0b6 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 25 Oct 2020 14:46:30 +0000 Subject: [PATCH 228/337] Add more info to debug statemetns when running tests --- getssl | 60 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/getssl b/getssl index f99c53e..9fd63ea 100755 --- a/getssl +++ b/getssl @@ -629,10 +629,10 @@ check_getssl_upgrade() { # check if a more recent version of code is available a if [[ "${latestvdec:-0}" -gt "$localvdec" ]]; then if [[ ${_UPGRADE} -eq 1 ]]; then if ! install "$0" "${0}.v${VERSION}"; then - error_exit "problem renaming old version while updating, check permissions" + error_exit "problem renaming old version while updating, check permissions" fi if ! install -m 700 "$TEMP_UPGRADE_FILE" "$0"; then - error_exit "problem installing new version while updating, check permissions" + error_exit "problem installing new version while updating, check permissions" fi if [[ ${_MUTE} -eq 0 ]]; then echo "Updated getssl from v${VERSION} to v${latestversion}" @@ -819,7 +819,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) for d in $alldomains; do if [[ "$(echo "${domains_in_csr}"| grep "^${d}$")" != "${d}" ]]; then info "existing csr at $csr_file does not contain ${d} - re-create-csr"\ - ".... $(echo "${domains_in_csr}"| grep "^${d}$")" + ".... $(echo "${domains_in_csr}"| grep "^${d}$")" _RECREATE_CSR=1 fi done @@ -889,6 +889,7 @@ create_order() { OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') debug "Order link $OrderLink" FinalizeLink=$(json_get "$response" "finalize") + debug "Finalize link $FinalizeLink" if [[ $API -eq 1 ]]; then dn=0 @@ -915,6 +916,7 @@ create_order() { d=$(echo "$d" | tr "[:upper:]" "[:lower:]") if [ "$d" == "$authdomain" ]; then debug "Saving authorization response for $authdomain for domain alldomains[$dn]" + debug "Response = ${response//['\t\r\n']}" AuthLinkResponse[$dn]=$response AuthLinkResponseHeader[$dn]=$responseHeaders fi @@ -957,7 +959,7 @@ debug() { # write out debug info if the debug flag has been set if [[ ${_USE_DEBUG} -eq 1 ]]; then # If running tests then output in TAP format (for debugging tests) if [[ ${_RUNNING_TEST} -eq 1 ]]; then - echo "#" "$@" >&3 + echo "# $(date "+%b %d %T") ${FUNCNAME[1]}:${BASH_LINENO[1]}" "$@" >&3 else echo " " echo "$@" @@ -1047,7 +1049,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - debug "authlink response = $response" + debug "authlink response = ${response//['\t\r\n']}" # get the token and uri from the dns-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") uri=$(json_get "$response" "challenges" "type" "dns-01" "url") @@ -1058,10 +1060,11 @@ for d in $alldomains; do debug keyauthorization "$keyauthorization" #create signed authorization key from token. - auth_key=$(printf '%s' "$keyauthorization" | openssl dgst -sha256 -binary \ - | openssl base64 -e \ - | tr -d '\n\r' \ - | sed -e 's:=*$::g' -e 'y:+/:-_:') + auth_key=$(printf '%s' "$keyauthorization" \ + | openssl dgst -sha256 -binary \ + | openssl base64 -e \ + | tr -d '\n\r' \ + | sed -e 's:=*$::g' -e 'y:+/:-_:') debug auth_key "$auth_key" # shellcheck disable=SC2018,SC2019 @@ -1102,7 +1105,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - debug "authlink response = $response" + debug "authlink response = ${response//['\t\r\n']}" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") # get the uri from the http component @@ -1198,18 +1201,18 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then while [[ "$check_dns" == "fail" ]]; do if [[ "$os" == "cygwin" ]]; then check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ - | grep ^_acme -A2\ - | grep '"'|awk -F'"' '{ print $2}') + | grep ^_acme -A2\ + | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ - | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ - | grep 'descriptive text'|awk -F'"' '{ print $2}') + | grep 'descriptive text'|awk -F'"' '{ print $2}') else check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ - | grep 'text ='|awk -F'"' '{ print $2}') + | grep 'text ='|awk -F'"' '{ print $2}') fi debug "expecting $auth_key" debug "${ns} gave ... $check_result" @@ -1515,7 +1518,7 @@ get_cr() { # get curl response debug url "$url" response=$(curl --user-agent "$CURL_USERAGENT" --silent "$url") ret=$? - debug response "$response" + debug response "${response//[$'\t\r\n']}" code=$(json_get "$response" status) debug code "$code" debug "get_cr return code $ret" @@ -1566,8 +1569,8 @@ get_signing_params() { # get signing parameters from key crv="$(openssl ec -in "$skey" -noout -text 2>/dev/null | awk '$2 ~ "CURVE:" {print $3}')" if [[ -z "$crv" ]]; then gsp_keytype="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ - | grep "^ASN1 OID:" \ - | awk '{print $3}')" + | grep "^ASN1 OID:" \ + | awk '{print $3}')" case "$gsp_keytype" in prime256v1) crv="P-256" ;; secp384r1) crv="P-384" ;; @@ -1582,8 +1585,8 @@ get_signing_params() { # get signing parameters from key *) error_exit "invalid curve algorithm type $crv";; esac pubtext="$(openssl ec -in "$skey" -noout -text 2>/dev/null \ - | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \ - | tr -d ": \n\r")" + | awk '/^pub:/{p=1;next}/^ASN1 OID:/{p=0}p' \ + | tr -d ": \n\r")" mid=$(( (${#pubtext} -2) / 2 + 2 )) x64=$(echo "$pubtext" | cut -b 3-$mid | hex2bin | urlbase64) y64=$(echo "$pubtext" | cut -b $((mid+1))-${#pubtext} | hex2bin | urlbase64) @@ -1634,6 +1637,11 @@ hex2bin() { # Remove spaces, add leading zero, escape as hex string ensuring no info() { # write out info as long as the quiet flag has not been set. if [[ ${_QUIET} -eq 0 ]]; then + # If running tests then output in TAP format as well (for debugging tests) + if [[ ${_RUNNING_TEST} -eq 1 ]]; then + echo "# $(date "+%b %d %T") ${FUNCNAME[1]}:${BASH_LINENO[1]}" "$@" >&3 + fi + echo "$@" fi } @@ -2111,7 +2119,7 @@ send_signed_request() { # Sends a request to the ACME server, signed with your p fi debug responseHeaders "$responseHeaders" - debug response "$response" + debug response "${response//[$'\t\r\n']}" code=$(awk ' $1 ~ "^HTTP" {print $2}' "$CURL_HEADER" | tail -1) debug code "$code" if [[ "$code" == 4* && $response != *"error:badNonce"* && "$code" != 409 ]]; then @@ -2223,9 +2231,9 @@ usage() { # echos out the program usage write_domain_template() { # write out a template file for a domain. if [[ -s "$WORKING_DIR/getssl_default.cfg" ]]; then - export DOMAIN="$DOMAIN" - export EX_SANS="$EX_SANS" - envsubst < "$WORKING_DIR/getssl_default.cfg" > "$1" + export DOMAIN="$DOMAIN" + export EX_SANS="$EX_SANS" + envsubst < "$WORKING_DIR/getssl_default.cfg" > "$1" else cat > "$1" <<- _EOF_domain_ # vim: filetype=sh @@ -2786,10 +2794,10 @@ fi # if not reusing private key, then remove the old keys if [[ "$REUSE_PRIVATE_KEY" != "true" ]]; then if [[ -s "$DOMAIN_DIR/${DOMAIN}.key" ]]; then - rm -f "$DOMAIN_DIR/${DOMAIN}.key" + rm -f "$DOMAIN_DIR/${DOMAIN}.key" fi if [[ -s "$DOMAIN_DIR/${DOMAIN}.ec.key" ]]; then - rm -f "$DOMAIN_DIR/${DOMAIN}.ec.key" + rm -f "$DOMAIN_DIR/${DOMAIN}.ec.key" fi fi # create new domain keys if they don't already exist From 8871da0b2c65ec7160137253eccce9349312968d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 25 Oct 2020 14:48:27 +0000 Subject: [PATCH 229/337] Add tests for changing SANS (bug not fixed) --- test/19-test-add-to-sans.bats | 61 +++++++++++++++++++ .../getssl-dns01-add-to-sans-1.cfg | 37 +++++++++++ .../getssl-dns01-add-to-sans-2.cfg | 37 +++++++++++ 3 files changed, 135 insertions(+) create mode 100644 test/19-test-add-to-sans.bats create mode 100644 test/test-config/getssl-dns01-add-to-sans-1.cfg create mode 100644 test/test-config/getssl-dns01-add-to-sans-2.cfg diff --git a/test/19-test-add-to-sans.bats b/test/19-test-add-to-sans.bats new file mode 100644 index 0000000..8d6f560 --- /dev/null +++ b/test/19-test-add-to-sans.bats @@ -0,0 +1,61 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a +} + +teardown() { + curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a + curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a +} + + + +@test "Create certificate to check can add to SANS" { + skip "FIXME: Certificate is not recreated when SANS is updated" + if [ -n "$STAGING" ]; then + skip "Not trying on staging server yet" + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01-add-to-sans-1.cfg" + fi + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" + setup_environment + + + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check we can add a new domain to SANS" { + skip "FIXME: Certificate is not recreated when SANS is updated" + if [ -n "$STAGING" ]; then + skip "Not trying on staging server yet" + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01-add-to-sans-2.cfg" + fi + # . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" + # CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt + # KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key + # cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + + create_certificate + assert_success + check_output_for_errors + + # As the SANS list changed, a new certificate is needed + assert_line --partial "certificate installed OK on server" + refute_line --partial 'certificate is valid for more than' +} diff --git a/test/test-config/getssl-dns01-add-to-sans-1.cfg b/test/test-config/getssl-dns01-add-to-sans-1.cfg new file mode 100644 index 0000000..ddb514a --- /dev/null +++ b/test/test-config/getssl-dns01-add-to-sans-1.cfg @@ -0,0 +1,37 @@ +# 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 +# +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 + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}" + +# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" +IGNORE_DIRECTORY_DOMAIN="true" + +#_USE_DEBUG=1 +#_RUNNING_TEST=1 diff --git a/test/test-config/getssl-dns01-add-to-sans-2.cfg b/test/test-config/getssl-dns01-add-to-sans-2.cfg new file mode 100644 index 0000000..9196905 --- /dev/null +++ b/test/test-config/getssl-dns01-add-to-sans-2.cfg @@ -0,0 +1,37 @@ +# 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 +# +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 + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="b.${GETSSL_HOST}, a.${GETSSL_HOST}" + +# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="false" +IGNORE_DIRECTORY_DOMAIN="true" + +#_USE_DEBUG=1 +#_RUNNING_TEST=1 From f44346f835a706cd2043c708d46639668e12f41b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 25 Oct 2020 16:09:40 +0000 Subject: [PATCH 230/337] Reduce number of DNS lookup attempts and retry DNS_ADD --- test/test-config/getssl-staging-dns01-no-suffix.cfg | 11 +++++------ test/test-config/getssl-staging-dns01.cfg | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg index 47d2cec..b4f5202 100644 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ b/test/test-config/getssl-staging-dns01-no-suffix.cfg @@ -9,18 +9,17 @@ PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_WAIT_RETRY_ADD="true" + ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=('/var/www/html/.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" diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 348cabf..030eaa1 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -9,18 +9,17 @@ PUBLIC_DNS_SERVER=ns2.duckdns.org CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=120 +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_WAIT_RETRY_ADD="true" + ACCOUNT_KEY_TYPE="rsa" PRIVATE_KEY_ALG="rsa" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" -# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. -ACL=('/var/www/html/.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" From 9e4372c5b6a7c0621e1876b773616f42775f786c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 25 Oct 2020 16:34:38 +0000 Subject: [PATCH 231/337] Don't check all the auth servers as one DuckDNS server isn't updating --- test/test-config/getssl-staging-dns01.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 030eaa1..d7eb7a4 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -6,7 +6,7 @@ VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" PUBLIC_DNS_SERVER=ns2.duckdns.org -CHECK_ALL_AUTH_DNS=true +CHECK_ALL_AUTH_DNS="false" DNS_EXTRA_WAIT=120 # Speed up the test by reducing the number or retries and the wait between retries. From c6b75847a486d89454608bd8519429ed8bee6198 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sun, 25 Oct 2020 21:24:53 +0000 Subject: [PATCH 232/337] When retrying DNS_ADD, delete first --- getssl | 9 +++++---- test/test-config/getssl-staging-dns01.cfg | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index cdd18e0..2fc8275 100755 --- a/getssl +++ b/getssl @@ -916,7 +916,7 @@ create_order() { d=$(echo "$d" | tr "[:upper:]" "[:lower:]") if [ "$d" == "$authdomain" ]; then debug "Saving authorization response for $authdomain for domain alldomains[$dn]" - debug "Response = ${response//['\t\r\n']}" + debug "Response = ${response//[$'\t\r\n']}" AuthLinkResponse[$dn]=$response AuthLinkResponseHeader[$dn]=$responseHeaders fi @@ -1049,7 +1049,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 - debug "authlink response = ${response//['\t\r\n']}" + debug "authlink response = ${response//[$'\t\r\n']}" # get the token and uri from the dns-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") uri=$(json_get "$response" "challenges" "type" "dns-01" "url") @@ -1105,7 +1105,7 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - debug "authlink response = ${response//['\t\r\n']}" + debug "authlink response = ${response//[$'\t\r\n']}" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") # get the uri from the http component @@ -1224,8 +1224,9 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then if [[ $ntries -lt $DNS_WAIT_COUNT ]]; then ntries=$(( ntries + 1 )) - if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 == 0 )) ]]; then + if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" fi diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index d7eb7a4..910c7fe 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -5,7 +5,7 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org +PUBLIC_DNS_SERVER=1.1.1.1 CHECK_ALL_AUTH_DNS="false" DNS_EXTRA_WAIT=120 From e045239a356219a2de71bf51e2a0727b4223614e Mon Sep 17 00:00:00 2001 From: Wade Fitzpatrick Date: Fri, 30 Oct 2020 17:00:56 +1000 Subject: [PATCH 233/337] Allow scoped Cloudflare API Tokens --- dns_scripts/Cloudflare-README.md | 52 +++++++++++ dns_scripts/dns_add_cloudflare | 148 ++++++++++++++++-------------- dns_scripts/dns_del_cloudflare | 150 ++++++++++++++++--------------- 3 files changed, 211 insertions(+), 139 deletions(-) create mode 100644 dns_scripts/Cloudflare-README.md diff --git a/dns_scripts/Cloudflare-README.md b/dns_scripts/Cloudflare-README.md new file mode 100644 index 0000000..f831cfb --- /dev/null +++ b/dns_scripts/Cloudflare-README.md @@ -0,0 +1,52 @@ +## Using Cloudflare DNS for LetsEncrypt domain validation + +### Enabling the scripts + +Set the following options in `getssl.cfg` (either global or domain-specific): + +``` +VALIDATE_VIA_DNS="true" +DNS_ADD_COMMAND="/usr/share/getssl/dns_scripts/dns_add_cloudflare" +DNS_DEL_COMMAND="/usr/share/getssl/dns_scripts/dns_del_cloudflare" +``` + +### Authentication + +There are 2 methods of authenticating with Cloudflare: + +1. API Keys - Account level, all-purpose tokens +2. API Tokens - Scoped and permissioned access to resources + +Both are configured from your profile in the [Cloudflare dashboard][1] + +[1]: https://dash.cloudflare.com/profile/api-tokens + +#### API Keys + +The **Zone ID** for the domain will be searched for programmatically. + +Set the following options in `getssl.cfg`: + +``` +export CF_EMAIL="..." # Cloudflare account email address +export CF_KEY="..." # Global API Key +``` + +#### API Tokens + +Cloudflare provides a template for creating an API Token with access to edit +zone records. Tokens must be created with at least '**DNS:Edit** permissions +for the domain to add/delete records. + +The API requires higher privileges to be able to list zones, therefore this +method also requires the **Zone ID** from the Overview tab in the Cloudflare +Dashboard. + +Set the following options in the domain-specific `getssl.cfg` + +``` +export CF_API_TOKEN="..." +export CF_ZONE_ID="..." +``` + +__Note__: API Keys will be used instead if also configured diff --git a/dns_scripts/dns_add_cloudflare b/dns_scripts/dns_add_cloudflare index ea323c1..b1a6a16 100755 --- a/dns_scripts/dns_add_cloudflare +++ b/dns_scripts/dns_add_cloudflare @@ -1,9 +1,11 @@ #!/usr/bin/env bash -# need to add your email address and API key to cloudflare below or set as env variables +# either configure here or export environment variables in getssl.cfg email=${CF_EMAIL:-''} key=${CF_KEY:-''} +api_token=${CF_API_TOKEN:-''} +zone_id=${CF_ZONE_ID:-''} -# This script adds a token to cloudflare DNS for the ACME challenge +# This script adds a TXT record to cloudflare DNS for the ACME challenge # usage dns_add_cloudflare "domain name" "token" # return codes are; # 0 - success @@ -14,7 +16,11 @@ key=${CF_KEY:-''} fulldomain="${1}" token="${2}" API='https://api.cloudflare.com/client/v4/zones' -curl_params=( -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H 'Content-Type: application/json' ) +if [[ -z "$api_token" ]]; then + curl_params=( -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H 'Content-Type: application/json' ) +else + curl_params=( -H "Authorization: Bearer $api_token" -H 'Content-Type: application/json' ) +fi # check initial parameters @@ -28,84 +34,88 @@ if [[ -z "$token" ]]; then exit 1 fi -if [[ -z "$email" ]]; then - echo "CF_EMAIL (email) parameter not set" - exit 1 -fi +if [[ -z "$api_token" ]]; then + if [[ -z "$email" ]]; then + echo "CF_EMAIL (email) parameter not set" + exit 1 + fi -if [[ -z "$key" ]]; then - echo "CF_KEY (key) parameter not set" - exit 1 + if [[ -z "$key" ]]; then + echo "CF_KEY (key) parameter not set" + exit 1 + fi fi -# get a list of all domain names from cloudflare -# If you have a lot, you may need add "&page=1&per_page=1000" and/or "&status=active" -resp=$(curl --silent "${curl_params[@]}" -X GET "$API") -re='"result":\[(([^][]*\[[^][]*])*[^][]*)]' # find result section -if [[ "${resp// }" =~ $re ]]; then - resp="${BASH_REMATCH[1]}" -fi - -# iterate through all sections to obtain a list of domains -while [[ "$resp" ]]; do - re='[^}{]*\{(([^}{]*\{[^}{]*})*[^}{]*)}(.*)' - if [[ "$resp" =~ $re ]]; then - first="${BASH_REMATCH[1]}" - resp="${BASH_REMATCH[3]}" +if [[ -z "$zone_id" ]]; then + # get a list of all domain names from cloudflare + # If you have a lot, you may need add "&page=1&per_page=1000" and/or "&status=active" + resp=$(curl --silent "${curl_params[@]}" -X GET "$API") + re='"result":\[(([^][]*\[[^][]*])*[^][]*)]' # find result section + if [[ "${resp// }" =~ $re ]]; then + resp="${BASH_REMATCH[1]}" fi - # remove subsections - leave only domain level - while [[ "$first" =~ (.*)[\[\{][^]\{\}[]*[\]\}](.*) ]]; do - first="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" - done - re='"name":"([^"]*)"' - if [[ "$first" =~ $re ]]; then - domains=( "${domains[@]}" "${BASH_REMATCH[1]}" ) - else - echo "Error getting domain name" - exit 2 - fi - re='"id":"([^"]*)"' - if [[ "$first" =~ $re ]]; then - ids=( "${ids[@]}" "${BASH_REMATCH[1]}" ) - else - echo "Error getting domain id" - exit 2 - fi -done -# split required domain name into an array -dnarray=(${fulldomain//./ }) -# get number of parts in required domain name -NumParts=${#dnarray[@]} -# build a test domain name, starting with the largest, and reduce it -# until a match is found, set domain = first ( longest) match. -domain="" -i=1 -while [ $i -lt "$NumParts" ]; do - testdomain="${dnarray[i-1]}" - for ((j=i; j Date: Wed, 18 Nov 2020 21:33:39 +0000 Subject: [PATCH 234/337] Wildcard support in ACMEv2 Fixes #347 and #400 --- getssl | 273 +++++++++++++++++++++++++++------------------------------ 1 file changed, 128 insertions(+), 145 deletions(-) diff --git a/getssl b/getssl index 2fc8275..f62174e 100755 --- a/getssl +++ b/getssl @@ -469,7 +469,7 @@ check_challenge_completion() { # checks with the ACME server if our challenge is # if ACME response is that their check gave an invalid response, error exit if [[ "$status" == "invalid" ]] ; then err_detail=$(echo "$response" | grep "detail") - #! FIXME need to check for "DNS problem: SERVFAIL looking up CAA ..." and retry + # TODO need to check for "DNS problem: SERVFAIL looking up CAA ..." and retry error_exit "$domain:Verify error:$err_detail" fi @@ -491,6 +491,84 @@ check_challenge_completion() { # checks with the ACME server if our challenge is fi } +check_challenge_completion_dns() { # perform validation via DNS challenge + token=$1 + uri=$2 + keyauthorization=$3 + d=$4 + primary_ns=$5 + auth_key=$6 + + # Always use lowercase domain name when querying DNS servers + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "${d##\*.}" | tr A-Z a-z) + + # check for token at public dns server, waiting for a valid response. + for ns in $primary_ns; do + debug "checking dns at $ns" + ntries=0 + check_dns="fail" + while [[ "$check_dns" == "fail" ]]; do + if [[ "$os" == "cygwin" ]]; then + check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ + | grep ^_acme -A2\ + | grep '"'|awk -F'"' '{ print $2}') + elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" + check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ + | grep -i "^_acme-challenge.${lower_d}" \ + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then + check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ + | grep 'descriptive text'|awk -F'"' '{ print $2}') + else + check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ + | grep 'text ='|awk -F'"' '{ print $2}') + fi + debug "expecting $auth_key" + debug "${ns} gave ... $check_result" + + if [[ "$check_result" == *"$auth_key"* ]]; then + check_dns="success" + else + if [[ $ntries -lt $DNS_WAIT_COUNT ]]; then + ntries=$(( ntries + 1 )) + + if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then + debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" + if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then + error_exit "DNS_ADD_COMMAND failed for domain $d" + fi + + fi + info "checking DNS at ${ns} for ${lower_d}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ + "waiting $DNS_WAIT secs before checking again" + sleep $DNS_WAIT + else + debug "dns check failed - removing existing value" + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" + + error_exit "checking _acme-challenge.${lower_d} gave $check_result not $auth_key" + fi + fi + done + done + + if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then + info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME server to check the dns" + sleep "$DNS_EXTRA_WAIT" + fi + + check_challenge_completion "$uri" "$d" "$keyauthorization" + + debug "remove DNS entry" + # shellcheck disable=SC2018,SC2019 + lower_d=$(echo "${d##\*.}" | tr A-Z a-z) + eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" +} +# end of ... perform validation if via DNS challenge + check_config() { # check the config files for all obvious errors debug "checking config" @@ -518,13 +596,13 @@ check_config() { # check the config files for all obvious errors config_errors=true fi - # get all domains + # get all domains into an array if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//[, ]/ } + read -r -a alldomains <<< "${SANS//[, ]/ }" else - alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") + read -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")" fi - if [[ -z "$alldomains" ]]; then + if [[ -z "${alldomains[*]}" ]]; then info "${DOMAIN}: no domains specified" config_errors=true fi @@ -542,11 +620,14 @@ check_config() { # check the config files for all obvious errors dn=0 tmplist=$(mktemp 2>/dev/null || mktemp -t getssl) - for d in $alldomains; do # loop over domains (dn is domain number) + for d in "${alldomains[@]}"; do # loop over domains (dn is domain number) debug "checking domain $d" if [[ "$(grep "^${d}$" "$tmplist")" = "$d" ]]; then info "${DOMAIN}: $d appears to be duplicated in domain, SAN list" config_errors=true + elif [[ "$d" != "${d##\*.}" ]] && [[ "$VALIDATE_VIA_DNS" != "true" ]]; then + info "${DOMAIN}: cannot use http-01 validation for wildcard domains" + config_errors=true else echo "$d" >> "$tmplist" fi @@ -682,7 +763,7 @@ clean_up() { # Perform pre-exit housekeeping # shellcheck source=/dev/null . "$dnsfile" debug "attempting to clean up DNS entry for $d" - eval "$DNS_DEL_COMMAND" "$d" "$auth_key" + eval "$DNS_DEL_COMMAND" "${d##\*.}" "$auth_key" done shopt -u nullglob fi @@ -809,14 +890,14 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) debug "domain csr exists at - $csr_file" # check all domains in config are in csr if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u) + read -r -a alldomains <<< "$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u)" else - alldomains=$(echo "$DOMAIN,$SANS" | sed -e 's/,/ /g; s/ $//; y/ /\n/' | sort -u) + read -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed -e 's/,/ /g; s/ $//; y/ /\n/' | sort -u)" fi domains_in_csr=$(openssl req -text -noout -in "$csr_file" \ | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ | sort -u) - for d in $alldomains; do + for d in "${alldomains[@]}"; do if [[ "$(echo "${domains_in_csr}"| grep "^${d}$")" != "${d}" ]]; then info "existing csr at $csr_file does not contain ${d} - re-create-csr"\ ".... $(echo "${domains_in_csr}"| grep "^${d}$")" @@ -824,7 +905,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) fi done # check all domains in csr are in config - if [[ "$alldomains" != "$domains_in_csr" ]]; then + if [[ "${alldomains[*]}" != "$domains_in_csr" ]]; then info "existing csr at $csr_file does not have the same domains as the config - re-create-csr" _RECREATE_CSR=1 fi @@ -877,7 +958,7 @@ create_key() { # create a domain key (if it doesn't already exist) create_order() { dstring="[" - for d in $alldomains; do + for d in "${alldomains[@]}"; do dstring="${dstring}{\"type\":\"dns\",\"value\":\"$d\"}," done dstring="${dstring::${#dstring}-1}]" @@ -893,9 +974,9 @@ create_order() { if [[ $API -eq 1 ]]; then dn=0 - for d in $alldomains; do + for d in "${alldomains[@]}"; do # get authorizations link - AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") + AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "${d##\*.}" "authorizations" "x") debug "authorizations link for $d - ${AuthLink[$dn]}" ((dn++)) done @@ -909,12 +990,14 @@ create_order() { send_signed_request "$l" "" # Get domain from response authdomain=$(json_get "$response" "identifier" "value") - # find array position (This is O(n2) but that doubt we'll see performance issues) + wildcard=$(json_get "$response" "wildcard") + debug wildcard="$wildcard" + # find array position (This is O(n2) but doubt that we'll see performance issues) dn=0 - for d in $alldomains; do + for d in "${alldomains[@]}"; do # Convert domain to lowercase as response from server will be in lowercase - d=$(echo "$d" | tr "[:upper:]" "[:lower:]") - if [ "$d" == "$authdomain" ]; then + lower_d=$(echo "$d" | tr "[:upper:]" "[:lower:]") + if [[ ( "$lower_d" == "$authdomain" && -z "$wildcard" ) || ( "$lower_d" == "*.${authdomain}" && -n "$wildcard" ) ]]; then debug "Saving authorization response for $authdomain for domain alldomains[$dn]" debug "Response = ${response//[$'\t\r\n']}" AuthLinkResponse[$dn]=$response @@ -1004,7 +1087,7 @@ find_dns_utils() { fulfill_challenges() { dn=0 -for d in $alldomains; do +for d in "${alldomains[@]}"; do # $d is domain in current loop, which is number $dn for ACL info "Verifying $d" if [[ "$USE_SINGLE_ACL" == "true" ]]; then @@ -1015,7 +1098,7 @@ for d in $alldomains; do # request a challenge token from ACME server if [[ $API -eq 1 ]]; then - request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"$d\"}}" + request="{\"resource\":\"new-authz\",\"identifier\":{\"type\":\"dns\",\"value\":\"${d##\*.}\"}}" send_signed_request "$URL_new_authz" "$request" debug "completed send_signed_request" @@ -1068,7 +1151,7 @@ for d in $alldomains; do debug auth_key "$auth_key" # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "$d" | tr A-Z a-z) + lower_d=$(echo "${d##\*.}" | tr A-Z a-z) debug "adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" @@ -1082,21 +1165,7 @@ for d in $alldomains; do fi debug primary_ns "$primary_ns" - # make a directory to hold pending dns-challenges - if [[ ! -d "$TEMP_DIR/dns_verify" ]]; then - mkdir "$TEMP_DIR/dns_verify" - fi - - # generate a file with the current variables for the dns-challenge - cat > "$TEMP_DIR/dns_verify/$d" <<- _EOF_ - token="${token}" - uri="${uri}" - keyauthorization="${keyauthorization}" - d="${d}" - primary_ns="${primary_ns}" - auth_key="${auth_key}" - _EOF_ - + check_challenge_completion_dns "${token}" "${uri}" "${keyauthorization}" "${d}" "${primary_ns}" "${auth_key}" else # set up the correct http token for verification if [[ $API -eq 1 ]]; then # get the token from the http component @@ -1179,101 +1248,7 @@ for d in $alldomains; do ((dn++)) fi done # end of ... loop through domains for cert ( from SANS list) - -# perform validation if via DNS challenge -if [[ $VALIDATE_VIA_DNS == "true" ]]; then - # loop through dns-variable files to check if dns has been changed - for dnsfile in "$TEMP_DIR"/dns_verify/*; do - if [[ -e "$dnsfile" ]]; then - debug "loading DNSfile: $dnsfile" - # shellcheck source=/dev/null - . "$dnsfile" - - # Always use lowercase domain name when querying DNS servers - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "$d" | tr A-Z a-z) - - # check for token at public dns server, waiting for a valid response. - for ns in $primary_ns; do - debug "checking dns at $ns" - ntries=0 - check_dns="fail" - while [[ "$check_dns" == "fail" ]]; do - if [[ "$os" == "cygwin" ]]; then - check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ - | grep ^_acme -A2\ - | grep '"'|awk -F'"' '{ print $2}') - elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" - check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ - | grep -i "^_acme-challenge.${lower_d}" \ - | grep 'IN\WTXT'|awk -F'"' '{ print $2}') - elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ - | grep 'descriptive text'|awk -F'"' '{ print $2}') - else - check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ - | grep 'text ='|awk -F'"' '{ print $2}') - fi - debug "expecting $auth_key" - debug "${ns} gave ... $check_result" - - if [[ "$check_result" == *"$auth_key"* ]]; then - check_dns="success" - else - if [[ $ntries -lt $DNS_WAIT_COUNT ]]; then - ntries=$(( ntries + 1 )) - - if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then - debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" - if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then - error_exit "DNS_ADD_COMMAND failed for domain $d" - fi - - fi - info "checking DNS at ${ns} for ${lower_d}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ - "waiting $DNS_WAIT secs before checking again" - sleep $DNS_WAIT - else - debug "dns check failed - removing existing value" - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" - # remove $dnsfile after each loop. - rm -f "$dnsfile" - - error_exit "checking _acme-challenge.${lower_d} gave $check_result not $auth_key" - fi - fi - done - done - fi - done - - if [[ "$DNS_EXTRA_WAIT" -gt 0 && "$PREVIOUSLY_VALIDATED" != "true" ]]; then - info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME server to check the dns" - sleep "$DNS_EXTRA_WAIT" - fi - - # loop through dns-variable files to let the ACME server check the challenges - for dnsfile in "$TEMP_DIR"/dns_verify/*; do - if [[ -e "$dnsfile" ]]; then - debug "loading DNSfile: $dnsfile" - # shellcheck source=/dev/null - . "$dnsfile" - - check_challenge_completion "$uri" "$d" "$keyauthorization" - - debug "remove DNS entry" - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "$d" | tr A-Z a-z) - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" - # remove $dnsfile after each loop. - rm -f "$dnsfile" - fi - done -fi -# end of ... perform validation if via DNS challenge -#end of varify each domain. +#end of verify each domain. } get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) @@ -2557,7 +2532,7 @@ if [[ ${_CHECK_ALL} -eq 1 ]]; then fi # check if $dir is a directory with a getssl.cfg in it if [[ -f "$dir/getssl.cfg" ]]; then - cmd="$cmd -w $WORKING_DIR $(basename "$dir")" + cmd="$cmd -w $WORKING_DIR \"$(basename "$dir")\"" debug "CMD: $cmd" eval "$cmd" fi @@ -2590,15 +2565,20 @@ if [[ ${_CREATE_CONFIG} -eq 1 ]]; then info "creating domain config file in $DOMAIN_DIR/getssl.cfg" # if domain has an existing cert, copy from domain and use to create defaults. EX_CERT=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:443" 2>/dev/null \ + | openssl s_client -servername "${DOMAIN##\*.}" -connect "${DOMAIN##\*.}:443" 2>/dev/null \ | openssl x509 2>/dev/null) - EX_SANS="www.${DOMAIN}" + EX_SANS="www.${DOMAIN##\*.}" if [[ -n "${EX_CERT}" ]]; then + # Putting this inside the EX_SANS line below doesn't work on Centos7 + escaped_d=${DOMAIN/\*/\\\*} EX_SANS=$(echo "$EX_CERT" \ | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ - | grep -Eo "DNS:[a-zA-Z 0-9.-]*" | sed "s@DNS:$DOMAIN@@g" | grep -v '^$' | cut -c 5-) + | grep -Eo "DNS:[a-zA-Z 0-9.-\*]*" | sed "s@DNS:${escaped_d}@@g" | grep -v '^$' | cut -c 5-) EX_SANS=${EX_SANS//$'\n'/','} fi + if [[ -n "${EX_SANS}" ]]; then + info "Adding SANS=$EX_SANS from certificate installed on ${DOMAIN##\*.} to new configuration file" + fi write_domain_template "$DOMAIN_DIR/getssl.cfg" fi TEMP_DIR="$DOMAIN_DIR/tmp" @@ -2674,11 +2654,12 @@ fi # 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 - debug "getting certificate for $DOMAIN from remote server" + real_d=${DOMAIN##\*.} + debug "getting certificate for $DOMAIN from remote server ($real_d)" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then # shellcheck disable=SC2086 # check if openssl supports RSA-PSS - if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then + if [[ $(echo | openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA512" else CIPHER="-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" @@ -2688,7 +2669,7 @@ if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then fi # shellcheck disable=SC2086 EX_CERT=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${CIPHER} 2>/dev/null \ + | openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${CIPHER} 2>/dev/null \ | openssl x509 2>/dev/null) if [[ -n "$EX_CERT" ]]; then # if obtained a cert if [[ -s "$CERT_FILE" ]]; then # if local exists @@ -2877,9 +2858,9 @@ info "Verify each domain" # loop through domains for cert ( from SANS list) if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - alldomains=${SANS//[, ]/ } + read -r -a alldomains <<< "${SANS//[, ]/ }" else - alldomains=$(echo "$DOMAIN,$SANS" | sed "s/,/ /g") + read -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed "s/,/ /g")" fi if [[ $API -eq 2 ]]; then @@ -2941,11 +2922,12 @@ fi # Check if the certificate is installed correctly if [[ ${CHECK_REMOTE} == "true" ]]; then + real_d=${DOMAIN##\*.} sleep "$CHECK_REMOTE_WAIT" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then # shellcheck disable=SC2086 # check if openssl supports RSA-PSS - if [[ $(echo | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then + if [[ $(echo | openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} -sigalgs RSA-PSS+SHA256 2>/dev/null) ]]; then PARAMS=("-sigalgs RSA-PSS+SHA256:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") else PARAMS=("-sigalgs RSA+SHA256:RSA+SHA384:RSA+SHA512" "-sigalgs ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512") @@ -2962,20 +2944,21 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then for ((i=0; i<${#PARAMS[@]};++i)); do debug "Checking ${CERTS[i]}" # shellcheck disable=SC2086 + debug openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} CERT_REMOTE=$(echo \ - | openssl s_client -servername "${DOMAIN}" -connect "${DOMAIN}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} 2>/dev/null \ + | openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} 2>/dev/null \ | openssl x509 -noout -fingerprint 2>/dev/null) CERT_LOCAL=$(openssl x509 -noout -fingerprint < "${CERTS[i]}" 2>/dev/null) debug CERT_LOCAL="${CERT_LOCAL}" debug CERT_REMOTE="${CERT_REMOTE}" if [[ "$CERT_LOCAL" == "$CERT_REMOTE" ]]; then - info "${DOMAIN} - ${TYPES[i]} certificate installed OK on server" + info "${real_d} - ${TYPES[i]} certificate installed OK on server" elif [[ "$CERT_REMOTE" == "" ]]; then info "${CERTS[i]} not returned by server" - error_exit "${DOMAIN} - ${TYPES[i]} certificate obtained but not installed on server" + error_exit "${real_d} - ${TYPES[i]} certificate obtained but not installed on server" else info "${CERTS[i]} didn't match server" - error_exit "${DOMAIN} - ${TYPES[i]} certificate obtained but certificate on server is different from the new certificate" + error_exit "${real_d} - ${TYPES[i]} certificate obtained but certificate on server is different from the new certificate" fi done fi From d359059971c8868a8835f924b8beef16aa09094f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 18 Nov 2020 21:35:55 +0000 Subject: [PATCH 235/337] Tests for wildcard changes --- test/10-mixed-case.bats | 4 +- test/14-test-revoke.bats | 4 +- test/15-test-revoke-no-suffix.bats | 4 +- test/17-test-spaces-in-sans-dns01.bats | 4 +- test/17-test-spaces-in-sans-http01.bats | 4 +- test/19-test-add-to-sans.bats | 15 ++- test/2-simple-dns01-dig.bats | 4 +- test/2-simple-dns01-nslookup.bats | 4 +- test/20-wildcard-simple.bats | 68 ++++++++++ test/21-wildcard-dual-rsa.bats | 82 ++++++++++++ ...dcard-dual-rsa-ecdsa-copy-2-locations.bats | 65 ++++++++++ test/23-wildcard-check-globbing.bats | 46 +++++++ test/24-wildcard-sans.bats | 69 ++++++++++ test/25-wildcard-all.bats | 42 ++++++ test/26-wildcard-revoke.bats | 49 +++++++ test/27-wildcard-existing-cert.bats | 45 +++++++ test/28-wildcard-error-http01-validation.bats | 31 +++++ test/README-Testing.md | 2 +- test/run-test.cmd | 1 + test/run-test.sh | 1 + ...etssl-dns01-dual-rsa-ecdsa-2-locations.cfg | 37 ++++++ test/test-config/getssl-dns01-secp384.cfg | 36 ++++++ test/test-config/getssl-dns01.cfg | 7 + test/test-config/getssl-staging-dns01.cfg | 6 +- test/test_helper.bash | 122 +++++++++--------- 25 files changed, 678 insertions(+), 74 deletions(-) create mode 100644 test/20-wildcard-simple.bats create mode 100644 test/21-wildcard-dual-rsa.bats create mode 100644 test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats create mode 100644 test/23-wildcard-check-globbing.bats create mode 100644 test/24-wildcard-sans.bats create mode 100644 test/25-wildcard-all.bats create mode 100644 test/26-wildcard-revoke.bats create mode 100644 test/27-wildcard-existing-cert.bats create mode 100644 test/28-wildcard-error-http01-validation.bats create mode 100644 test/test-config/getssl-dns01-dual-rsa-ecdsa-2-locations.cfg create mode 100644 test/test-config/getssl-dns01-secp384.cfg diff --git a/test/10-mixed-case.bats b/test/10-mixed-case.bats index 12b787c..f144058 100644 --- a/test/10-mixed-case.bats +++ b/test/10-mixed-case.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi } @test "Check that HTTP-01 verification works if the domain is not lowercase" { diff --git a/test/14-test-revoke.bats b/test/14-test-revoke.bats index e624552..5bdb72b 100644 --- a/test/14-test-revoke.bats +++ b/test/14-test-revoke.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi } diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 26f5f3f..5e8d797 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi } diff --git a/test/17-test-spaces-in-sans-dns01.bats b/test/17-test-spaces-in-sans-dns01.bats index 75dce4b..9f3b3dc 100644 --- a/test/17-test-spaces-in-sans-dns01.bats +++ b/test/17-test-spaces-in-sans-dns01.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi } diff --git a/test/17-test-spaces-in-sans-http01.bats b/test/17-test-spaces-in-sans-http01.bats index f3ba482..fab530f 100644 --- a/test/17-test-spaces-in-sans-http01.bats +++ b/test/17-test-spaces-in-sans-http01.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi } diff --git a/test/19-test-add-to-sans.bats b/test/19-test-add-to-sans.bats index 8d6f560..8ba9f20 100644 --- a/test/19-test-add-to-sans.bats +++ b/test/19-test-add-to-sans.bats @@ -7,18 +7,21 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a - curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + fi } teardown() { - curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a - curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a + if [ -z "$STAGING" ]; then + curl --silent -X POST -d '{"host":"a.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a + curl --silent -X POST -d '{"host":"b.'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a + fi } - @test "Create certificate to check can add to SANS" { skip "FIXME: Certificate is not recreated when SANS is updated" if [ -n "$STAGING" ]; then diff --git a/test/2-simple-dns01-dig.bats b/test/2-simple-dns01-dig.bats index 6e1eca3..6f701b3 100644 --- a/test/2-simple-dns01-dig.bats +++ b/test/2-simple-dns01-dig.bats @@ -6,7 +6,9 @@ load '/getssl/test/test_helper.bash' setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi if [ -f /usr/bin/host ]; then mv /usr/bin/host /usr/bin/host.getssl.bak fi diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index d13b318..c883485 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -7,7 +7,9 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { - export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi if [ -f /usr/bin/dig ]; then mv /usr/bin/dig /usr/bin/dig.getssl.bak fi diff --git a/test/20-wildcard-simple.bats b/test/20-wildcard-simple.bats new file mode 100644 index 0000000..e9b0f98 --- /dev/null +++ b/test/20-wildcard-simple.bats @@ -0,0 +1,68 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Create wildcard certificate" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check CHECK_REMOTE works for wildcard certificates" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + run ${CODE_DIR}/getssl "*.$GETSSL_HOST" + assert_success + assert_line --partial "certificate is valid for more than" + check_output_for_errors +} + + +@test "Force renewal of wildcard certificate" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + run ${CODE_DIR}/getssl -f "*.$GETSSL_HOST" + assert_success + refute_line --partial "certificate is valid for more than" + check_output_for_errors +} + + +@test "Check renewal of near-expiration wildcard certificate" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + echo "RENEW_ALLOW=2000" >> "${INSTALL_DIR}/.getssl/*.${GETSSL_HOST}/getssl.cfg" + + run ${CODE_DIR}/getssl "*.$GETSSL_HOST" + assert_success + refute_line --partial "certificate is valid for more than" + check_output_for_errors + cleanup_environment +} diff --git a/test/21-wildcard-dual-rsa.bats b/test/21-wildcard-dual-rsa.bats new file mode 100644 index 0000000..7f0943f --- /dev/null +++ b/test/21-wildcard-dual-rsa.bats @@ -0,0 +1,82 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Create secp384r1 wildcard certificate" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +ACCOUNT_KEY_TYPE="secp384r1" +PRIVATE_KEY_ALG="secp384r1" +EOF + + create_certificate + assert_success + check_output_for_errors + run openssl x509 -noout -text -in "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" + assert_line --partial "Public Key Algorithm: id-ecPublicKey" + cleanup_environment +} + + +@test "Create dual certificates using DNS-01 verification" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" +EOF + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + echo 'RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + else + echo 'CHECK_REMOTE="false"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + fi + + create_certificate + assert_success + check_output_for_errors + check_certificates + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] + + run openssl x509 -noout -text -in "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" + assert_line --partial "Public Key Algorithm: rsaEncryption" + + run openssl x509 -noout -text -in "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" + assert_line --partial "Public Key Algorithm: id-ecPublicKey" + + cleanup_environment +} diff --git a/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats b/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats new file mode 100644 index 0000000..238142e --- /dev/null +++ b/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats @@ -0,0 +1,65 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# These are run for every test, not once per file +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Create dual certificates (one wildcard) and copy RSA and ECDSA chain and key to two locations" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + + setup_environment + init_getssl + + cat <<- 'EOF' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key;/root/a.${GETSSL_HOST}/server.key" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # this is the domain cert and CA cert +EOF + + check_nginx + if [ "$OLD_NGINX" = "false" ]; then + echo 'RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + else + echo 'CHECK_REMOTE="false"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + fi + + create_certificate + assert_success + check_output_for_errors + + if [ "$OLD_NGINX" = "false" ]; then + assert_line --partial "rsa certificate installed OK on server" + assert_line --partial "prime256v1 certificate installed OK on server" + fi + + # Check that the RSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.crt" ] + assert [ -e "/etc/nginx/pki/private/server.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.key" ] + + # Check that the ECDSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.ec.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.ec.crt" ] + assert [ -e "/etc/nginx/pki/private/server.ec.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.ec.key" ] + + cleanup_environment +} diff --git a/test/23-wildcard-check-globbing.bats b/test/23-wildcard-check-globbing.bats new file mode 100644 index 0000000..1c812b1 --- /dev/null +++ b/test/23-wildcard-check-globbing.bats @@ -0,0 +1,46 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Check for globbing for wildcard domains" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + + init_getssl + + # Create a directory in /root which looks like a domain so that if glob expansion is performed a certificate for the wrong domain will be created + mkdir -p "${INSTALL_DIR}/a.${GETSSL_HOST}" + + create_certificate + assert_success + check_output_for_errors +} + + +@test "Force renewal of wildcard certificate" { + if [ -n "$STAGING" ]; then + skip "Not trying on staging server yet" + fi + + run ${CODE_DIR}/getssl -f "*.$GETSSL_HOST" + assert_success + refute_line --partial "certificate is valid for more than" + check_output_for_errors +} diff --git a/test/24-wildcard-sans.bats b/test/24-wildcard-sans.bats new file mode 100644 index 0000000..6e463c8 --- /dev/null +++ b/test/24-wildcard-sans.bats @@ -0,0 +1,69 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"wild-'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + fi +} + + +teardown() { + if [ -z "$STAGING" ]; then + curl --silent -X POST -d '{"host":"wild-'$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/clear-a + fi +} + + +@test "Check can create certificate for wildcard domain as arg and non-wildcard in SANS" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + # Staging server generates an error if try to create a certificate for *.domain and a.domain + # so create for *.wild-domain and a.domain instead + GETSSL_CMD_HOST="*.wild-${GETSSL_HOST}" + setup_environment + init_getssl + + echo 'SANS="${GETSSL_HOST}"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + + create_certificate + assert_success + check_output_for_errors + run openssl x509 -noout -text -in "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" + # verify certificate is for wildcard domain with non-wildcard domain in the Subject Alternative Name list + assert_output --regexp "Subject: CN[ ]?=[ ]?\*.wild-${GETSSL_HOST}" + assert_output --partial "DNS:${GETSSL_HOST}" +} + + +@test "Check can create certificate for non-wildcard domain as arg and wildcard in SANS" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="${GETSSL_HOST}" + setup_environment + init_getssl + + echo 'SANS="*.wild-${GETSSL_HOST}"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + + create_certificate + assert_success + check_output_for_errors + run openssl x509 -noout -text -in "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" + # verify certificate is for non-wildcard domain with wildcard domain in the Subject Alternative Name list + assert_output --regexp "Subject: CN[ ]?=[ ]?${GETSSL_HOST}" + assert_output --partial "DNS:*.wild-${GETSSL_HOST}" +} diff --git a/test/25-wildcard-all.bats b/test/25-wildcard-all.bats new file mode 100644 index 0000000..8e428ab --- /dev/null +++ b/test/25-wildcard-all.bats @@ -0,0 +1,42 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Check can create certificate for wildcard domain using --all" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + # Create .getssl directory and .getssl/*.{host} directory + init_getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/*.${GETSSL_HOST}/getssl.cfg" + + # create another domain in the .getssl directory + run ${CODE_DIR}/getssl -c "a.${GETSSL_HOST}" + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/a.${GETSSL_HOST}/getssl.cfg" + + # Create a directory in /root which looks like a domain so that if glob expansion is performed the wildcard certificate won't be created + mkdir -p "${INSTALL_DIR}/a.${GETSSL_HOST}" + + run ${CODE_DIR}/getssl --all + + assert_success + assert_line --partial "Certificate saved in /root/.getssl/*.${GETSSL_HOST}/*.${GETSSL_HOST}" + assert_line --partial "Certificate saved in /root/.getssl/a.${GETSSL_HOST}/a.${GETSSL_HOST}" + check_output_for_errors +} diff --git a/test/26-wildcard-revoke.bats b/test/26-wildcard-revoke.bats new file mode 100644 index 0000000..1416ef4 --- /dev/null +++ b/test/26-wildcard-revoke.bats @@ -0,0 +1,49 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Create certificate to check wildcard revoke" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + init_getssl + create_certificate + assert_success + check_output_for_errors +} + + +@test "Check we can revoke a wildcard certificate" { + if [ -n "$STAGING" ]; then + CONFIG_FILE="getssl-staging-dns01.cfg" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" + + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + + CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt + KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key + + run ${CODE_DIR}/getssl -d --revoke $CERT $KEY $CA + assert_line "certificate revoked" + assert_success + check_output_for_errors "debug" +} diff --git a/test/27-wildcard-existing-cert.bats b/test/27-wildcard-existing-cert.bats new file mode 100644 index 0000000..76f2aa7 --- /dev/null +++ b/test/27-wildcard-existing-cert.bats @@ -0,0 +1,45 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Check that new creating a new configuration files uses details from existing certificate" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + else + CONFIG_FILE="getssl-dns01.cfg" + fi + + # Create and install certificate for wildcard + another domain + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + init_getssl + + echo 'SANS="a.${GETSSL_HOST}"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + + create_certificate + assert_success + check_output_for_errors + + # Delete configuration + rm -r ${INSTALL_DIR}/.getssl + + # Create configuration + run ${CODE_DIR}/getssl -c "${GETSSL_CMD_HOST}" + + # Assert that the newly created configuration contains the additional domain in SANS + # if this fails then error in tests will be "grep failed" - this means SANS did not hold the expected value + # eg SANS="a.centos7.getssl.test" + grep -q "SANS=\"a.${GETSSL_HOST}\"" ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg + assert_success +} diff --git a/test/28-wildcard-error-http01-validation.bats b/test/28-wildcard-error-http01-validation.bats new file mode 100644 index 0000000..dbb64b7 --- /dev/null +++ b/test/28-wildcard-error-http01-validation.bats @@ -0,0 +1,31 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Check that trying to create a wildcard certificate using http-01 validation shows an error message" { + if [ -n "$STAGING" ]; then + skip "Internal test, no need to test on staging server" + else + CONFIG_FILE="getssl-http01.cfg" + fi + + # Try and create a wildcard certificate using http-01 validation + GETSSL_CMD_HOST="*.${GETSSL_HOST}" + setup_environment + init_getssl + + create_certificate + assert_failure + assert_line --partial "cannot use http-01 validation for wildcard domains" +} diff --git a/test/README-Testing.md b/test/README-Testing.md index 3cd4b2c..d713914 100644 --- a/test/README-Testing.md +++ b/test/README-Testing.md @@ -8,7 +8,7 @@ For continuous integration testing we have the following: 1. Uses `docker-compose` to start `pebble` (letsencrypt test server) and `challtestsrv` (minimal dns client for pebble) 2. Then runs the `bats` test scripts (all the files with a ".bats" extension) for each OS (alpine, centos6, debian, ubuntu) -3. Runs the `bats` test script against the staging server (using nn ubuntu docker image and duckdns.org) +3. Runs the `bats` test script against the staging server (using ubuntu docker image and duckdns.org) ## To run all the tests on a single OS diff --git a/test/run-test.cmd b/test/run-test.cmd index 43c4e40..23235c9 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -49,6 +49,7 @@ docker run -it ^ --network-alias i.%OS%.getssl.test ^ --network-alias j.%OS%.getssl.test ^ --network-alias k.%OS%.getssl.test ^ + --network-alias wild-%ALIAS% ^ --name getssl-%OS% ^ getssl-%OS% ^ %COMMAND% diff --git a/test/run-test.sh b/test/run-test.sh index d99d5a3..8a2a9f2 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -42,6 +42,7 @@ docker run \ --network-alias "i.$OS.getssl.test" \ --network-alias "j.$OS.getssl.test" \ --network-alias "k.$OS.getssl.test" \ + --network-alias "wild-$OS.getssl.test" \ --name "getssl-$OS" \ "getssl-$OS" \ $COMMAND diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa-2-locations.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa-2-locations.cfg new file mode 100644 index 0000000..829e73c --- /dev/null +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa-2-locations.cfg @@ -0,0 +1,37 @@ +# Test that more than one location can be specified for CERT and KEY locations and that the +# files are copied to both locations when both RSA and ECDSA certificates are created +# +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 + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" +# SANS="a.${GETSSL_HOST}" + +# 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;/root/a.${GETSSL_HOST}/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # 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-dual-certs ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-dns01-secp384.cfg b/test/test-config/getssl-dns01-secp384.cfg new file mode 100644 index 0000000..2cc360c --- /dev/null +++ b/test/test-config/getssl-dns01-secp384.cfg @@ -0,0 +1,36 @@ +# 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 +# +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 + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 + +ACCOUNT_KEY_TYPE="secp384r1" +PRIVATE_KEY_ALG="secp384r1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="" + +# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index e995c80..c67f995 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -31,3 +31,10 @@ RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /get # Define the server type and confirm correct certificate is installed SERVER_TYPE="https" CHECK_REMOTE="true" + +#_USE_DEBUG=1 +#_RUNNING_TEST=1 + +if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then + . $DOMAIN_DIR/getssl_test_specific.cfg +fi diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 910c7fe..905c44d 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -31,6 +31,10 @@ 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" -# Define the server type and confirm correct certificate is installed (using a custom port) +# Define the server type and confirm correct certificate is installed SERVER_TYPE="https" CHECK_REMOTE="true" + +if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then + . $DOMAIN_DIR/getssl_test_specific.cfg +fi diff --git a/test/test_helper.bash b/test/test_helper.bash index db79ea4..c5d5320 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -3,99 +3,103 @@ CODE_DIR=/getssl check_certificates() { - assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.crt" ] - assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" ] - assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" ] } # Only nginx > 1.11.0 support dual certificates in a single configuration file # https://unix.stackexchange.com/questions/285924/how-to-compare-a-programs-version-in-a-shell-script check_nginx() { - requiredver="1.11.0" - currentver=$(nginx -v 2>&1 | awk -F"/" '{print $2}') - if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then - export OLD_NGINX="false" - else - echo "INFO: Running nginx version $currentver which doesn't support dual certificates" >&3 - echo "INFO: not checking that certificate is installed correctly" >&3 - export OLD_NGINX="true" - fi + requiredver="1.11.0" + currentver=$(nginx -v 2>&1 | awk -F"/" '{print $2}') + if [ "$(printf '%s\n' "$requiredver" "$currentver" | sort -V | head -n1)" = "$requiredver" ]; then + export OLD_NGINX="false" + else + echo "INFO: Running nginx version $currentver which doesn't support dual certificates" >&3 + echo "INFO: not checking that certificate is installed correctly" >&3 + export OLD_NGINX="true" + fi } check_output_for_errors() { - refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' - # less strict tests if running with debug output - if [ -n "$1" ]; then - # don't fail for :error:badNonce - refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' - else - refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' - fi - refute_line --partial 'command not found' + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + # less strict tests if running with debug output + if [ -n "$1" ]; then + # don't fail for :error:badNonce + refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' + # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' + else + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + fi + refute_line --partial 'command not found' } cleanup_environment() { + if [ -z "$STAGING" ]; then curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'"}' http://10.30.50.3:8055/clear-a + fi } create_certificate() { - # Create certificate - cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" - # shellcheck disable=SC2086 - run ${CODE_DIR}/getssl $1 "$GETSSL_CMD_HOST" + # Create certificate + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + # shellcheck disable=SC2086 + run ${CODE_DIR}/getssl $1 "$GETSSL_CMD_HOST" } init_getssl() { - # Run initialisation (create account key, etc) - run ${CODE_DIR}/getssl -c "$GETSSL_CMD_HOST" - assert_success - [ -d "$INSTALL_DIR/.getssl" ] + # Run initialisation (create account key, etc) + run ${CODE_DIR}/getssl -c "$GETSSL_CMD_HOST" + assert_success + [ -d "$INSTALL_DIR/.getssl" ] } setup_environment() { - # One-off test setup - if [[ -d ${INSTALL_DIR}/.getssl ]]; then - rm -r ${INSTALL_DIR}/.getssl - fi + # One-off test setup + if [[ -d ${INSTALL_DIR}/.getssl ]]; then + rm -r ${INSTALL_DIR}/.getssl + fi + if [ -z "$STAGING" ]; then curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a - cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" - /getssl/test/restart-nginx + fi + cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" + /getssl/test/restart-nginx } # start nginx in background on alpine via supervisord # shellcheck disable=SC2153 # Ignore GETSSL_OS looks like typo of GETSSL_IP if [[ -f /usr/bin/supervisord && -f /etc/supervisord.conf ]]; then - if [[ ! $(pgrep supervisord) ]]; then - /usr/bin/supervisord -c /etc/supervisord.conf >&3- - fi + if [[ ! $(pgrep supervisord) ]]; then + /usr/bin/supervisord -c /etc/supervisord.conf >&3- + fi elif [[ "$GETSSL_OS" == "centos"[78] ]]; then - if [ -z "$(pgrep nginx)" ]; then - nginx >&3- - fi + if [ -z "$(pgrep nginx)" ]; then + nginx >&3- + fi fi # Find NGINX configuration directory for HTTP-01 testing (need to add SSL to config) if [[ -f /etc/nginx/conf.d/default.conf ]]; then - export NGINX_CONFIG=/etc/nginx/conf.d/default.conf + export NGINX_CONFIG=/etc/nginx/conf.d/default.conf elif [[ -f /etc/nginx/sites-enabled/default ]]; then - export NGINX_CONFIG=/etc/nginx/sites-enabled/default + export NGINX_CONFIG=/etc/nginx/sites-enabled/default else - echo "Can't find NGINX directory" - exit 1 + echo "Can't find NGINX directory" + exit 1 fi # Find IP address if [[ -n "$(command -v ip)" ]]; then - GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') + GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') elif [[ -n "$(command -v hostname)" ]]; then - GETSSL_IP=$(hostname -I | sed -e 's/[[:space:]]*$//') + GETSSL_IP=$(hostname -I | sed -e 's/[[:space:]]*$//') else - echo "Cannot find IP address" - exit 1 + echo "Cannot find IP address" + exit 1 fi export GETSSL_IP @@ -103,11 +107,11 @@ export GETSSL_IP GETSSL_CMD_HOST=$GETSSL_HOST export GETSSL_CMD_HOST -if [ ! -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 - CERT_FILE=/etc/ssl/certs/ca-certificates.crt - if [ ! -f $CERT_FILE ]; then - CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt - fi - cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt +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 + CERT_FILE=/etc/ssl/certs/ca-certificates.crt + if [ ! -f $CERT_FILE ]; then + CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt + fi + cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt fi From 36f9b1912b96ea1b2ca2ec22d7e2c81ddb248bfc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 18 Nov 2020 21:36:19 +0000 Subject: [PATCH 236/337] Hardcode bats release to avoid test fragility --- test/Dockerfile-alpine | 2 +- test/Dockerfile-centos6 | 2 +- test/Dockerfile-centos7 | 2 +- test/Dockerfile-centos7-staging | 2 +- test/Dockerfile-centos8 | 2 +- test/Dockerfile-debian | 2 +- test/Dockerfile-ubuntu | 3 +-- test/Dockerfile-ubuntu-staging | 2 +- test/Dockerfile-ubuntu16 | 2 +- test/Dockerfile-ubuntu18 | 2 +- 10 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index e0f3a6c..b609938 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -12,7 +12,7 @@ RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 862e680..d578f4b 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -16,7 +16,7 @@ RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index afbcf23..e86f521 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -12,7 +12,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index a539cac..0b2ff08 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -17,7 +17,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos8 b/test/Dockerfile-centos8 index 15c14d6..4ccb817 100644 --- a/test/Dockerfile-centos8 +++ b/test/Dockerfile-centos8 @@ -14,7 +14,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index d066813..b39f915 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -11,7 +11,7 @@ RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 2ef9e25..3849e55 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -9,7 +9,6 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light RUN apt-get install -y vim dos2unix # for debugging -# TODO test with drill, dig, host WORKDIR /root @@ -17,7 +16,7 @@ WORKDIR /root RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 05f0471..1ee3f83 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -20,7 +20,7 @@ WORKDIR /root RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index ba6164c..41be837 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -13,7 +13,7 @@ RUN mkdir /etc/nginx/pki/private 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 /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 1735646..5e4c574 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -16,7 +16,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local From ccab4baeb6763c7a8db6375de4d9593c05b1f14c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 18 Nov 2020 21:47:12 +0000 Subject: [PATCH 237/337] Update revision history --- getssl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/getssl b/getssl index f62174e..be7f2bc 100755 --- a/getssl +++ b/getssl @@ -240,6 +240,8 @@ # 2020-09-02 Fix issue when SANS is space and comma separated (#579) (2.30) # 2020-10-02 Various fixes to get_auth_dns and changes to support unit tests (#308) # 2020-10-04 Add CHECK_PUBLIC_DNS_SERVER to check the DNS challenge has been updated there +# 2020-10-13 Bugfix: strip comments in drill/dig output (mhameed) +# 2020-11-18 Wildcard support (#347)(#400) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -2945,6 +2947,7 @@ if [[ ${CHECK_REMOTE} == "true" ]]; then debug "Checking ${CERTS[i]}" # shellcheck disable=SC2086 debug openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} + # shellcheck disable=SC2086 CERT_REMOTE=$(echo \ | openssl s_client -servername "${real_d}" -connect "${real_d}:${REMOTE_PORT}" ${REMOTE_EXTRA} ${PARAMS[i]} 2>/dev/null \ | openssl x509 -noout -fingerprint 2>/dev/null) From 5677a13fd59f6cc4e1c749a33a405365b2c5cee1 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 19 Nov 2020 14:55:25 +0000 Subject: [PATCH 238/337] Don't check certificate is installed when testing against staging server --- test/24-wildcard-sans.bats | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/24-wildcard-sans.bats b/test/24-wildcard-sans.bats index 6e463c8..da5fea9 100644 --- a/test/24-wildcard-sans.bats +++ b/test/24-wildcard-sans.bats @@ -35,6 +35,9 @@ teardown() { init_getssl echo 'SANS="${GETSSL_HOST}"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + if [ -n "$STAGING" ]; then + echo 'CHECK_REMOTE="false"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + fi create_certificate assert_success From 60bda5a3d466aef8665825c835bbcedab7848c47 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Mon, 23 Nov 2020 22:50:00 +0800 Subject: [PATCH 239/337] Improve README.md code block readability by correct syntax highlight --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 12eaf66..7803ef8 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ command line). Within the **working directory** is a config file `getssl.cfg` which is a simple bash file containing variables, an example of which is: -```getssl +```sh # Uncomment and modify any variables you need # The staging server is best for testing (hence set as default) CA="https://acme-staging-v02.api.letsencrypt.org" @@ -200,7 +200,7 @@ then, within the **working directory** there will be a folder for each certificate (based on its domain name). Within that folder will be a config file (again called `getssl.cfg`). An example of which is: -```getssl +```sh # 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 @@ -270,7 +270,7 @@ Multiple locations can be defined for a file by separating the locations with a A typical config file for `example.com` and `www.example.com` on the same server would be: -```getssl +```sh # uncomment and modify any variables you need # The staging server is best for testing CA="https://acme-staging-v02.api.letsencrypt.org" From 9a3dc5c553992b3772810122305d214ef8dac75a Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 24 Nov 2020 22:56:55 +0800 Subject: [PATCH 240/337] Remove deprecated Travis CI config Looks like getssl was not using Travis CI for a while: https://travis-ci.org/github/srvrco/getssl And the GitHub Actions was introduced for a while: https://github.com/srvrco/getssl/commits/master/.github/workflows Remove Travis CI config in the repository will help contributor reduce the confusion about which CI platform is currently using. --- .travis.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 867a6d2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: bash - -# Use container-based infrastructure for quicker build start-up -sudo: false - -addons: - apt: - sources: - - debian-sid # Grab shellcheck from the Debian repo (o_O) - packages: - - shellcheck - -script: - - bash -c 'shopt -s globstar; shellcheck getssl' - -matrix: - fast_finish: true From f361492fae1461adb083b910dc1d766da10294a8 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 25 Nov 2020 03:43:45 +0800 Subject: [PATCH 241/337] Add "Table of Contents" in README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 7803ef8..14878a5 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,19 @@ Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. +## Table of Contents + +- [Features](#features) +- [Installation](#installation) +- [Overview](#overview) +- [Getting started](#getting-started) +- [Automating updates](#automating-updates) +- [Structure](#structure) +- [Server-Types](#server-types) +- [Revoke a certificate](#revoke-a-certificate) +- [Elliptic curve keys](#elliptic-curve-keys) +- [Issues / problems / help](#issues--problems--help) + ## Features * **Bash** - It runs on virtually all unix machines, including BSD, most From 970004eda76b9bdaa137bac21d7b50274bc729d5 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 25 Nov 2020 03:45:07 +0800 Subject: [PATCH 242/337] Correct Makefile indentation in .editorconfig --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 91d16ec..5abccad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,6 @@ end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 + +[Makefile] +indent_style = tab From 7ed9a01314862fd1026e9a75b3ff1d49a5b0fcab Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 25 Nov 2020 03:52:14 +0800 Subject: [PATCH 243/337] Follow EditorConfig, use consistent space indent in Nginx test configs --- test/test-config/nginx-ubuntu-dual-certs | 104 +++++++++++------------ test/test-config/nginx-ubuntu-no-ssl | 36 ++++---- test/test-config/nginx-ubuntu-ssl | 104 +++++++++++------------ 3 files changed, 122 insertions(+), 122 deletions(-) diff --git a/test/test-config/nginx-ubuntu-dual-certs b/test/test-config/nginx-ubuntu-dual-certs index 1eceecd..b8f4bcc 100644 --- a/test/test-config/nginx-ubuntu-dual-certs +++ b/test/test-config/nginx-ubuntu-dual-certs @@ -14,66 +14,66 @@ # Default server configuration # server { - listen 80 default_server; - listen 5002 default_server; - listen [::]:5002 default_server; + listen 80 default_server; + listen 5002 default_server; + listen [::]:5002 default_server; - # SSL configuration - # + # SSL configuration + # listen 443 ssl default_server; - 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; + 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; + root /var/www/html; - # Add index.php to the list if you are using PHP - index index.html index.htm index.nginx-debian.html; + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; - server_name _; + server_name _; ssl_certificate /etc/nginx/pki/server.crt; ssl_certificate_key /etc/nginx/pki/private/server.key; ssl_certificate /etc/nginx/pki/server.ec.crt; ssl_certificate_key /etc/nginx/pki/private/server.ec.key; - location / { - # First attempt to serve request as file, then - # as directory, then fall back to displaying a 404. - try_files $uri $uri/ =404; - } + 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; - #} + # 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; - #} + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} } @@ -83,15 +83,15 @@ server { # to sites-enabled/ to enable it. # #server { -# listen 80; -# listen [::]:80; +# listen 80; +# listen [::]:80; # -# server_name example.com; +# server_name example.com; # -# root /var/www/example.com; -# index index.html; +# root /var/www/example.com; +# index index.html; # -# location / { -# try_files $uri $uri/ =404; -# } +# location / { +# try_files $uri $uri/ =404; +# } #} diff --git a/test/test-config/nginx-ubuntu-no-ssl b/test/test-config/nginx-ubuntu-no-ssl index e7b046e..8dc55ff 100644 --- a/test/test-config/nginx-ubuntu-no-ssl +++ b/test/test-config/nginx-ubuntu-no-ssl @@ -1,30 +1,30 @@ # Default server configuration # server { - listen 80 default_server; - listen 5002 default_server; - listen [::]:5002 default_server; + listen 80 default_server; + listen 5002 default_server; + listen [::]:5002 default_server; - # SSL configuration - # - listen 443 default_server; - listen [::]:443 default_server; + # SSL configuration + # + listen 443 default_server; + listen [::]:443 default_server; - listen 5001 default_server; - listen [::]:5001 default_server; + listen 5001 default_server; + listen [::]:5001 default_server; - root /var/www/html; + root /var/www/html; - # Add index.php to the list if you are using PHP - index index.html index.htm index.nginx-debian.html; + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; - server_name _; + 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; - } + location / { + # First attempt to serve request as file, then + # as directory, then fall back to displaying a 404. + try_files $uri $uri/ =404; + } } diff --git a/test/test-config/nginx-ubuntu-ssl b/test/test-config/nginx-ubuntu-ssl index 9f79407..882b36a 100644 --- a/test/test-config/nginx-ubuntu-ssl +++ b/test/test-config/nginx-ubuntu-ssl @@ -14,61 +14,61 @@ # Default server configuration # server { - listen 80 default_server; - listen 5002 default_server; - listen [::]:5002 default_server; + listen 80 default_server; + listen 5002 default_server; + listen [::]:5002 default_server; - # SSL configuration - # + # SSL configuration + # listen 443 ssl default_server; - 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; + 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; + root /var/www/html; - # Add index.php to the list if you are using PHP - index index.html index.htm index.nginx-debian.html; + # Add index.php to the list if you are using PHP + index index.html index.htm index.nginx-debian.html; - server_name _; + 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; - } + 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; - #} + # 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; - #} + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} } @@ -78,15 +78,15 @@ server { # to sites-enabled/ to enable it. # #server { -# listen 80; -# listen [::]:80; +# listen 80; +# listen [::]:80; # -# server_name example.com; +# server_name example.com; # -# root /var/www/example.com; -# index index.html; +# root /var/www/example.com; +# index index.html; # -# location / { -# try_files $uri $uri/ =404; -# } +# location / { +# try_files $uri $uri/ =404; +# } #} From 1c617c1bf68389d84bbb192c986ae1eee1b5d821 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 25 Nov 2020 04:02:02 +0800 Subject: [PATCH 244/337] Make markdownlint happy about README.md ``` README.md:1 MD041/first-line-heading/first-line-h1 First line in file should be a top level heading [Context: "![Run all tests](https://githu..."] README.md:2 MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Above] [Context: "# getssl"] ``` --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7803ef8..e5441d9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) ![shellcheck](https://github.com/srvrco/getssl/workflows/shellcheck/badge.svg) # getssl +![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) ![shellcheck](https://github.com/srvrco/getssl/workflows/shellcheck/badge.svg) + Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. From 9fec3b445c291e91d3b8d2ddd72be5e85ffc4a6c Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 25 Nov 2020 03:55:33 +0800 Subject: [PATCH 245/337] Fix indentation to two spaces in other_scripts/cpanel_cert_upload --- other_scripts/cpanel_cert_upload | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/other_scripts/cpanel_cert_upload b/other_scripts/cpanel_cert_upload index a76181b..757504f 100755 --- a/other_scripts/cpanel_cert_upload +++ b/other_scripts/cpanel_cert_upload @@ -14,12 +14,12 @@ rawurlencode() { local pos c o for (( pos=0 ; pos Date: Fri, 27 Nov 2020 14:01:41 +0800 Subject: [PATCH 246/337] Reduce git clone depth in the Dockerfile for test This change will drop the history of the repository when cloning, which will save bandwidth and disk space, speed up the clone and checkout process during the test and Docker build process. --- test/Dockerfile-alpine | 6 +++--- test/Dockerfile-centos6 | 6 +++--- test/Dockerfile-centos7 | 6 +++--- test/Dockerfile-centos7-staging | 6 +++--- test/Dockerfile-centos8 | 6 +++--- test/Dockerfile-debian | 6 +++--- test/Dockerfile-ubuntu | 6 +++--- test/Dockerfile-ubuntu-staging | 6 +++--- test/Dockerfile-ubuntu16 | 6 +++--- test/Dockerfile-ubuntu18 | 6 +++--- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index b609938..7728c8c 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -12,9 +12,9 @@ RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Use supervisord to run nginx in the background diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index d578f4b..6db7a74 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -16,9 +16,9 @@ RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index e86f521..620a9c6 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -12,7 +12,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 0b2ff08..9fdb29d 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -17,9 +17,9 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 diff --git a/test/Dockerfile-centos8 b/test/Dockerfile-centos8 index 4ccb817..9c144d3 100644 --- a/test/Dockerfile-centos8 +++ b/test/Dockerfile-centos8 @@ -14,7 +14,7 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index b39f915..2cf919d 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -11,9 +11,9 @@ RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 3849e55..21169ea 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -16,9 +16,9 @@ WORKDIR /root RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 1ee3f83..15d5a59 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -20,9 +20,9 @@ WORKDIR /root RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 41be837..038fd79 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -13,9 +13,9 @@ RUN mkdir /etc/nginx/pki/private 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 /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 5e4c574..01d33d0 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -16,9 +16,9 @@ COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default RUN touch /root/.rnd # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 -RUN git clone https://github.com/bats-core/bats-support /bats-support -RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone --depth 1 https://github.com/bats-core/bats-support /bats-support +RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local EXPOSE 80 443 From 6a6851e185f0336c95aeb4b5d46b567fbc187e32 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Thu, 26 Nov 2020 12:04:48 +0800 Subject: [PATCH 247/337] Reduce Docker image layer by merging mkdir operation --- test/Dockerfile-alpine | 3 +-- test/Dockerfile-centos6 | 3 +-- test/Dockerfile-centos7 | 3 +-- test/Dockerfile-centos7-staging | 3 +-- test/Dockerfile-centos8 | 3 +-- test/Dockerfile-debian | 3 +-- test/Dockerfile-ubuntu16 | 3 +-- test/Dockerfile-ubuntu18 | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index 7728c8c..5b1dbf3 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -8,8 +8,7 @@ WORKDIR /root # Create nginx directories in standard places RUN mkdir /run/nginx -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private # BATS (Bash Automated Testings) RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 6db7a74..108add0 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -11,8 +11,7 @@ RUN yum -y install epel-release RUN yum -y install git curl dnsutils ldns wget nginx WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf # BATS (Bash Automated Testings) diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index 620a9c6..382a703 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -6,8 +6,7 @@ RUN yum -y install epel-release RUN yum -y install git curl ldns bind-utils wget which nginx WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 9fdb29d..abb697c 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -11,8 +11,7 @@ ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf diff --git a/test/Dockerfile-centos8 b/test/Dockerfile-centos8 index 9c144d3..2b20d8f 100644 --- a/test/Dockerfile-centos8 +++ b/test/Dockerfile-centos8 @@ -8,8 +8,7 @@ RUN yum -y install epel-release RUN yum -y install git curl bind-utils wget which nginx WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index 2cf919d..6da08d5 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -7,8 +7,7 @@ RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private # BATS (Bash Automated Testings) RUN git clone --depth 1 https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 038fd79..6b13f68 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -8,8 +8,7 @@ RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default # BATS (Bash Automated Testings) diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 01d33d0..0979cab 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -8,8 +8,7 @@ RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget gawk nginx-light WORKDIR /root -RUN mkdir /etc/nginx/pki -RUN mkdir /etc/nginx/pki/private +RUN mkdir -p /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default # Prevent "Can't load /root/.rnd into RNG" error from openssl From fb7b3ee145203257ee59c6a81fb1a9995f29084c Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Thu, 26 Nov 2020 20:08:44 +0800 Subject: [PATCH 248/337] Use JSON notation for Dockerfile CMD arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reference: - https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#cmd > The `CMD` instruction should be used to run the software contained in your image, along with any arguments. `CMD` should almost always be used in the form of `CMD ["executable", "param1", "param2"…]` --- test/Dockerfile-alpine | 2 +- test/Dockerfile-centos6 | 2 +- test/Dockerfile-centos7-staging | 2 +- test/Dockerfile-debian | 2 +- test/Dockerfile-ubuntu | 2 +- test/Dockerfile-ubuntu-staging | 2 +- test/Dockerfile-ubuntu16 | 2 +- test/Dockerfile-ubuntu18 | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index 5b1dbf3..399e10f 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -18,4 +18,4 @@ RUN /bats-core/install.sh /usr/local # Use supervisord to run nginx in the background COPY ./test/test-config/alpine-supervisord.conf /etc/supervisord.conf -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 108add0..66cc468 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -23,4 +23,4 @@ RUN /bats-core/install.sh /usr/local EXPOSE 80 443 # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index abb697c..f3d985b 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -24,4 +24,4 @@ RUN /bats-core/install.sh /usr/local EXPOSE 80 443 # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index 6da08d5..640f069 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -16,4 +16,4 @@ RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 21169ea..953c3bf 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -22,4 +22,4 @@ RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 15d5a59..8fc3455 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -26,4 +26,4 @@ RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 6b13f68..bb521ff 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -18,4 +18,4 @@ RUN git clone --depth 1 https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 0979cab..98b71af 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -23,4 +23,4 @@ RUN /bats-core/install.sh /usr/local EXPOSE 80 443 # Run eternal loop - for testing -CMD tail -f /dev/null +CMD [ "tail", "-f", "/dev/null" ] From 9d43cf81f144771be537a10e93a91880e0ad991a Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Thu, 26 Nov 2020 23:21:16 +0800 Subject: [PATCH 249/337] Remove useless comment in nginx test configs --- test/test-config/nginx-centos7.conf | 8 --- test/test-config/nginx-ubuntu-dual-certs | 69 ------------------------ test/test-config/nginx-ubuntu-no-ssl | 9 ---- test/test-config/nginx-ubuntu-ssl | 69 ------------------------ 4 files changed, 155 deletions(-) diff --git a/test/test-config/nginx-centos7.conf b/test/test-config/nginx-centos7.conf index 2327039..f487519 100644 --- a/test/test-config/nginx-centos7.conf +++ b/test/test-config/nginx-centos7.conf @@ -1,13 +1,8 @@ -# For more information on configuration, see: -# * Official English Documentation: http://nginx.org/en/docs/ -# * Official Russian Documentation: http://nginx.org/ru/docs/ - user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; -# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { @@ -30,8 +25,5 @@ http { include /etc/nginx/mime.types; default_type application/octet-stream; - # Load modular configuration files from the /etc/nginx/conf.d directory. - # See http://nginx.org/en/docs/ngx_core_module.html#include - # for more information. include /etc/nginx/conf.d/*.conf; } diff --git a/test/test-config/nginx-ubuntu-dual-certs b/test/test-config/nginx-ubuntu-dual-certs index b8f4bcc..f6a4c80 100644 --- a/test/test-config/nginx-ubuntu-dual-certs +++ b/test/test-config/nginx-ubuntu-dual-certs @@ -1,45 +1,16 @@ -## -# 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; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; @@ -52,46 +23,6 @@ server { ssl_certificate_key /etc/nginx/pki/private/server.ec.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; -# } -#} diff --git a/test/test-config/nginx-ubuntu-no-ssl b/test/test-config/nginx-ubuntu-no-ssl index 8dc55ff..aa32d05 100644 --- a/test/test-config/nginx-ubuntu-no-ssl +++ b/test/test-config/nginx-ubuntu-no-ssl @@ -1,12 +1,8 @@ -# Default server configuration -# server { listen 80 default_server; listen 5002 default_server; listen [::]:5002 default_server; - # SSL configuration - # listen 443 default_server; listen [::]:443 default_server; @@ -15,16 +11,11 @@ server { 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; } } diff --git a/test/test-config/nginx-ubuntu-ssl b/test/test-config/nginx-ubuntu-ssl index 882b36a..e87a497 100644 --- a/test/test-config/nginx-ubuntu-ssl +++ b/test/test-config/nginx-ubuntu-ssl @@ -1,45 +1,16 @@ -## -# 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 _; @@ -47,46 +18,6 @@ server { 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 8333a1e8175d8cd20a207a6222a3ff2441bc3125 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 28 Nov 2020 11:26:13 +0000 Subject: [PATCH 250/337] Update version to 2.31 Fix bug with nslookup server param --- getssl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index be7f2bc..1fb58fb 100755 --- a/getssl +++ b/getssl @@ -241,12 +241,12 @@ # 2020-10-02 Various fixes to get_auth_dns and changes to support unit tests (#308) # 2020-10-04 Add CHECK_PUBLIC_DNS_SERVER to check the DNS challenge has been updated there # 2020-10-13 Bugfix: strip comments in drill/dig output (mhameed) -# 2020-11-18 Wildcard support (#347)(#400) +# 2020-11-18 Wildcard support (#347)(#400)(2.31) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.30" +VERSION="2.31" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1255,13 +1255,11 @@ done # end of ... loop through domains for cert ( from SANS list) get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) orig_gad_d="$1" # domain name - gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER - if [[ -n "$gad_s" ]]; then - gad_s="@$gad_s" - fi + orig_gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER + gad_d="$orig_gad_d" + gad_s="$orig_gad_s" if [[ "$os" == "cygwin" ]]; then - gad_d="$orig_gad_d" # shellcheck disable=SC2086 all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ | grep "primary name server" \ @@ -1278,7 +1276,10 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi if [[ -n "$HAS_DIG_OR_DRILL" ]]; then - gad_d="$orig_gad_d" + if [[ -n "$gad_s" ]]; then + gad_s="@$gad_s" + fi + # Use SOA +trace to find the name server if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then if [[ "$HAS_DIG_OR_DRILL" == "dig" ]]; then @@ -1350,6 +1351,9 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi fi + # Remove leading '@' if we tried using dig/drill + gad_s="$orig_gad_s" + if [[ "$HAS_HOST" == "true" ]]; then gad_d="$orig_gad_d" debug Using "host -t NS" to find primary name server for "$gad_d" From e5d44d31178cfb3faf64a9eb62811883c2ca0a30 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 28 Nov 2020 11:26:57 +0000 Subject: [PATCH 251/337] Tweaks to try and make staging tests more robust --- test/test-config/getssl-staging-dns01.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 905c44d..19413cd 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -6,12 +6,12 @@ VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" PUBLIC_DNS_SERVER=1.1.1.1 -CHECK_ALL_AUTH_DNS="false" +CHECK_ALL_AUTH_DNS="true" +CHECK_PUBLIC_DNS_SERVER="true" DNS_EXTRA_WAIT=120 -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 +# Speed up the test by reducing the number or retries and retrying DNS_ADD after 10 failures +DNS_WAIT_COUNT=20 DNS_WAIT_RETRY_ADD="true" ACCOUNT_KEY_TYPE="rsa" From c8415df61de275c65f5ae1dd6f2074e62cf659bb Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 8 Dec 2020 21:41:47 +0000 Subject: [PATCH 252/337] mktemp not working correctly on Alpine Linux (added XXXXXX to template) Fixes #612 --- getssl | 11 +++++--- test/29-check-mktemp-failure.bats | 47 +++++++++++++++++++++++++++++++ test/u3-mktemp-template.bats | 11 ++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 test/29-check-mktemp-failure.bats create mode 100644 test/u3-mktemp-template.bats diff --git a/getssl b/getssl index 1fb58fb..f509060 100755 --- a/getssl +++ b/getssl @@ -621,7 +621,7 @@ check_config() { # check the config files for all obvious errors fi dn=0 - tmplist=$(mktemp 2>/dev/null || mktemp -t getssl) + tmplist=$(mktemp 2>/dev/null || mktemp -t getssl.XXXXXX) || error_exit "mktemp failed" for d in "${alldomains[@]}"; do # loop over domains (dn is domain number) debug "checking domain $d" if [[ "$(grep "^${d}$" "$tmplist")" = "$d" ]]; then @@ -695,7 +695,10 @@ check_config() { # check the config files for all obvious errors } check_getssl_upgrade() { # check if a more recent version of code is available available - TEMP_UPGRADE_FILE="$(mktemp 2>/dev/null || mktemp -t getssl)" + TEMP_UPGRADE_FILE="$(mktemp 2>/dev/null || mktemp -t getssl.XXXXXX)" + if [ "$TEMP_UPGRADE_FILE" == "" ]; then + error_exit "mktemp failed" + fi curl --user-agent "$CURL_USERAGENT" --silent "$CODE_LOCATION" --output "$TEMP_UPGRADE_FILE" errcode=$? if [[ $errcode -eq 60 ]]; then @@ -918,7 +921,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) if [[ ! -s "$csr_file" ]] || [[ "$_RECREATE_CSR" == "1" ]]; then info "creating domain csr - $csr_file" # create a temporary config file, for portability. - tmp_conf=$(mktemp 2>/dev/null || mktemp -t getssl) + tmp_conf=$(mktemp 2>/dev/null || mktemp -t getssl) || error_exit "mktemp failed" cat "$SSLCONF" > "$tmp_conf" printf "[SAN]\n%s" "$SANLIST" >> "$tmp_conf" # add OCSP Must-Staple to the domain csr @@ -1928,7 +1931,7 @@ revoke_certificate() { # revoke a certificate ACCOUNT_KEY="$REVOKE_KEY" # need to set the revoke key as "account_key" since it's used in send_signed_request. get_signing_params "$REVOKE_KEY" - TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) + TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t getssl) || error_exit "mktemp failed" debug "revoking from $URL_revoke" rcertdata=$(sed '1d;$d' "$REVOKE_CERT" | tr -d "\r\n" | tr '/+' '_-' | tr -d '= ') send_signed_request "$URL_revoke" "{\"certificate\": \"$rcertdata\",\"reason\": $REVOKE_REASON}" diff --git a/test/29-check-mktemp-failure.bats b/test/29-check-mktemp-failure.bats new file mode 100644 index 0000000..782dc64 --- /dev/null +++ b/test/29-check-mktemp-failure.bats @@ -0,0 +1,47 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Check that getssl -c fails with an error message if mktemp fails" { + if [ -n "$STAGING" ]; then + skip "Internal test, no need to test on staging server" + else + CONFIG_FILE="getssl-http01.cfg" + fi + + # set TMPDIR to an invalid directory and check for failure + export TMPDIR=/getssl.invalid.directory + setup_environment + run ${CODE_DIR}/getssl -c "$GETSSL_CMD_HOST" + assert_failure + assert_line --partial "mktemp failed" +} + + +@test "Check that getssl fails with an error message if mktemp fails" { + if [ -n "$STAGING" ]; then + skip "Internal test, no need to test on staging server" + else + CONFIG_FILE="getssl-http01.cfg" + fi + + setup_environment + init_getssl + + # set TMPDIR to an invalid directory and check for failure + export TMPDIR=/getssl.invalid.directory + create_certificate + assert_failure + assert_line --partial "mktemp failed" +} diff --git a/test/u3-mktemp-template.bats b/test/u3-mktemp-template.bats new file mode 100644 index 0000000..2cb8040 --- /dev/null +++ b/test/u3-mktemp-template.bats @@ -0,0 +1,11 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +@test "Check mktemp -t getssl.XXXXXX works on all platforms" { + run mktemp -t getssl.XXXXXX + assert_success +} From 9350bce17f453a5c0ba05cba4e9c430f4ca1d513 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 10 Dec 2020 18:39:29 +0000 Subject: [PATCH 253/337] Add dns scripts for Dynu.com --- dns_scripts/dns_add_dynu | 68 ++++++++++++++++++++++++++++++++++++++++ dns_scripts/dns_del_dynu | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 dns_scripts/dns_add_dynu create mode 100644 dns_scripts/dns_del_dynu diff --git a/dns_scripts/dns_add_dynu b/dns_scripts/dns_add_dynu new file mode 100644 index 0000000..7a08ce8 --- /dev/null +++ b/dns_scripts/dns_add_dynu @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Need to add your API key below or set as env variable +apikey=${DYNU_API_KEY:-''} + +# This script adds a token to dynu.com DNS for the ACME challenge +# usage dns_add_dynu "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in dynu.com etc) + +fulldomain="${1}" +token="${2}" + +API='https://api.dynu.com/v2/dns' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi + +curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X GET "$API") + +# Match domain id +re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +if [[ "$resp" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your Dynu account' + exit 3 +fi + +# Check for existing _acme-challenge TXT record +resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") +re="\"id\":([^,]*)" +if [[ "$resp" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + # Add new TXT challenge record + resp=$(curl --silent \ + "${curl_params[@]}" \ + -X POST "${API}/${domain_id}/record" \ + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") +else + resp=$(curl --silent \ + "${curl_params[@]}" \ + -X POST "${API}/${domain_id}/record/${record_id}" \ + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") +fi + +# If adding record failed (exception:) then print error message +if [[ "${resp// }" == *'"exception"'* ]]; then + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 +fi diff --git a/dns_scripts/dns_del_dynu b/dns_scripts/dns_del_dynu new file mode 100644 index 0000000..310bb61 --- /dev/null +++ b/dns_scripts/dns_del_dynu @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Need to add your API key below or set as env variable +apikey=${DYNU_API_KEY:-''} + +# This script deletes the _acme-challenge TXT record from the dynu.com DNS entry for the domain +# usage dns_del_dynu "domain name" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in dynu.com etc) + +fulldomain="${1}" + +API='https://api.dynu.com/v2/dns' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$apikey" ]]; then + echo "DNS script requires an apikey to be set" + exit 1 +fi + +curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X GET "$API") + +# Match domain id +re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +if [[ "$resp" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your Dynu account' + exit 3 +fi + +# Find existing _acme-challenge TXT record +resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") +re="\"id\":([^,]*)" +if [[ "$resp" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + echo "No _acme-challenge TXT record found for $fulldomain" + exit 3 +fi + +resp=$(curl --silent \ + "${curl_params[@]}" \ + -X DELETE "${API}/${domain_id}/record/${record_id}") + +# If adding record failed (exception:) then print error message +if [[ "${resp// }" == *'"exception"'* ]]; then + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 +fi From fa9a8c7f4532934d5c0fc9efd85d8bb88e4e0088 Mon Sep 17 00:00:00 2001 From: Veit Wahlich Date: Thu, 17 Dec 2020 15:12:57 +0100 Subject: [PATCH 254/337] Fix delimiter issues with ${alldomains[]} in create_csr() - use \n as delimiter for read to array - use \n as delimiter for comparing SANs --- getssl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index f509060..31885d6 100755 --- a/getssl +++ b/getssl @@ -895,9 +895,9 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) debug "domain csr exists at - $csr_file" # check all domains in config are in csr if [[ "$IGNORE_DIRECTORY_DOMAIN" == "true" ]]; then - read -r -a alldomains <<< "$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u)" + read -d '\n' -r -a alldomains <<< "$(echo "$SANS" | sed -e 's/ //g; s/,$//; y/,/\n/' | sort -u)" else - read -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed -e 's/,/ /g; s/ $//; y/ /\n/' | sort -u)" + read -d '\n' -r -a alldomains <<< "$(echo "$DOMAIN,$SANS" | sed -e 's/,/ /g; s/ $//; y/ /\n/' | sort -u)" fi domains_in_csr=$(openssl req -text -noout -in "$csr_file" \ | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ @@ -910,7 +910,7 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) fi done # check all domains in csr are in config - if [[ "${alldomains[*]}" != "$domains_in_csr" ]]; then + if [[ "$(IFS=$'\n'; echo -n "${alldomains[*]}")" != "$domains_in_csr" ]]; then info "existing csr at $csr_file does not have the same domains as the config - re-create-csr" _RECREATE_CSR=1 fi From d35f7baf9139edeacdc35e732ed28ae04ccd1a82 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 17 Dec 2020 21:58:47 +0000 Subject: [PATCH 255/337] Some fixes to get_auth_dns --- getssl | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index f509060..8c85771 100755 --- a/getssl +++ b/getssl @@ -520,12 +520,24 @@ check_challenge_completion_dns() { # perform validation via DNS challenge check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ | grep -i "^_acme-challenge.${lower_d}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + debug "check_result=$check_result" + if [[ -z "$check_result" ]]; then + debug "$DNS_CHECK_FUNC" ANY "_acme-challenge.${lower_d}" "@${ns}" + check_result=$($DNS_CHECK_FUNC ANY "_acme-challenge.${lower_d}" "@${ns}" \ + | grep -i "^_acme-challenge.${lower_d}" \ + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + debug "check_result=$check_result" + fi elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') else check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ | grep 'text ='|awk -F'"' '{ print $2}') + if [[ -z "$check_result" ]]; then + check_result=$(nslookup -type=any "_acme-challenge.${lower_d}" "${ns}" \ + | grep 'text ='|awk -F'"' '{ print $2}') + fi fi debug "expecting $auth_key" debug "${ns} gave ... $check_result" @@ -538,6 +550,7 @@ check_challenge_completion_dns() { # perform validation via DNS challenge if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + test_output "Retrying adding dns via command: $DNS_ADD_COMMAND" eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" @@ -1165,10 +1178,12 @@ for d in "${alldomains[@]}"; do # find a primary / authoritative DNS server for the domain if [[ -z "$AUTH_DNS_SERVER" ]]; then get_auth_dns "$d" + elif [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$AUTH_DNS_SERVER $PUBLIC_DNS_SERVER" else primary_ns="$AUTH_DNS_SERVER" fi - debug primary_ns "$primary_ns" + debug set primary_ns = "$primary_ns" check_challenge_completion_dns "${token}" "${uri}" "${keyauthorization}" "${d}" "${primary_ns}" "${auth_key}" else # set up the correct http token for verification @@ -1270,11 +1285,11 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -z "$all_auth_dns_servers" ]]; then error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" fi + primary_ns="$all_auth_dns_servers" if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then - primary_ns="$all_auth_dns_servers $PUBLIC_DNS_SERVER" - else - primary_ns="$all_auth_dns_servers" + primary_ns="$primary_ns $PUBLIC_DNS_SERVER" fi + return fi @@ -1348,8 +1363,8 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n primary_ns="$primary_ns $PUBLIC_DNS_SERVER" fi - debug set primary_ns = "$primary_ns" test_output set primary_ns ="$primary_ns" + return fi fi @@ -1428,9 +1443,6 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi - if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then - primary_ns="$primary_ns $PUBLIC_DNS_SERVER" - fi return fi fi From 1199739fc97bf8b845933d17e42dbd002812e884 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 17 Dec 2020 22:00:04 +0000 Subject: [PATCH 256/337] Move DNS used for staging tests to Dynu --- dns_scripts/dns_add_dynu | 14 +++++--- dns_scripts/dns_del_dynu | 22 ++++++++---- test/15-test-revoke-no-suffix.bats | 6 +++- test/18-staging-retry-dns-add.bats | 16 +++++++-- test/Dockerfile-centos7-staging | 1 + test/Dockerfile-ubuntu-staging | 1 + test/dns_add_fail | 6 ++++ test/dns_fail_add_duckdns | 19 ---------- test/run-test.cmd | 2 +- test/run-test.sh | 3 +- .../getssl-staging-dns01-fail-dns-add.cfg | 33 ----------------- .../getssl-staging-dns01-no-suffix.cfg | 36 ------------------- test/test-config/getssl-staging-dns01.cfg | 11 ++++-- 13 files changed, 62 insertions(+), 108 deletions(-) create mode 100644 test/dns_add_fail delete mode 100755 test/dns_fail_add_duckdns delete mode 100644 test/test-config/getssl-staging-dns01-fail-dns-add.cfg delete mode 100644 test/test-config/getssl-staging-dns01-no-suffix.cfg diff --git a/dns_scripts/dns_add_dynu b/dns_scripts/dns_add_dynu index 7a08ce8..e20470d 100644 --- a/dns_scripts/dns_add_dynu +++ b/dns_scripts/dns_add_dynu @@ -28,10 +28,11 @@ fi curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) # Get domain id -resp=$(curl --silent "${curl_params[@]}" -X GET "$API") +# curl -X GET https://api.dynu.com/v2/dns/getroot/ubuntu-getssl.freeddns.org +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/getroot/${fulldomain}") # Match domain id -re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +re="\"id\":([^,]*),\"domainName\":\"${fulldomain}\"" if [[ "$resp" =~ $re ]]; then domain_id="${BASH_REMATCH[1]}" fi @@ -42,6 +43,7 @@ if [[ -z "$domain_id" ]]; then fi # Check for existing _acme-challenge TXT record +# curl -X GET "https://api.dynu.com/v2/dns/record/_acme-challenge.ubuntu-getssl.freeddns.org?recordType=TXT" resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") re="\"id\":([^,]*)" if [[ "$resp" =~ $re ]]; then @@ -53,16 +55,18 @@ if [[ -z "$record_id" ]]; then resp=$(curl --silent \ "${curl_params[@]}" \ -X POST "${API}/${domain_id}/record" \ - --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"$token\"}") else + # Update existing record + # curl -X POST https://api.dynu.com/v2/dns/9329328/record/7082063 -d "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"Test2\"}" resp=$(curl --silent \ "${curl_params[@]}" \ -X POST "${API}/${domain_id}/record/${record_id}" \ - --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"$token\"}") fi # If adding record failed (exception:) then print error message -if [[ "${resp// }" == *'"exception"'* ]]; then +if [[ "$resp" != *"\"statusCode\":200"* ]]; then echo "Error: DNS challenge not added: unknown error - ${resp}" exit 3 fi diff --git a/dns_scripts/dns_del_dynu b/dns_scripts/dns_del_dynu index 310bb61..1d8d588 100644 --- a/dns_scripts/dns_del_dynu +++ b/dns_scripts/dns_del_dynu @@ -10,6 +10,12 @@ apikey=${DYNU_API_KEY:-''} # 2 - error within internal processing # 3 - error in result ( domain not found in dynu.com etc) +# After deleting the TXT record from Dynu.com it takes over 30 minutes to add a new TXT record! +# This doesn't happen when updating the TXT record, just for delete then add +# As this is used for testing, changed the delete to a no-op. + +exit 0 + fulldomain="${1}" API='https://api.dynu.com/v2/dns' @@ -20,17 +26,18 @@ if [[ -z "$fulldomain" ]]; then exit 1 fi if [[ -z "$apikey" ]]; then - echo "DNS script requires an apikey to be set" + echo "DNS script requires apikey environment variable to be set" exit 1 fi curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) # Get domain id -resp=$(curl --silent "${curl_params[@]}" -X GET "$API") +# curl -X GET https://api.dynu.com/v2/dns/getroot/ubuntu-getssl.freeddns.org +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/getroot/${fulldomain}") # Match domain id -re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +re="\"id\":([^,]*),\"domainName\":\"${fulldomain}\"" if [[ "$resp" =~ $re ]]; then domain_id="${BASH_REMATCH[1]}" fi @@ -40,7 +47,8 @@ if [[ -z "$domain_id" ]]; then exit 3 fi -# Find existing _acme-challenge TXT record +# Check for existing _acme-challenge TXT record +# curl -X GET "https://api.dynu.com/v2/dns/record/_acme-challenge.ubuntu-getssl.freeddns.org?recordType=TXT" resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") re="\"id\":([^,]*)" if [[ "$resp" =~ $re ]]; then @@ -48,8 +56,8 @@ if [[ "$resp" =~ $re ]]; then fi if [[ -z "$record_id" ]]; then - echo "No _acme-challenge TXT record found for $fulldomain" - exit 3 + echo "No _acme-challenge.${fulldomain} TXT record found" + exit 0 fi resp=$(curl --silent \ @@ -57,7 +65,7 @@ resp=$(curl --silent \ -X DELETE "${API}/${domain_id}/record/${record_id}") # If adding record failed (exception:) then print error message -if [[ "${resp// }" == *'"exception"'* ]]; then +if [[ "$resp" != *"\"statusCode\":200"* ]]; then echo "Error: DNS challenge not added: unknown error - ${resp}" exit 3 fi diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 5e8d797..96e174b 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -15,10 +15,12 @@ setup() { @test "Create certificate to check revoke (no suffix)" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01-no-suffix.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" else CONFIG_FILE="getssl-http01-no-suffix.cfg" fi + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" setup_environment init_getssl @@ -34,6 +36,8 @@ setup() { else CONFIG_FILE="getssl-http01.cfg" fi + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key diff --git a/test/18-staging-retry-dns-add.bats b/test/18-staging-retry-dns-add.bats index 8b636d6..bca7c05 100644 --- a/test/18-staging-retry-dns-add.bats +++ b/test/18-staging-retry-dns-add.bats @@ -6,14 +6,26 @@ load '/getssl/test/test_helper.bash' -@test "Check retry add dns command if dns isn't updated (DuckDNS)" { +@test "Check retry add dns command if dns isn't updated" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-staging-dns01-fail-dns-add.cfg" + + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +DNS_ADD_COMMAND="/getssl/test/dns_add_fail" + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 +CHECK_ALL_AUTH_DNS="false" +CHECK_PUBLIC_DNS_SERVER="false" +EOF create_certificate -d assert_failure assert_line --partial "Retrying adding dns via command" diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 0b2ff08..ad49896 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -9,6 +9,7 @@ RUN yum -y install git curl bind-utils ldns wget which nginx ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 1ee3f83..3032c85 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -8,6 +8,7 @@ ENV DEBIAN_FRONTEND noninteractive # Ensure tests in this image use the staging server ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 # Update and install required software RUN apt-get update --fix-missing diff --git a/test/dns_add_fail b/test/dns_add_fail new file mode 100644 index 0000000..44ab42b --- /dev/null +++ b/test/dns_add_fail @@ -0,0 +1,6 @@ +#!/bin/bash + +# Special test script which will always fail to update dns + +echo "This is a test script to check retry works if DNS isn't updated" +exit 0 diff --git a/test/dns_fail_add_duckdns b/test/dns_fail_add_duckdns deleted file mode 100755 index 03df89f..0000000 --- a/test/dns_fail_add_duckdns +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Special test script which will always fail to update dns - -token=${DUCKDNS_TOKEN:-} - -if [ -z "$token" ]; then - echo "DUCKDNS_TOKEN not set" - exit 1 -fi - -domain="$1" - -response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=FAIL") -if [ "$response" != "OK" ]; then - echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" - echo "Response: $response" - exit 1 -fi diff --git a/test/run-test.cmd b/test/run-test.cmd index 23235c9..0462617 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -23,7 +23,7 @@ set COMMAND=bats /getssl/test GOTO CheckAlias :staging -set ALIAS=%OS:-staging=%-getssl.duckdns.org +set ALIAS=%OS:-staging=%-getssl.freeddns.org set STAGING=--env STAGING=true :Run diff --git a/test/run-test.sh b/test/run-test.sh index 8a2a9f2..760c600 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -15,7 +15,8 @@ else fi if [[ "$OS" == *"staging"* ]]; then - ALIAS="${OS%-staging}-getssl.duckdns.org" + #ALIAS="${OS%-staging}-getssl.duckdns.org" + ALIAS="${OS%-staging}-getssl.freeddns.org" STAGING="--env STAGING=true" else ALIAS="$OS.getssl.test" diff --git a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg deleted file mode 100644 index 2985d32..0000000 --- a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg +++ /dev/null @@ -1,33 +0,0 @@ -# Special config to test that the retry dns_add_command logic works -# -CA="https://acme-staging-v02.api.letsencrypt.org/directory" - -# Generic staging config -VALIDATE_VIA_DNS=true -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org -CHECK_ALL_AUTH_DNS=true - -# Test that the retry works (dns_add_command will always fail) -DNS_WAIT_RETRY_ADD="true" -DNS_ADD_COMMAND="/getssl/test/dns_fail_add_duckdns" - -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 -DNS_EXTRA_WAIT=0 - -# Standard config -ACCOUNT_KEY_TYPE="rsa" -PRIVATE_KEY_ALG="rsa" -SANS="" -ACL=('/var/www/html/.well-known/acme-challenge') -USE_SINGLE_ACL="false" -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 -RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" -SERVER_TYPE="https" -CHECK_REMOTE="true" diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg deleted file mode 100644 index b4f5202..0000000 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ /dev/null @@ -1,36 +0,0 @@ -# Test that the script works with external dns provider and staging server -# -CA="https://acme-staging-v02.api.letsencrypt.org" - -VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org -CHECK_ALL_AUTH_DNS=true -DNS_EXTRA_WAIT=120 - -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 -DNS_WAIT_RETRY_ADD="true" - -ACCOUNT_KEY_TYPE="rsa" -PRIVATE_KEY_ALG="rsa" - -# Additional domains - this could be multiple domains / subdomains in a comma separated list -SANS="" - -# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" - -# Define the server type and confirm correct certificate is installed (using a custom port) -SERVER_TYPE="https" -CHECK_REMOTE="true" diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 19413cd..64a7388 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -3,15 +3,17 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=1.1.1.1 +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_dynu" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_dynu" +PUBLIC_DNS_SERVER="8.8.8.8 resolver1.infoserve.de" +AUTH_DNS_SERVER=ns1.dynu.com CHECK_ALL_AUTH_DNS="true" CHECK_PUBLIC_DNS_SERVER="true" DNS_EXTRA_WAIT=120 # Speed up the test by reducing the number or retries and retrying DNS_ADD after 10 failures DNS_WAIT_COUNT=20 +DNS_WAIT=30 DNS_WAIT_RETRY_ADD="true" ACCOUNT_KEY_TYPE="rsa" @@ -38,3 +40,6 @@ CHECK_REMOTE="true" if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then . $DOMAIN_DIR/getssl_test_specific.cfg fi + +#_RUNNING_TEST=1 +#_USE_DEBUG=1 From 27265df6671568a0873df4f666cc77ac1a8dba66 Mon Sep 17 00:00:00 2001 From: atisne Date: Fri, 18 Dec 2020 10:28:58 +0100 Subject: [PATCH 257/337] Wrong SANS when domain contains a minus character A regexp to extract 'Subject Alternative Name' doesn't work when the domain use a minus character. For grep regexp, to lose its special meaning inside brackets, the minus character must be placed in the first or last position in the list. https://www.gnu.org/software/grep/manual/grep.html#Character-Classes-and-Bracket-Expressions --- getssl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/getssl b/getssl index f509060..0caa2a4 100755 --- a/getssl +++ b/getssl @@ -2578,11 +2578,10 @@ if [[ ${_CREATE_CONFIG} -eq 1 ]]; then | openssl x509 2>/dev/null) EX_SANS="www.${DOMAIN##\*.}" if [[ -n "${EX_CERT}" ]]; then - # Putting this inside the EX_SANS line below doesn't work on Centos7 escaped_d=${DOMAIN/\*/\\\*} EX_SANS=$(echo "$EX_CERT" \ | openssl x509 -noout -text 2>/dev/null| grep "Subject Alternative Name" -A2 \ - | grep -Eo "DNS:[a-zA-Z 0-9.-\*]*" | sed "s@DNS:${escaped_d}@@g" | grep -v '^$' | cut -c 5-) + | grep -Eo "DNS:[a-zA-Z 0-9.\*-]*" | sed "s@DNS:${escaped_d}@@g" | grep -v '^$' | cut -c 5-) EX_SANS=${EX_SANS//$'\n'/','} fi if [[ -n "${EX_SANS}" ]]; then From c90ca25ede458182fb4b7c7ee37cbda57cdac8cc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 18 Dec 2020 21:11:10 +0000 Subject: [PATCH 258/337] Add unit test for create_csr --- getssl | 2 ++ test/u4-create-csr-and-ifs.bats | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 test/u4-create-csr-and-ifs.bats diff --git a/getssl b/getssl index 31885d6..cf82e0c 100755 --- a/getssl +++ b/getssl @@ -913,6 +913,8 @@ create_csr() { # create a csr using a given key (if it doesn't already exist) if [[ "$(IFS=$'\n'; echo -n "${alldomains[*]}")" != "$domains_in_csr" ]]; then info "existing csr at $csr_file does not have the same domains as the config - re-create-csr" _RECREATE_CSR=1 + else + test_output "Existing csr at $csr_file contains same domains as the config" fi fi # end of ... check if domain csr exists - if not then create it diff --git a/test/u4-create-csr-and-ifs.bats b/test/u4-create-csr-and-ifs.bats new file mode 100644 index 0000000..c30277c --- /dev/null +++ b/test/u4-create-csr-and-ifs.bats @@ -0,0 +1,54 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + . /getssl/getssl --source + find_dns_utils + _RUNNING_TEST=1 + _USE_DEBUG=0 +} + + +@test "Check create_csr works for multiple domains" { + # Create a key + csr_key=$(mktemp -t getssl.XXXXXX.key) || error_exit "mktemp failed" + csr_file=$(mktemp -t getssl.XXXXXX.csr) || error_exit "mktemp failed" + SANS="a.getssl.test,b.getssl.test" + SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" + create_key "$ACCOUNT_KEY_TYPE" "$csr_key" "$ACCOUNT_KEY_LENGTH" + + # Create an initial csr + run create_csr $csr_file $csr_key + assert_success + + # Check that calling create_csr with the same SANSLIST doesn't re-create the csr + run create_csr $csr_file $csr_key + assert_success + refute_line --partial "does not have the same domains" + + # Check that calling create_csr with a different SANSLIST does re-create the csr + SANS="a.getssl.test,b.getssl.test,c.getssl.test" + SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" + run create_csr $csr_file $csr_key + assert_success + assert_line --partial "does not contain" + + # Check that calling create_csr with the same SANSLIST, but in a different order does not re-create the csr + SANS="c.getssl.test,a.getssl.test,b.getssl.test" + SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" + run create_csr $csr_file $csr_key + assert_success + refute_line --partial "does not contain" + + # Check that removing a domain from the SANSLIST causes the csr to be re-created + SANS="c.getssl.test,a.getssl.test" + SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" + run create_csr $csr_file $csr_key + assert_success + assert_line --partial "does not have the same domains as the config" +} From 92673dae6ea8aeb31f95eb6fff33fd263f3ddcd1 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 10 Dec 2020 18:39:29 +0000 Subject: [PATCH 259/337] Add dns scripts for Dynu.com --- dns_scripts/dns_add_dynu | 68 ++++++++++++++++++++++++++++++++++++++++ dns_scripts/dns_del_dynu | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 dns_scripts/dns_add_dynu create mode 100644 dns_scripts/dns_del_dynu diff --git a/dns_scripts/dns_add_dynu b/dns_scripts/dns_add_dynu new file mode 100644 index 0000000..7a08ce8 --- /dev/null +++ b/dns_scripts/dns_add_dynu @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# Need to add your API key below or set as env variable +apikey=${DYNU_API_KEY:-''} + +# This script adds a token to dynu.com DNS for the ACME challenge +# usage dns_add_dynu "domain name" "token" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in dynu.com etc) + +fulldomain="${1}" +token="${2}" + +API='https://api.dynu.com/v2/dns' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi + +curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X GET "$API") + +# Match domain id +re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +if [[ "$resp" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your Dynu account' + exit 3 +fi + +# Check for existing _acme-challenge TXT record +resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") +re="\"id\":([^,]*)" +if [[ "$resp" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + # Add new TXT challenge record + resp=$(curl --silent \ + "${curl_params[@]}" \ + -X POST "${API}/${domain_id}/record" \ + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") +else + resp=$(curl --silent \ + "${curl_params[@]}" \ + -X POST "${API}/${domain_id}/record/${record_id}" \ + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") +fi + +# If adding record failed (exception:) then print error message +if [[ "${resp// }" == *'"exception"'* ]]; then + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 +fi diff --git a/dns_scripts/dns_del_dynu b/dns_scripts/dns_del_dynu new file mode 100644 index 0000000..310bb61 --- /dev/null +++ b/dns_scripts/dns_del_dynu @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Need to add your API key below or set as env variable +apikey=${DYNU_API_KEY:-''} + +# This script deletes the _acme-challenge TXT record from the dynu.com DNS entry for the domain +# usage dns_del_dynu "domain name" +# return codes are; +# 0 - success +# 1 - error in input +# 2 - error within internal processing +# 3 - error in result ( domain not found in dynu.com etc) + +fulldomain="${1}" + +API='https://api.dynu.com/v2/dns' + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$apikey" ]]; then + echo "DNS script requires an apikey to be set" + exit 1 +fi + +curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) + +# Get domain id +resp=$(curl --silent "${curl_params[@]}" -X GET "$API") + +# Match domain id +re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +if [[ "$resp" =~ $re ]]; then + domain_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$domain_id" ]]; then + echo 'Domain name not found on your Dynu account' + exit 3 +fi + +# Find existing _acme-challenge TXT record +resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") +re="\"id\":([^,]*)" +if [[ "$resp" =~ $re ]]; then + record_id="${BASH_REMATCH[1]}" +fi + +if [[ -z "$record_id" ]]; then + echo "No _acme-challenge TXT record found for $fulldomain" + exit 3 +fi + +resp=$(curl --silent \ + "${curl_params[@]}" \ + -X DELETE "${API}/${domain_id}/record/${record_id}") + +# If adding record failed (exception:) then print error message +if [[ "${resp// }" == *'"exception"'* ]]; then + echo "Error: DNS challenge not added: unknown error - ${resp}" + exit 3 +fi From 2e558854c3a9ef18c8df5460880641a92997011a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 17 Dec 2020 21:58:47 +0000 Subject: [PATCH 260/337] Some fixes to get_auth_dns --- getssl | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/getssl b/getssl index 0caa2a4..84ef2cc 100755 --- a/getssl +++ b/getssl @@ -520,12 +520,24 @@ check_challenge_completion_dns() { # perform validation via DNS challenge check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ | grep -i "^_acme-challenge.${lower_d}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + debug "check_result=$check_result" + if [[ -z "$check_result" ]]; then + debug "$DNS_CHECK_FUNC" ANY "_acme-challenge.${lower_d}" "@${ns}" + check_result=$($DNS_CHECK_FUNC ANY "_acme-challenge.${lower_d}" "@${ns}" \ + | grep -i "^_acme-challenge.${lower_d}" \ + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') + debug "check_result=$check_result" + fi elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') else check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ | grep 'text ='|awk -F'"' '{ print $2}') + if [[ -z "$check_result" ]]; then + check_result=$(nslookup -type=any "_acme-challenge.${lower_d}" "${ns}" \ + | grep 'text ='|awk -F'"' '{ print $2}') + fi fi debug "expecting $auth_key" debug "${ns} gave ... $check_result" @@ -538,6 +550,7 @@ check_challenge_completion_dns() { # perform validation via DNS challenge if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" + test_output "Retrying adding dns via command: $DNS_ADD_COMMAND" eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then error_exit "DNS_ADD_COMMAND failed for domain $d" @@ -1165,10 +1178,12 @@ for d in "${alldomains[@]}"; do # find a primary / authoritative DNS server for the domain if [[ -z "$AUTH_DNS_SERVER" ]]; then get_auth_dns "$d" + elif [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then + primary_ns="$AUTH_DNS_SERVER $PUBLIC_DNS_SERVER" else primary_ns="$AUTH_DNS_SERVER" fi - debug primary_ns "$primary_ns" + debug set primary_ns = "$primary_ns" check_challenge_completion_dns "${token}" "${uri}" "${keyauthorization}" "${d}" "${primary_ns}" "${auth_key}" else # set up the correct http token for verification @@ -1270,11 +1285,11 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -z "$all_auth_dns_servers" ]]; then error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" fi + primary_ns="$all_auth_dns_servers" if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then - primary_ns="$all_auth_dns_servers $PUBLIC_DNS_SERVER" - else - primary_ns="$all_auth_dns_servers" + primary_ns="$primary_ns $PUBLIC_DNS_SERVER" fi + return fi @@ -1348,8 +1363,8 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n primary_ns="$primary_ns $PUBLIC_DNS_SERVER" fi - debug set primary_ns = "$primary_ns" test_output set primary_ns ="$primary_ns" + return fi fi @@ -1428,9 +1443,6 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') fi - if [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then - primary_ns="$primary_ns $PUBLIC_DNS_SERVER" - fi return fi fi From 5732867d2dfda26576aa1eb17da7d4355d7f2b13 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 17 Dec 2020 22:00:04 +0000 Subject: [PATCH 261/337] Move DNS used for staging tests to Dynu --- dns_scripts/dns_add_dynu | 14 +++++--- dns_scripts/dns_del_dynu | 22 ++++++++---- test/15-test-revoke-no-suffix.bats | 6 +++- test/18-staging-retry-dns-add.bats | 16 +++++++-- test/Dockerfile-centos7-staging | 1 + test/Dockerfile-ubuntu-staging | 1 + test/dns_add_fail | 6 ++++ test/dns_fail_add_duckdns | 19 ---------- test/run-test.cmd | 2 +- test/run-test.sh | 3 +- .../getssl-staging-dns01-fail-dns-add.cfg | 33 ----------------- .../getssl-staging-dns01-no-suffix.cfg | 36 ------------------- test/test-config/getssl-staging-dns01.cfg | 11 ++++-- 13 files changed, 62 insertions(+), 108 deletions(-) create mode 100644 test/dns_add_fail delete mode 100755 test/dns_fail_add_duckdns delete mode 100644 test/test-config/getssl-staging-dns01-fail-dns-add.cfg delete mode 100644 test/test-config/getssl-staging-dns01-no-suffix.cfg diff --git a/dns_scripts/dns_add_dynu b/dns_scripts/dns_add_dynu index 7a08ce8..e20470d 100644 --- a/dns_scripts/dns_add_dynu +++ b/dns_scripts/dns_add_dynu @@ -28,10 +28,11 @@ fi curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) # Get domain id -resp=$(curl --silent "${curl_params[@]}" -X GET "$API") +# curl -X GET https://api.dynu.com/v2/dns/getroot/ubuntu-getssl.freeddns.org +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/getroot/${fulldomain}") # Match domain id -re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +re="\"id\":([^,]*),\"domainName\":\"${fulldomain}\"" if [[ "$resp" =~ $re ]]; then domain_id="${BASH_REMATCH[1]}" fi @@ -42,6 +43,7 @@ if [[ -z "$domain_id" ]]; then fi # Check for existing _acme-challenge TXT record +# curl -X GET "https://api.dynu.com/v2/dns/record/_acme-challenge.ubuntu-getssl.freeddns.org?recordType=TXT" resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") re="\"id\":([^,]*)" if [[ "$resp" =~ $re ]]; then @@ -53,16 +55,18 @@ if [[ -z "$record_id" ]]; then resp=$(curl --silent \ "${curl_params[@]}" \ -X POST "${API}/${domain_id}/record" \ - --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"$token\"}") else + # Update existing record + # curl -X POST https://api.dynu.com/v2/dns/9329328/record/7082063 -d "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"Test2\"}" resp=$(curl --silent \ "${curl_params[@]}" \ -X POST "${API}/${domain_id}/record/${record_id}" \ - --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"textData\":\"$token\"}") + --data "{\"nodeName\":\"_acme-challenge\",\"recordType\":\"TXT\",\"state\":\"true\",\"textData\":\"$token\"}") fi # If adding record failed (exception:) then print error message -if [[ "${resp// }" == *'"exception"'* ]]; then +if [[ "$resp" != *"\"statusCode\":200"* ]]; then echo "Error: DNS challenge not added: unknown error - ${resp}" exit 3 fi diff --git a/dns_scripts/dns_del_dynu b/dns_scripts/dns_del_dynu index 310bb61..1d8d588 100644 --- a/dns_scripts/dns_del_dynu +++ b/dns_scripts/dns_del_dynu @@ -10,6 +10,12 @@ apikey=${DYNU_API_KEY:-''} # 2 - error within internal processing # 3 - error in result ( domain not found in dynu.com etc) +# After deleting the TXT record from Dynu.com it takes over 30 minutes to add a new TXT record! +# This doesn't happen when updating the TXT record, just for delete then add +# As this is used for testing, changed the delete to a no-op. + +exit 0 + fulldomain="${1}" API='https://api.dynu.com/v2/dns' @@ -20,17 +26,18 @@ if [[ -z "$fulldomain" ]]; then exit 1 fi if [[ -z "$apikey" ]]; then - echo "DNS script requires an apikey to be set" + echo "DNS script requires apikey environment variable to be set" exit 1 fi curl_params=( -H "accept: application/json" -H "API-Key: $apikey" -H 'Content-Type: application/json' ) # Get domain id -resp=$(curl --silent "${curl_params[@]}" -X GET "$API") +# curl -X GET https://api.dynu.com/v2/dns/getroot/ubuntu-getssl.freeddns.org +resp=$(curl --silent "${curl_params[@]}" -X GET "$API/getroot/${fulldomain}") # Match domain id -re="\"id\":([^,]*),\"name\":\"getssl-testing.freeddns.org\"" +re="\"id\":([^,]*),\"domainName\":\"${fulldomain}\"" if [[ "$resp" =~ $re ]]; then domain_id="${BASH_REMATCH[1]}" fi @@ -40,7 +47,8 @@ if [[ -z "$domain_id" ]]; then exit 3 fi -# Find existing _acme-challenge TXT record +# Check for existing _acme-challenge TXT record +# curl -X GET "https://api.dynu.com/v2/dns/record/_acme-challenge.ubuntu-getssl.freeddns.org?recordType=TXT" resp=$(curl --silent "${curl_params[@]}" -X GET "${API}/record/_acme-challenge.${fulldomain}?recordType=TXT") re="\"id\":([^,]*)" if [[ "$resp" =~ $re ]]; then @@ -48,8 +56,8 @@ if [[ "$resp" =~ $re ]]; then fi if [[ -z "$record_id" ]]; then - echo "No _acme-challenge TXT record found for $fulldomain" - exit 3 + echo "No _acme-challenge.${fulldomain} TXT record found" + exit 0 fi resp=$(curl --silent \ @@ -57,7 +65,7 @@ resp=$(curl --silent \ -X DELETE "${API}/${domain_id}/record/${record_id}") # If adding record failed (exception:) then print error message -if [[ "${resp// }" == *'"exception"'* ]]; then +if [[ "$resp" != *"\"statusCode\":200"* ]]; then echo "Error: DNS challenge not added: unknown error - ${resp}" exit 3 fi diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 5e8d797..96e174b 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -15,10 +15,12 @@ setup() { @test "Create certificate to check revoke (no suffix)" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01-no-suffix.cfg" + CONFIG_FILE="getssl-staging-dns01.cfg" else CONFIG_FILE="getssl-http01-no-suffix.cfg" fi + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" setup_environment init_getssl @@ -34,6 +36,8 @@ setup() { else CONFIG_FILE="getssl-http01.cfg" fi + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt KEY=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.key diff --git a/test/18-staging-retry-dns-add.bats b/test/18-staging-retry-dns-add.bats index 8b636d6..bca7c05 100644 --- a/test/18-staging-retry-dns-add.bats +++ b/test/18-staging-retry-dns-add.bats @@ -6,14 +6,26 @@ load '/getssl/test/test_helper.bash' -@test "Check retry add dns command if dns isn't updated (DuckDNS)" { +@test "Check retry add dns command if dns isn't updated" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-staging-dns01-fail-dns-add.cfg" + + CONFIG_FILE="getssl-staging-dns01.cfg" setup_environment init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +DNS_ADD_COMMAND="/getssl/test/dns_add_fail" + +# Speed up the test by reducing the number or retries and the wait between retries. +DNS_WAIT=2 +DNS_WAIT_COUNT=11 +DNS_EXTRA_WAIT=0 +CHECK_ALL_AUTH_DNS="false" +CHECK_PUBLIC_DNS_SERVER="false" +EOF create_certificate -d assert_failure assert_line --partial "Retrying adding dns via command" diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-staging index 0b2ff08..ad49896 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-staging @@ -9,6 +9,7 @@ RUN yum -y install git curl bind-utils ldns wget which nginx ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-staging index 1ee3f83..3032c85 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-staging @@ -8,6 +8,7 @@ ENV DEBIAN_FRONTEND noninteractive # Ensure tests in this image use the staging server ENV staging "true" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 # Update and install required software RUN apt-get update --fix-missing diff --git a/test/dns_add_fail b/test/dns_add_fail new file mode 100644 index 0000000..44ab42b --- /dev/null +++ b/test/dns_add_fail @@ -0,0 +1,6 @@ +#!/bin/bash + +# Special test script which will always fail to update dns + +echo "This is a test script to check retry works if DNS isn't updated" +exit 0 diff --git a/test/dns_fail_add_duckdns b/test/dns_fail_add_duckdns deleted file mode 100755 index 03df89f..0000000 --- a/test/dns_fail_add_duckdns +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# Special test script which will always fail to update dns - -token=${DUCKDNS_TOKEN:-} - -if [ -z "$token" ]; then - echo "DUCKDNS_TOKEN not set" - exit 1 -fi - -domain="$1" - -response=$(curl --retry 5 --silent "https://www.duckdns.org/update?domains=${domain}&token=${token}&txt=FAIL") -if [ "$response" != "OK" ]; then - echo "Failed to update TXT record for ${domain} at duckdns.org (is the TOKEN valid?)" - echo "Response: $response" - exit 1 -fi diff --git a/test/run-test.cmd b/test/run-test.cmd index 23235c9..0462617 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -23,7 +23,7 @@ set COMMAND=bats /getssl/test GOTO CheckAlias :staging -set ALIAS=%OS:-staging=%-getssl.duckdns.org +set ALIAS=%OS:-staging=%-getssl.freeddns.org set STAGING=--env STAGING=true :Run diff --git a/test/run-test.sh b/test/run-test.sh index 8a2a9f2..760c600 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -15,7 +15,8 @@ else fi if [[ "$OS" == *"staging"* ]]; then - ALIAS="${OS%-staging}-getssl.duckdns.org" + #ALIAS="${OS%-staging}-getssl.duckdns.org" + ALIAS="${OS%-staging}-getssl.freeddns.org" STAGING="--env STAGING=true" else ALIAS="$OS.getssl.test" diff --git a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg b/test/test-config/getssl-staging-dns01-fail-dns-add.cfg deleted file mode 100644 index 2985d32..0000000 --- a/test/test-config/getssl-staging-dns01-fail-dns-add.cfg +++ /dev/null @@ -1,33 +0,0 @@ -# Special config to test that the retry dns_add_command logic works -# -CA="https://acme-staging-v02.api.letsencrypt.org/directory" - -# Generic staging config -VALIDATE_VIA_DNS=true -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org -CHECK_ALL_AUTH_DNS=true - -# Test that the retry works (dns_add_command will always fail) -DNS_WAIT_RETRY_ADD="true" -DNS_ADD_COMMAND="/getssl/test/dns_fail_add_duckdns" - -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 -DNS_EXTRA_WAIT=0 - -# Standard config -ACCOUNT_KEY_TYPE="rsa" -PRIVATE_KEY_ALG="rsa" -SANS="" -ACL=('/var/www/html/.well-known/acme-challenge') -USE_SINGLE_ACL="false" -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 -RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" -SERVER_TYPE="https" -CHECK_REMOTE="true" diff --git a/test/test-config/getssl-staging-dns01-no-suffix.cfg b/test/test-config/getssl-staging-dns01-no-suffix.cfg deleted file mode 100644 index b4f5202..0000000 --- a/test/test-config/getssl-staging-dns01-no-suffix.cfg +++ /dev/null @@ -1,36 +0,0 @@ -# Test that the script works with external dns provider and staging server -# -CA="https://acme-staging-v02.api.letsencrypt.org" - -VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=ns2.duckdns.org -CHECK_ALL_AUTH_DNS=true -DNS_EXTRA_WAIT=120 - -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 -DNS_WAIT_RETRY_ADD="true" - -ACCOUNT_KEY_TYPE="rsa" -PRIVATE_KEY_ALG="rsa" - -# Additional domains - this could be multiple domains / subdomains in a comma separated list -SANS="" - -# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" - -# Define the server type and confirm correct certificate is installed (using a custom port) -SERVER_TYPE="https" -CHECK_REMOTE="true" diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 19413cd..64a7388 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -3,15 +3,17 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -PUBLIC_DNS_SERVER=1.1.1.1 +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_dynu" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_dynu" +PUBLIC_DNS_SERVER="8.8.8.8 resolver1.infoserve.de" +AUTH_DNS_SERVER=ns1.dynu.com CHECK_ALL_AUTH_DNS="true" CHECK_PUBLIC_DNS_SERVER="true" DNS_EXTRA_WAIT=120 # Speed up the test by reducing the number or retries and retrying DNS_ADD after 10 failures DNS_WAIT_COUNT=20 +DNS_WAIT=30 DNS_WAIT_RETRY_ADD="true" ACCOUNT_KEY_TYPE="rsa" @@ -38,3 +40,6 @@ CHECK_REMOTE="true" if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then . $DOMAIN_DIR/getssl_test_specific.cfg fi + +#_RUNNING_TEST=1 +#_USE_DEBUG=1 From 5969d5b3fa4d4cc3f7bfbae44d0fc7ed932e6e7b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 18 Dec 2020 21:36:44 +0000 Subject: [PATCH 262/337] Fix mktemp template format for alpine --- test/u4-create-csr-and-ifs.bats | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/u4-create-csr-and-ifs.bats b/test/u4-create-csr-and-ifs.bats index c30277c..344a536 100644 --- a/test/u4-create-csr-and-ifs.bats +++ b/test/u4-create-csr-and-ifs.bats @@ -16,8 +16,8 @@ setup() { @test "Check create_csr works for multiple domains" { # Create a key - csr_key=$(mktemp -t getssl.XXXXXX.key) || error_exit "mktemp failed" - csr_file=$(mktemp -t getssl.XXXXXX.csr) || error_exit "mktemp failed" + csr_key=$(mktemp -t getssl.key.XXXXXX) || error_exit "mktemp failed" + csr_file=$(mktemp -t getssl.csr.XXXXXX) || error_exit "mktemp failed" SANS="a.getssl.test,b.getssl.test" SANLIST="subjectAltName=DNS:${SANS//[, ]/,DNS:}" create_key "$ACCOUNT_KEY_TYPE" "$csr_key" "$ACCOUNT_KEY_LENGTH" From 26dadf3c7fb3d58b06df0564bcf8f02ac260a041 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 12:48:06 +0000 Subject: [PATCH 263/337] Split tests into pebble/staging --- ...run-all-tests.yml => run-tests-pebble.yml} | 27 +------------------ .../workflows/run-tests-staging-duckdns.yml | 25 +++++++++++++++++ .github/workflows/run-tests-staging-dynu.yml | 25 +++++++++++++++++ 3 files changed, 51 insertions(+), 26 deletions(-) rename .github/workflows/{run-all-tests.yml => run-tests-pebble.yml} (67%) create mode 100644 .github/workflows/run-tests-staging-duckdns.yml create mode 100644 .github/workflows/run-tests-staging-dynu.yml diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-tests-pebble.yml similarity index 67% rename from .github/workflows/run-all-tests.yml rename to .github/workflows/run-tests-pebble.yml index 7bb05dc..0e26962 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-tests-pebble.yml @@ -1,4 +1,4 @@ -name: Run all tests +name: Run all tests on pebble on: push: branches: @@ -15,14 +15,6 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Alpine run: test/run-test.sh alpine - test-centos6: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on CentOS6 - run: test/run-test.sh centos6 test-centos7: runs-on: ubuntu-latest steps: @@ -31,14 +23,6 @@ jobs: run: docker-compose up -d --build - name: Run test suite on CentOS7 run: test/run-test.sh centos7 - test-centos7-staging: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on CentOS7 against Staging using DuckDNS - run: test/run-test.sh centos7-staging test-centos8: runs-on: ubuntu-latest steps: @@ -79,12 +63,3 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Ubuntu18 run: test/run-test.sh ubuntu18 - test-ubuntu-staging: - needs: test-centos7-staging - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Build the docker-compose stack - run: docker-compose up -d --build - - name: Run test suite on Ubuntu against Staging using DuckDNS - run: test/run-test.sh ubuntu-staging diff --git a/.github/workflows/run-tests-staging-duckdns.yml b/.github/workflows/run-tests-staging-duckdns.yml new file mode 100644 index 0000000..54da950 --- /dev/null +++ b/.github/workflows/run-tests-staging-duckdns.yml @@ -0,0 +1,25 @@ +name: Run all tests using DuckDNS +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test-centos7-duckdns: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 against Staging using DuckDNS + run: test/run-test.sh centos7-duckdns + test-ubuntu-duckdns: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu against Staging using DuckDNS + run: test/run-test.sh ubuntu-duckdns diff --git a/.github/workflows/run-tests-staging-dynu.yml b/.github/workflows/run-tests-staging-dynu.yml new file mode 100644 index 0000000..c523278 --- /dev/null +++ b/.github/workflows/run-tests-staging-dynu.yml @@ -0,0 +1,25 @@ +name: Run all tests using Dynu +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + test-centos7-dynu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 against Staging using Dynu + run: test/run-test.sh centos7-dynu + test-ubuntu-dynu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Ubuntu against Staging using Dynu + run: test/run-test.sh ubuntu-dynu From 5d6988f0eca3175f3ef3cdbd72fb41e1b15b8b4a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 12:48:57 +0000 Subject: [PATCH 264/337] Test using both Dynu.com and Duckdns.org --- ...os7-staging => Dockerfile-centos7-duckdns} | 2 +- test/Dockerfile-centos7-dynu | 29 ++++++++++++++++++ ...untu-staging => Dockerfile-ubuntu-duckdns} | 2 +- test/Dockerfile-ubuntu-dynu | 30 +++++++++++++++++++ test/run-test.cmd | 18 +++++++---- test/run-test.sh | 14 +++++---- test/test-config/getssl-staging-dns01.cfg | 10 +++++-- test/u1-test-get_auth_dns-dig.bats | 14 ++++----- test/u2-test-get_auth_dns-drill.bats | 14 ++++----- 9 files changed, 104 insertions(+), 29 deletions(-) rename test/{Dockerfile-centos7-staging => Dockerfile-centos7-duckdns} (94%) create mode 100644 test/Dockerfile-centos7-dynu rename test/{Dockerfile-ubuntu-staging => Dockerfile-ubuntu-duckdns} (94%) create mode 100644 test/Dockerfile-ubuntu-dynu diff --git a/test/Dockerfile-centos7-staging b/test/Dockerfile-centos7-duckdns similarity index 94% rename from test/Dockerfile-centos7-staging rename to test/Dockerfile-centos7-duckdns index ad49896..719c9de 100644 --- a/test/Dockerfile-centos7-staging +++ b/test/Dockerfile-centos7-duckdns @@ -8,8 +8,8 @@ RUN yum -y install epel-release RUN yum -y install git curl bind-utils ldns wget which nginx ENV staging "true" +ENV dynamic_dns "dynu" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb -ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 WORKDIR /root RUN mkdir /etc/nginx/pki diff --git a/test/Dockerfile-centos7-dynu b/test/Dockerfile-centos7-dynu new file mode 100644 index 0000000..01d41a7 --- /dev/null +++ b/test/Dockerfile-centos7-dynu @@ -0,0 +1,29 @@ +FROM centos:centos7 + +# Note this image uses gawk + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl bind-utils ldns wget which nginx + +ENV staging "true" +ENV dynamic_dns "duckdns" +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf +COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local + +EXPOSE 80 443 + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/Dockerfile-ubuntu-staging b/test/Dockerfile-ubuntu-duckdns similarity index 94% rename from test/Dockerfile-ubuntu-staging rename to test/Dockerfile-ubuntu-duckdns index 3032c85..f4cf9e3 100644 --- a/test/Dockerfile-ubuntu-staging +++ b/test/Dockerfile-ubuntu-duckdns @@ -7,8 +7,8 @@ ENV DEBIAN_FRONTEND noninteractive # Ensure tests in this image use the staging server ENV staging "true" +ENV dynamic_dns "duckdns" ENV DUCKDNS_TOKEN 1d616aa9-b8e4-4bb4-b312-3289de82badb -ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 # Update and install required software RUN apt-get update --fix-missing diff --git a/test/Dockerfile-ubuntu-dynu b/test/Dockerfile-ubuntu-dynu new file mode 100644 index 0000000..2ea8c31 --- /dev/null +++ b/test/Dockerfile-ubuntu-dynu @@ -0,0 +1,30 @@ +FROM ubuntu:latest + +# Note this image uses mawk1.3 + +# Set noninteractive otherwise tzdata hangs +ENV DEBIAN_FRONTEND noninteractive + +# Ensure tests in this image use the staging server +ENV staging "true" +ENV dynamic_dns "dynu" +ENV DYNU_API_KEY 65cXefd35XbYf36546eg5dYcZT6X52Y2 + +# Update and install required software +RUN apt-get update --fix-missing +RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light +RUN apt-get install -y vim dos2unix # for debugging + +WORKDIR /root + +# Prevent "Can't load /root/.rnd into RNG" error from openssl +RUN touch /root/.rnd + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local + +# Run eternal loop - for testing +CMD tail -f /dev/null diff --git a/test/run-test.cmd b/test/run-test.cmd index 0462617..af5fb30 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -8,7 +8,8 @@ set COMMAND=%2 %3 :CheckAlias REM check if OS *contains* staging -IF NOT x%OS:staging=%==x%OS% GOTO staging +IF NOT x%OS:duck=%==x%OS% GOTO duckdns +IF NOT x%OS:dynu=%==x%OS% GOTO dynu set ALIAS=%OS%.getssl.test set STAGING= GOTO Run @@ -22,9 +23,16 @@ REM set COMMAND=/getssl/test/run-bats.sh set COMMAND=bats /getssl/test GOTO CheckAlias -:staging -set ALIAS=%OS:-staging=%-getssl.freeddns.org -set STAGING=--env STAGING=true +:duckdns +set ALIAS=%OS:-duckdns=%-getssl.duckdns.org +set STAGING=--env STAGING=true --env dynamic_dns=duckdns +set GETSSL_OS=%OS:-duckdns=% +GOTO Run + +:dynu +set ALIAS=%OS:-dynu=%-getssl.freeddns.org +set STAGING=--env STAGING=true --env dynamic_dns=dynu +set GETSSL_OS=%OS:-dynu=% :Run for %%I in (.) do set CurrDirName=%%~nxI @@ -33,7 +41,7 @@ docker build --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . @echo on docker run -it ^ --env GETSSL_HOST=%ALIAS% %STAGING% ^ - --env GETSSL_OS=%OS:-staging=% ^ + --env GETSSL_OS=%GETSSL_OS% ^ -v %cd%:/getssl ^ --rm ^ --network %CurrDirName%_acmenet ^ diff --git a/test/run-test.sh b/test/run-test.sh index 760c600..0503c2d 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -14,10 +14,14 @@ else COMMAND="bats /getssl/test" fi -if [[ "$OS" == *"staging"* ]]; then - #ALIAS="${OS%-staging}-getssl.duckdns.org" - ALIAS="${OS%-staging}-getssl.freeddns.org" - STAGING="--env STAGING=true" +if [[ "$OS" == *"duckdns"* ]]; then + ALIAS="${OS%-duckdns}-getssl.duckdns.org" + STAGING="--env STAGING=true --env dynamic_dns=duckdns" + GETSSL_OS="${OS%-duckdns}" +elif [[ "$OS" == *"dynu"* ]]; then + ALIAS="${OS%-dynu}-getssl.freeddns.org" + STAGING="--env STAGING=true --env dynamic_dns=dynu" + GETSSL_OS="${OS%-dynu}" else ALIAS="$OS.getssl.test" STAGING="" @@ -27,7 +31,7 @@ docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . # shellcheck disable=SC2086 docker run \ --env GETSSL_HOST=$ALIAS $STAGING \ - --env GETSSL_OS=${OS%-staging} \ + --env GETSSL_OS=$GETSSL_OS \ -v "$(pwd)":/getssl \ --rm \ --network ${PWD##*/}_acmenet \ diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg index 64a7388..8859686 100644 --- a/test/test-config/getssl-staging-dns01.cfg +++ b/test/test-config/getssl-staging-dns01.cfg @@ -3,10 +3,14 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_dynu" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_dynu" +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_${dynamic_dns}" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_${dynamic_dns}" PUBLIC_DNS_SERVER="8.8.8.8 resolver1.infoserve.de" -AUTH_DNS_SERVER=ns1.dynu.com +if [[ "${dynamic_dns}" == "dynu" ]]; then + AUTH_DNS_SERVER=ns1.dynu.com +else + AUTH_DNS_SERVER=ns1.duckdns.org +fi CHECK_ALL_AUTH_DNS="true" CHECK_PUBLIC_DNS_SERVER="true" DNS_EXTRA_WAIT=120 diff --git a/test/u1-test-get_auth_dns-dig.bats b/test/u1-test-get_auth_dns-dig.bats index d13cc56..6e64e68 100644 --- a/test/u1-test-get_auth_dns-dig.bats +++ b/test/u1-test-get_auth_dns-dig.bats @@ -59,14 +59,14 @@ teardown() { run get_auth_dns ubuntu-getssl.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we had to use dig NS assert_line --partial 'Using dig NS' # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' } @@ -86,7 +86,7 @@ teardown() { run get_auth_dns ubuntu-getssl.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we had to use dig NS assert_line --partial 'Using dig SOA' @@ -95,12 +95,12 @@ teardown() { # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' # Check that we also check the public DNS server if requested CHECK_PUBLIC_DNS_SERVER=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org 1\.0\.0\.1' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org 1\.0\.0\.1' } @@ -165,7 +165,7 @@ teardown() { run get_auth_dns www.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we found a CNAME but didn't use dig NS assert_line --partial 'Using dig CNAME' @@ -174,5 +174,5 @@ teardown() { # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns www.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' } diff --git a/test/u2-test-get_auth_dns-drill.bats b/test/u2-test-get_auth_dns-drill.bats index 1db1011..c5feadf 100644 --- a/test/u2-test-get_auth_dns-drill.bats +++ b/test/u2-test-get_auth_dns-drill.bats @@ -78,14 +78,14 @@ teardown() { run get_auth_dns ubuntu-getssl.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we had to use drill NS assert_line --partial 'Using drill NS' # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' } @@ -110,7 +110,7 @@ teardown() { run get_auth_dns ubuntu-getssl.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we had to use drill NS assert_line --partial 'Using drill SOA' @@ -119,12 +119,12 @@ teardown() { # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' # Check that we also check the public DNS server if requested CHECK_PUBLIC_DNS_SERVER=true run get_auth_dns ubuntu-getssl.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org 1\.0\.0\.1' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org 1\.0\.0\.1' } @@ -202,7 +202,7 @@ teardown() { run get_auth_dns www.duckdns.org # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org' # Assert that we found a CNAME but didn't use drill NS assert_line --partial 'Using drill CNAME' @@ -211,5 +211,5 @@ teardown() { # Check all Authoritive DNS servers are returned if requested CHECK_ALL_AUTH_DNS=true run get_auth_dns www.duckdns.org - assert_output --regexp 'set primary_ns = ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org ns[1-3]+\.duckdns\.org' + assert_output --regexp 'set primary_ns = ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org ns[1-4]+\.duckdns\.org' } From f33f796c932b6031e3db518054d3a37b7b6bb08f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 14:43:54 +0000 Subject: [PATCH 265/337] Ensure GETSSL_OS variable is set --- test/run-test.cmd | 1 + test/run-test.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/test/run-test.cmd b/test/run-test.cmd index af5fb30..47b2a79 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -12,6 +12,7 @@ IF NOT x%OS:duck=%==x%OS% GOTO duckdns IF NOT x%OS:dynu=%==x%OS% GOTO dynu set ALIAS=%OS%.getssl.test set STAGING= +set GETSSL_OS=%OS% GOTO Run :NoOS diff --git a/test/run-test.sh b/test/run-test.sh index 0503c2d..715dae3 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -25,6 +25,7 @@ elif [[ "$OS" == *"dynu"* ]]; then else ALIAS="$OS.getssl.test" STAGING="" + GETSSL_OS=$OS fi docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . From b931b79d622af7728f0727268df08467f2e68cf0 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 14:49:12 +0000 Subject: [PATCH 266/337] Fix execute permission on dns scripts --- dns_scripts/dns_add_dynu | 0 dns_scripts/dns_del_dynu | 0 test/dns_add_fail | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 dns_scripts/dns_add_dynu mode change 100644 => 100755 dns_scripts/dns_del_dynu mode change 100644 => 100755 test/dns_add_fail diff --git a/dns_scripts/dns_add_dynu b/dns_scripts/dns_add_dynu old mode 100644 new mode 100755 diff --git a/dns_scripts/dns_del_dynu b/dns_scripts/dns_del_dynu old mode 100644 new mode 100755 diff --git a/test/dns_add_fail b/test/dns_add_fail old mode 100644 new mode 100755 From 1d1159f946ccc47ef037ca86dfdeb2ca0290aa16 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 18:57:33 +0000 Subject: [PATCH 267/337] Add timing information to tests --- getssl | 2 +- test/run-test.cmd | 2 +- test/run-test.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index 924c835..2257349 100755 --- a/getssl +++ b/getssl @@ -507,7 +507,7 @@ check_challenge_completion_dns() { # perform validation via DNS challenge # check for token at public dns server, waiting for a valid response. for ns in $primary_ns; do - debug "checking dns at $ns" + info "checking dns at $ns" ntries=0 check_dns="fail" while [[ "$check_dns" == "fail" ]]; do diff --git a/test/run-test.cmd b/test/run-test.cmd index 47b2a79..ed6ad6e 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -21,7 +21,7 @@ GOTO CheckCommand :NoCmd REM set COMMAND=/getssl/test/run-bats.sh -set COMMAND=bats /getssl/test +set COMMAND=bats /getssl/test --timing GOTO CheckAlias :duckdns diff --git a/test/run-test.sh b/test/run-test.sh index 715dae3..18443cd 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -11,7 +11,7 @@ if [ $# -gt 1 ]; then shift COMMAND=$* else - COMMAND="bats /getssl/test" + COMMAND="bats /getssl/test --timing" fi if [[ "$OS" == *"duckdns"* ]]; then From aef203287c5c06a51f5b3d791f9731c3eff469b5 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 20:46:06 +0000 Subject: [PATCH 268/337] Check that dig doesn't return an error before setting HAS_DIG_OR_DRILL --- getssl | 2 +- test/30-handle-dig-failure.bats | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 test/30-handle-dig-failure.bats diff --git a/getssl b/getssl index 2257349..b714129 100755 --- a/getssl +++ b/getssl @@ -1094,7 +1094,7 @@ find_dns_utils() { if [[ -n "$(command -v drill 2>/dev/null)" ]]; then debug "HAS DIG_OR_DRILL=drill" HAS_DIG_OR_DRILL="drill" - elif [[ -n "$(command -v dig 2>/dev/null)" ]]; then + elif [[ -n "$(command -v dig 2>/dev/null)" ]] && dig >/dev/null 2>&1; then debug "HAS DIG_OR_DRILL=dig" HAS_DIG_OR_DRILL="dig" fi diff --git a/test/30-handle-dig-failure.bats b/test/30-handle-dig-failure.bats new file mode 100644 index 0000000..7ebd2c3 --- /dev/null +++ b/test/30-handle-dig-failure.bats @@ -0,0 +1,44 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/drill ]; then + mv /usr/bin/drill /usr/bin/drill.getssl.bak + fi + if [ -f /usr/bin/dig ]; then + chmod +x /usr/bin/dig + fi +} + + +teardown() { + if [ -f /usr/bin/drill.getssl.bak ]; then + mv /usr/bin/drill.getssl.bak /usr/bin/drill + fi + if [ -f /usr/bin/dig ]; then + chmod -x /usr/bin/dig + fi +} + + +@test "Test that if dig exists but errors HAS_DIG is not set" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + if [ ! -f /usr/bin/dig ]; then + skip "dig not installed, skipping dig test" + fi + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + create_certificate -d + assert_success + refute_line --partial "HAS DIG_OR_DRILL=dig" + check_output_for_errors "debug" +} From 836b3d8ebcafd14b6020394e949e58c33e1bb993 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 22 Dec 2020 20:55:14 +0000 Subject: [PATCH 269/337] Update revision history and version --- getssl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index b714129..eefe4fa 100755 --- a/getssl +++ b/getssl @@ -242,11 +242,16 @@ # 2020-10-04 Add CHECK_PUBLIC_DNS_SERVER to check the DNS challenge has been updated there # 2020-10-13 Bugfix: strip comments in drill/dig output (mhameed) # 2020-11-18 Wildcard support (#347)(#400)(2.31) +# 2020-12-08 Fix mktemp template on alpine (#612) +# 2020-12-17 Fix delimiter issues with ${alldomains[]} in create_csr (#614)(vietw) +# 2020-12-18 Wrong SANS when domain contains a minus character (atisne) +# 2020-12-22 Fixes to get_auth_dns +# 2020-12-22 Check that dig doesn't return an error (#611)(2.32) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.31" +VERSION="2.32" # defaults ACCOUNT_KEY_LENGTH=4096 From bd11be65ccb942c356232c6b78534885f2e682d7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 23 Dec 2020 10:08:41 +0000 Subject: [PATCH 270/337] Fix typo --- test/30-handle-dig-failure.bats | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/30-handle-dig-failure.bats b/test/30-handle-dig-failure.bats index 7ebd2c3..1102745 100644 --- a/test/30-handle-dig-failure.bats +++ b/test/30-handle-dig-failure.bats @@ -12,7 +12,7 @@ setup() { mv /usr/bin/drill /usr/bin/drill.getssl.bak fi if [ -f /usr/bin/dig ]; then - chmod +x /usr/bin/dig + chmod -x /usr/bin/dig fi } @@ -22,7 +22,7 @@ teardown() { mv /usr/bin/drill.getssl.bak /usr/bin/drill fi if [ -f /usr/bin/dig ]; then - chmod -x /usr/bin/dig + chmod +x /usr/bin/dig fi } From 35a3e0226f9e5fb39428a472f82beed1d2d1b917 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 28 Dec 2020 15:21:43 +0000 Subject: [PATCH 271/337] Remove duplicate teardown() function --- test/u2-test-get_auth_dns-drill.bats | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/u2-test-get_auth_dns-drill.bats b/test/u2-test-get_auth_dns-drill.bats index c5feadf..33b2277 100644 --- a/test/u2-test-get_auth_dns-drill.bats +++ b/test/u2-test-get_auth_dns-drill.bats @@ -31,19 +31,6 @@ teardown() { } -teardown() { - if [ -f /usr/bin/host.getssl.bak ]; then - mv /usr/bin/host.getssl.bak /usr/bin/host - fi - if [ -f /usr/bin/nslookup.getssl.bak ]; then - mv /usr/bin/nslookup.getssl.bak /usr/bin/nslookup - fi - if [ -f /usr/bin/dig.getssl.bak ]; then - mv /usr/bin/dig.getssl.bak /usr/bin/dig - fi -} - - @test "Check get_auth_dns using drill NS" { if [ ! -f /usr/bin/drill ]; then # Can't find drill package for centos8 From dd3d72a806bfd710beadb101b24618dafc99fa4d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 28 Dec 2020 15:25:25 +0000 Subject: [PATCH 272/337] Add tests for issue #617 --- .../u5-test-get_auth_dns-no-root-servers.bats | 89 +++++++++++++++++ .../u6-test-get_auth_dns-no-root-servers.bats | 99 +++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 test/u5-test-get_auth_dns-no-root-servers.bats create mode 100644 test/u6-test-get_auth_dns-no-root-servers.bats diff --git a/test/u5-test-get_auth_dns-no-root-servers.bats b/test/u5-test-get_auth_dns-no-root-servers.bats new file mode 100644 index 0000000..40d79a4 --- /dev/null +++ b/test/u5-test-get_auth_dns-no-root-servers.bats @@ -0,0 +1,89 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + for app in drill host nslookup + do + if [ -f /usr/bin/${app} ]; then + mv /usr/bin/${app} /usr/bin/${app}.getssl.bak + fi + done + + . /getssl/getssl --source + find_dns_utils + _RUNNING_TEST=1 + _USE_DEBUG=0 +} + + +teardown() { + for app in drill host nslookup + do + if [ -f /usr/bin/${app}.getssl.bak ]; then + mv /usr/bin/${app}.getssl.bak /usr/bin/${app} + fi + done +} + + +@test "Check get_auth_dns doesn't include root servers (dig NS)" { + # Test that get_auth_dns() handles scenario where NS query returns root servers + # Issue #617 + # + # Log output was: + # Verifying example.com + # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again + # ... (retried until max attempts then failed) + + # Disable SOA and CNAME check + _TEST_SKIP_CNAME_CALL=1 + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=8.8.8.8 + CHECK_PUBLIC_DNS_SERVER=false + CHECK_ALL_AUTH_DNS=true + + run get_auth_dns example.com + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ' + # Assert that we had to use dig NS + assert_line --partial 'Using dig NS' + + # Check we didn't include any root servers + refute_line --partial 'IN\WNS\W\.root-servers\.net\.' +} + + +@test "Check get_auth_dns doesn't include root servers (dig SOA)" { + # Test that get_auth_dns() handles scenario where NS query returns root servers + # Issue #617 + # + # Log output was: + # Verifying example.com + # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again + # ... (retried until max attempts then failed) + + # Disable CNAME check, ensure SOA check is enabled + _TEST_SKIP_CNAME_CALL=1 + _TEST_SKIP_SOA_CALL=0 + + PUBLIC_DNS_SERVER=8.8.8.8 + CHECK_PUBLIC_DNS_SERVER=false + CHECK_ALL_AUTH_DNS=true + + run get_auth_dns example.com + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ' + # Assert that we had to use dig SOA + assert_line --partial 'Using dig SOA' + + # Check we didn't include any root servers + refute_line --partial 'IN\WNS\W\.root-servers\.net\.' +} diff --git a/test/u6-test-get_auth_dns-no-root-servers.bats b/test/u6-test-get_auth_dns-no-root-servers.bats new file mode 100644 index 0000000..84012c3 --- /dev/null +++ b/test/u6-test-get_auth_dns-no-root-servers.bats @@ -0,0 +1,99 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + for app in dig host nslookup + do + if [ -f /usr/bin/${app} ]; then + mv /usr/bin/${app} /usr/bin/${app}.getssl.bak + fi + done + + . /getssl/getssl --source + find_dns_utils + _RUNNING_TEST=1 + _USE_DEBUG=0 +} + + +teardown() { + for app in dig host nslookup + do + if [ -f /usr/bin/${app}.getssl.bak ]; then + mv /usr/bin/${app}.getssl.bak /usr/bin/${app} + fi + done +} + + +@test "Check get_auth_dns doesn't include root servers (drill NS)" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where NS query returns root servers + # Issue #617 + # + # Log output was: + # Verifying example.com + # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again + # ... (retried until max attempts then failed) + + # Disable CNAME check, ensure SOA check is enabled + _TEST_SKIP_CNAME_CALL=1 + _TEST_SKIP_SOA_CALL=1 + + PUBLIC_DNS_SERVER=8.8.8.8 + CHECK_PUBLIC_DNS_SERVER=false + CHECK_ALL_AUTH_DNS=true + + run get_auth_dns example.com + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ' + # Assert that we had to use drill NS + assert_line --partial 'Using drill NS' + + # Check we didn't include any root servers + refute_line --partial 'IN\WNS\W\.root-servers\.net\.' +} + + +@test "Check get_auth_dns doesn't include root servers (drill SOA)" { + if [ ! -f /usr/bin/drill ]; then + # Can't find drill package for centos8 + skip "Drill not installed on this system" + fi + + # Test that get_auth_dns() handles scenario where NS query returns root servers + # Issue #617 + # + # Log output was: + # Verifying example.com + # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again + # ... (retried until max attempts then failed) + + # Disable SOA and CNAME check + _TEST_SKIP_CNAME_CALL=1 + _TEST_SKIP_SOA_CALL=0 + + PUBLIC_DNS_SERVER=8.8.8.8 + CHECK_PUBLIC_DNS_SERVER=false + CHECK_ALL_AUTH_DNS=true + + run get_auth_dns example.com + + # Assert that we've found the primary_ns server + assert_output --regexp 'set primary_ns = ' + # Assert that we had to use drill SOA + assert_line --partial 'Using drill SOA' + + # Check we didn't include any root servers + refute_line --partial 'IN\WNS\W\.root-servers\.net\.' +} From 630554073e5a54caa5b8a8a538c84361cc0302f7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 28 Dec 2020 16:15:38 +0000 Subject: [PATCH 273/337] Remove spaces around server when calling dig or drill --- getssl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index eefe4fa..afa5fba 100755 --- a/getssl +++ b/getssl @@ -1310,11 +1310,11 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ "$HAS_DIG_OR_DRILL" == "dig" ]]; then debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver test_output "Using $HAS_DIG_OR_DRILL SOA" - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" $gad_s 2>/dev/null | grep "IN\WNS\W") else debug Using "$HAS_DIG_OR_DRILL -T $gad_d $gad_s" to find primary nameserver test_output "Using $HAS_DIG_OR_DRILL SOA" - res=$($HAS_DIG_OR_DRILL -T SOA "$gad_d" "$gad_s" 2>/dev/null | grep "IN\WNS\W") + res=$($HAS_DIG_OR_DRILL -T SOA "$gad_d" $gad_s 2>/dev/null | grep "IN\WNS\W") fi fi @@ -1324,12 +1324,12 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n # Two options here; either dig CNAME will return the CNAME and the NS or just the CNAME debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d $gad_s" - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "$gad_s"| grep "^$gad_d") + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep "^$gad_d") cname=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') if [[ $_TEST_SKIP_CNAME_CALL == 0 ]]; then debug Checking if CNAME result contains NS records - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "$gad_s"| grep -E "IN\W(NS|SOA)\W") + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep -E "IN\W(NS|SOA)\W") else res="" fi @@ -1385,7 +1385,8 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n if [[ -z "$gad_s" ]]; then res=$(host -t NS "$gad_d"| grep "name server") else - res=$(host -t NS "$gad_d" "$gad_s"| grep "name server") + # shellcheck disable=SC2086 + res=$(host -t NS "$gad_d" $gad_s| grep "name server") fi if [[ -n "$res" ]]; then all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ') From f8f3e5b901eb0d622795fad0b88be43946b9b952 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 29 Dec 2020 21:36:44 +0000 Subject: [PATCH 274/337] Updated tests to reproduce bug in #617 --- .../u5-test-get_auth_dns-no-root-servers.bats | 15 ++- .../u6-test-get_auth_dns-no-root-servers.bats | 99 ------------------- 2 files changed, 11 insertions(+), 103 deletions(-) delete mode 100644 test/u6-test-get_auth_dns-no-root-servers.bats diff --git a/test/u5-test-get_auth_dns-no-root-servers.bats b/test/u5-test-get_auth_dns-no-root-servers.bats index 40d79a4..b88fd41 100644 --- a/test/u5-test-get_auth_dns-no-root-servers.bats +++ b/test/u5-test-get_auth_dns-no-root-servers.bats @@ -7,6 +7,12 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { + cp /etc/resolv.conf /etc/resolv.conf.getssl + cat <<- EOF > /etc/resolv.conf +nameserver 8.8.8.8 +options ndots:0 +EOF + for app in drill host nslookup do if [ -f /usr/bin/${app} ]; then @@ -22,6 +28,7 @@ setup() { teardown() { + cat /etc/resolv.conf.getssl > /etc/resolv.conf for app in drill host nslookup do if [ -f /usr/bin/${app}.getssl.bak ]; then @@ -44,7 +51,7 @@ teardown() { _TEST_SKIP_CNAME_CALL=1 _TEST_SKIP_SOA_CALL=1 - PUBLIC_DNS_SERVER=8.8.8.8 + PUBLIC_DNS_SERVER= CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=true @@ -56,7 +63,7 @@ teardown() { assert_line --partial 'Using dig NS' # Check we didn't include any root servers - refute_line --partial 'IN\WNS\W\.root-servers\.net\.' + refute_line --partial 'root-servers.net' } @@ -73,7 +80,7 @@ teardown() { _TEST_SKIP_CNAME_CALL=1 _TEST_SKIP_SOA_CALL=0 - PUBLIC_DNS_SERVER=8.8.8.8 + PUBLIC_DNS_SERVER= CHECK_PUBLIC_DNS_SERVER=false CHECK_ALL_AUTH_DNS=true @@ -85,5 +92,5 @@ teardown() { assert_line --partial 'Using dig SOA' # Check we didn't include any root servers - refute_line --partial 'IN\WNS\W\.root-servers\.net\.' + refute_line --partial 'root-servers.net' } diff --git a/test/u6-test-get_auth_dns-no-root-servers.bats b/test/u6-test-get_auth_dns-no-root-servers.bats deleted file mode 100644 index 84012c3..0000000 --- a/test/u6-test-get_auth_dns-no-root-servers.bats +++ /dev/null @@ -1,99 +0,0 @@ -#! /usr/bin/env bats - -load '/bats-support/load.bash' -load '/bats-assert/load.bash' -load '/getssl/test/test_helper.bash' - - -# This is run for every test -setup() { - for app in dig host nslookup - do - if [ -f /usr/bin/${app} ]; then - mv /usr/bin/${app} /usr/bin/${app}.getssl.bak - fi - done - - . /getssl/getssl --source - find_dns_utils - _RUNNING_TEST=1 - _USE_DEBUG=0 -} - - -teardown() { - for app in dig host nslookup - do - if [ -f /usr/bin/${app}.getssl.bak ]; then - mv /usr/bin/${app}.getssl.bak /usr/bin/${app} - fi - done -} - - -@test "Check get_auth_dns doesn't include root servers (drill NS)" { - if [ ! -f /usr/bin/drill ]; then - # Can't find drill package for centos8 - skip "Drill not installed on this system" - fi - - # Test that get_auth_dns() handles scenario where NS query returns root servers - # Issue #617 - # - # Log output was: - # Verifying example.com - # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again - # ... (retried until max attempts then failed) - - # Disable CNAME check, ensure SOA check is enabled - _TEST_SKIP_CNAME_CALL=1 - _TEST_SKIP_SOA_CALL=1 - - PUBLIC_DNS_SERVER=8.8.8.8 - CHECK_PUBLIC_DNS_SERVER=false - CHECK_ALL_AUTH_DNS=true - - run get_auth_dns example.com - - # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ' - # Assert that we had to use drill NS - assert_line --partial 'Using drill NS' - - # Check we didn't include any root servers - refute_line --partial 'IN\WNS\W\.root-servers\.net\.' -} - - -@test "Check get_auth_dns doesn't include root servers (drill SOA)" { - if [ ! -f /usr/bin/drill ]; then - # Can't find drill package for centos8 - skip "Drill not installed on this system" - fi - - # Test that get_auth_dns() handles scenario where NS query returns root servers - # Issue #617 - # - # Log output was: - # Verifying example.com - # checking DNS at h.root-servers.net for example.com. Attempt 1/100 gave wrong result, waiting 10 secs before checking again - # ... (retried until max attempts then failed) - - # Disable SOA and CNAME check - _TEST_SKIP_CNAME_CALL=1 - _TEST_SKIP_SOA_CALL=0 - - PUBLIC_DNS_SERVER=8.8.8.8 - CHECK_PUBLIC_DNS_SERVER=false - CHECK_ALL_AUTH_DNS=true - - run get_auth_dns example.com - - # Assert that we've found the primary_ns server - assert_output --regexp 'set primary_ns = ' - # Assert that we had to use drill SOA - assert_line --partial 'Using drill SOA' - - # Check we didn't include any root servers - refute_line --partial 'IN\WNS\W\.root-servers\.net\.' -} From a1d4eb00ccb8102faf8c4ae3095c49a060913c4f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 29 Dec 2020 21:39:50 +0000 Subject: [PATCH 275/337] Update version and revision history --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index afa5fba..a20a9bd 100755 --- a/getssl +++ b/getssl @@ -247,11 +247,12 @@ # 2020-12-18 Wrong SANS when domain contains a minus character (atisne) # 2020-12-22 Fixes to get_auth_dns # 2020-12-22 Check that dig doesn't return an error (#611)(2.32) +# 2020-12-29 Fix dig SOA lookup (#617)(2.33) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.32" +VERSION="2.33" # defaults ACCOUNT_KEY_LENGTH=4096 From 140456f602bc0af525611a35fd18df3260a01c6a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Dec 2020 20:55:19 +0000 Subject: [PATCH 276/337] Run tests on older versions of bash --- .github/workflows/run-tests-pebble.yml | 24 ++++++++++++++++++++++++ test/Dockerfile-bash4-0 | 22 ++++++++++++++++++++++ test/Dockerfile-bash4-2 | 22 ++++++++++++++++++++++ test/Dockerfile-bash5-0 | 22 ++++++++++++++++++++++ test/run-test.cmd | 7 +++++++ test/run-test.sh | 26 ++++++++++++++------------ 6 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 test/Dockerfile-bash4-0 create mode 100644 test/Dockerfile-bash4-2 create mode 100644 test/Dockerfile-bash5-0 diff --git a/.github/workflows/run-tests-pebble.yml b/.github/workflows/run-tests-pebble.yml index 0e26962..058bb35 100644 --- a/.github/workflows/run-tests-pebble.yml +++ b/.github/workflows/run-tests-pebble.yml @@ -15,6 +15,30 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Alpine run: test/run-test.sh alpine + test-bash-4-0: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Alpine using Bash 4.0 + run: test/run-test.sh bash4-0 + test-bash-4-2: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Alpine using Bash 4.2 + run: test/run-test.sh bash4-2 + test-bash-5-0: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on Alpine using Bash 5 + run: test/run-test.sh bash5-0 test-centos7: runs-on: ubuntu-latest steps: diff --git a/test/Dockerfile-bash4-0 b/test/Dockerfile-bash4-0 new file mode 100644 index 0000000..e75b74a --- /dev/null +++ b/test/Dockerfile-bash4-0 @@ -0,0 +1,22 @@ +FROM bash:4.0 + +# https://hub.docker.com/_/bash + +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash + +WORKDIR /root + +# Create nginx directories in standard places +RUN mkdir /run/nginx +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local + +# Use supervisord to run nginx in the background +COPY ./test/test-config/alpine-supervisord.conf /etc/supervisord.conf +CMD tail -f /dev/null diff --git a/test/Dockerfile-bash4-2 b/test/Dockerfile-bash4-2 new file mode 100644 index 0000000..720884c --- /dev/null +++ b/test/Dockerfile-bash4-2 @@ -0,0 +1,22 @@ +FROM bash:4.2 + +# https://hub.docker.com/_/bash + +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash + +WORKDIR /root + +# Create nginx directories in standard places +RUN mkdir /run/nginx +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local + +# Use supervisord to run nginx in the background +COPY ./test/test-config/alpine-supervisord.conf /etc/supervisord.conf +CMD tail -f /dev/null diff --git a/test/Dockerfile-bash5-0 b/test/Dockerfile-bash5-0 new file mode 100644 index 0000000..ba0295a --- /dev/null +++ b/test/Dockerfile-bash5-0 @@ -0,0 +1,22 @@ +FROM bash:5.0 + +# https://hub.docker.com/_/bash + +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash + +WORKDIR /root + +# Create nginx directories in standard places +RUN mkdir /run/nginx +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-support /bats-support +RUN git clone https://github.com/bats-core/bats-assert /bats-assert +RUN /bats-core/install.sh /usr/local + +# Use supervisord to run nginx in the background +COPY ./test/test-config/alpine-supervisord.conf /etc/supervisord.conf +CMD tail -f /dev/null diff --git a/test/run-test.cmd b/test/run-test.cmd index ed6ad6e..d33c005 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -10,6 +10,7 @@ set COMMAND=%2 %3 REM check if OS *contains* staging IF NOT x%OS:duck=%==x%OS% GOTO duckdns IF NOT x%OS:dynu=%==x%OS% GOTO dynu +IF NOT x%OS:bash=%==x%OS% GOTO bash set ALIAS=%OS%.getssl.test set STAGING= set GETSSL_OS=%OS% @@ -34,6 +35,12 @@ GOTO Run set ALIAS=%OS:-dynu=%-getssl.freeddns.org set STAGING=--env STAGING=true --env dynamic_dns=dynu set GETSSL_OS=%OS:-dynu=% +GOTO Run + +:bash +set ALIAS=%OS%.getssl.test +set STAGING= +set GETSSL_OS=alpine :Run for %%I in (.) do set CurrDirName=%%~nxI diff --git a/test/run-test.sh b/test/run-test.sh index 18443cd..4aa9d9f 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -1,27 +1,29 @@ #! /usr/bin/env bash if [ $# -eq 0 ]; then - echo "Usage: $(basename "$0") []" - echo "e.g. $(basename "$0") alpine bats /getssl/test" - exit 1 + echo "Usage: $(basename "$0") []" + echo "e.g. $(basename "$0") alpine bats /getssl/test" + exit 1 fi OS=$1 if [ $# -gt 1 ]; then - shift - COMMAND=$* + shift + COMMAND=$* else - COMMAND="bats /getssl/test --timing" + COMMAND="bats /getssl/test --timing" fi if [[ "$OS" == *"duckdns"* ]]; then - ALIAS="${OS%-duckdns}-getssl.duckdns.org" - STAGING="--env STAGING=true --env dynamic_dns=duckdns" - GETSSL_OS="${OS%-duckdns}" + ALIAS="${OS%-duckdns}-getssl.duckdns.org" + STAGING="--env STAGING=true --env dynamic_dns=duckdns" + GETSSL_OS="${OS%-duckdns}" elif [[ "$OS" == *"dynu"* ]]; then - ALIAS="${OS%-dynu}-getssl.freeddns.org" - STAGING="--env STAGING=true --env dynamic_dns=dynu" - GETSSL_OS="${OS%-dynu}" + ALIAS="${OS%-dynu}-getssl.freeddns.org" + STAGING="--env STAGING=true --env dynamic_dns=dynu" + GETSSL_OS="${OS%-dynu}" +elif [[ "$OS" == "bash"* ]]; then + GETSSL_OS="alpine" else ALIAS="$OS.getssl.test" STAGING="" From 4b2d4e4109a821d99c59cff1b6d734dfc5b58f75 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Dec 2020 20:56:01 +0000 Subject: [PATCH 277/337] Use vault.centos6 --- test/Dockerfile-centos6 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index d578f4b..6874cb2 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -5,8 +5,14 @@ FROM centos:centos6 # [wsl2] # kernelCommandLine = vsyscall=emulate +# Centos 6 is EOL and is no longer available from the usual mirrors, so switch +# to https://vault.centos.org +RUN sed -i 's/enabled=1/enabled=0/g' /etc/yum/pluginconf.d/fastestmirror.conf && \ + sed -i 's/^mirrorlist/#mirrorlist/g' /etc/yum.repos.d/*.repo && \ + sed -i 's;^#baseurl=http://mirror;baseurl=https://vault;g' /etc/yum.repos.d/*.repo + # Update and install required software -RUN yum -y update +#RUN yum -y update RUN yum -y install epel-release RUN yum -y install git curl dnsutils ldns wget nginx From a02306294abaf66109c55a06f67a6ae004497c2d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Dec 2020 20:59:58 +0000 Subject: [PATCH 278/337] Add centos6 tests back --- .github/workflows/run-tests-pebble.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/run-tests-pebble.yml b/.github/workflows/run-tests-pebble.yml index 058bb35..29af06d 100644 --- a/.github/workflows/run-tests-pebble.yml +++ b/.github/workflows/run-tests-pebble.yml @@ -39,6 +39,14 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Alpine using Bash 5 run: test/run-test.sh bash5-0 + test-centos76 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS6 + run: test/run-test.sh centos6 test-centos7: runs-on: ubuntu-latest steps: From 12f2921c3bf08e451ddea848d8c6f7ce69ea1897 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 30 Dec 2020 22:01:20 +0000 Subject: [PATCH 279/337] Fix typo --- .github/workflows/run-tests-pebble.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests-pebble.yml b/.github/workflows/run-tests-pebble.yml index 29af06d..5dc53a4 100644 --- a/.github/workflows/run-tests-pebble.yml +++ b/.github/workflows/run-tests-pebble.yml @@ -39,7 +39,7 @@ jobs: run: docker-compose up -d --build - name: Run test suite on Alpine using Bash 5 run: test/run-test.sh bash5-0 - test-centos76 + test-centos6: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 From 1b24a5820f243d2f837e0da93aeb311ad0ea3eb2 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 31 Dec 2020 10:32:16 +0000 Subject: [PATCH 280/337] Set env variables when running bash containers --- test/run-test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/run-test.sh b/test/run-test.sh index 4aa9d9f..ef7e403 100755 --- a/test/run-test.sh +++ b/test/run-test.sh @@ -14,6 +14,10 @@ else COMMAND="bats /getssl/test --timing" fi +ALIAS="$OS.getssl.test" +STAGING="" +GETSSL_OS=$OS + if [[ "$OS" == *"duckdns"* ]]; then ALIAS="${OS%-duckdns}-getssl.duckdns.org" STAGING="--env STAGING=true --env dynamic_dns=duckdns" @@ -24,10 +28,6 @@ elif [[ "$OS" == *"dynu"* ]]; then GETSSL_OS="${OS%-dynu}" elif [[ "$OS" == "bash"* ]]; then GETSSL_OS="alpine" -else - ALIAS="$OS.getssl.test" - STAGING="" - GETSSL_OS=$OS fi docker build --rm -f "test/Dockerfile-$OS" -t "getssl-$OS" . From 0c48343c7a35e299595c6af70c7191a5cc402262 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 1 Jan 2021 18:39:08 +0000 Subject: [PATCH 281/337] Check for posix mode and show a sensible error --- getssl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/getssl b/getssl index a20a9bd..be34b3b 100755 --- a/getssl +++ b/getssl @@ -250,6 +250,10 @@ # 2020-12-29 Fix dig SOA lookup (#617)(2.33) # ---------------------------------------------------------------------------------------- +case :$SHELLOPTS: in + *:posix:*) echo -e "${0##*/}: Running with POSIX mode enabled is not supported" >&2; exit 1;; +esac + PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" VERSION="2.33" From 6ce5114b6bfbc0dfbf529101d6767c4a00630a39 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 5 Jan 2021 18:41:29 +0000 Subject: [PATCH 282/337] Show error message if starting new getssl after upgrade fails --- getssl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index be34b3b..5d74222 100755 --- a/getssl +++ b/getssl @@ -771,7 +771,9 @@ check_getssl_upgrade() { # check if a more recent version of code is available a getssl_versions=("${getssl_versions[@]:1}") done fi - eval "$ORIGCMD" + if ! eval "$ORIGCMD"; then + error_exit "Running upgraded getssl failed" + fi graceful_exit else info "" From a749cb460687fc197cee52fc579e8c8683874133 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 5 Jan 2021 18:42:52 +0000 Subject: [PATCH 283/337] Add tests for --upgrade and posix error --- test/31-test-posix-error.bats | 25 ++++++++++ test/32-test-upgrade.bats | 78 ++++++++++++++++++++++++++++++ test/test-config/getssl-http01.cfg | 7 +++ 3 files changed, 110 insertions(+) create mode 100644 test/31-test-posix-error.bats create mode 100644 test/32-test-upgrade.bats diff --git a/test/31-test-posix-error.bats b/test/31-test-posix-error.bats new file mode 100644 index 0000000..e9950fd --- /dev/null +++ b/test/31-test-posix-error.bats @@ -0,0 +1,25 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Test that running in POSIX mode shows an error" { + # v2.31 uses read to create an array in the get_auth_dns function which causes a parse error in posix mode + # Could be re-written to not use this functionality if it causes for required. + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + run bash --posix "${CODE_DIR}/getssl" + assert_failure + assert_line "getssl: Running with POSIX mode enabled is not supported" + check_output_for_errors +} diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats new file mode 100644 index 0000000..5b66dee --- /dev/null +++ b/test/32-test-upgrade.bats @@ -0,0 +1,78 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + CURRENT_VERSION=$(awk -F '"' '$1 == "VERSION=" {print $2}' ${CODE_DIR}/getssl) + PREVIOUS_VERSION=$(echo ${CURRENT_VERSION} | awk -F. '{ print $1 "." --$2}') + run git clone https://github.com/srvrco/getssl.git "$INSTALL_DIR/upgrade-getssl" +} + + +teardown() { + rm -r "$INSTALL_DIR/upgrade-getssl" +} + + +@test "Test that we are told that a newer version is available" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + cd "$INSTALL_DIR/upgrade-getssl" + git checkout tags/v${PREVIOUS_VERSION} + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config ${GETSSL_CMD_HOST} + assert_success + #assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" + assert_line "A more recent version (v${CURRENT_VERSION}) of getssl is available, please update" + check_output_for_errors +} + + +@test "Test that we can upgrade to the newer version" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + cd "$INSTALL_DIR/upgrade-getssl" + git checkout tags/v${PREVIOUS_VERSION} + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config --upgrade ${GETSSL_CMD_HOST} + assert_success + assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" + check_output_for_errors +} + + +@test "Test that we can upgrade to the newer version when invoking as \"bash ./getssl\"" { + # Note that `bash getssl` will fail if the CWD isn't in the PATH and an upgrade occurs + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + cd "$INSTALL_DIR/upgrade-getssl" + git checkout tags/v${PREVIOUS_VERSION} + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + run bash ./getssl --check-config --upgrade ${GETSSL_CMD_HOST} + assert_success + assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" + check_output_for_errors +} diff --git a/test/test-config/getssl-http01.cfg b/test/test-config/getssl-http01.cfg index f7d75ea..9a713bc 100644 --- a/test/test-config/getssl-http01.cfg +++ b/test/test-config/getssl-http01.cfg @@ -27,3 +27,10 @@ RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /get # Define the server type and confirm correct certificate is installed SERVER_TYPE="https" CHECK_REMOTE="true" + +if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then + . $DOMAIN_DIR/getssl_test_specific.cfg +fi + +#_USE_DEBUG=1 +#_RUNNING_TEST=1 From 01299603dece01f0d9877b024880df2fe6099e7c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 5 Jan 2021 18:46:47 +0000 Subject: [PATCH 284/337] Update revision history --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 5d74222..4ca7f1b 100755 --- a/getssl +++ b/getssl @@ -248,6 +248,7 @@ # 2020-12-22 Fixes to get_auth_dns # 2020-12-22 Check that dig doesn't return an error (#611)(2.32) # 2020-12-29 Fix dig SOA lookup (#617)(2.33) +# 2021-01-05 Show error if running in POSIX mode (#611) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in From 3fb75d493f29d201c910068dfe5ca24b380fe213 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 5 Jan 2021 21:42:40 +0000 Subject: [PATCH 285/337] Change --$var to $var-1 for Alpine --- test/32-test-upgrade.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats index 5b66dee..1c8af6d 100644 --- a/test/32-test-upgrade.bats +++ b/test/32-test-upgrade.bats @@ -9,7 +9,7 @@ load '/getssl/test/test_helper.bash' setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt CURRENT_VERSION=$(awk -F '"' '$1 == "VERSION=" {print $2}' ${CODE_DIR}/getssl) - PREVIOUS_VERSION=$(echo ${CURRENT_VERSION} | awk -F. '{ print $1 "." --$2}') + PREVIOUS_VERSION=$(echo ${CURRENT_VERSION} | awk -F. '{ print $1 "." $2-1}') run git clone https://github.com/srvrco/getssl.git "$INSTALL_DIR/upgrade-getssl" } From 79ac70653d5432eb6db3153ed6754de050a7c291 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 7 Jan 2021 21:54:16 +0000 Subject: [PATCH 286/337] Add chmod to ftp command --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 4ca7f1b..9a42b28 100755 --- a/getssl +++ b/getssl @@ -859,6 +859,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. cd $ftpdirn lcd $fromdir put ./$fromfile + chmod 644 $fromfile _EOF elif [[ "${to:0:5}" == "sftp:" ]] ; then debug "using sftp to copy the file from $from" From 12eb0ae0d1ec2395d9a0cae5dfa914307bceae0a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 7 Jan 2021 21:55:33 +0000 Subject: [PATCH 287/337] First ftp test --- test/33-ftp.bats | 37 +++++++++++++++++++++++++++++++++++++ test/Dockerfile-ubuntu | 10 ++++++++++ test/restart-ftpd | 11 +++++++++++ test/run-test.cmd | 2 +- 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/33-ftp.bats create mode 100644 test/restart-ftpd diff --git a/test/33-ftp.bats b/test/33-ftp.bats new file mode 100644 index 0000000..d6387df --- /dev/null +++ b/test/33-ftp.bats @@ -0,0 +1,37 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Use FTP to create challenge file" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + ${CODE_DIR}/test/restart-ftpd + if [[ ! -d /var/www/html/.well-known/acme-challenge ]]; then + mkdir -p /var/www/html/.well-known/acme-challenge + chgrp -R www-data /var/www/html/.well-known + chmod -R g+w /var/www/html/.well-known + fi + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +ACL="ftp:ftpuser:ftpuser:${GETSSL_CMD_HOST}:/var/www/html/.well-known/acme-challenge" +EOF + + create_certificate -d + assert_success + check_output_for_errors +} diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 3849e55..4e80409 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -9,6 +9,16 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light RUN apt-get install -y vim dos2unix # for debugging +RUN apt-get install -y ftp vsftpd +RUN apt-get install -y openssh-server + +RUN echo "write_enable=YES" >> /etc/vsftpd.conf +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www WORKDIR /root diff --git a/test/restart-ftpd b/test/restart-ftpd new file mode 100644 index 0000000..279c2ff --- /dev/null +++ b/test/restart-ftpd @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +if [ "$GETSSL_OS" = "alpine" ]; then + killall -HUP vsftpd >&3- + sleep 5 +elif [[ "$GETSSL_OS" == "centos"[78] ]]; then + pgrep vsftpd | head -1 | xargs kill -HUP + sleep 5 +else + service vsftpd restart >/dev/null >&3- +fi diff --git a/test/run-test.cmd b/test/run-test.cmd index d33c005..5908de7 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -45,7 +45,7 @@ set GETSSL_OS=alpine :Run for %%I in (.) do set CurrDirName=%%~nxI -docker build --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . +docker build --pull --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . @echo on docker run -it ^ --env GETSSL_HOST=%ALIAS% %STAGING% ^ From f46a36db8969a6fd42ef23e4958032e237613587 Mon Sep 17 00:00:00 2001 From: ionos Date: Fri, 15 Jan 2021 20:48:12 -0500 Subject: [PATCH 288/337] Fix double slash when using root directory with DAVS --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 4ca7f1b..f16da93 100755 --- a/getssl +++ b/getssl @@ -886,12 +886,13 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. davsport=$(echo "$to"| awk -F: '{print $5}') davslocn=$(echo "$to"| awk -F: '{print $6}') davsdirn=$(dirname "$davslocn") + davsdirn=$(echo "${davsdirn}/" | sed 's,//,/,g') davsfile=$(basename "$davslocn") fromdir=$(dirname "$from") fromfile=$(basename "$from") debug "davs user=$davsuser - pass=$davspass - host=$davshost port=$davsport dir=$davsdirn file=$davsfile" debug "from dir=$fromdir file=$fromfile" - curl -u "${davsuser}:${davspass}" -T "${fromdir}/${fromfile}" "https://${davshost}:${davsport}${davsdirn}/${davsfile}" + curl -u "${davsuser}:${davspass}" -T "${fromdir}/${fromfile}" "https://${davshost}:${davsport}${davsdirn}${davsfile}" else if ! mkdir -p "$(dirname "$to")" ; then error_exit "cannot create ACL directory $(basename "$to")" From 4d23e085e146b8712860e60a13a6780c35b81262 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 16 Jan 2021 15:12:22 +0000 Subject: [PATCH 289/337] Add FTP_OPTIONS --- getssl | 36 ++++++++++++++++++++++++++++++------ test/33-ftp.bats | 3 ++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/getssl b/getssl index 9a42b28..f34cac0 100755 --- a/getssl +++ b/getssl @@ -274,6 +274,7 @@ DEACTIVATE_AUTH="false" DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" +FTP_OPTIONS="" GETSSL_IGNORE_CP_PRESERVE="false" HTTP_TOKEN_CHECK_WAIT=0 IGNORE_DIRECTORY_DOMAIN="false" @@ -842,6 +843,9 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. if [[ "$cert" != "challenge token" ]] ; then error_exit "ftp is not a secure method for copying certificates or keys" fi + if [[ -z "$FTP_COMMAND" ]]; then + error_exit "No ftp command found" + fi debug "using ftp to copy the file from $from" ftpuser=$(echo "$to"| awk -F: '{print $2}') ftppass=$(echo "$to"| awk -F: '{print $3}') @@ -853,13 +857,18 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. fromfile=$(basename "$from") debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" debug "from dir=$fromdir file=$fromfile" - ftp -n <<- _EOF - open $ftphost - user $ftpuser $ftppass + if [ -n "$FTP_OPTIONS" ]; then + # Use eval to expand any variables in FTP_OPTIONS + FTP_OPTIONS=$(eval echo "$FTP_OPTIONS") + debug "FTP_OPTIONS=$FTP_OPTIONS" + fi + $FTP_COMMAND <<- _EOF + connect $ftphost + user $ftpuser $ftppass cd $ftpdirn lcd $fromdir put ./$fromfile - chmod 644 $fromfile + $FTP_OPTIONS _EOF elif [[ "${to:0:5}" == "sftp:" ]] ; then debug "using sftp to copy the file from $from" @@ -1119,6 +1128,18 @@ find_dns_utils() { fi } +find_ftp_command() { + FTP_COMMAND="" + if [[ -n "$(command -v ftp 2>/dev/null)" ]]; then + debug "Has ftp" + FTP_COMMAND="ftp -n" + elif [[ -n "$(command -v lftp 2>/dev/null)" ]]; then + debug "Has lftp" + FTP_COMMAND="lftp" + fi +} + + fulfill_challenges() { dn=0 for d in "${alldomains[@]}"; do @@ -1268,8 +1289,8 @@ for d in "${alldomains[@]}"; do ftppass=$(echo "${t_loc}"| awk -F: '{print $3}') ftphost=$(echo "${t_loc}"| awk -F: '{print $4}') ftplocn=$(echo "${t_loc}"| awk -F: '{print $5}') - debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost location=$ftplocn" - ftp -n <<- EOF + debug "$FTP_COMMAND user=$ftpuser - pass=$ftppass - host=$ftphost location=$ftplocn" + $FTP_COMMAND <<- EOF open $ftphost user $ftpuser $ftppass cd $ftplocn @@ -2654,6 +2675,9 @@ set_server_type # check what dns utils are installed find_dns_utils +# Find what ftp client is installed +find_ftp_command + # auto upgrade clients to v2 auto_upgrade_v2 diff --git a/test/33-ftp.bats b/test/33-ftp.bats index d6387df..9c52bb2 100644 --- a/test/33-ftp.bats +++ b/test/33-ftp.bats @@ -29,9 +29,10 @@ setup() { cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg ACL="ftp:ftpuser:ftpuser:${GETSSL_CMD_HOST}:/var/www/html/.well-known/acme-challenge" +FTP_OPTIONS="chmod 644 \\\$fromfile" EOF - create_certificate -d + create_certificate assert_success check_output_for_errors } From d7869561a3ca88d76eb0fc2b48f15d2fed45df3c Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 19 Jan 2021 12:04:47 +0000 Subject: [PATCH 290/337] Change ftp command back to use `open` --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index f34cac0..69bb152 100755 --- a/getssl +++ b/getssl @@ -863,7 +863,7 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. debug "FTP_OPTIONS=$FTP_OPTIONS" fi $FTP_COMMAND <<- _EOF - connect $ftphost + open $ftphost user $ftpuser $ftppass cd $ftpdirn lcd $fromdir From e407426bdba2ed46a6d24f2f97a6d18c928b9049 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 20 Jan 2021 22:00:32 +0000 Subject: [PATCH 291/337] Move FTP_OPTIONS earlier otherwise cd and put will fail --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 69bb152..aae7ad9 100755 --- a/getssl +++ b/getssl @@ -865,10 +865,10 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. $FTP_COMMAND <<- _EOF open $ftphost user $ftpuser $ftppass + $FTP_OPTIONS cd $ftpdirn lcd $fromdir put ./$fromfile - $FTP_OPTIONS _EOF elif [[ "${to:0:5}" == "sftp:" ]] ; then debug "using sftp to copy the file from $from" From 8f32f9f0788034121f76115e794a54ad46478d31 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 20 Jan 2021 22:02:09 +0000 Subject: [PATCH 292/337] Test ftp and passive ftp --- test/33-ftp.bats | 37 +++++++- test/34-ftp-passive.bats | 67 +++++++++++++++ test/Dockerfile-alpine | 14 ++- test/Dockerfile-bash4-0 | 14 ++- test/Dockerfile-bash4-2 | 14 ++- test/Dockerfile-bash5-0 | 14 ++- test/Dockerfile-centos6 | 23 ++++- test/Dockerfile-centos7 | 15 ++++ test/Dockerfile-centos8 | 15 ++++ test/Dockerfile-debian | 13 +++ test/Dockerfile-ubuntu | 5 +- test/Dockerfile-ubuntu16 | 15 ++++ test/Dockerfile-ubuntu18 | 15 ++++ test/restart-ftpd | 2 - test/run-test.cmd | 39 +++++---- test/test-config/alpine-supervisord.conf | 9 ++ test/test-config/vsftpd.conf | 66 +++++++++++++++ test/test-config/vsftpd.initd | 103 +++++++++++++++++++++++ test/test_helper.bash | 7 +- 19 files changed, 453 insertions(+), 34 deletions(-) create mode 100644 test/34-ftp-passive.bats create mode 100644 test/test-config/vsftpd.conf create mode 100644 test/test-config/vsftpd.initd diff --git a/test/33-ftp.bats b/test/33-ftp.bats index 9c52bb2..9c0bb2f 100644 --- a/test/33-ftp.bats +++ b/test/33-ftp.bats @@ -8,6 +8,21 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl + + # enable passive and disable active mode + # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode + cat <<- _FTP >> $VSFTPD_CONF +pasv_enable=NO +_FTP + + ${CODE_DIR}/test/restart-ftpd +} + + +teardown() { + cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF + ${CODE_DIR}/test/restart-ftpd } @@ -16,23 +31,37 @@ setup() { skip "Using staging server, skipping internal test" fi - ${CODE_DIR}/test/restart-ftpd if [[ ! -d /var/www/html/.well-known/acme-challenge ]]; then mkdir -p /var/www/html/.well-known/acme-challenge - chgrp -R www-data /var/www/html/.well-known - chmod -R g+w /var/www/html/.well-known fi + # Always change ownership and permissions in case previous tests created the directories as root + chgrp -R www-data /var/www/html/.well-known + chmod -R g+w /var/www/html/.well-known + CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg ACL="ftp:ftpuser:ftpuser:${GETSSL_CMD_HOST}:/var/www/html/.well-known/acme-challenge" -FTP_OPTIONS="chmod 644 \\\$fromfile" EOF + if [[ "$GETSSL_OS" = "alpine" ]]; then + cat <<- EOF2 >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +FTP_OPTIONS="set ftp:passive-mode off" +EOF2 + elif [[ "$FTP_PASSIVE_DEFAULT" == "true" ]]; then + cat <<- EOF3 >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +FTP_OPTIONS="passive" +EOF3 + fi + create_certificate assert_success + assert_line --partial "ftp:ftpuser:ftpuser:" + if [[ "$GETSSL_OS" != "alpine" ]] && [[ "$FTP_PASSIVE_DEFAULT" == "true" ]]; then + assert_line --partial "Passive mode off" + fi check_output_for_errors } diff --git a/test/34-ftp-passive.bats b/test/34-ftp-passive.bats new file mode 100644 index 0000000..cb61dda --- /dev/null +++ b/test/34-ftp-passive.bats @@ -0,0 +1,67 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl + + # enable passive and disable active mode + # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode + cat <<- _FTP >> $VSFTPD_CONF +pasv_enable=YES +pasv_max_port=10100 +pasv_min_port=10090 +connect_from_port_20=NO +_FTP + + ${CODE_DIR}/test/restart-ftpd +} + + +teardown() { + cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF + ${CODE_DIR}/test/restart-ftpd +} + + +@test "Use Passive FTP to create challenge file" { + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + + if [[ ! -d /var/www/html/.well-known/acme-challenge ]]; then + mkdir -p /var/www/html/.well-known/acme-challenge + fi + + # Always change ownership and permissions in case previous tests created the directories as root + chgrp -R www-data /var/www/html/.well-known + chmod -R g+w /var/www/html/.well-known + + CONFIG_FILE="getssl-http01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +ACL="ftp:ftpuser:ftpuser:${GETSSL_CMD_HOST}:/var/www/html/.well-known/acme-challenge" +EOF + if [[ "$FTP_PASSIVE_DEFAULT" == "false" ]]; then + cat <<- EOF3 >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +FTP_OPTIONS="passive" +EOF3 + fi + + create_certificate + assert_success + assert_line --partial "ftp:ftpuser:ftpuser:" + if [[ "$FTP_PASSIVE_DEFAULT" == "false" ]]; then + assert_line --partial "Passive mode on" + else + refute_line --partial "Passive mode off" + fi + check_output_for_errors +} diff --git a/test/Dockerfile-alpine b/test/Dockerfile-alpine index b609938..924950b 100644 --- a/test/Dockerfile-alpine +++ b/test/Dockerfile-alpine @@ -2,7 +2,7 @@ FROM alpine:latest # Note this image uses busybox awk instead of gawk -RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash lftp vsftpd openssh-server WORKDIR /root @@ -11,6 +11,18 @@ RUN mkdir /run/nginx RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY ./test/test-config/vsftpd.conf /etc/vsftpd.conf +RUN echo "seccomp_sandbox=NO" >> /etc/vsftpd.conf +RUN adduser -D ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R ftpuser.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-bash4-0 b/test/Dockerfile-bash4-0 index e75b74a..9b1f790 100644 --- a/test/Dockerfile-bash4-0 +++ b/test/Dockerfile-bash4-0 @@ -2,7 +2,7 @@ FROM bash:4.0 # https://hub.docker.com/_/bash -RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx lftp vsftpd openssh-server WORKDIR /root @@ -11,6 +11,18 @@ RUN mkdir /run/nginx RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY ./test/test-config/vsftpd.conf /etc/vsftpd.conf +RUN echo "seccomp_sandbox=NO" >> /etc/vsftpd.conf +RUN adduser -D ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R ftpuser.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-bash4-2 b/test/Dockerfile-bash4-2 index 720884c..d9386f6 100644 --- a/test/Dockerfile-bash4-2 +++ b/test/Dockerfile-bash4-2 @@ -2,7 +2,7 @@ FROM bash:4.2 # https://hub.docker.com/_/bash -RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx lftp vsftpd openssh-server WORKDIR /root @@ -11,6 +11,18 @@ RUN mkdir /run/nginx RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY ./test/test-config/vsftpd.conf /etc/vsftpd.conf +RUN echo "seccomp_sandbox=NO" >> /etc/vsftpd.conf +RUN adduser -D ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R ftpuser.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-bash5-0 b/test/Dockerfile-bash5-0 index ba0295a..2d776eb 100644 --- a/test/Dockerfile-bash5-0 +++ b/test/Dockerfile-bash5-0 @@ -2,7 +2,7 @@ FROM bash:5.0 # https://hub.docker.com/_/bash -RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx bash +RUN apk --no-cache add supervisor openssl git curl bind-tools drill wget nginx lftp vsftpd openssh-server WORKDIR /root @@ -11,6 +11,18 @@ RUN mkdir /run/nginx RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY ./test/test-config/vsftpd.conf /etc/vsftpd.conf +RUN echo "seccomp_sandbox=NO" >> /etc/vsftpd.conf +RUN adduser -D ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R ftpuser.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-centos6 b/test/Dockerfile-centos6 index 6874cb2..a8d59b0 100644 --- a/test/Dockerfile-centos6 +++ b/test/Dockerfile-centos6 @@ -5,16 +5,29 @@ FROM centos:centos6 # [wsl2] # kernelCommandLine = vsyscall=emulate -# Centos 6 is EOL and is no longer available from the usual mirrors, so switch -# to https://vault.centos.org +# Centos 6 is EOL and is no longer available from the usual mirrors, so switch to https://vault.centos.org RUN sed -i 's/enabled=1/enabled=0/g' /etc/yum/pluginconf.d/fastestmirror.conf && \ sed -i 's/^mirrorlist/#mirrorlist/g' /etc/yum.repos.d/*.repo && \ sed -i 's;^#baseurl=http://mirror;baseurl=https://vault;g' /etc/yum.repos.d/*.repo # Update and install required software -#RUN yum -y update RUN yum -y install epel-release RUN yum -y install git curl dnsutils ldns wget nginx +RUN yum -y install ftp vsftpd +RUN yum -y install openssh-server + +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY test/test-config/vsftpd.conf /etc/vsftpd/vsftpd.conf +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser www-data +RUN usermod -G www-data ftpuser +RUN usermod -G www-data root +RUN mkdir -p /var/www/.well-known/acme-challenge +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www WORKDIR /root RUN mkdir /etc/nginx/pki @@ -22,10 +35,12 @@ RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf # BATS (Bash Automated Testings) -RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 +RUN git clone https://github.com/bats-core/bats-core.git /bats-core # --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support RUN git clone https://github.com/bats-core/bats-assert /bats-assert RUN /bats-core/install.sh /usr/local +# Hack to disable BATS pretty formatter which stopped working on centos6 +ENV CI=yes EXPOSE 80 443 diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 index e86f521..8bf8fa9 100644 --- a/test/Dockerfile-centos7 +++ b/test/Dockerfile-centos7 @@ -4,6 +4,8 @@ FROM centos:centos7 RUN yum -y update RUN yum -y install epel-release RUN yum -y install git curl ldns bind-utils wget which nginx +RUN yum -y install ftp vsftpd +RUN yum -y install openssh-server WORKDIR /root RUN mkdir /etc/nginx/pki @@ -11,6 +13,19 @@ RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY test/test-config/vsftpd.conf /etc/vsftpd/vsftpd.conf +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser www-data +RUN usermod -G www-data ftpuser +RUN usermod -G www-data root +RUN mkdir -p /var/www/.well-known/acme-challenge +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-centos8 b/test/Dockerfile-centos8 index 4ccb817..64f4381 100644 --- a/test/Dockerfile-centos8 +++ b/test/Dockerfile-centos8 @@ -6,6 +6,8 @@ FROM centos:centos8 RUN yum -y update RUN yum -y install epel-release RUN yum -y install git curl bind-utils wget which nginx +RUN yum -y install ftp vsftpd +RUN yum -y install openssh-server WORKDIR /root RUN mkdir /etc/nginx/pki @@ -13,6 +15,19 @@ RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=true +COPY test/test-config/vsftpd.conf /etc/vsftpd/vsftpd.conf +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser www-data +RUN usermod -G www-data ftpuser +RUN usermod -G www-data root +RUN mkdir -p /var/www/.well-known/acme-challenge +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-debian b/test/Dockerfile-debian index b39f915..fa74995 100644 --- a/test/Dockerfile-debian +++ b/test/Dockerfile-debian @@ -5,11 +5,24 @@ FROM debian:latest # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light +RUN apt-get install -y ftp vsftpd +RUN apt-get install -y openssh-server WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=false +COPY test/test-config/vsftpd.conf /etc/vsftpd.conf +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-ubuntu b/test/Dockerfile-ubuntu index 4e80409..68813f8 100644 --- a/test/Dockerfile-ubuntu +++ b/test/Dockerfile-ubuntu @@ -12,7 +12,10 @@ RUN apt-get install -y vim dos2unix # for debugging RUN apt-get install -y ftp vsftpd RUN apt-get install -y openssh-server -RUN echo "write_enable=YES" >> /etc/vsftpd.conf +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=false +COPY test/test-config/vsftpd.conf /etc/vsftpd.conf RUN adduser ftpuser RUN echo 'ftpuser:ftpuser' | chpasswd RUN adduser ftpuser www-data diff --git a/test/Dockerfile-ubuntu16 b/test/Dockerfile-ubuntu16 index 41be837..184db87 100644 --- a/test/Dockerfile-ubuntu16 +++ b/test/Dockerfile-ubuntu16 @@ -6,12 +6,27 @@ FROM ubuntu:xenial # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget nginx-light +RUN apt-get install -y ftp vsftpd +RUN apt-get install -y openssh-server WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=false +COPY test/test-config/vsftpd.conf /etc/vsftpd.conf +# The default init.d script seems to have an incorrect check that vsftpd has started +COPY test/test-config/vsftpd.initd /etc/init.d/vsftpd +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www + # BATS (Bash Automated Testings) RUN git clone https://github.com/bats-core/bats-core.git /bats-core --branch v1.2.1 RUN git clone https://github.com/bats-core/bats-support /bats-support diff --git a/test/Dockerfile-ubuntu18 b/test/Dockerfile-ubuntu18 index 5e4c574..ed1fae3 100644 --- a/test/Dockerfile-ubuntu18 +++ b/test/Dockerfile-ubuntu18 @@ -6,12 +6,27 @@ FROM ubuntu:bionic # Update and install required software RUN apt-get update --fix-missing RUN apt-get install -y git curl dnsutils ldnsutils wget gawk nginx-light +RUN apt-get install -y ftp vsftpd +RUN apt-get install -y openssh-server WORKDIR /root RUN mkdir /etc/nginx/pki RUN mkdir /etc/nginx/pki/private COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/sites-enabled/default +# Setup ftp +ENV VSFTPD_CONF=/etc/vsftpd.conf +ENV FTP_PASSIVE_DEFAULT=false +COPY test/test-config/vsftpd.conf /etc/vsftpd.conf +# The default init.d script seems to have an incorrect check that vsftpd has started +COPY test/test-config/vsftpd.initd /etc/init.d/vsftpd +RUN adduser ftpuser +RUN echo 'ftpuser:ftpuser' | chpasswd +RUN adduser ftpuser www-data +RUN adduser root www-data +RUN chown -R www-data.www-data /var/www +RUN chmod g+w -R /var/www + # Prevent "Can't load /root/.rnd into RNG" error from openssl RUN touch /root/.rnd diff --git a/test/restart-ftpd b/test/restart-ftpd index 279c2ff..f87d9d8 100644 --- a/test/restart-ftpd +++ b/test/restart-ftpd @@ -2,10 +2,8 @@ if [ "$GETSSL_OS" = "alpine" ]; then killall -HUP vsftpd >&3- - sleep 5 elif [[ "$GETSSL_OS" == "centos"[78] ]]; then pgrep vsftpd | head -1 | xargs kill -HUP - sleep 5 else service vsftpd restart >/dev/null >&3- fi diff --git a/test/run-test.cmd b/test/run-test.cmd index 5908de7..61b360e 100644 --- a/test/run-test.cmd +++ b/test/run-test.cmd @@ -1,51 +1,52 @@ @echo off IF %1.==. GOTO NoOS -set OS=%1 +SET OS=%1 :CheckCommand IF %2.==. GOTO NoCmd -set COMMAND=%2 %3 +SET COMMAND=%2 %3 :CheckAlias REM check if OS *contains* staging IF NOT x%OS:duck=%==x%OS% GOTO duckdns IF NOT x%OS:dynu=%==x%OS% GOTO dynu IF NOT x%OS:bash=%==x%OS% GOTO bash -set ALIAS=%OS%.getssl.test -set STAGING= -set GETSSL_OS=%OS% +SET ALIAS=%OS%.getssl.test +SET STAGING= +SET GETSSL_OS=%OS% GOTO Run :NoOS -set OS=ubuntu +SET OS=ubuntu GOTO CheckCommand :NoCmd -REM set COMMAND=/getssl/test/run-bats.sh -set COMMAND=bats /getssl/test --timing +REM SET COMMAND=/getssl/test/run-bats.sh +SET COMMAND=bats /getssl/test --timing GOTO CheckAlias :duckdns -set ALIAS=%OS:-duckdns=%-getssl.duckdns.org -set STAGING=--env STAGING=true --env dynamic_dns=duckdns -set GETSSL_OS=%OS:-duckdns=% +SET ALIAS=%OS:-duckdns=%-getssl.duckdns.org +SET STAGING=--env STAGING=true --env dynamic_dns=duckdns +SET GETSSL_OS=%OS:-duckdns=% GOTO Run :dynu -set ALIAS=%OS:-dynu=%-getssl.freeddns.org -set STAGING=--env STAGING=true --env dynamic_dns=dynu -set GETSSL_OS=%OS:-dynu=% +SET ALIAS=%OS:-dynu=%-getssl.freeddns.org +SET STAGING=--env STAGING=true --env dynamic_dns=dynu +SET GETSSL_OS=%OS:-dynu=% GOTO Run :bash -set ALIAS=%OS%.getssl.test -set STAGING= -set GETSSL_OS=alpine +SET ALIAS=%OS%.getssl.test +SET STAGING= +SET GETSSL_OS=alpine :Run -for %%I in (.) do set CurrDirName=%%~nxI +FOR %%I in (.) DO SET CurrDirName=%%~nxI docker build --pull --rm -f "test\Dockerfile-%OS%" -t getssl-%OS% . +IF %ErrorLevel% EQU 1 GOTO End @echo on docker run -it ^ --env GETSSL_HOST=%ALIAS% %STAGING% ^ @@ -69,3 +70,5 @@ docker run -it ^ --name getssl-%OS% ^ getssl-%OS% ^ %COMMAND% + +:End diff --git a/test/test-config/alpine-supervisord.conf b/test/test-config/alpine-supervisord.conf index 9759570..bbf671e 100644 --- a/test/test-config/alpine-supervisord.conf +++ b/test/test-config/alpine-supervisord.conf @@ -12,3 +12,12 @@ stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 autorestart=false startretries=0 + +[program:vsftpd] +command=vsftpd +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +autorestart=false +startretries=0 diff --git a/test/test-config/vsftpd.conf b/test/test-config/vsftpd.conf new file mode 100644 index 0000000..59ca78a --- /dev/null +++ b/test/test-config/vsftpd.conf @@ -0,0 +1,66 @@ +# Example config file /etc/vsftpd.conf (alpine) /etc/vsftpd/vsftpd.conf +# +# The default compiled in settings are fairly paranoid. This sample file +# loosens things up a bit, to make the ftp daemon more usable. +# Please see vsftpd.conf.5 for all compiled in defaults. +# +# Run standalone? vsftpd can run either from an inetd or as a standalone +# daemon started from an initscript. +listen=YES +# +# This directive enables listening on IPv6 sockets. By default, listening +# on the IPv6 "any" address (::) will accept connections from both IPv6 +# and IPv4 clients. It is not necessary to listen on *both* IPv4 and IPv6 +# sockets. If you want that (perhaps because you want to listen on specific +# addresses) then you must run two copies of vsftpd with two configuration +# files. +#listen_ipv6=NO +# +# Allow anonymous FTP? (Disabled by default). +anonymous_enable=NO +# +# Uncomment this to allow local users to log in. +local_enable=YES +# +# Uncomment this to enable any form of FTP write command. +write_enable=YES +# +# Default umask for local users is 077. You may wish to change this to 022, +# if your users expect that (022 is used by most other ftpd's) +local_umask=022 +# +# Activate directory messages - messages given to remote users when they +# go into a certain directory. +dirmessage_enable=YES +# +# If enabled, vsftpd will display directory listings with the time +# in your local time zone. The default is to display GMT. The +# times returned by the MDTM FTP command are also affected by this +# option. +use_localtime=YES +# +# Activate logging of uploads/downloads. +xferlog_enable=YES +# +# Make sure PORT transfer connections originate from port 20 (ftp-data). +connect_from_port_20=YES +# +# You may change the default value for timing out an idle session. +#idle_session_timeout=600 +# +# You may change the default value for timing out a data connection. +#data_connection_timeout=120 +# +# You may restrict local users to their home directories. See the FAQ for +# the possible risks in this before using chroot_local_user or +# chroot_list_enable below. +chroot_local_user=NO +# +# This string is the name of the PAM service vsftpd will use. +pam_service_name=vsftpd +# +# This option specifies the location of the RSA certificate to use for SSL +# encrypted connections. +rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem +rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key +ssl_enable=NO diff --git a/test/test-config/vsftpd.initd b/test/test-config/vsftpd.initd new file mode 100644 index 0000000..d5b2b00 --- /dev/null +++ b/test/test-config/vsftpd.initd @@ -0,0 +1,103 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: vsftpd +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Very secure FTP server +# Description: Provides a lightweight, efficient FTP server written +# for security. +### END INIT INFO + +set -e + +DAEMON="/usr/sbin/vsftpd" +NAME="vsftpd" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" +LOGFILE="/var/log/vsftpd.log" +CHROOT="/var/run/vsftpd/empty" + +test -x "${DAEMON}" || exit 0 + +. /lib/lsb/init-functions + +if [ ! -e "${LOGFILE}" ] +then + touch "${LOGFILE}" + chmod 640 "${LOGFILE}" + chown root:adm "${LOGFILE}" +fi + +if [ ! -d "${CHROOT}" ] +then + mkdir -p "${CHROOT}" +fi + +case "${1}" in + start) + log_daemon_msg "Starting FTP server" "${NAME}" + + if [ -e /etc/vsftpd.conf ] && ! egrep -iq "^ *listen(_ipv6)? *= *yes" /etc/vsftpd.conf + then + log_warning_msg "vsftpd disabled - listen disabled in config." + exit 0 + fi + + start-stop-daemon --start --background -m --oknodo --pidfile /var/run/vsftpd/vsftpd.pid --exec ${DAEMON} + + n=0 + while [ ${n} -le 5 ] + do + _PID="$(if [ -e /var/run/vsftpd/vsftpd.pid ]; then cat /var/run/vsftpd/vsftpd.pid; fi)" + if ! ps -C vsftpd | grep -qs "${_PID}" + then + break + fi + sleep 1 + n=$(( $n + 1 )) + done + + if ps -C vsftpd | grep -qs "${_PID}" + then + log_warning_msg "vsftpd failed - probably invalid config." + exit 1 + fi + + log_end_msg 0 + ;; + + stop) + log_daemon_msg "Stopping FTP server" "${NAME}" + + start-stop-daemon --stop --pidfile /var/run/vsftpd/vsftpd.pid --oknodo --exec ${DAEMON} + rm -f /var/run/vsftpd/vsftpd.pid + + log_end_msg 0 + ;; + + restart) + ${0} stop + ${0} start + ;; + + reload|force-reload) + log_daemon_msg "Reloading FTP server configuration" + + start-stop-daemon --stop --pidfile /var/run/vsftpd/vsftpd.pid --signal 1 --exec $DAEMON + + log_end_msg "${?}" + ;; + + status) + status_of_proc "${DAEMON}" "FTP server" + ;; + + *) + echo "Usage: ${0} {start|stop|restart|reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/test/test_helper.bash b/test/test_helper.bash index c5d5320..3adcdfa 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -70,16 +70,21 @@ setup_environment() { /getssl/test/restart-nginx } -# start nginx in background on alpine via supervisord +# start nginx and vsftpd in background on alpine via supervisord # shellcheck disable=SC2153 # Ignore GETSSL_OS looks like typo of GETSSL_IP if [[ -f /usr/bin/supervisord && -f /etc/supervisord.conf ]]; then if [[ ! $(pgrep supervisord) ]]; then /usr/bin/supervisord -c /etc/supervisord.conf >&3- + # Give supervisord time to start + sleep 1 fi elif [[ "$GETSSL_OS" == "centos"[78] ]]; then if [ -z "$(pgrep nginx)" ]; then nginx >&3- fi + if [ -z "$(pgrep vsftpd)" ]; then + vsftpd >&3- + fi fi # Find NGINX configuration directory for HTTP-01 testing (need to add SSL to config) From 170e65d1cdc50a961fe49b22f61173f8fe6c1fe7 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 22 Jan 2021 15:32:44 +0000 Subject: [PATCH 293/337] Add +x for scripts used in testing --- test/restart-ftpd | 0 test/test-config/vsftpd.initd | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/restart-ftpd mode change 100644 => 100755 test/test-config/vsftpd.initd diff --git a/test/restart-ftpd b/test/restart-ftpd old mode 100644 new mode 100755 diff --git a/test/test-config/vsftpd.initd b/test/test-config/vsftpd.initd old mode 100644 new mode 100755 From 99d1fcccf66bb35a9c6e86d35228dbae4b382e0f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 22 Jan 2021 17:51:50 +0000 Subject: [PATCH 294/337] Fix staging tests where vsftpd is not installed --- test/33-ftp.bats | 18 +++++++++++------- test/34-ftp-passive.bats | 18 +++++++++++------- test/test_helper.bash | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/test/33-ftp.bats b/test/33-ftp.bats index 9c0bb2f..9c2e387 100644 --- a/test/33-ftp.bats +++ b/test/33-ftp.bats @@ -8,21 +8,25 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl + if [ -n "${VSFTPD_CONF}" ]; then + cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl - # enable passive and disable active mode - # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode - cat <<- _FTP >> $VSFTPD_CONF + # enable passive and disable active mode + # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode + cat <<- _FTP >> $VSFTPD_CONF pasv_enable=NO _FTP - ${CODE_DIR}/test/restart-ftpd + ${CODE_DIR}/test/restart-ftpd + fi } teardown() { - cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF - ${CODE_DIR}/test/restart-ftpd + if [ -n "${VSFTPD_CONF}" ]; then + cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF + ${CODE_DIR}/test/restart-ftpd + fi } diff --git a/test/34-ftp-passive.bats b/test/34-ftp-passive.bats index cb61dda..0d16574 100644 --- a/test/34-ftp-passive.bats +++ b/test/34-ftp-passive.bats @@ -8,24 +8,28 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl + if [ -n "${VSFTPD_CONF}" ]; then + cp $VSFTPD_CONF ${VSFTPD_CONF}.getssl - # enable passive and disable active mode - # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode - cat <<- _FTP >> $VSFTPD_CONF + # enable passive and disable active mode + # https://www.pixelstech.net/article/1364817664-FTP-active-mode-and-passive-mode + cat <<- _FTP >> $VSFTPD_CONF pasv_enable=YES pasv_max_port=10100 pasv_min_port=10090 connect_from_port_20=NO _FTP - ${CODE_DIR}/test/restart-ftpd + ${CODE_DIR}/test/restart-ftpd + fi } teardown() { - cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF - ${CODE_DIR}/test/restart-ftpd + if [ -n "${VSFTPD_CONF}" ]; then + cp ${VSFTPD_CONF}.getssl $VSFTPD_CONF + ${CODE_DIR}/test/restart-ftpd + fi } diff --git a/test/test_helper.bash b/test/test_helper.bash index 3adcdfa..11cdf44 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -82,7 +82,7 @@ elif [[ "$GETSSL_OS" == "centos"[78] ]]; then if [ -z "$(pgrep nginx)" ]; then nginx >&3- fi - if [ -z "$(pgrep vsftpd)" ]; then + if [ -z "$(pgrep vsftpd)" ] && [ "$(command -v vsftpd)" ]; then vsftpd >&3- fi fi From 7a9a9ca684bcb2d1b56cdde1a67f4e244ca960b1 Mon Sep 17 00:00:00 2001 From: atisne Date: Wed, 27 Jan 2021 17:08:32 +0100 Subject: [PATCH 295/337] Add the ability to set several reload commands Use a bash array to set several commands. (Commands use the usual syntax.) The current syntax is unchanged when setting a single command. --- README.md | 4 +++- getssl | 41 ++++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 11ab90e..84d2835 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,9 @@ DOMAIN_KEY_LOCATION="ssh:server5:/etc/ssl/domain.key" #DOMAIN_PEM_LOCATION="" this is the domain_key. domain cert and CA cert -# The command needed to reload apache / nginx or whatever you use +# The command needed to reload apache / nginx or whatever you use. +# Several (ssh) commands may be given using a bash array: +# RELOAD_CMD=('ssh:sshuserid@server5:systemctl reload httpd' 'logger getssl for server5 efficient.') RELOAD_CMD="service apache2 reload" # Define the server type. This can be https, ftp, ftpi, imap, imaps, pop3, pop3s, smtp, diff --git a/getssl b/getssl index 747027e..5ce3420 100755 --- a/getssl +++ b/getssl @@ -1957,22 +1957,25 @@ purge_archive() { # purge archive of old, invalid, certificates reload_service() { # Runs a command to reload services ( via ssh if needed) if [[ -n "$RELOAD_CMD" ]]; then info "reloading SSL services" - if [[ "${RELOAD_CMD:0:4}" == "ssh:" ]] ; then - sshhost=$(echo "$RELOAD_CMD"| awk -F: '{print $2}') - command=${RELOAD_CMD:(( ${#sshhost} + 5))} - debug "running following command to reload cert" - debug "ssh $SSH_OPTS $sshhost ${command}" - # shellcheck disable=SC2029 - # shellcheck disable=SC2086 - ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 - # allow 2 seconds for services to restart - sleep 2 - else - debug "running reload command $RELOAD_CMD" - if ! eval "$RELOAD_CMD" ; then - error_exit "error running $RELOAD_CMD" + for ARELOAD_CMD in "${RELOAD_CMD[@]}" + do + if [[ "${ARELOAD_CMD:0:4}" == "ssh:" ]] ; then + sshhost=$(echo "$ARELOAD_CMD"| awk -F: '{print $2}') + command=${ARELOAD_CMD:(( ${#sshhost} + 5))} + debug "running following command to reload cert:" + debug "ssh $SSH_OPTS $sshhost ${command}" + # shellcheck disable=SC2029 + # shellcheck disable=SC2086 + ssh $SSH_OPTS "$sshhost" "${command}" 1>/dev/null 2>&1 + # allow 2 seconds for services to restart + sleep 2 + else + debug "running reload command: $ARELOAD_CMD" + if ! eval "$ARELOAD_CMD" ; then + error_exit "error running: $ARELOAD_CMD" + fi fi - fi + done fi } @@ -2326,7 +2329,9 @@ write_domain_template() { # write out a template file for a domain. #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 + # The command needed to reload apache / nginx or whatever you use. + # Several (ssh) commands may be given using a bash array: + # RELOAD_CMD=('ssh:sshuserid@server5:systemctl reload httpd' 'logger getssl for server5 efficient.') #RELOAD_CMD="" # Uncomment the following line to prevent non-interactive renewals of certificates @@ -2370,7 +2375,9 @@ write_getssl_template() { # write out the main template file PRIVATE_KEY_ALG="rsa" #REUSE_PRIVATE_KEY="true" - # The command needed to reload apache / nginx or whatever you use + # The command needed to reload apache / nginx or whatever you use. + # Several (ssh) commands may be given using a bash array: + # RELOAD_CMD=('ssh:sshuserid@server5:systemctl reload httpd' 'logger getssl for server5 efficient.') #RELOAD_CMD="" # The time period within which you want to allow renewal of a certificate From 858dfc75a5f84a692ceea5682f967667e57019b9 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 29 Jan 2021 17:31:50 +0000 Subject: [PATCH 296/337] Use -r option for dig to force default dig output Fixes #630 --- getssl | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/getssl b/getssl index 5ce3420..bbeb969 100755 --- a/getssl +++ b/getssl @@ -249,6 +249,10 @@ # 2020-12-22 Check that dig doesn't return an error (#611)(2.32) # 2020-12-29 Fix dig SOA lookup (#617)(2.33) # 2021-01-05 Show error if running in POSIX mode (#611) +# 2021-01-16 Fix double slash when using root directory with DAVS (ionos) +# 2021-01-22 Add FTP_OPTIONS +# 2021-01-27 Add the ability to set several reload commands (atisne) +# 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -1119,8 +1123,13 @@ find_dns_utils() { debug "HAS DIG_OR_DRILL=drill" HAS_DIG_OR_DRILL="drill" elif [[ -n "$(command -v dig 2>/dev/null)" ]] && dig >/dev/null 2>&1; then - debug "HAS DIG_OR_DRILL=dig" - HAS_DIG_OR_DRILL="dig" + if [[ $(dig -r >/dev/null 2>&1) ]]; then + # use dig -r so ~/.digrc is not used + HAS_DIG_OR_DRILL="dig -r" + else + HAS_DIG_OR_DRILL="dig" + fi + debug "HAS DIG_OR_DRILL=$HAS_DIG_OR_DRILL" fi if [[ -n "$(command -v host 2>/dev/null)" ]]; then @@ -1338,14 +1347,14 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n # Use SOA +trace to find the name server if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then - if [[ "$HAS_DIG_OR_DRILL" == "dig" ]]; then - debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver - test_output "Using $HAS_DIG_OR_DRILL SOA" - res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" $gad_s 2>/dev/null | grep "IN\WNS\W") - else + if [[ "$HAS_DIG_OR_DRILL" == "drill" ]]; then debug Using "$HAS_DIG_OR_DRILL -T $gad_d $gad_s" to find primary nameserver test_output "Using $HAS_DIG_OR_DRILL SOA" res=$($HAS_DIG_OR_DRILL -T SOA "$gad_d" $gad_s 2>/dev/null | grep "IN\WNS\W") + else + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d $gad_s" to find primary nameserver + test_output "Using $HAS_DIG_OR_DRILL SOA" + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" $gad_s 2>/dev/null | grep "IN\WNS\W") fi fi From d167d729271db12a402bd06d595951db3e062480 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 29 Jan 2021 17:32:41 +0000 Subject: [PATCH 297/337] Test specific config must be updated after setup_environment --- test/15-test-revoke-no-suffix.bats | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 96e174b..4c54961 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -19,11 +19,13 @@ setup() { else CONFIG_FILE="getssl-http01-no-suffix.cfg" fi - echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" setup_environment init_getssl + + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + create_certificate assert_success check_output_for_errors @@ -36,7 +38,7 @@ setup() { else CONFIG_FILE="getssl-http01.cfg" fi - echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' >> ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg + echo 'CA="https://acme-staging-v02.api.letsencrypt.org"' > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" CERT=${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt From 51ac8a3483f9427b298104fdec588d0d1e950d9a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 29 Jan 2021 20:05:21 +0000 Subject: [PATCH 298/337] Check for Valid ACME response and continue waiting --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index bbeb969..b14e465 100755 --- a/getssl +++ b/getssl @@ -491,8 +491,9 @@ check_challenge_completion() { # checks with the ACME server if our challenge is error_exit "$domain:Verify error:$err_detail" fi - # if ACME response is pending ( they haven't completed checks yet) then wait and try again. - if [[ "$status" == "pending" ]] ; then + # if ACME response is pending (they haven't completed checks yet) + # or valid (completed checks but not created certificate) then wait and try again. + if [[ "$status" == "pending" ]] || [[ "$status" == "valid" ]]; then info "Pending" else err_detail=$(echo "$response" | grep "detail") From 347ec532272031c835780726559dd6f20473fd3f Mon Sep 17 00:00:00 2001 From: Benno-K Date: Sun, 7 Feb 2021 16:25:48 +0100 Subject: [PATCH 299/337] Implement #634 - allow -u without domain(s) --- README.md | 2 +- getssl | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 84d2835..cad2775 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Options: -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) -Q, --mute Like -q, but mutes notification about successful upgrade -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) - -u, --upgrade Upgrade getssl if a more recent version is available + -u, --upgrade Upgrade getssl if a more recent version is available - can be used with or without domain(s) -k, --keep "#" Maximum amount of old getssl versions to keep when upgrading -U, --nocheck Do not check if a more recent version is available -w working_dir "Working directory" diff --git a/getssl b/getssl index b14e465..cad52c3 100755 --- a/getssl +++ b/getssl @@ -253,6 +253,7 @@ # 2021-01-22 Add FTP_OPTIONS # 2021-01-27 Add the ability to set several reload commands (atisne) # 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630) +# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -261,7 +262,7 @@ esac PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.33" +VERSION="2.34" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -1669,7 +1670,7 @@ help_message() { # print out the help message -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) -Q, --mute Like -q, but also mute notification about successful upgrade -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) - -u, --upgrade Upgrade getssl if a more recent version is available + -u, --upgrade Upgrade getssl if a more recent version is available - can be used with or without domain(s) -k, --keep "#" Maximum number of old getssl versions to keep when upgrading -U, --nocheck Do not check if a more recent version is available -w working_dir "Working directory" @@ -2513,6 +2514,10 @@ requires mktemp # Check if upgrades are available (unless they have specified -U to ignore Upgrade checks) if [[ $_UPGRADE_CHECK -eq 1 ]]; then check_getssl_upgrade + # if nothing in command line, then exit after upgrade + if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then + graceful_exit + fi fi # Revoke a certificate if requested From d616aa5d1a463923ac40ee4cbc2105cfc58978d6 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Sun, 7 Feb 2021 21:27:52 +0100 Subject: [PATCH 300/337] Test revealed that revocation (-r) also should not exit after upgrade --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index cad52c3..c319eed 100755 --- a/getssl +++ b/getssl @@ -2514,8 +2514,8 @@ requires mktemp # Check if upgrades are available (unless they have specified -U to ignore Upgrade checks) if [[ $_UPGRADE_CHECK -eq 1 ]]; then check_getssl_upgrade - # if nothing in command line, then exit after upgrade - if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]]; then + # if nothing in command line and no revocation, then exit after upgrade + if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]]; then graceful_exit fi fi From baa87f6cdbd24b441c2ac3ed3e569e3d813dbb19 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 13:43:26 +0100 Subject: [PATCH 301/337] Trying to fix failing tests in 32-test-upgrade.bats --- getssl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index c319eed..2b70249 100755 --- a/getssl +++ b/getssl @@ -2514,8 +2514,9 @@ requires mktemp # Check if upgrades are available (unless they have specified -U to ignore Upgrade checks) if [[ $_UPGRADE_CHECK -eq 1 ]]; then check_getssl_upgrade - # if nothing in command line and no revocation, then exit after upgrade - if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]]; then + # if nothing in command line and no revocation and not only config check, + # then exit after upgrade + if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]] && [ "${_ONLY_CHECK_CONFIG}" -eq 1 ]; then graceful_exit fi fi From 9946eb789fcbee975788b8ce9ef25eb5717dd0f7 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 14:05:06 +0100 Subject: [PATCH 302/337] Still trying to fix failing tests in 32-test-upgrade.bats --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 2b70249..151cff0 100755 --- a/getssl +++ b/getssl @@ -2517,7 +2517,7 @@ if [[ $_UPGRADE_CHECK -eq 1 ]]; then # if nothing in command line and no revocation and not only config check, # then exit after upgrade if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]] && [ "${_ONLY_CHECK_CONFIG}" -eq 1 ]; then - graceful_exit + echo graceful_exit fi fi From 088e55131354f9e491862b4d004ce2a7bf1c57e7 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 14:15:33 +0100 Subject: [PATCH 303/337] after wild trying go back to what I think is it, even if I know that the upgrade tests fail with that --- getssl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 151cff0..f0e40ab 100755 --- a/getssl +++ b/getssl @@ -2516,8 +2516,8 @@ if [[ $_UPGRADE_CHECK -eq 1 ]]; then check_getssl_upgrade # if nothing in command line and no revocation and not only config check, # then exit after upgrade - if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]] && [ "${_ONLY_CHECK_CONFIG}" -eq 1 ]; then - echo graceful_exit + if [[ -z "$DOMAIN" ]] && [[ ${_CHECK_ALL} -ne 1 ]] && [[ ${_REVOKE} -ne 1 ]] && [ "${_ONLY_CHECK_CONFIG}" -ne 1 ]; then + graceful_exit fi fi From a342bf7f4c90b369b2b8cfbff1d64cb9e03ce488 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 17:27:15 +0100 Subject: [PATCH 304/337] Changed 32-test-upgrade.bats to actually check the update functionality of the newly developped version - also change the way of determining the versions --- test/32-test-upgrade.bats | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats index 1c8af6d..cb47c7e 100644 --- a/test/32-test-upgrade.bats +++ b/test/32-test-upgrade.bats @@ -8,9 +8,16 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - CURRENT_VERSION=$(awk -F '"' '$1 == "VERSION=" {print $2}' ${CODE_DIR}/getssl) - PREVIOUS_VERSION=$(echo ${CURRENT_VERSION} | awk -F. '{ print $1 "." $2-1}') run git clone https://github.com/srvrco/getssl.git "$INSTALL_DIR/upgrade-getssl" + # Don't do version arithmetics any longer, look what there really is + cd "$INSTALL_DIR/upgrade-getssl" + CURRENT_VERSION=$(git tag -l|grep -e '^v'|tail -1|cut -b2-) + PREVIOUS_VERSION=$(git tag -l|grep -e '^v'|tail -2|head -1|cut -b2-) + # The version in the file, which we will overwrite + FILE_VERSION=$(awk -F'"' '/^VERSION=/{print $2}' "$CODE_DIR/getssl") +echo "============ FILE_VERSION ===========" +echo "${FILE_VERSION}" +echo "============ FILE_VERSION ===========" } @@ -31,6 +38,15 @@ teardown() { setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + # Overwrite checked out getssl-script with copy of new one, + # but write the previous version into the copy + # Note that this way we actually downgrade getssl, but we are testing + # the upgrading of the version in development + cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" +echo "============ VERSION ===========" +egrep '^VERSION=' "$INSTALL_DIR/upgrade-getssl/getssl" +echo "============ VERSION ===========" run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config ${GETSSL_CMD_HOST} assert_success #assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" @@ -45,12 +61,18 @@ teardown() { fi cd "$INSTALL_DIR/upgrade-getssl" - git checkout tags/v${PREVIOUS_VERSION} + git checkout tags/v${CURRENT_VERSION} CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + # Overwrite checked out getssl-script with copy of new one, + # but write the previous version into the copy + # Note that this way we actually downgrade getssl, but we are testing + # the upgrading of the version in development + cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config --upgrade ${GETSSL_CMD_HOST} assert_success assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" @@ -71,6 +93,12 @@ teardown() { setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" + # Overwrite checked out getssl-script with copy of new one, + # but write the previous version into the copy + # Note that this way we actually downgrade getssl, but we are testing + # the upgrading of the version in development + cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" run bash ./getssl --check-config --upgrade ${GETSSL_CMD_HOST} assert_success assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" From 15a8f9f1eeb8784e3847d6f4cf785fc2dadc7d62 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 17:42:34 +0100 Subject: [PATCH 305/337] Removed echo-statements --- test/32-test-upgrade.bats | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats index cb47c7e..b8f98cd 100644 --- a/test/32-test-upgrade.bats +++ b/test/32-test-upgrade.bats @@ -15,9 +15,6 @@ setup() { PREVIOUS_VERSION=$(git tag -l|grep -e '^v'|tail -2|head -1|cut -b2-) # The version in the file, which we will overwrite FILE_VERSION=$(awk -F'"' '/^VERSION=/{print $2}' "$CODE_DIR/getssl") -echo "============ FILE_VERSION ===========" -echo "${FILE_VERSION}" -echo "============ FILE_VERSION ===========" } @@ -44,9 +41,6 @@ teardown() { # the upgrading of the version in development cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" -echo "============ VERSION ===========" -egrep '^VERSION=' "$INSTALL_DIR/upgrade-getssl/getssl" -echo "============ VERSION ===========" run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config ${GETSSL_CMD_HOST} assert_success #assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" From 9853fd39d6f03bc3da0df1565755561ac687e835 Mon Sep 17 00:00:00 2001 From: Benno-K Date: Mon, 8 Feb 2021 19:18:11 +0100 Subject: [PATCH 306/337] More elegant way to get versions --- test/32-test-upgrade.bats | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats index b8f98cd..baa7b0f 100644 --- a/test/32-test-upgrade.bats +++ b/test/32-test-upgrade.bats @@ -10,9 +10,11 @@ setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt run git clone https://github.com/srvrco/getssl.git "$INSTALL_DIR/upgrade-getssl" # Don't do version arithmetics any longer, look what there really is + # by getting the last line (starting with v) and the one before of the + # list of tags. cd "$INSTALL_DIR/upgrade-getssl" - CURRENT_VERSION=$(git tag -l|grep -e '^v'|tail -1|cut -b2-) - PREVIOUS_VERSION=$(git tag -l|grep -e '^v'|tail -2|head -1|cut -b2-) + # This sets CURRENT_VERSION and PREVIOUS_VERSION bash variables + eval $(git tag -l | awk 'BEGIN {cur="?.??"};/^v/{prv=cur;cur=substr($1,2)};END{ printf("CURRENT_VERSION=\"%s\";PREVIOUS_VERSION=\"%s\"\n",cur,prv)}') # The version in the file, which we will overwrite FILE_VERSION=$(awk -F'"' '/^VERSION=/{print $2}' "$CODE_DIR/getssl") } From d96414050330297abdcf73890db15aae7ac5f284 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 9 Feb 2021 11:54:12 +0000 Subject: [PATCH 307/337] Prevent listing the complete file if version tag missing Also added missing (2.34) tag to end of the change log. Couple of minor changes to output messages. --- getssl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index f0e40ab..da65999 100755 --- a/getssl +++ b/getssl @@ -253,7 +253,7 @@ # 2021-01-22 Add FTP_OPTIONS # 2021-01-27 Add the ability to set several reload commands (atisne) # 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630) -# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script +# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (2.34) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -753,10 +753,10 @@ check_getssl_upgrade() { # check if a more recent version of code is available a fi if [[ ${_MUTE} -eq 0 ]]; then echo "Updated getssl from v${VERSION} to v${latestversion}" - echo "these update notification can be turned off using the -Q option" + echo "These update notifications can be turned off using the -Q option" echo "" echo "Updates are;" - awk "/\(${VERSION}\)$/ {s=1} s; /\(${latestversion}\)$/ {s=0}" "$TEMP_UPGRADE_FILE" | awk '{if(NR>1)print}' + awk "/\(${VERSION}\)$/ {s=1} s; /\(${latestversion}\)$/ || /^# ----/ {s=0}" "$TEMP_UPGRADE_FILE" | awk '{if(NR>1)print}' echo "" fi if [[ -n "$_KEEP_VERSIONS" ]] && [[ "$_KEEP_VERSIONS" =~ ^[0-9]+$ ]]; then @@ -786,7 +786,7 @@ check_getssl_upgrade() { # check if a more recent version of code is available a else info "" info "A more recent version (v${latestversion}) of getssl is available, please update" - info "the easiest way is to use the -u or --upgrade flag" + info "The easiest way is to use the -u or --upgrade flag" info "" fi fi From a5512c00ff7f3c10c884f6e9cfe238e3bba2ead2 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 9 Feb 2021 11:58:43 +0000 Subject: [PATCH 308/337] Add description of change to the change log. --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index da65999..583f88a 100755 --- a/getssl +++ b/getssl @@ -254,6 +254,7 @@ # 2021-01-27 Add the ability to set several reload commands (atisne) # 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630) # 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (2.34) +# 2021-02-09 Prevent listing the complete file if version tag missing (#637) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in From d9559b1e42e6bac69666c5f35c33aeb6ddb83a3d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Wed, 10 Feb 2021 21:48:45 +0000 Subject: [PATCH 309/337] Fix tests failure when pushing new version to master --- test/32-test-upgrade.bats | 67 ++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/test/32-test-upgrade.bats b/test/32-test-upgrade.bats index baa7b0f..f8fc68a 100644 --- a/test/32-test-upgrade.bats +++ b/test/32-test-upgrade.bats @@ -8,15 +8,21 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + + # Turn off warning about detached head + git config --global advice.detachedHead false run git clone https://github.com/srvrco/getssl.git "$INSTALL_DIR/upgrade-getssl" - # Don't do version arithmetics any longer, look what there really is - # by getting the last line (starting with v) and the one before of the - # list of tags. + + # Don't do version arithmetics any longer, look what was the previous version by getting the last + # line (starting with v) and the one before that from the list of tags. cd "$INSTALL_DIR/upgrade-getssl" - # This sets CURRENT_VERSION and PREVIOUS_VERSION bash variables - eval $(git tag -l | awk 'BEGIN {cur="?.??"};/^v/{prv=cur;cur=substr($1,2)};END{ printf("CURRENT_VERSION=\"%s\";PREVIOUS_VERSION=\"%s\"\n",cur,prv)}') + + # This sets CURRENT_TAG and PREVIOUS_TAG bash variables + eval $(git tag -l | awk 'BEGIN {cur="?.??"};/^v/{prv=cur;cur=substr($1,2)};END{ printf("CURRENT_TAG=\"%s\";PREVIOUS_TAG=\"%s\"\n",cur,prv)}') + # The version in the file, which we will overwrite FILE_VERSION=$(awk -F'"' '/^VERSION=/{print $2}' "$CODE_DIR/getssl") + # If FILE_VERSION > CURRENT_TAG then either we are testing a push to master or the last version wasn't released } @@ -31,22 +37,23 @@ teardown() { fi cd "$INSTALL_DIR/upgrade-getssl" - git checkout tags/v${PREVIOUS_VERSION} + git checkout tags/v${PREVIOUS_TAG} CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" - # Overwrite checked out getssl-script with copy of new one, - # but write the previous version into the copy - # Note that this way we actually downgrade getssl, but we are testing - # the upgrading of the version in development + + # Overwrite checked out getssl-script with copy of new one, but write the previous version into the copy + # Note that this way we mock downgrading getssl and are testing the upgrading of the version in development cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" - sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_TAG}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config ${GETSSL_CMD_HOST} assert_success - #assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" - assert_line "A more recent version (v${CURRENT_VERSION}) of getssl is available, please update" + + # Check for current tag or file version otherwise push to master fails on a new version (or if the tag hasn't been updated) + assert_line --regexp "A more recent version \(v(${CURRENT_TAG}|${FILE_VERSION})\) of getssl is available, please update" check_output_for_errors } @@ -57,22 +64,23 @@ teardown() { fi cd "$INSTALL_DIR/upgrade-getssl" - git checkout tags/v${CURRENT_VERSION} + git checkout tags/v${CURRENT_TAG} CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" - # Overwrite checked out getssl-script with copy of new one, - # but write the previous version into the copy - # Note that this way we actually downgrade getssl, but we are testing - # the upgrading of the version in development + + # Overwrite checked out getssl-script with copy of new one, but write the previous version into the copy + # Note that this way we mock downgrading getssl and are testing the upgrading of the version in development cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" - sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_TAG}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + run "$INSTALL_DIR/upgrade-getssl/getssl" --check-config --upgrade ${GETSSL_CMD_HOST} assert_success - assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" - check_output_for_errors + + # Check for current tag or file version otherwise push to master fails on a new version (or if the tag hasn't been updated) + assert_line --regexp "Updated getssl from v${PREVIOUS_TAG} to v(${CURRENT_TAG}|${FILE_VERSION})" } @@ -83,20 +91,21 @@ teardown() { fi cd "$INSTALL_DIR/upgrade-getssl" - git checkout tags/v${PREVIOUS_VERSION} + git checkout tags/v${PREVIOUS_TAG} CONFIG_FILE="getssl-http01.cfg" setup_environment init_getssl cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl.cfg" - # Overwrite checked out getssl-script with copy of new one, - # but write the previous version into the copy - # Note that this way we actually downgrade getssl, but we are testing - # the upgrading of the version in development + + # Overwrite checked out getssl-script with copy of new one, but write the previous version into the copy + # Note that this way we mock downgrading getssl and are testing the upgrading of the version in development cp "$CODE_DIR/getssl" "$INSTALL_DIR/upgrade-getssl/" - sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_VERSION}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + sed -i -e "s/VERSION=\"${FILE_VERSION}\"/VERSION=\"${PREVIOUS_TAG}\"/" "$INSTALL_DIR/upgrade-getssl/getssl" + run bash ./getssl --check-config --upgrade ${GETSSL_CMD_HOST} assert_success - assert_line "Updated getssl from v${PREVIOUS_VERSION} to v${CURRENT_VERSION}" - check_output_for_errors + + # Check for current tag or file version otherwise push to master fails on a new version (or if the tag hasn't been updated) + assert_line --regexp "Updated getssl from v${PREVIOUS_TAG} to v(${CURRENT_TAG}|${FILE_VERSION})" } From 67a7e5d4530c901ba8985e141a5891c2df2aabf0 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 12 Feb 2021 16:05:36 +0000 Subject: [PATCH 310/337] Merge getssl-dns01 and getssl-staging-dns01 into a single cfg file --- test/10-mixed-case.bats | 6 +-- test/14-test-revoke.bats | 4 +- test/15-test-revoke-no-suffix.bats | 4 +- ...try-dns-add.bats => 18-retry-dns-add.bats} | 9 +++- test/19-test-add-to-sans.bats | 4 +- test/2-simple-dns01-dig.bats | 3 -- test/2-simple-dns01-nslookup.bats | 3 -- test/20-wildcard-simple.bats | 6 +-- test/21-wildcard-dual-rsa.bats | 12 +---- ...dcard-dual-rsa-ecdsa-copy-2-locations.bats | 6 +-- test/24-wildcard-sans.bats | 12 +---- test/26-wildcard-revoke.bats | 12 +---- test/8-staging-ecdsa.bats | 8 +-- test/test-config/getssl-dns01.cfg | 47 ++++++++++++------ test/test-config/getssl-staging-dns01.cfg | 49 ------------------- 15 files changed, 59 insertions(+), 126 deletions(-) rename test/{18-staging-retry-dns-add.bats => 18-retry-dns-add.bats} (79%) delete mode 100644 test/test-config/getssl-staging-dns01.cfg diff --git a/test/10-mixed-case.bats b/test/10-mixed-case.bats index f144058..974f669 100644 --- a/test/10-mixed-case.bats +++ b/test/10-mixed-case.bats @@ -29,11 +29,7 @@ setup() { } @test "Check that DNS-01 verification works if the domain is not lowercase" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST=$(echo $GETSSL_HOST | tr a-z A-Z) setup_environment diff --git a/test/14-test-revoke.bats b/test/14-test-revoke.bats index 5bdb72b..d31e832 100644 --- a/test/14-test-revoke.bats +++ b/test/14-test-revoke.bats @@ -15,7 +15,7 @@ setup() { @test "Create certificate to check revoke" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-http01.cfg" fi @@ -30,7 +30,7 @@ setup() { @test "Check we can revoke a certificate" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-http01.cfg" fi diff --git a/test/15-test-revoke-no-suffix.bats b/test/15-test-revoke-no-suffix.bats index 4c54961..30802d8 100644 --- a/test/15-test-revoke-no-suffix.bats +++ b/test/15-test-revoke-no-suffix.bats @@ -15,7 +15,7 @@ setup() { @test "Create certificate to check revoke (no suffix)" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-http01-no-suffix.cfg" fi @@ -34,7 +34,7 @@ setup() { @test "Check we can revoke a certificate (no suffix)" { if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-http01.cfg" fi diff --git a/test/18-staging-retry-dns-add.bats b/test/18-retry-dns-add.bats similarity index 79% rename from test/18-staging-retry-dns-add.bats rename to test/18-retry-dns-add.bats index bca7c05..3a79880 100644 --- a/test/18-staging-retry-dns-add.bats +++ b/test/18-retry-dns-add.bats @@ -4,14 +4,18 @@ load '/bats-support/load.bash' load '/bats-assert/load.bash' load '/getssl/test/test_helper.bash' +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} @test "Check retry add dns command if dns isn't updated" { - if [ -z "$STAGING" ]; then + if [ -n "$STAGING" ]; then skip "Running internal tests, skipping external test" fi - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl @@ -25,6 +29,7 @@ DNS_WAIT_COUNT=11 DNS_EXTRA_WAIT=0 CHECK_ALL_AUTH_DNS="false" CHECK_PUBLIC_DNS_SERVER="false" +DNS_WAIT_RETRY_ADD="true" EOF create_certificate -d assert_failure diff --git a/test/19-test-add-to-sans.bats b/test/19-test-add-to-sans.bats index 8ba9f20..da9deb7 100644 --- a/test/19-test-add-to-sans.bats +++ b/test/19-test-add-to-sans.bats @@ -26,7 +26,7 @@ teardown() { skip "FIXME: Certificate is not recreated when SANS is updated" if [ -n "$STAGING" ]; then skip "Not trying on staging server yet" - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-dns01-add-to-sans-1.cfg" fi @@ -45,7 +45,7 @@ teardown() { skip "FIXME: Certificate is not recreated when SANS is updated" if [ -n "$STAGING" ]; then skip "Not trying on staging server yet" - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" else CONFIG_FILE="getssl-dns01-add-to-sans-2.cfg" fi diff --git a/test/2-simple-dns01-dig.bats b/test/2-simple-dns01-dig.bats index 6f701b3..f8f0cb4 100644 --- a/test/2-simple-dns01-dig.bats +++ b/test/2-simple-dns01-dig.bats @@ -30,9 +30,6 @@ teardown() { @test "Create new certificate using DNS-01 verification (dig)" { CONFIG_FILE="getssl-dns01.cfg" - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - fi setup_environment init_getssl diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index c883485..f97b5dc 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -31,9 +31,6 @@ teardown() { @test "Create new certificate using DNS-01 verification (nslookup)" { CONFIG_FILE="getssl-dns01.cfg" - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - fi setup_environment init_getssl diff --git a/test/20-wildcard-simple.bats b/test/20-wildcard-simple.bats index e9b0f98..b56a876 100644 --- a/test/20-wildcard-simple.bats +++ b/test/20-wildcard-simple.bats @@ -14,11 +14,7 @@ setup() { @test "Create wildcard certificate" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="*.${GETSSL_HOST}" setup_environment diff --git a/test/21-wildcard-dual-rsa.bats b/test/21-wildcard-dual-rsa.bats index 7f0943f..550bbe0 100644 --- a/test/21-wildcard-dual-rsa.bats +++ b/test/21-wildcard-dual-rsa.bats @@ -14,11 +14,7 @@ setup() { @test "Create secp384r1 wildcard certificate" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="*.${GETSSL_HOST}" @@ -40,11 +36,7 @@ EOF @test "Create dual certificates using DNS-01 verification" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="*.${GETSSL_HOST}" diff --git a/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats b/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats index 238142e..b005af5 100644 --- a/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats +++ b/test/22-wildcard-dual-rsa-ecdsa-copy-2-locations.bats @@ -14,11 +14,7 @@ setup() { @test "Create dual certificates (one wildcard) and copy RSA and ECDSA chain and key to two locations" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="*.${GETSSL_HOST}" diff --git a/test/24-wildcard-sans.bats b/test/24-wildcard-sans.bats index da5fea9..a9ca2a5 100644 --- a/test/24-wildcard-sans.bats +++ b/test/24-wildcard-sans.bats @@ -22,11 +22,7 @@ teardown() { @test "Check can create certificate for wildcard domain as arg and non-wildcard in SANS" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" # Staging server generates an error if try to create a certificate for *.domain and a.domain # so create for *.wild-domain and a.domain instead @@ -50,11 +46,7 @@ teardown() { @test "Check can create certificate for non-wildcard domain as arg and wildcard in SANS" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="${GETSSL_HOST}" setup_environment diff --git a/test/26-wildcard-revoke.bats b/test/26-wildcard-revoke.bats index 1416ef4..37f8f10 100644 --- a/test/26-wildcard-revoke.bats +++ b/test/26-wildcard-revoke.bats @@ -14,11 +14,7 @@ setup() { @test "Create certificate to check wildcard revoke" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" GETSSL_CMD_HOST="*.${GETSSL_HOST}" setup_environment @@ -30,11 +26,7 @@ setup() { @test "Check we can revoke a wildcard certificate" { - if [ -n "$STAGING" ]; then - CONFIG_FILE="getssl-staging-dns01.cfg" - else - CONFIG_FILE="getssl-dns01.cfg" - fi + CONFIG_FILE="getssl-dns01.cfg" . "${CODE_DIR}/test/test-config/${CONFIG_FILE}" GETSSL_CMD_HOST="*.${GETSSL_HOST}" diff --git a/test/8-staging-ecdsa.bats b/test/8-staging-ecdsa.bats index 127e989..6aebd60 100644 --- a/test/8-staging-ecdsa.bats +++ b/test/8-staging-ecdsa.bats @@ -9,9 +9,9 @@ load '/getssl/test/test_helper.bash' @test "Create new certificate using staging server and prime256v1" { if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" + skip "Running external tests, skipping internal testing" fi - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl @@ -35,9 +35,9 @@ load '/getssl/test/test_helper.bash' @test "Create new certificate using staging server and secp384r1" { if [ -z "$STAGING" ]; then - skip "Running internal tests, skipping external test" + skip "Running external tests, skipping internal testing" fi - CONFIG_FILE="getssl-staging-dns01.cfg" + CONFIG_FILE="getssl-dns01.cfg" setup_environment init_getssl diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index c67f995..bd02c50 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -1,19 +1,38 @@ -# 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 -# -CA="https://pebble:14000/dir" +# Test that the script works with dns 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 - -# Speed up the test by reducing the number or retries and the wait between retries. -DNS_WAIT=2 -DNS_WAIT_COUNT=11 -DNS_EXTRA_WAIT=0 - +if [ -z "$STAGING" ]; then + # Settings for challtestserv dns provider running in local docker + CA="https://pebble:14000/dir" + + 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 + + # Speed up the test by reducing the number or retries and the wait between retries. + DNS_WAIT=2 + DNS_WAIT_COUNT=11 + DNS_EXTRA_WAIT=0 +else + # Settings for external dns provider and staging server + CA="https://acme-staging-v02.api.letsencrypt.org/directory" + + DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_${dynamic_dns}" + DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_${dynamic_dns}" + PUBLIC_DNS_SERVER="8.8.8.8 resolver1.infoserve.de" + if [[ "${dynamic_dns}" == "dynu" ]]; then + AUTH_DNS_SERVER=ns1.dynu.com + else + AUTH_DNS_SERVER=ns1.duckdns.org + fi + CHECK_ALL_AUTH_DNS="true" + CHECK_PUBLIC_DNS_SERVER="true" + DNS_EXTRA_WAIT=120 + + DNS_WAIT_COUNT=20 + DNS_WAIT=30 + DNS_WAIT_RETRY_ADD="true" +fi # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" diff --git a/test/test-config/getssl-staging-dns01.cfg b/test/test-config/getssl-staging-dns01.cfg deleted file mode 100644 index 8859686..0000000 --- a/test/test-config/getssl-staging-dns01.cfg +++ /dev/null @@ -1,49 +0,0 @@ -# Test that the script works with external dns provider and staging server -# -CA="https://acme-staging-v02.api.letsencrypt.org/directory" - -VALIDATE_VIA_DNS=true -DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_${dynamic_dns}" -DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_${dynamic_dns}" -PUBLIC_DNS_SERVER="8.8.8.8 resolver1.infoserve.de" -if [[ "${dynamic_dns}" == "dynu" ]]; then - AUTH_DNS_SERVER=ns1.dynu.com -else - AUTH_DNS_SERVER=ns1.duckdns.org -fi -CHECK_ALL_AUTH_DNS="true" -CHECK_PUBLIC_DNS_SERVER="true" -DNS_EXTRA_WAIT=120 - -# Speed up the test by reducing the number or retries and retrying DNS_ADD after 10 failures -DNS_WAIT_COUNT=20 -DNS_WAIT=30 -DNS_WAIT_RETRY_ADD="true" - -ACCOUNT_KEY_TYPE="rsa" -PRIVATE_KEY_ALG="rsa" - -# Additional domains - this could be multiple domains / subdomains in a comma separated list -SANS="" - -# 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 ${NGINX_CONFIG} && /getssl/test/restart-nginx" - -# Define the server type and confirm correct certificate is installed -SERVER_TYPE="https" -CHECK_REMOTE="true" - -if [[ -s "$DOMAIN_DIR/getssl_test_specific.cfg" ]]; then - . $DOMAIN_DIR/getssl_test_specific.cfg -fi - -#_RUNNING_TEST=1 -#_USE_DEBUG=1 From afabbc6506f9a3e9acfe9bf19bd6905fa413e4bc Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 12 Feb 2021 16:06:26 +0000 Subject: [PATCH 311/337] Add PREFERED_CHAIN --- getssl | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/getssl b/getssl index 583f88a..08487b4 100755 --- a/getssl +++ b/getssl @@ -253,8 +253,9 @@ # 2021-01-22 Add FTP_OPTIONS # 2021-01-27 Add the ability to set several reload commands (atisne) # 2021-01-29 Use dig -r (if supported) to ignore.digrc (#630) -# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (2.34) -# 2021-02-09 Prevent listing the complete file if version tag missing (#637) +# 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (Benno-K)(2.34) +# 2021-02-09 Prevent listing the complete file if version tag missing (#637)(softins) +# 2021-02-12 Add PREFERRED_CHAIN # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -285,6 +286,7 @@ GETSSL_IGNORE_CP_PRESERVE="false" HTTP_TOKEN_CHECK_WAIT=0 IGNORE_DIRECTORY_DOMAIN="false" ORIG_UMASK=$(umask) +PREFERRED_CHAIN="" # Set this to use an alternative root certificate PREVIOUSLY_VALIDATED="true" PRIVATE_KEY_ALG="rsa" RELOAD_CMD="" @@ -1556,7 +1558,32 @@ get_certificate() { # get certificate for csr, if all domains validated. info "Requesting certificate" CertData=$(json_get "$response" "certificate") send_signed_request "$CertData" "" "" "$gc_fullchain" - info "Full certificate saved in $gc_fullchain" + IFS=$'\n' read -r -d '' -a alternate_links < <(echo "$responseHeaders" | grep "^Link" | grep "alternate" | awk -F"[<>]" '{print $2}') + debug "Alternate Links are ${alternate_links[*]}" + if [[ -n "$PREFERRED_CHAIN" ]]; then + cert_to_check=$(mktemp 2>/dev/null || mktemp -t getssl.XXXXXX) || error_exit "mktemp failed" + # Check the default certificate to see if that has the required chain + cp "$gc_fullchain" "$cert_to_check" + i=0 + while [[ $i -le ${#alternate_links[@]} ]]; do + cert_issuer=$(openssl crl2pkcs7 -nocrl -certfile "$cert_to_check" | openssl pkcs7 -print_certs -text -noout | grep 'Issuer:' | tail -1 | cut -d= -f2) + debug Certificate issued by "$cert_issuer" + if [[ $cert_issuer = *${PREFERRED_CHAIN}* ]]; then + debug "Found required certificate" + cp "$cert_to_check" "$gc_fullchain" + break + fi + + if [[ $i -lt ${#alternate_links[@]} ]]; then + debug "Fetching next alternate certificate $i ${alternate_links[$i]}" + send_signed_request "${alternate_links[$i]}" "" "" "$cert_to_check" + fi + i=$(( i + 1 )) + done + + # tidy up + rm -f "$cert_to_check" + fi awk -v CERT_FILE="$gc_certfile" -v CA_CERT="$gc_cafile" 'BEGIN {outfile=CERT_FILE} split_after==1 {outfile=CA_CERT;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > outfile}' "$gc_fullchain" info "Certificate saved in $gc_certfile" fi @@ -1675,6 +1702,7 @@ help_message() { # print out the help message -k, --keep "#" Maximum number of old getssl versions to keep when upgrading -U, --nocheck Do not check if a more recent version is available -w working_dir "Working directory" + --preferred-chain "chain" Use an alternate chain for the certificate _EOF_ } @@ -2278,7 +2306,8 @@ urlbase64_decode() { usage() { # echos out the program usage echo "Usage: $PROGNAME [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet]"\ - "[-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] domain" + "[-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir]"\ + "[--preferred-chain chain] domain" } write_domain_template() { # write out a template file for a domain. @@ -2333,6 +2362,11 @@ write_domain_template() { # write out a template file for a domain. # Set USE_SINGLE_ACL="true" to use a single ACL for all checks #USE_SINGLE_ACL="false" + # Preferred Chain - use an different certificate root from the default + # Staging options are: "Fake LE Root X1" and "Fake LE Root X2" + # Production options are: "ISRG Root X1" and "ISRG Root X2" + #PREFERRED_CHAIN="" + # 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/ssl/${DOMAIN}.crt" # this is domain cert @@ -2387,6 +2421,11 @@ write_getssl_template() { # write out the main template file PRIVATE_KEY_ALG="rsa" #REUSE_PRIVATE_KEY="true" + # Preferred Chain - use an different certificate root from the default + # Staging options are: "Fake LE Root X1" and "Fake LE Root X2" + # Production options are: "ISRG Root X1" and "ISRG Root X2" + #PREFERRED_CHAIN="" + # The command needed to reload apache / nginx or whatever you use. # Several (ssh) commands may be given using a bash array: # RELOAD_CMD=('ssh:sshuserid@server5:systemctl reload httpd' 'logger getssl for server5 efficient.') @@ -2469,6 +2508,8 @@ while [[ -n ${1+defined} ]]; do _ONLY_CHECK_CONFIG=1 ;; -w) shift; WORKING_DIR="$1" ;; + -preferred-chain) + shift; PREFERRED_CHAIN="$1" ;; --source) return ;; -*) From fa89d7bfedd6448860196aa966ad623ea45ac9cf Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 12 Feb 2021 16:06:51 +0000 Subject: [PATCH 312/337] Add tests for PREFERRED_CHAIN --- docker-compose.yml | 1 + test/35-preferred-chain.bats | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 test/35-preferred-chain.bats diff --git a/docker-compose.yml b/docker-compose.yml index ec5c24a..f2b1489 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: environment: # with Go 1.13.x which defaults TLS 1.3 to on GODEBUG: "tls13=1" + PEBBLE_ALTERNATE_ROOTS: 2 ports: - 14000:14000 # HTTPS ACME API - 15000:15000 # HTTPS Management API diff --git a/test/35-preferred-chain.bats b/test/35-preferred-chain.bats new file mode 100644 index 0000000..4389d3b --- /dev/null +++ b/test/35-preferred-chain.bats @@ -0,0 +1,95 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Use PREFERRED_CHAIN to select an alternate root" { + if [ -n "$STAGING" ]; then + PREFERRED_CHAIN="Fake LE Root X2" + else + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | cut -d= -f2) + PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace + fi + + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +PREFERRED_CHAIN="${PREFERRED_CHAIN}" +EOF + + create_certificate + assert_success + check_output_for_errors + + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + # verify certificate is issued by preferred chain root + [ "$PREFERRED_CHAIN" = "$issuer" ] +} + + +@test "Use PREFERRED_CHAIN to select the default root" { + if [ -n "$STAGING" ]; then + PREFERRED_CHAIN="Fake LE Root X1" + else + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/0 | openssl x509 -text -noout | grep Issuer: | cut -d= -f2 ) + PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace + fi + + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +PREFERRED_CHAIN="${PREFERRED_CHAIN}" +EOF + + create_certificate + assert_success + check_output_for_errors + + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + # verify certificate is issued by preferred chain root + [ "$PREFERRED_CHAIN" = "$issuer" ] +} + + +@test "Use PREFERRED_CHAIN to select an alternate root by suffix" { + if [ -n "$STAGING" ]; then + FULL_PREFERRED_CHAIN="Fake LE Root X2" + else + FULL_PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | cut -d= -f2) + FULL_PREFERRED_CHAIN="${FULL_PREFERRED_CHAIN# }" # remove leading whitespace + fi + + # Take the last word from FULL_PREFERRED_CHAIN as the chain to use + PREFERRED_CHAIN="${FULL_PREFERRED_CHAIN##* }" + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +PREFERRED_CHAIN="${PREFERRED_CHAIN}" +EOF + + create_certificate + assert_success + check_output_for_errors + + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + # verify certificate is issued by preferred chain root + echo "# ${issuer}" + echo "# ${FULL_PREFERRED_CHAIN}" + [ "$FULL_PREFERRED_CHAIN" = "$issuer" ] +} From 10061936bd5c6aec85251d8658e0157bed203b46 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 12 Feb 2021 16:36:23 +0000 Subject: [PATCH 313/337] Fix interpreter line in dns_add_fail --- test/dns_add_fail | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dns_add_fail b/test/dns_add_fail index 44ab42b..c39a89a 100755 --- a/test/dns_add_fail +++ b/test/dns_add_fail @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Special test script which will always fail to update dns From 973f5886ae0a4b5d585747dfdc6171074d58f541 Mon Sep 17 00:00:00 2001 From: update process Date: Mon, 15 Feb 2021 08:26:44 +0100 Subject: [PATCH 314/337] ftpes added to curl upload of challenge --- getssl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/getssl b/getssl index 08487b4..30cbdbe 100755 --- a/getssl +++ b/getssl @@ -912,6 +912,19 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. debug "davs user=$davsuser - pass=$davspass - host=$davshost port=$davsport dir=$davsdirn file=$davsfile" debug "from dir=$fromdir file=$fromfile" curl -u "${davsuser}:${davspass}" -T "${fromdir}/${fromfile}" "https://${davshost}:${davsport}${davsdirn}${davsfile}" + elif [[ "${to:0:6}" == "ftpes:" ]] ; then + debug "using ftp to copy the file from $from" + ftpuser=$(echo "$to"| awk -F: '{print $2}') + ftppass=$(echo "$to"| awk -F: '{print $3}') + ftphost=$(echo "$to"| awk -F: '{print $4}') + ftplocn=$(echo "$to"| awk -F: '{print $5}') + ftpdirn=$(dirname "$ftplocn") + ftpfile=$(basename "$ftplocn") + fromdir=$(dirname "$from") + fromfile=$(basename "$from") + debug "ftp user=$ftpuser - pass=$ftppass - host=$ftphost dir=$ftpdirn file=$ftpfile" + debug "from dir=$fromdir file=$fromfile" + curl --insecure --ftp-ssl -u "${ftpuser}:${ftppass}" -T "${fromdir}/${fromfile}" "ftp://${ftphost}${ftpdirn}/" else if ! mkdir -p "$(dirname "$to")" ; then error_exit "cannot create ACL directory $(basename "$to")" @@ -2343,7 +2356,7 @@ write_domain_template() { # write out a template file for a domain. # 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 + # If these start with ftp:/ftpes: 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. # You can also user WebDAV over HTTPS as transport mechanism. To do so, start with davs: followed by username, @@ -2353,7 +2366,8 @@ write_domain_template() { # write out a template file for a domain. # 'ssh:server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ssh:sshuserid@server5:/var/www/${DOMAIN}/web/.well-known/acme-challenge' # 'ftp:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge' - # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge') + # 'davs:davsuserid:davspassword:{DOMAIN}:443:/web/.well-known/acme-challenge' + # 'ftpes:ftpuserid:ftppassword:${DOMAIN}:/web/.well-known/acme-challenge') # Specify SSH options, e.g. non standard port in SSH_OPTS # (Can also use SCP_OPTS and SFTP_OPTS) From aae71fd7638e3fcfacd28ac53853d68eb91706cc Mon Sep 17 00:00:00 2001 From: update process Date: Mon, 15 Feb 2021 08:33:51 +0100 Subject: [PATCH 315/337] ftpes added to curl upload of challenge --- getssl | 1 + 1 file changed, 1 insertion(+) diff --git a/getssl b/getssl index 30cbdbe..066a83c 100755 --- a/getssl +++ b/getssl @@ -256,6 +256,7 @@ # 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (Benno-K)(2.34) # 2021-02-09 Prevent listing the complete file if version tag missing (#637)(softins) # 2021-02-12 Add PREFERRED_CHAIN +# 2021-02-15 ADD ftp explicit SSL with curl for upload the challenge # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in From 522918f023d2be3093583c834efc48db74627ffb Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Feb 2021 17:06:13 +0000 Subject: [PATCH 316/337] Add FULL_CHAIN_INCLUDE_ROOT Fixes #594 #272 #564 --- getssl | 30 +++++++++++- test/36-full-chain-inc-root.bats | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 test/36-full-chain-inc-root.bats diff --git a/getssl b/getssl index 066a83c..80a3fae 100755 --- a/getssl +++ b/getssl @@ -256,7 +256,8 @@ # 2021-02-07 Allow -u --upgrade without any domain, so that one can only update the script (Benno-K)(2.34) # 2021-02-09 Prevent listing the complete file if version tag missing (#637)(softins) # 2021-02-12 Add PREFERRED_CHAIN -# 2021-02-15 ADD ftp explicit SSL with curl for upload the challenge +# 2021-02-15 ADD ftp explicit SSL with curl for upload the challenge (CoolMischa) +# 2021-02-18 Add FULL_CHAIN_INCLUDE_ROOT # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -283,6 +284,7 @@ DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" FTP_OPTIONS="" +FULL_CHAIN_INCLUDE_ROOT="false" GETSSL_IGNORE_CP_PRESERVE="false" HTTP_TOKEN_CHECK_WAIT=0 IGNORE_DIRECTORY_DOMAIN="false" @@ -1598,7 +1600,27 @@ get_certificate() { # get certificate for csr, if all domains validated. # tidy up rm -f "$cert_to_check" fi + awk -v CERT_FILE="$gc_certfile" -v CA_CERT="$gc_cafile" 'BEGIN {outfile=CERT_FILE} split_after==1 {outfile=CA_CERT;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > outfile}' "$gc_fullchain" + if [[ "$FULL_CHAIN_INCLUDE_ROOT" = "true" ]]; then + # Some of the code below was copied from zakjan/cert-chain-resolver + + # Download the certificate for the issuer using the "CA Issuers" attribute from the AIA x509 extension + issuer_url=$(openssl x509 -inform pem -noout -text -in "$gc_certfile" | awk 'BEGIN {FS="CA Issuers - URI:"} NF==2 {print $2; exit}') + debug Issuer for "$gc_certfile" is "$issuer_url" + + # Keep downloading issuer certficates until we find the root certificate (which doesn't have a "CA Issuers" attribure) + cp "$gc_certfile" "$gc_fullchain" + while [[ -n "$issuer_url" ]]; do + debug Fetching certificate issuer from "$issuer_url" + issuer_cert=$(curl --user-agent "$CURL_USERAGENT" --silent "$issuer_url" | openssl x509 -inform der -outform pem) + debug Fetched issuer certificate "$(echo "$issuer_cert" | openssl x509 -inform pem -noout -text | awk 'BEGIN {FS="Subject: "} NF==2 {print $2; exit}')" + echo "$issuer_cert" >> "$gc_fullchain" + + # get issuer for the certificate that's just been downloaded + issuer_url=$(echo "$issuer_cert" | openssl x509 -inform pem -noout -text | awk 'BEGIN {FS="CA Issuers - URI:"} NF==2 {print $2; exit}') + done + fi info "Certificate saved in $gc_certfile" fi } @@ -2382,6 +2404,9 @@ write_domain_template() { # write out a template file for a domain. # Production options are: "ISRG Root X1" and "ISRG Root X2" #PREFERRED_CHAIN="" + # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) + #FULL_CHAIN_INCLUDE_ROOT="true" + # 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/ssl/${DOMAIN}.crt" # this is domain cert @@ -2441,6 +2466,9 @@ write_getssl_template() { # write out the main template file # Production options are: "ISRG Root X1" and "ISRG Root X2" #PREFERRED_CHAIN="" + # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) + #FULL_CHAIN_INCLUDE_ROOT="true" + # The command needed to reload apache / nginx or whatever you use. # Several (ssh) commands may be given using a bash array: # RELOAD_CMD=('ssh:sshuserid@server5:systemctl reload httpd' 'logger getssl for server5 efficient.') diff --git a/test/36-full-chain-inc-root.bats b/test/36-full-chain-inc-root.bats new file mode 100644 index 0000000..5932ea7 --- /dev/null +++ b/test/36-full-chain-inc-root.bats @@ -0,0 +1,81 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + if [ -z "$STAGING" ]; then + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + fi +} + + +@test "Use FULL_CHAIN_INCLUDE_ROOT to include the root certificate in the fullchain" { + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +FULL_CHAIN_INCLUDE_ROOT="true" +EOF + + create_certificate + assert_success + check_output_for_errors + + if [ -n "$STAGING" ]; then + PREFERRED_CHAIN="Fake LE Root 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 + PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + fi + + final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + # verify certificate includes the chain root + [ "$PREFERRED_CHAIN" = "$final_issuer" ] +} + + +@test "Use FULL_CHAIN_INCLUDE_ROOT with dual certificates" { + if [ -n "$STAGING" ]; then + PREFERRED_CHAIN="Fake LE Root X1" + fi + + CONFIG_FILE="getssl-dns01.cfg" + setup_environment + init_getssl + + cat <<- EOF > ${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/getssl_test_specific.cfg +FULL_CHAIN_INCLUDE_ROOT="true" +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" +CHECK_REMOTE="false" +EOF + + create_certificate + assert_success + check_output_for_errors + check_certificates + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/chain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" ] + assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] + + if [ -n "$STAGING" ]; then + PREFERRED_CHAIN="Fake LE Root 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 + PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + fi + + # verify both rsa and ecdsa certificates include the chain root + final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + [ "$PREFERRED_CHAIN" = "$final_issuer" ] + ecdsa_final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + [ "$PREFERRED_CHAIN" = "$ecdsa_final_issuer" ] +} From e19afe87af0863c95f570997c537e8bbad3d006a Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 18 Feb 2021 17:14:44 +0000 Subject: [PATCH 317/337] Fixes double slash reported in #585 --- getssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/getssl b/getssl index 80a3fae..88b4d9f 100755 --- a/getssl +++ b/getssl @@ -302,7 +302,7 @@ OCSP_MUST_STAPLE="false" TEMP_UPGRADE_FILE="" TOKEN_USER_ID="" USE_SINGLE_ACL="false" -WORKING_DIR_CANDIDATES=("/etc/getssl/" "${PROGDIR}/conf" "${PROGDIR}/.getssl" "${HOME}/.getssl") +WORKING_DIR_CANDIDATES=("/etc/getssl" "${PROGDIR}/conf" "${PROGDIR}/.getssl" "${HOME}/.getssl") # Variables used when validating using a DNS entry VALIDATE_VIA_DNS="" # Set this to "true" to enable DNS validation From 27af6d9f2f8043e4df47e1df2a23c2bf0c9de7ee Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 19 Feb 2021 15:51:56 +0000 Subject: [PATCH 318/337] Fix test scripts to add to dns once and remove once --- test/17-test-spaces-in-sans-dns01.bats | 19 +++++-------------- test/17-test-spaces-in-sans-http01.bats | 19 +++++-------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/test/17-test-spaces-in-sans-dns01.bats b/test/17-test-spaces-in-sans-dns01.bats index 9f3b3dc..9d425af 100644 --- a/test/17-test-spaces-in-sans-dns01.bats +++ b/test/17-test-spaces-in-sans-dns01.bats @@ -50,11 +50,6 @@ setup() { CONFIG_FILE="getssl-dns01-spaces-sans-and-ignore-dir-domain.cfg" setup_environment - # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a - done - init_getssl create_certificate assert_success @@ -70,10 +65,6 @@ setup() { assert_success check_output_for_errors cleanup_environment - - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a - done } @@ -84,13 +75,13 @@ setup() { CONFIG_FILE="getssl-dns01-spaces-and-commas-sans.cfg" setup_environment - # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a - done - init_getssl create_certificate assert_success check_output_for_errors + cleanup_environment + + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a + done } diff --git a/test/17-test-spaces-in-sans-http01.bats b/test/17-test-spaces-in-sans-http01.bats index fab530f..1730e99 100644 --- a/test/17-test-spaces-in-sans-http01.bats +++ b/test/17-test-spaces-in-sans-http01.bats @@ -50,11 +50,6 @@ setup() { CONFIG_FILE="getssl-http01-spaces-sans-and-ignore-dir-domain.cfg" setup_environment - # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a - done - init_getssl create_certificate assert_success @@ -70,10 +65,6 @@ setup() { assert_success check_output_for_errors cleanup_environment - - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a - done } @@ -84,13 +75,13 @@ setup() { CONFIG_FILE="getssl-http01-spaces-and-commas-sans.cfg" setup_environment - # Add hosts to DNS (also need to be added as aliases in docker-compose.yml) - for prefix in a b c; do - curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a - done - init_getssl create_certificate assert_success check_output_for_errors + cleanup_environment + + for prefix in a b c; do + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'"}' http://10.30.50.3:8055/clear-a + done } From 2863e9e52d0f2c65536a41891fe7be79ede80253 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 19 Feb 2021 15:52:24 +0000 Subject: [PATCH 319/337] Clear previous dns entries before adding a new entry --- test/test_helper.bash | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_helper.bash b/test/test_helper.bash index 11cdf44..48a8d4b 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -64,6 +64,8 @@ setup_environment() { fi if [ -z "$STAGING" ]; then + # Make sure that we have cleared any previous entries, otherwise get random dns failures + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'"}' http://10.30.50.3:8055/clear-a curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a fi cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" From 4f5b518038116405aef6ed554fab6bd54b392e8d Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 20 Feb 2021 11:55:20 +0000 Subject: [PATCH 320/337] Update staging certificate names --- getssl | 12 +++++---- test/35-preferred-chain.bats | 45 +++++++++++++++++++++----------- test/36-full-chain-inc-root.bats | 35 +++++++++++++++++-------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/getssl b/getssl index 88b4d9f..d73faea 100755 --- a/getssl +++ b/getssl @@ -1582,7 +1582,7 @@ get_certificate() { # get certificate for csr, if all domains validated. cp "$gc_fullchain" "$cert_to_check" i=0 while [[ $i -le ${#alternate_links[@]} ]]; do - cert_issuer=$(openssl crl2pkcs7 -nocrl -certfile "$cert_to_check" | openssl pkcs7 -print_certs -text -noout | grep 'Issuer:' | tail -1 | cut -d= -f2) + cert_issuer=$(openssl crl2pkcs7 -nocrl -certfile "$cert_to_check" | openssl pkcs7 -print_certs -text -noout | grep 'Issuer:' | tail -1 | awk -F"CN=" '{ print $2 }') debug Certificate issued by "$cert_issuer" if [[ $cert_issuer = *${PREFERRED_CHAIN}* ]]; then debug "Found required certificate" @@ -2400,9 +2400,10 @@ write_domain_template() { # write out a template file for a domain. #USE_SINGLE_ACL="false" # Preferred Chain - use an different certificate root from the default - # Staging options are: "Fake LE Root X1" and "Fake LE Root X2" + # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters + # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" # Production options are: "ISRG Root X1" and "ISRG Root X2" - #PREFERRED_CHAIN="" + #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) #FULL_CHAIN_INCLUDE_ROOT="true" @@ -2462,9 +2463,10 @@ write_getssl_template() { # write out the main template file #REUSE_PRIVATE_KEY="true" # Preferred Chain - use an different certificate root from the default - # Staging options are: "Fake LE Root X1" and "Fake LE Root X2" + # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters + # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" # Production options are: "ISRG Root X1" and "ISRG Root X2" - #PREFERRED_CHAIN="" + #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) #FULL_CHAIN_INCLUDE_ROOT="true" diff --git a/test/35-preferred-chain.bats b/test/35-preferred-chain.bats index 4389d3b..0a5821c 100644 --- a/test/35-preferred-chain.bats +++ b/test/35-preferred-chain.bats @@ -15,10 +15,12 @@ setup() { @test "Use PREFERRED_CHAIN to select an alternate root" { if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="Fake LE Root X2" + PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" + CHECK_CHAIN="(STAGING) Pretend Pear X1" else - PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | cut -d= -f2) + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN=" '{ print $2 }') PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace + CHECK_CHAIN=$PREFERRED_CHAIN fi CONFIG_FILE="getssl-dns01.cfg" @@ -29,21 +31,27 @@ setup() { PREFERRED_CHAIN="${PREFERRED_CHAIN}" EOF - create_certificate + create_certificate -d assert_success check_output_for_errors - issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | awk -F"CN=" '{ print $2 }') # verify certificate is issued by preferred chain root - [ "$PREFERRED_CHAIN" = "$issuer" ] + if [[ "${CHECK_CHAIN}" != "$issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# issuer=$issuer" + fi + + [ "${CHECK_CHAIN}" = "$issuer" ] } @test "Use PREFERRED_CHAIN to select the default root" { if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="Fake LE Root X1" + PREFERRED_CHAIN="\(STAGING\) Doctored Durian Root CA X3" + CHECK_CHAIN="(STAGING) Doctored Durian Root CA X3" else - PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/0 | openssl x509 -text -noout | grep Issuer: | cut -d= -f2 ) + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/0 | openssl x509 -text -noout | grep Issuer: | awk -F"CN=" '{ print $2 }') PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace fi @@ -59,17 +67,21 @@ EOF assert_success check_output_for_errors - issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | awk -F"CN=" '{ print $2 }') # verify certificate is issued by preferred chain root - [ "$PREFERRED_CHAIN" = "$issuer" ] + if [[ "${CHECK_CHAIN}" != "$issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# issuer=$issuer" + fi + [ "${CHECK_CHAIN}" = "$issuer" ] } @test "Use PREFERRED_CHAIN to select an alternate root by suffix" { if [ -n "$STAGING" ]; then - FULL_PREFERRED_CHAIN="Fake LE Root X2" + FULL_PREFERRED_CHAIN="(STAGING) Pretend Pear X1" else - FULL_PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | cut -d= -f2) + FULL_PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN=" '{ print $2 }') FULL_PREFERRED_CHAIN="${FULL_PREFERRED_CHAIN# }" # remove leading whitespace fi @@ -87,9 +99,12 @@ EOF assert_success check_output_for_errors - issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | cut -d= -f2) + issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Issuer: | tail -1 | awk -F"CN=" '{ print $2 }') # verify certificate is issued by preferred chain root - echo "# ${issuer}" - echo "# ${FULL_PREFERRED_CHAIN}" - [ "$FULL_PREFERRED_CHAIN" = "$issuer" ] + if [[ "${FULL_PREFERRED_CHAIN}" != "$issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# FULL_PREFERRED_CHAIN=$FULL_PREFERRED_CHAIN" + echo "# issuer=$issuer" + fi + [ "${FULL_PREFERRED_CHAIN}" = "$issuer" ] } diff --git a/test/36-full-chain-inc-root.bats b/test/36-full-chain-inc-root.bats index 5932ea7..5b29d0b 100644 --- a/test/36-full-chain-inc-root.bats +++ b/test/36-full-chain-inc-root.bats @@ -27,22 +27,27 @@ EOF check_output_for_errors if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="Fake LE Root X1" + PREFERRED_CHAIN="(STAGING) Doctored Durian Root CA X3" 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 - PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | awk -F"CN=" '{ print $2 }') fi - final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | awk -F"CN=" '{ print $2 }') + # verify certificate includes the chain root - [ "$PREFERRED_CHAIN" = "$final_issuer" ] + if [[ "${PREFERRED_CHAIN}" != "$final_issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# final_issuer=$final_issuer" + fi + [ "${PREFERRED_CHAIN}" = "$final_issuer" ] } @test "Use FULL_CHAIN_INCLUDE_ROOT with dual certificates" { if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="Fake LE Root X1" + PREFERRED_CHAIN="(STAGING) Doctored Durian Root CA X3" fi CONFIG_FILE="getssl-dns01.cfg" @@ -66,16 +71,24 @@ EOF assert [ -e "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.ec.crt" ] if [ -n "$STAGING" ]; then - PREFERRED_CHAIN="Fake LE Root X1" + PREFERRED_CHAIN="(STAGING) Doctored Durian Root CA X3" 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 - PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) + PREFERRED_CHAIN=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/${GETSSL_CMD_HOST}.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | awk -F"CN=" '{ print $2 }') fi # verify both rsa and ecdsa certificates include the chain root - final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) - [ "$PREFERRED_CHAIN" = "$final_issuer" ] - ecdsa_final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | cut -d= -f2) - [ "$PREFERRED_CHAIN" = "$ecdsa_final_issuer" ] + final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | awk -F"CN=" '{ print $2 }') + if [[ "${PREFERRED_CHAIN}" != "$final_issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# final_issuer=$final_issuer" + fi + [ "${PREFERRED_CHAIN}" = "$final_issuer" ] + ecdsa_final_issuer=$(openssl crl2pkcs7 -nocrl -certfile "${INSTALL_DIR}/.getssl/${GETSSL_CMD_HOST}/fullchain.ec.crt" | openssl pkcs7 -print_certs -text -noout | grep Subject: | tail -1 | awk -F"CN=" '{ print $2 }') + if [[ "$PREFERRED_CHAIN" != "$ecdsa_final_issuer" ]]; then + echo "# PREFERRED_CHAIN=$PREFERRED_CHAIN" + echo "# ecdsa_final_issuer=$ecdsa_final_issuer" + fi + [ "${PREFERRED_CHAIN}" = "$ecdsa_final_issuer" ] } From da63cf3ac42b43c18af8e42a982bdc236c80e668 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 20 Feb 2021 15:07:58 +0000 Subject: [PATCH 321/337] Fix awk command for pebble --- test/35-preferred-chain.bats | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/35-preferred-chain.bats b/test/35-preferred-chain.bats index 0a5821c..b16e77a 100644 --- a/test/35-preferred-chain.bats +++ b/test/35-preferred-chain.bats @@ -18,7 +18,7 @@ setup() { PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" CHECK_CHAIN="(STAGING) Pretend Pear X1" else - PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN=" '{ print $2 }') + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN *= *" '{ print $2 }') PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace CHECK_CHAIN=$PREFERRED_CHAIN fi @@ -51,8 +51,9 @@ EOF PREFERRED_CHAIN="\(STAGING\) Doctored Durian Root CA X3" CHECK_CHAIN="(STAGING) Doctored Durian Root CA X3" else - PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/0 | openssl x509 -text -noout | grep Issuer: | awk -F"CN=" '{ print $2 }') + PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/0 | openssl x509 -text -noout | grep Issuer: | awk -F"CN *= *" '{ print $2 }') PREFERRED_CHAIN="${PREFERRED_CHAIN# }" # remove leading whitespace + CHECK_CHAIN=$PREFERRED_CHAIN fi CONFIG_FILE="getssl-dns01.cfg" @@ -81,7 +82,7 @@ EOF if [ -n "$STAGING" ]; then FULL_PREFERRED_CHAIN="(STAGING) Pretend Pear X1" else - FULL_PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN=" '{ print $2 }') + FULL_PREFERRED_CHAIN=$(curl --silent https://pebble:15000/roots/2 | openssl x509 -text -noout | grep "Issuer:" | awk -F"CN *= *" '{ print $2 }') FULL_PREFERRED_CHAIN="${FULL_PREFERRED_CHAIN# }" # remove leading whitespace fi From 8bb211417457bf20e0a964d703a86acb9bf83023 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 20 Feb 2021 15:49:37 +0000 Subject: [PATCH 322/337] Remove debug --- test/35-preferred-chain.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/35-preferred-chain.bats b/test/35-preferred-chain.bats index b16e77a..9c3fc6b 100644 --- a/test/35-preferred-chain.bats +++ b/test/35-preferred-chain.bats @@ -31,7 +31,7 @@ setup() { PREFERRED_CHAIN="${PREFERRED_CHAIN}" EOF - create_certificate -d + create_certificate assert_success check_output_for_errors From 39ddb2da7a116d6499b5aa06ea304e5339a2dd97 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 13 Mar 2021 20:27:07 +0000 Subject: [PATCH 323/337] Tweaked code in requires() so it doesn't break function auto complete in vscode --- getssl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/getssl b/getssl index d73faea..1f1ca7c 100755 --- a/getssl +++ b/getssl @@ -2073,16 +2073,18 @@ revoke_certificate() { # revoke a certificate } requires() { # check if required function is available + args=("${@}") + lastarg=${args[${#args[@]}-1]} if [[ "$#" -gt 1 ]]; then # if more than 1 value, check list for i in "$@"; do - if [[ "$i" == "${!#}" ]]; then # if on last variable then exit as not found + if [[ "$i" == "$lastarg" ]]; then # if on last variable then exit as not found error_exit "this script requires one of: ${*:1:$(($#-1))}" fi res=$(command -v "$i" 2>/dev/null) debug "checking for $i ... $res" if [[ -n "$res" ]]; then # if function found, then set variable to function and return - debug "function $i found at $res - setting ${!#} to $i" - eval "${!#}=\$i" + debug "function $i found at $res - setting ${lastarg} to $i" + eval "${lastarg}=\$i" return fi done From 761e61fdeda1c0fc768c0327e455c5c701ea145e Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 13 Mar 2021 20:27:42 +0000 Subject: [PATCH 324/337] Add dns scripts for cpanel --- dns_scripts/dns_add_cpanel | 76 ++++++++++++++++++++++++++++++++++++++ dns_scripts/dns_del_cpanel | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 dns_scripts/dns_add_cpanel create mode 100644 dns_scripts/dns_del_cpanel diff --git a/dns_scripts/dns_add_cpanel b/dns_scripts/dns_add_cpanel new file mode 100644 index 0000000..24a1ca8 --- /dev/null +++ b/dns_scripts/dns_add_cpanel @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +# Need to add your email address and API key to cpanel below or set as env variables +user=${CPANEL_USERNAME:-''} +password=${CPANEL_PASSWORD:-''} +url=${CPANEL_URL:-''} # e.g. https://www.cpanel-host.test:2083 +apitoken=${CPANEL_APITOKEN:-''} + +fulldomain="${1}" +token="${2}" + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$token" ]]; then + echo "DNS script requires challenge token as second parameter" + exit 1 +fi +if [[ -z "$user" ]]; then + echo "CPANEL_USERNAME (username) parameter not set" + exit 1 +fi +if [[ -z "$apitoken" ]] && [[ -z "$password" ]]; then + echo "Must set either CPANEL_APITOKEN or CPANEL_PASSWORD in dns script, environment variable or getssl.cfg" + exit 1 +fi +if [[ -z "$url" ]]; then + echo "CPANEL_URL (url) parameter not set" + exit 1 +fi + +# Setup +request_func="${url}/json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit" +if [[ -n $apitoken ]]; then + curl_params=( -H "Authorization: cpanel $user:$apitoken" ) +else + auth_string=$(echo -ne "$user:$password" | base64 --wrap 0) + curl_params=( -H "Authorization: Basic $auth_string" ) +fi + +# Check if domain is a CNAME +res=$(dig CNAME "$fulldomain") +domain=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') +if [[ -n "$domain" ]]; then + name=".${fulldomain%.$domain}" +else + domain=$fulldomain + name="" +fi + +# Check to see if challenge dns entry already exists (update or delete?) +request_params="&cpanel_jsonapi_func=fetchzone_records&domain=${domain}&type=TXT&name=_acme-challenge.${fulldomain}." +resp=$(curl --silent "${curl_params[@]}" "$request_func$request_params") +if [[ "$resp" = *\"error\":* ]]; then + echo -n "cpanel fetchzone records failed: " + echo "$resp" | awk -F"error" '{ print $2 }' | awk -F\" '{ print $3 }' + exit 1 +fi + +# If no existing record, create a new TXT record, otherwise edit the existing record +if [[ "$resp" == *\"data\":[]* ]]; then + request_params="&cpanel_jsonapi_func=add_zone_record&domain=$domain&type=TXT&name=_acme-challenge$name&txtdata=$token" +else + # shellcheck disable=SC2001 + line=$(echo "$resp" | sed -e 's/.*line":\([0-9]*\),.*/\1/') + request_params="&cpanel_jsonapi_func=edit_zone_record&domain=$domain&type=TXT&name=_acme-challenge$name&txtdata=${token}&line=${line}" +fi +resp=$(curl --silent "${curl_params[@]}" "$request_func$request_params") + +if [[ "$resp" = *\"status\":0* ]]; then + echo -n "cpanel edit zone record failed: " + echo "$resp" | awk -F"statusmsg" '{ print $2 }' | awk -F\" '{ print $3 }' + exit 1 +fi diff --git a/dns_scripts/dns_del_cpanel b/dns_scripts/dns_del_cpanel new file mode 100644 index 0000000..922151a --- /dev/null +++ b/dns_scripts/dns_del_cpanel @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Need to add your email address and API key to cpanel below or set as env variables +user=${CPANEL_USERNAME:-''} +password=${CPANEL_PASSWORD:-''} +url=${CPANEL_URL:-''} # e.g. https://www.cpanel-host.test:2083 +apitoken=${CPANEL_APITOKEN:-''} + +fulldomain="${1}" + +# Check initial parameters +if [[ -z "$fulldomain" ]]; then + echo "DNS script requires full domain name as first parameter" + exit 1 +fi +if [[ -z "$user" ]]; then + echo "CPANEL_USERNAME (username) parameter not set" + exit 1 +fi +if [[ -z "$apitoken" ]] && [[ -z "$password" ]]; then + echo "Must set either CPANEL_APITOKEN or CPANEL_PASSWORD in dns script, environment variable or getssl.cfg" + exit 1 +fi +if [[ -z "$url" ]]; then + echo "CPANEL_URL (url) parameter not set" + exit 1 +fi + +# Setup +request_func="${url}/json-api/cpanel?cpanel_jsonapi_apiversion=2&cpanel_jsonapi_module=ZoneEdit" +if [[ -n $apitoken ]]; then + curl_params=( -H "Authorization: cpanel $user:$apitoken" ) +else + auth_string=$(echo -ne "$user:$password" | base64 --wrap 0) + curl_params=( -H "Authorization: Basic $auth_string" ) +fi + +# Check if domain is a CNAME +res=$(dig CNAME "$fulldomain") +domain=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') +if [[ -n "$domain" ]]; then + name=".${fulldomain%.$domain}" +else + domain=$fulldomain + name="" +fi + +# Find line number of existing record +request_params="&cpanel_jsonapi_func=fetchzone_records&domain=${domain}&type=TXT&name=_acme-challenge.${fulldomain}." +resp=$(curl --silent "${curl_params[@]}" "$request_func$request_params") +if [[ "$resp" = *\"error\":* ]]; then + echo -n "cpanel fetchzone records failed: " + echo "$resp" | awk -F"error" '{ print $2 }' | awk -F\" '{ print $3 }' + exit 1 +fi + +# shellcheck disable=SC2001 +line=$(echo "$resp" | sed -e 's/.*line":\([0-9]*\),.*/\1/') +if [[ "$line" != "" ]]; then + # Delete the challenge token + request_params="&cpanel_jsonapi_func=remove_zone_record&domain=$domain&type=TXT&name=_acme-challenge$name&line=$line" + resp=$(curl --silent "${curl_params[@]}" "$request_func$request_params") +fi + +if [[ "$resp" = *\"status\":0* ]]; then + echo -n "cpanel remove zone record failed: " + echo "$resp" | awk -F"statusmsg" '{ print $2 }' | awk -F\" '{ print $3 }' + exit 1 +fi From 349210e2d924c445b39f0e7fef8cf9d509e9afff Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Sat, 13 Mar 2021 20:29:52 +0000 Subject: [PATCH 325/337] Document wildcards, PREFERRED_CHAIN and FULL_CHAIN_INCLUDE_ROOT --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cad2775..cb226e3 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ -# getssl +# getssl ![Run all tests](https://github.com/srvrco/getssl/workflows/Run%20all%20tests/badge.svg) ![shellcheck](https://github.com/srvrco/getssl/workflows/shellcheck/badge.svg) Obtain SSL certificates from the letsencrypt.org ACME server. Suitable for automating the process on remote servers. -## Table of Contents - +## Table of Contents - [Features](#features) - [Installation](#installation) - [Overview](#overview) - [Getting started](#getting-started) +- [Wildcard certificates](#wildcard-certificates) - [Automating updates](#automating-updates) - [Structure](#structure) - [Server-Types](#server-types) - [Revoke a certificate](#revoke-a-certificate) - [Elliptic curve keys](#elliptic-curve-keys) +- [Preferred Chain](#preferred-chain) +- [Full chain](#full-chain) - [Issues / problems / help](#issues--problems--help) ## Features @@ -163,9 +165,35 @@ Change the server in your config file to get a fully valid certificate. dns. The certificate can be used (and checked with getssl) on alternate ports. +## Wildcard certificates + +`getssl` supports creating wildcard certificates, i.e. _*.example.com_ which allows a single certificate to be used for any domain under *example.com*, e.g. *www.example.com*, *mail.example.com*. These must be validated using the dns-01 method. + +A *partial* example `getssl.cfg` file is: + +```sh +VALIDATE_VIA_DNS=true +export CPANEL_USERNAME='' +export CPANEL_URL='https://www.cpanel.host:2083' +export CPANEL_APITOKEN='1ABC2DEF3GHI4JKL5MNO6PQR7STU8VWX9YZA' +DNS_ADD_COMMAND=/home/root/getssl/dns_scripts/dns_add_cpanel +DNS_DEL_COMMAND=/home/root/getssl/dns_scripts/dns_del_cpanel +``` + +Create the wildcard certificate (need to use quotes to prevent globbing): + +```sh +getssl "*.example.domain" +``` + +You can renew the certificate using `getssl -a` to renew all configured certificates. + +You can also specify additional domains in the `SANS` line, e.g. `SANS="www.test.example.com"`. +This cannot contain any of the domains which would be covered by the wildcard certificate. + ## Automating updates -I use the following cron +I use the following **cron** job ```cron 23 5 * * * /root/scripts/getssl -u -a -q @@ -353,6 +381,34 @@ key (different of course, don't use the same key for both). prime256v1 secp521r1 (NIST P-521) is included in the code, but not currently supported by Let's Encrypt). +## Preferred Chain + +If a CA offers multiple chains then it is possible to select which chain +is used by using the `PREFERRED_CHAIN` variable in `getssl.cfg` or specifying + `--preferred-chain` in the call to `getssl` + +This uses wildcard matching so requesting "X1" returns the first certificate +returned by the CA which contains the text "X1", Note you may need to escape +any characters which special characters, e.g. +` PREFERRED_CHAIN="\(STAGING\) Doctored Durian Root CA X3"` + +* Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" +* Production options are: "ISRG Root X1" and "ISRG Root X2" + +## Full chain + +Some servers, including those that use Java keystores, will not accept a server certificate if it cannot valid the full chain of signers. + +Specifically, Nutanix Prism (Element and Central) will not accept the `fullchain.crt` until the root CA's certificate has been appended to it manually. + +If your application requires the full chain, i.e. including the +root certificate of the CA, then this can be included in the `fullchain.crt` file by +adding the following line to `getssl.cfg` + +```sh +FULL_CHAIN_INCLUDE_ROOT="true" +``` + ## Issues / problems / help If you have any issues, please log them at From 2dd9e9acd41cd68ef5ab637b369b3dce3738211e Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 16 Mar 2021 12:02:03 +0000 Subject: [PATCH 326/337] Tweak full chain heading --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cb226e3..def44c5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ for automating the process on remote servers. - [Revoke a certificate](#revoke-a-certificate) - [Elliptic curve keys](#elliptic-curve-keys) - [Preferred Chain](#preferred-chain) -- [Full chain](#full-chain) +- [Include Root certificate in full chain](#include-root-certificate-in-full-chain) - [Issues / problems / help](#issues--problems--help) ## Features @@ -395,7 +395,7 @@ any characters which special characters, e.g. * Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" * Production options are: "ISRG Root X1" and "ISRG Root X2" -## Full chain +## Include Root certificate in full chain Some servers, including those that use Java keystores, will not accept a server certificate if it cannot valid the full chain of signers. From 073b33c7f617ded56e3533a9e4b240825c1d0e4b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 16 Mar 2021 12:03:14 +0000 Subject: [PATCH 327/337] Add more info to message for "Certificate on remote domain does not match" --- getssl | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/getssl b/getssl index 1f1ca7c..cebd7f7 100755 --- a/getssl +++ b/getssl @@ -2401,14 +2401,14 @@ write_domain_template() { # write out a template file for a domain. # Set USE_SINGLE_ACL="true" to use a single ACL for all checks #USE_SINGLE_ACL="false" - # Preferred Chain - use an different certificate root from the default - # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters - # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" - # Production options are: "ISRG Root X1" and "ISRG Root X2" - #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" + # Preferred Chain - use an different certificate root from the default + # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters + # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" + # Production options are: "ISRG Root X1" and "ISRG Root X2" + #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" - # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) - #FULL_CHAIN_INCLUDE_ROOT="true" + # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) + #FULL_CHAIN_INCLUDE_ROOT="true" # Location for all your certs, these can either be on the server (full path name) # or using ssh /sftp as for the ACL @@ -2464,14 +2464,14 @@ write_getssl_template() { # write out the main template file PRIVATE_KEY_ALG="rsa" #REUSE_PRIVATE_KEY="true" - # Preferred Chain - use an different certificate root from the default - # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters - # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" - # Production options are: "ISRG Root X1" and "ISRG Root X2" - #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" + # Preferred Chain - use an different certificate root from the default + # This uses wildcard matching so requesting "X1" returns the correct certificate - may need to escape characters + # Staging options are: "(STAGING) Doctored Durian Root CA X3" and "(STAGING) Pretend Pear X1" + # Production options are: "ISRG Root X1" and "ISRG Root X2" + #PREFERRED_CHAIN="\(STAGING\) Pretend Pear X1" - # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) - #FULL_CHAIN_INCLUDE_ROOT="true" + # Uncomment this if you need the full chain file to include the root certificate (Java keystores, Nutanix Prism) + #FULL_CHAIN_INCLUDE_ROOT="true" # The command needed to reload apache / nginx or whatever you use. # Several (ssh) commands may be given using a bash array: @@ -2857,7 +2857,7 @@ if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then else # check if the certificate is for the right domain EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -text \ - | sed -n -e 's/^ *Subject: .* CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ + | sed -n -e 's/^ *Subject: .*CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ | sort -u | grep "^$DOMAIN\$") if [[ "$EX_CERT_DOMAIN" == "$DOMAIN" ]]; then # check renew-date on ex_cert and compare to local ( if local exists) @@ -2901,7 +2901,11 @@ if [[ "${CHECK_REMOTE}" == "true" ]] && [[ $_FORCE_RENEW -eq 0 ]]; then reload_service fi else - info "${DOMAIN}: Certificate on remote domain does not match, ignoring remote certificate" + # Get the domain from the existing certificate for the error message + EX_CERT_DOMAIN=$(echo "$EX_CERT" | openssl x509 -text \ + | sed -n -e 's/^ *Subject: .*CN=\([A-Za-z0-9.-]*\).*$/\1/p; /^ *DNS:.../ { s/ *DNS://g; y/,/\n/; p; }' \ + | sort -u | head -1) + info "${DOMAIN}: Certificate on remote domain does not match, ignoring remote certificate ($EX_CERT_DOMAIN != $real_d)" fi fi else From 8d24863cb940ae523491cf25dd5b5f483fc4912c Mon Sep 17 00:00:00 2001 From: Maciej Modzelewski Date: Fri, 19 Mar 2021 06:21:36 +0100 Subject: [PATCH 328/337] Update GoDaddy DNS scripts --- dns_scripts/dns_add_godaddy | 2 +- dns_scripts/dns_godaddy | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dns_scripts/dns_add_godaddy b/dns_scripts/dns_add_godaddy index dfd3b3b..835bb22 100755 --- a/dns_scripts/dns_add_godaddy +++ b/dns_scripts/dns_add_godaddy @@ -37,4 +37,4 @@ fi export GODADDY_KEY export GODADDY_SECRET -$GODADDY_SCRIPT -q add "${fulldomain}" "_acme-challenge.${fulldomain}." "${token}" +$GODADDY_SCRIPT -q add "${fulldomain}" "_acme-challenge" "${token}" diff --git a/dns_scripts/dns_godaddy b/dns_scripts/dns_godaddy index 4443bd4..7c36d19 100644 --- a/dns_scripts/dns_godaddy +++ b/dns_scripts/dns_godaddy @@ -155,7 +155,6 @@ if [ -z "$name" ]; then echo "'name' parameter is required, see -h" >&2 exit 3 fi -! [[ "$name" =~ [.]$ ]] && name="${name}.${domain}." data="$4" if [ -z "$data" ]; then echo "'data' parameter is required, see -h" >&2 @@ -209,7 +208,7 @@ if [ "$op" = "add" ]; then url="$API/$domain/records/TXT/$name" - request='{"data":"'$data'","ttl":'$ttl'}' + request='[{"data":"'$data'","ttl":'$ttl'}]' [ -n "$DEBUG" ] && cat >&2 < Date: Tue, 23 Mar 2021 12:29:16 +0800 Subject: [PATCH 329/337] Create Aliyun DNS Scripts Founded in 2009, Aliyun is a leading cloud computing and artificial intelligence technology company in China --- dns_scripts/dns_add_del_aliyun.sh | 182 ++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 dns_scripts/dns_add_del_aliyun.sh diff --git a/dns_scripts/dns_add_del_aliyun.sh b/dns_scripts/dns_add_del_aliyun.sh new file mode 100644 index 0000000..de324e0 --- /dev/null +++ b/dns_scripts/dns_add_del_aliyun.sh @@ -0,0 +1,182 @@ +#!/bin/bash +#https://blog.aymar.cn +#https://protocol.aymar.cn +PROGNAME=${0##*/} +VERSION="2021年3月22日 16:07:05" +Ali_API="https://dns.aliyuncs.com/" +_timestamp=$(date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ") +__debug="0" +__delete="0" + +#Wildcard certificates +#A partial example getssl.cfg file is: +#VALIDATE_VIA_DNS=true +#DNS_ADD_COMMAND=/root/.getssl/dns_add_del_aliyun.sh +#DNS_DEL_COMMAND=/root/.getssl/dns_add_del_aliyun.sh + +# either configure KeyId & KeySecret here or export environment variables in getssl.cfg +AccessKeyId=${ALI_KeyId:-''} +AccessKeySecret=${ALI_KeySecret:-''} + +usage() { # print out the program usage + echo "Usage: $PROGNAME [-a|--add ] [-d|--delete ] [-s|--search ] [-h|--help] [-t|--type] "\ + "[-q|--quiet] [-c|--check] [-S|--status] [-l|--lock #] [-T|--ttl] [-u|--update] [-w|--weight] [-L|--Line]" +} + +help_message() { # print out the help message + cat <<- _EOF_ + $PROGNAME Version. $VERSION + $(usage) + + Options: + -a, --add Add Domain Record 域名 ip (默认类型TXT) + -d, --delete Delete Domain Record 域名 (默认类型TXT) + -s, --search Search Domain Record 域名 + -t, --type Record Type 类型(A、MX、CNAME、TXT、REDIRECT_URL、FORWORD_URL、NS、AAAA、SRV) + _EOF_ +} + +_arg_check(){ + [ -z "$1" ] || _arg_count=$1 + shift + [ ${#} -lt $_arg_count ] && help_message && exit 1 || (echo $2 | grep "^-") && help_message && exit 1 + #If the number of arguments <$_ARG_COUNT print help and exit, and if the second argument begins with “-” print help and exit + return 0 +} + +#[ ${#} -lt 2 ] && help_message && exit 1 #Same as below +#[ -z "$2" ] && help_message && exit 1 #Same as below +_arg_check 2 $@ + +_debug (){ + if [ "$__debug" -eq 1 ]; then + echo -e "\033[1;31m # debug: $(date "+%m %d %T") | Func: ${FUNCNAME[@]} | Line:${BASH_LINENO[@]} \033[0m" "\n $@ " #"Current FUNCNAME ${FUNCNAME} #$LINENO " #"$(($RANDOM%10))" + fi + return 0 +} + +_requires() { + _cmds='' # Check if the commands exists + if [[ "$#" -gt 0 ]]; then + for i in "$@"; do + if eval type type >/dev/null 2>&1; then + eval type "$i" >/dev/null 2>&1 + elif command >/dev/null 2>&1; then + command -v "$i" >/dev/null 2>&1 + else + which "$i" >/dev/null 2>&1 + fi + #[ "$?" -eq 0 ] && _debug "checking for $i exists = ok" || _cmds=$_cmds"$i: " + if [ "$?" -eq 0 ]; then + #_debug "checking for $i exists = ok" + continue + else + _cmds=$_cmds"$i: " + fi + done + else + echo "Usage: _requires [command] " + return 1 + fi + [ -n "$_cmds" ] && { echo -e "\033[1;31m $_cmds command not found \033[0m" && return 1 ;} || return 0 +} + +_requires openssl + +_hex_dump() { #ascii hex + local _str='' + [ $# -gt 0 ] && _str=$@ || read _str + local _str_len=${#_str} + local i=1 + while [ "$i" -le "$_str_len" ]; do + local _str_c="$(printf "%s" "$_str" | cut -c "$i")" + printf " %02x" "'$_str_c" + i=$(($i + 1)) + done + #printf "%s" " 0a" +} + +_urlencode() { + local length="${#1}" + local i='' + for i in $(awk "BEGIN { for ( i=0; i<$length; i++ ) print i }") + do + #local _strc="$(printf "%s" "$1" | cut -c "$i")" #i=1; i<=$length; i++ + local _strc="${1:$i:1}" + case $_strc in [a-zA-Z0-9.~_-]) printf "%s" "$_strc" ;; *) printf "%%%02X" "'$_strc" ;; + esac + done +} + +_signature(){ + signature='' + _hexkey=$(printf "%s" "$AccessKeySecret&" | _hex_dump |sed 's/ //g') + #signature=$(printf "%s" "GET&%2F&$(_urlencode "$query")" | openssl dgst -sha1 -hmac $(printf "%s" "$AccessKeySecret&" | _hex_dump |sed 's/ //g'| xxd -r -p ) -binary | openssl base64 -e) + signature=$(printf "%s" "GET&%2F&$(_urlencode "$query")" | openssl dgst -sha1 -mac HMAC -macopt "hexkey:$_hexkey" -binary | openssl base64 -e) + signature=$(_urlencode "$signature") + } + +_query() { + [ -n "$__type" ] && { [[ "$_Action" = "AddDomainRecord" ]] && _Type="$__type" || { [ "$_Action" = "DescribeDomainRecords" ] && _TypeKeyWord="$__type"; } ; } + query='' + [ -n $AccessKeyId ] && query=$query'AccessKeyId='$AccessKeyId + query=$query'&Action='"$1" + [ -z $_DomainNames ] || query=$query'&DomainName='$_DomainNames + query=$query'&Format=json' + [ -z $_RR ] || query=$query'&RR='$_RR + [ -z $_RRKeyWord ] || query=$query'&RRKeyWord='$_RRKeyWord + [ -z $_RecordId ] || query=$query'&RecordId='$_RecordId + query=$query'&SignatureMethod=HMAC-SHA1' + query=$query"&SignatureNonce=$(date +"%s%N")" + query=$query'&SignatureVersion=1.0' + query=$query'&Timestamp='$_timestamp + [ -z $_Type ] || query=$query'&Type='$_Type + [ -z $_TypeKeyWord ] || query=$query'&TypeKeyWord='$_TypeKeyWord + [ -z $_Value ] || query=$query'&Value='$_Value + [ -z $_ValueKeyWord ] || query=$query'&ValueKeyWord='$_ValueKeyWord + query=$query'&Version=2015-01-09' + #_debug "$query" + _signature + return 0 +} + +_Get_RecordIds(){ + _Action="DescribeDomainRecords" + _query $_Action $_DomainNames + url="${Ali_API}?${query}&Signature=${signature}" + _debug $url + _RecordIds=$(curl -k -s $url | grep -Po 'RecordId[": "]+\K[^"]+') && __delete="1" #RecordId requisite + _debug $_RecordIds + return 0 +} + +__type='TXT' +_DomainNames=$(printf "%s" $1| awk -F"." '{if(NF>=2){print $(NF-1)"."$NF}}') #awk -F\. '{print $(NF-1) FS $NF}') #requisite +_RRKeyWord="_acme-challenge" +#_ValueKeyWord=$2 +_Get_RecordIds +_RRKeyWord='' +_TypeKeyWord='' +_ValueKeyWord='' + +if [ "$__delete" = "1" ];then + _Action="DeleteDomainRecord" #Action requisite + _DomainNames='' + for _RecordId in ${_RecordIds[@]} #Delete multiple txt domain record + do + _debug "_RecordId" $_RecordId + _query $_Action $_RecordId + url="${Ali_API}?${query}&Signature=${signature}" + _debug $url + curl -k -s $url && ( echo -e "\n\033[1;32m Aliyun DNS record _acme-challenge.$1 has been deleted \033[0m") + done +else +_Action="AddDomainRecord" #requisite +_RR=$(printf "_acme-challenge.%s" $1| awk -F'.' '{if(NF>2){gsub("."$(NF-1)"."$NF,"");print}}') #requisite +_Value=$2 #requisite +_query $_Action $_DomainNames +url="${Ali_API}?${query}&Signature=${signature}" +_debug $url +curl -k -s $url && (echo -e "\n\033[1;32m Start Checking aliyun DNS record _acme-challenge.$1 \033[0m") +exit 0 +fi \ No newline at end of file From efbe5ce7d366d50ed310186f02aa32eab8200849 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Tue, 23 Mar 2021 20:30:16 +0000 Subject: [PATCH 330/337] ignore some shellcheck warnings indentation / whitespace changes --- dns_scripts/dns_add_del_aliyun.sh | 125 +++++++++++++++--------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/dns_scripts/dns_add_del_aliyun.sh b/dns_scripts/dns_add_del_aliyun.sh index de324e0..d6d9461 100644 --- a/dns_scripts/dns_add_del_aliyun.sh +++ b/dns_scripts/dns_add_del_aliyun.sh @@ -8,7 +8,7 @@ _timestamp=$(date -u +"%Y-%m-%dT%H%%3A%M%%3A%SZ") __debug="0" __delete="0" -#Wildcard certificates +#Wildcard certificates #A partial example getssl.cfg file is: #VALIDATE_VIA_DNS=true #DNS_ADD_COMMAND=/root/.getssl/dns_add_del_aliyun.sh @@ -20,7 +20,7 @@ AccessKeySecret=${ALI_KeySecret:-''} usage() { # print out the program usage echo "Usage: $PROGNAME [-a|--add ] [-d|--delete ] [-s|--search ] [-h|--help] [-t|--type] "\ - "[-q|--quiet] [-c|--check] [-S|--status] [-l|--lock #] [-T|--ttl] [-u|--update] [-w|--weight] [-L|--Line]" + "[-q|--quiet] [-c|--check] [-S|--status] [-l|--lock #] [-T|--ttl] [-u|--update] [-w|--weight] [-L|--Line]" } help_message() { # print out the help message @@ -38,7 +38,7 @@ help_message() { # print out the help message _arg_check(){ [ -z "$1" ] || _arg_count=$1 - shift + shift [ ${#} -lt $_arg_count ] && help_message && exit 1 || (echo $2 | grep "^-") && help_message && exit 1 #If the number of arguments <$_ARG_COUNT print help and exit, and if the second argument begins with “-” print help and exit return 0 @@ -50,39 +50,41 @@ _arg_check 2 $@ _debug (){ if [ "$__debug" -eq 1 ]; then - echo -e "\033[1;31m # debug: $(date "+%m %d %T") | Func: ${FUNCNAME[@]} | Line:${BASH_LINENO[@]} \033[0m" "\n $@ " #"Current FUNCNAME ${FUNCNAME} #$LINENO " #"$(($RANDOM%10))" + echo -e "\033[1;31m # debug: $(date "+%m %d %T") | Func: ${FUNCNAME[@]} | Line:${BASH_LINENO[@]} \033[0m" "\n $@ " #"Current FUNCNAME ${FUNCNAME} #$LINENO " #"$(($RANDOM%10))" fi return 0 } -_requires() { +_requires() { _cmds='' # Check if the commands exists if [[ "$#" -gt 0 ]]; then for i in "$@"; do - if eval type type >/dev/null 2>&1; then - eval type "$i" >/dev/null 2>&1 - elif command >/dev/null 2>&1; then - command -v "$i" >/dev/null 2>&1 - else - which "$i" >/dev/null 2>&1 - fi - #[ "$?" -eq 0 ] && _debug "checking for $i exists = ok" || _cmds=$_cmds"$i: " - if [ "$?" -eq 0 ]; then - #_debug "checking for $i exists = ok" - continue - else - _cmds=$_cmds"$i: " - fi + if eval type type >/dev/null 2>&1; then + eval type "$i" >/dev/null 2>&1 + elif command >/dev/null 2>&1; then + command -v "$i" >/dev/null 2>&1 + else + which "$i" >/dev/null 2>&1 + fi + #[ "$?" -eq 0 ] && _debug "checking for $i exists = ok" || _cmds=$_cmds"$i: " + #shellcheck disable=SC2181 + if [ "$?" -eq 0 ]; then + #_debug "checking for $i exists = ok" + continue + else + _cmds=$_cmds"$i: " + fi done - else - echo "Usage: _requires [command] " - return 1 + else + echo "Usage: _requires [command] " + return 1 fi - [ -n "$_cmds" ] && { echo -e "\033[1;31m $_cmds command not found \033[0m" && return 1 ;} || return 0 + [ -n "$_cmds" ] && { echo -e "\033[1;31m $_cmds command not found \033[0m" && return 1 ;} || return 0 } _requires openssl +#shellcheck disable=SC2120 _hex_dump() { #ascii hex local _str='' [ $# -gt 0 ] && _str=$@ || read _str @@ -99,12 +101,12 @@ _hex_dump() { #ascii hex _urlencode() { local length="${#1}" local i='' - for i in $(awk "BEGIN { for ( i=0; i<$length; i++ ) print i }") - do - #local _strc="$(printf "%s" "$1" | cut -c "$i")" #i=1; i<=$length; i++ - local _strc="${1:$i:1}" - case $_strc in [a-zA-Z0-9.~_-]) printf "%s" "$_strc" ;; *) printf "%%%02X" "'$_strc" ;; - esac + for i in $(awk "BEGIN { for ( i=0; i<$length; i++ ) print i }") + do + #local _strc="$(printf "%s" "$1" | cut -c "$i")" #i=1; i<=$length; i++ + local _strc="${1:$i:1}" + case $_strc in [a-zA-Z0-9.~_-]) printf "%s" "$_strc" ;; *) printf "%%%02X" "'$_strc" ;; + esac done } @@ -114,7 +116,7 @@ _signature(){ #signature=$(printf "%s" "GET&%2F&$(_urlencode "$query")" | openssl dgst -sha1 -hmac $(printf "%s" "$AccessKeySecret&" | _hex_dump |sed 's/ //g'| xxd -r -p ) -binary | openssl base64 -e) signature=$(printf "%s" "GET&%2F&$(_urlencode "$query")" | openssl dgst -sha1 -mac HMAC -macopt "hexkey:$_hexkey" -binary | openssl base64 -e) signature=$(_urlencode "$signature") - } +} _query() { [ -n "$__type" ] && { [[ "$_Action" = "AddDomainRecord" ]] && _Type="$__type" || { [ "$_Action" = "DescribeDomainRecords" ] && _TypeKeyWord="$__type"; } ; } @@ -138,45 +140,46 @@ _query() { #_debug "$query" _signature return 0 -} - -_Get_RecordIds(){ - _Action="DescribeDomainRecords" - _query $_Action $_DomainNames - url="${Ali_API}?${query}&Signature=${signature}" - _debug $url - _RecordIds=$(curl -k -s $url | grep -Po 'RecordId[": "]+\K[^"]+') && __delete="1" #RecordId requisite - _debug $_RecordIds - return 0 +} + +_Get_RecordIds(){ + _Action="DescribeDomainRecords" + _query $_Action $_DomainNames + url="${Ali_API}?${query}&Signature=${signature}" + _debug $url + _RecordIds=$(curl -k -s $url | grep -Po 'RecordId[": "]+\K[^"]+') && __delete="1" #RecordId requisite + _debug $_RecordIds + return 0 } __type='TXT' _DomainNames=$(printf "%s" $1| awk -F"." '{if(NF>=2){print $(NF-1)"."$NF}}') #awk -F\. '{print $(NF-1) FS $NF}') #requisite _RRKeyWord="_acme-challenge" -#_ValueKeyWord=$2 + _Get_RecordIds + _RRKeyWord='' -_TypeKeyWord='' +_TypeKeyWord='' _ValueKeyWord='' if [ "$__delete" = "1" ];then - _Action="DeleteDomainRecord" #Action requisite - _DomainNames='' - for _RecordId in ${_RecordIds[@]} #Delete multiple txt domain record - do - _debug "_RecordId" $_RecordId - _query $_Action $_RecordId - url="${Ali_API}?${query}&Signature=${signature}" - _debug $url - curl -k -s $url && ( echo -e "\n\033[1;32m Aliyun DNS record _acme-challenge.$1 has been deleted \033[0m") - done + _Action="DeleteDomainRecord" #Action requisite + _DomainNames='' + for _RecordId in ${_RecordIds[@]} #Delete multiple txt domain record + do + _debug "_RecordId" $_RecordId + _query $_Action $_RecordId + url="${Ali_API}?${query}&Signature=${signature}" + _debug $url + curl -k -s $url && ( echo -e "\n\033[1;32m Aliyun DNS record _acme-challenge.$1 has been deleted \033[0m") + done else -_Action="AddDomainRecord" #requisite -_RR=$(printf "_acme-challenge.%s" $1| awk -F'.' '{if(NF>2){gsub("."$(NF-1)"."$NF,"");print}}') #requisite -_Value=$2 #requisite -_query $_Action $_DomainNames -url="${Ali_API}?${query}&Signature=${signature}" -_debug $url -curl -k -s $url && (echo -e "\n\033[1;32m Start Checking aliyun DNS record _acme-challenge.$1 \033[0m") -exit 0 -fi \ No newline at end of file + _Action="AddDomainRecord" #requisite + _RR=$(printf "_acme-challenge.%s" $1| awk -F'.' '{if(NF>2){gsub("."$(NF-1)"."$NF,"");print}}') #requisite + _Value=$2 #requisite + _query $_Action $_DomainNames + url="${Ali_API}?${query}&Signature=${signature}" + _debug $url + curl -k -s $url && (echo -e "\n\033[1;32m Start Checking aliyun DNS record _acme-challenge.$1 \033[0m") + exit 0 +fi From 3eb7ba35406c9759d9467e8d662327adfb34d565 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 25 Mar 2021 17:23:48 +0100 Subject: [PATCH 331/337] Split DNS add and remove into separate functions --- getssl | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/getssl b/getssl index cebd7f7..ff97639 100755 --- a/getssl +++ b/getssl @@ -573,20 +573,18 @@ check_challenge_completion_dns() { # perform validation via DNS challenge ntries=$(( ntries + 1 )) if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then - debug "Retrying adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" - test_output "Retrying adding dns via command: $DNS_ADD_COMMAND" - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" - if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then - error_exit "DNS_ADD_COMMAND failed for domain $d" - fi - + test_output "Deleting DNS RR via command: ${DNS_DEL_COMMAND}" + del_dns_rr "${lower_d}" "${auth_key}" + test_output "Retrying adding DNS via command: ${DNS_ADD_COMMAND}" + add_dns_rr "${lower_d}" "${auth_key}" \ + || error_exit "DNS_ADD_COMMAND failed for domain ${d}" fi info "checking DNS at ${ns} for ${lower_d}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ "waiting $DNS_WAIT secs before checking again" sleep $DNS_WAIT else debug "dns check failed - removing existing value" - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" + del_dns_rr "${lower_d}" "${auth_key}" error_exit "checking _acme-challenge.${lower_d} gave $check_result not $auth_key" fi @@ -601,10 +599,7 @@ check_challenge_completion_dns() { # perform validation via DNS challenge check_challenge_completion "$uri" "$d" "$keyauthorization" - debug "remove DNS entry" - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "${d##\*.}" | tr A-Z a-z) - eval "$DNS_DEL_COMMAND" "$lower_d" "$auth_key" + del_dns_rr "${d}" "${auth_key}" } # end of ... perform validation if via DNS challenge @@ -807,7 +802,7 @@ clean_up() { # Perform pre-exit housekeeping # shellcheck source=/dev/null . "$dnsfile" debug "attempting to clean up DNS entry for $d" - eval "$DNS_DEL_COMMAND" "${d##\*.}" "$auth_key" + del_dns_rr "${d}" "${auth_key}" done shopt -u nullglob fi @@ -1171,6 +1166,26 @@ find_ftp_command() { } +add_dns_rr() { + d=${1} + auth_key=${2} + + # shellcheck disable=SC2018,SC2019 + lower_d=$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z') + debug "adding DNS RR via command: ${DNS_ADD_COMMAND} ${lower_d} ${auth_key}" + eval "${DNS_ADD_COMMAND}" "${lower_d}" "${auth_key}" +} + +del_dns_rr() { + d=${1} + auth_key=${2} + + # shellcheck disable=SC2018,SC2019 + lower_d=$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z') + debug "removing DNS RR via command: ${DNS_DEL_COMMAND} ${lower_d} ${auth_key}" + eval "${DNS_DEL_COMMAND}" "${lower_d}" "${auth_key}" +} + fulfill_challenges() { dn=0 for d in "${alldomains[@]}"; do @@ -1236,12 +1251,8 @@ for d in "${alldomains[@]}"; do | sed -e 's:=*$::g' -e 'y:+/:-_:') debug auth_key "$auth_key" - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "${d##\*.}" | tr A-Z a-z) - debug "adding dns via command: $DNS_ADD_COMMAND $lower_d $auth_key" - if ! eval "$DNS_ADD_COMMAND" "$lower_d" "$auth_key" ; then - error_exit "DNS_ADD_COMMAND failed for domain $d" - fi + add_dns_rr "${d}" "${auth_key}" \ + || error_exit "DNS_ADD_COMMAND failed for domain $d" # find a primary / authoritative DNS server for the domain if [[ -z "$AUTH_DNS_SERVER" ]]; then From 87ae2500d970cc6dd60b22903b6169760909f47d Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 25 Mar 2021 17:44:01 +0100 Subject: [PATCH 332/337] Fix DNS challenge completion check if CNAMEs on different NS are used --- getssl | 65 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/getssl b/getssl index ff97639..e485737 100755 --- a/getssl +++ b/getssl @@ -518,48 +518,42 @@ check_challenge_completion() { # checks with the ACME server if our challenge is } check_challenge_completion_dns() { # perform validation via DNS challenge - token=$1 - uri=$2 - keyauthorization=$3 - d=$4 - primary_ns=$5 - auth_key=$6 - - # Always use lowercase domain name when querying DNS servers - # shellcheck disable=SC2018,SC2019 - lower_d=$(echo "${d##\*.}" | tr A-Z a-z) + d=${1} + rr=${2} + primary_ns=${3} + auth_key=${4} # check for token at public dns server, waiting for a valid response. for ns in $primary_ns; do - info "checking dns at $ns" + info "checking DNS at $ns" ntries=0 check_dns="fail" while [[ "$check_dns" == "fail" ]]; do if [[ "$os" == "cygwin" ]]; then - check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ + check_result=$(nslookup -type=txt "${rr}" "${ns}" \ | grep ^_acme -A2\ | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${lower_d}" "@${ns}" - check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${lower_d}" "@${ns}" \ - | grep -i "^_acme-challenge.${lower_d}" \ + debug "$DNS_CHECK_FUNC" TXT "${rr}" "@${ns}" + check_result=$($DNS_CHECK_FUNC TXT "${rr}" "@${ns}" \ + | grep -i "^${rr}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') debug "check_result=$check_result" if [[ -z "$check_result" ]]; then - debug "$DNS_CHECK_FUNC" ANY "_acme-challenge.${lower_d}" "@${ns}" - check_result=$($DNS_CHECK_FUNC ANY "_acme-challenge.${lower_d}" "@${ns}" \ - | grep -i "^_acme-challenge.${lower_d}" \ + debug "$DNS_CHECK_FUNC" ANY "${rr}" "@${ns}" + check_result=$($DNS_CHECK_FUNC ANY "${rr}" "@${ns}" \ + | grep -i "^${rr}" \ | grep 'IN\WTXT'|awk -F'"' '{ print $2}') debug "check_result=$check_result" fi elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then - check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${lower_d}" "${ns}" \ + check_result=$($DNS_CHECK_FUNC -t TXT "${rr}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') else - check_result=$(nslookup -type=txt "_acme-challenge.${lower_d}" "${ns}" \ + check_result=$(nslookup -type=txt "${rr}" "${ns}" \ | grep 'text ='|awk -F'"' '{ print $2}') if [[ -z "$check_result" ]]; then - check_result=$(nslookup -type=any "_acme-challenge.${lower_d}" "${ns}" \ + check_result=$(nslookup -type=any "${rr}" "${ns}" \ | grep 'text ='|awk -F'"' '{ print $2}') fi fi @@ -574,19 +568,19 @@ check_challenge_completion_dns() { # perform validation via DNS challenge if [[ $DNS_WAIT_RETRY_ADD == "true" && $(( ntries % 10 )) == 0 ]]; then test_output "Deleting DNS RR via command: ${DNS_DEL_COMMAND}" - del_dns_rr "${lower_d}" "${auth_key}" + del_dns_rr "${d}" "${auth_key}" test_output "Retrying adding DNS via command: ${DNS_ADD_COMMAND}" - add_dns_rr "${lower_d}" "${auth_key}" \ + add_dns_rr "${d}" "${auth_key}" \ || error_exit "DNS_ADD_COMMAND failed for domain ${d}" fi - info "checking DNS at ${ns} for ${lower_d}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ + info "checking DNS at ${ns} for ${rr}. Attempt $ntries/${DNS_WAIT_COUNT} gave wrong result, "\ "waiting $DNS_WAIT secs before checking again" sleep $DNS_WAIT else debug "dns check failed - removing existing value" - del_dns_rr "${lower_d}" "${auth_key}" + del_dns_rr "${d}" "${auth_key}" - error_exit "checking _acme-challenge.${lower_d} gave $check_result not $auth_key" + error_exit "checking ${rr} gave $check_result not $auth_key" fi fi done @@ -596,10 +590,6 @@ check_challenge_completion_dns() { # perform validation via DNS challenge info "sleeping $DNS_EXTRA_WAIT seconds before asking the ACME server to check the dns" sleep "$DNS_EXTRA_WAIT" fi - - check_challenge_completion "$uri" "$d" "$keyauthorization" - - del_dns_rr "${d}" "${auth_key}" } # end of ... perform validation if via DNS challenge @@ -1256,7 +1246,12 @@ for d in "${alldomains[@]}"; do # find a primary / authoritative DNS server for the domain if [[ -z "$AUTH_DNS_SERVER" ]]; then - get_auth_dns "$d" + # shellcheck disable=SC2018,SC2019 + rr="_acme-challenge.$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z')" + get_auth_dns "${rr}" + if test -n "${cname}"; then + rr=${cname} + fi elif [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then primary_ns="$AUTH_DNS_SERVER $PUBLIC_DNS_SERVER" else @@ -1264,7 +1259,13 @@ for d in "${alldomains[@]}"; do fi debug set primary_ns = "$primary_ns" - check_challenge_completion_dns "${token}" "${uri}" "${keyauthorization}" "${d}" "${primary_ns}" "${auth_key}" + # internal check + check_challenge_completion_dns "${d}" "${rr}" "${primary_ns}" "${auth_key}" + + # let Let's Encrypt check + check_challenge_completion "${uri}" "${d}" "${keyauthorization}" + + del_dns_rr "${d}" "${auth_key}" else # set up the correct http token for verification if [[ $API -eq 1 ]]; then # get the token from the http component From 20fc8affa6e926a82d1eceb1750ab0a810344370 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 25 Mar 2021 18:53:19 +0100 Subject: [PATCH 333/337] Resolve CNAMEs before searching for authoritative DNS server --- getssl | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/getssl b/getssl index e485737..8de8353 100755 --- a/getssl +++ b/getssl @@ -1378,6 +1378,27 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n gad_s="@$gad_s" fi + # Check if domain is a CNAME, first + test_output "Using $HAS_DIG_OR_DRILL CNAME" + + # Two options here; either dig CNAME will return the CNAME and the NS or just the CNAME + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d $gad_s" + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep "^$gad_d") + cname=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') + + if [[ $_TEST_SKIP_CNAME_CALL == 0 ]]; then + debug Checking if CNAME result contains NS records + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep -E "IN\W(NS|SOA)\W") + else + res= + fi + + if [[ -n "${cname}" ]]; then + # domain is a CNAME: resolve it and continue with that + debug Domain is a CNAME, actual domain is "$cname" + gad_d=${cname} + fi + # Use SOA +trace to find the name server if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then if [[ "$HAS_DIG_OR_DRILL" == "drill" ]]; then @@ -1391,27 +1412,6 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi fi - # Check if domain is a CNAME - if [[ -z "$res" ]]; then - test_output "Using $HAS_DIG_OR_DRILL CNAME" - - # Two options here; either dig CNAME will return the CNAME and the NS or just the CNAME - debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d $gad_s" - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep "^$gad_d") - cname=$(echo "$res"| awk '$4 ~ "CNAME" {print $5}' |sed 's/\.$//g') - - if [[ $_TEST_SKIP_CNAME_CALL == 0 ]]; then - debug Checking if CNAME result contains NS records - res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" $gad_s| grep -E "IN\W(NS|SOA)\W") - else - res="" - fi - - if [[ -n "$cname" ]]; then # domain is a CNAME so get main domain - debug Domain is a CNAME, actual domain is "$cname" - fi - fi - # Query for NS records if [[ -z "$res" ]]; then test_output "Using $HAS_DIG_OR_DRILL NS" From 4c3be42e97b288697c7e041cac22b1ab46fdc681 Mon Sep 17 00:00:00 2001 From: mokaton Date: Fri, 26 Mar 2021 22:12:42 +0300 Subject: [PATCH 334/337] DeprecationWarning: please use dns.resolver.Resolver.resolve() Fixed issue with deprecated function query for dns.resolver.Resolver --- dns_scripts/dns_route53.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns_scripts/dns_route53.py b/dns_scripts/dns_route53.py index f2011bf..6b88b37 100755 --- a/dns_scripts/dns_route53.py +++ b/dns_scripts/dns_route53.py @@ -70,7 +70,7 @@ if action == 'UPSERT': try: my_resolver = dns.resolver.Resolver(configure=False) my_resolver.nameservers = ['8.8.8.8', '8.8.4.4'] - results = my_resolver.query(challenge_fqdn, 'TXT') + results = my_resolver.resolve(challenge_fqdn, 'TXT') data = str(results.response.answer[0][0]).strip('\"') if data == challenge: print("found {f} entry".format(f=challenge_fqdn)) From 4846512269497a166aa9648b4e30bc277b8cdf9b Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 29 Mar 2021 12:56:48 +0100 Subject: [PATCH 335/337] Tweaks so non-cname domains work, fix broken retry dns add test --- getssl | 13 ++++++++++--- test/18-retry-dns-add.bats | 5 +++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/getssl b/getssl index 8de8353..1cf1454 100755 --- a/getssl +++ b/getssl @@ -1244,14 +1244,21 @@ for d in "${alldomains[@]}"; do add_dns_rr "${d}" "${auth_key}" \ || error_exit "DNS_ADD_COMMAND failed for domain $d" + # shellcheck disable=SC2018,SC2019 + rr="_acme-challenge.$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z')" + # find a primary / authoritative DNS server for the domain if [[ -z "$AUTH_DNS_SERVER" ]]; then - # shellcheck disable=SC2018,SC2019 - rr="_acme-challenge.$(printf '%s' "${d#\*.}" | tr 'A-Z' 'a-z')" + # Find authorative dns server for _acme-challenge.{domain} (for CNAMES/acme-dns) get_auth_dns "${rr}" if test -n "${cname}"; then rr=${cname} fi + + # If no authorative dns server found, try again for {domain} + if [[ -z "$primary_ns" ]]; then + get_auth_dns "$d" + fi elif [[ "$CHECK_PUBLIC_DNS_SERVER" == "true" ]]; then primary_ns="$AUTH_DNS_SERVER $PUBLIC_DNS_SERVER" else @@ -1400,7 +1407,7 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n fi # Use SOA +trace to find the name server - if [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then + if [[ -z "$res" ]] && [[ $_TEST_SKIP_SOA_CALL == 0 ]]; then if [[ "$HAS_DIG_OR_DRILL" == "drill" ]]; then debug Using "$HAS_DIG_OR_DRILL -T $gad_d $gad_s" to find primary nameserver test_output "Using $HAS_DIG_OR_DRILL SOA" diff --git a/test/18-retry-dns-add.bats b/test/18-retry-dns-add.bats index 3a79880..25318de 100644 --- a/test/18-retry-dns-add.bats +++ b/test/18-retry-dns-add.bats @@ -30,8 +30,9 @@ DNS_EXTRA_WAIT=0 CHECK_ALL_AUTH_DNS="false" CHECK_PUBLIC_DNS_SERVER="false" DNS_WAIT_RETRY_ADD="true" +_RUNNING_TEST=1 EOF - create_certificate -d + create_certificate assert_failure - assert_line --partial "Retrying adding dns via command" + assert_line --partial "Retrying adding DNS via command" } From 8625e0774b80c9a1c9a8e02f668a55bfc3b52f22 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Mon, 29 Mar 2021 20:54:38 +0100 Subject: [PATCH 336/337] Update change log and version --- getssl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getssl b/getssl index 1cf1454..bbc7afd 100755 --- a/getssl +++ b/getssl @@ -258,6 +258,7 @@ # 2021-02-12 Add PREFERRED_CHAIN # 2021-02-15 ADD ftp explicit SSL with curl for upload the challenge (CoolMischa) # 2021-02-18 Add FULL_CHAIN_INCLUDE_ROOT +# 2021-03-25 Fix DNS challenge completion check if CNAMEs on different NS are used (sideeffect42)(2.35) # ---------------------------------------------------------------------------------------- case :$SHELLOPTS: in @@ -266,7 +267,7 @@ esac PROGNAME=${0##*/} PROGDIR="$(cd "$(dirname "$0")" || exit; pwd -P;)" -VERSION="2.34" +VERSION="2.35" # defaults ACCOUNT_KEY_LENGTH=4096 From d8006d6585979b2cde6f02ee93b585db9811b577 Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Thu, 1 Apr 2021 11:57:53 +0100 Subject: [PATCH 337/337] Update README.md --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index def44c5..7ff8b2e 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ for automating the process on remote servers. - [Installation](#installation) - [Overview](#overview) - [Getting started](#getting-started) +- [Detailed guide to getting started with more examples](#detailed-guide-to-getting-started-with-more-examples) - [Wildcard certificates](#wildcard-certificates) - [Automating updates](#automating-updates) - [Structure](#structure) @@ -87,25 +88,27 @@ desktop computer, or even a virtualbox) and add the checks, and certificates to a remote server ( providing you have a ssh with key, sftp or ftp access to the remote server). -```getssl -getssl ver. 2.02 +```getssl -h +getssl ver. 2.35 Obtain SSL certificates from the letsencrypt.org ACME server -Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] domain +Usage: getssl [-h|--help] [-d|--debug] [-c|--create] [-f|--force] [-a|--all] [-q|--quiet] [-Q|--mute] [-u|--upgrade] [-k|--keep #] [-U|--nocheck] [-r|--revoke cert key] [-w working_dir] [--preferred-chain chain] domain Options: -a, --all Check all certificates - -d, --debug Outputs debug information + -d, --debug Output debug information -c, --create Create default config files -f, --force Force renewal of cert (overrides expiry checks) -h, --help Display this help message and exit + -i, --install Install certificates and reload service -q, --quiet Quiet mode (only outputs on error, success of new cert, or getssl was upgraded) - -Q, --mute Like -q, but mutes notification about successful upgrade + -Q, --mute Like -q, but also mute notification about successful upgrade -r, --revoke "cert" "key" [CA_server] Revoke a certificate (the cert and key are required) -u, --upgrade Upgrade getssl if a more recent version is available - can be used with or without domain(s) - -k, --keep "#" Maximum amount of old getssl versions to keep when upgrading + -k, --keep "#" Maximum number of old getssl versions to keep when upgrading -U, --nocheck Do not check if a more recent version is available -w working_dir "Working directory" + --preferred-chain "chain" Use an alternate chain for the certificate ``` ## Getting started @@ -165,6 +168,10 @@ Change the server in your config file to get a fully valid certificate. dns. The certificate can be used (and checked with getssl) on alternate ports. +## Detailed guide to getting started with more examples + +[Guide to getting a certificate for example.com and www.example.com](https://github.com/srvrco/getssl/wiki/Guide-to-getting-a-certificate-for-example.com-and-www.example.com) + ## Wildcard certificates `getssl` supports creating wildcard certificates, i.e. _*.example.com_ which allows a single certificate to be used for any domain under *example.com*, e.g. *www.example.com*, *mail.example.com*. These must be validated using the dns-01 method.