aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xacme-slave8
-rwxr-xr-xletsencrypt49
-rw-r--r--letsencryptauthorityx1.pem32
-rw-r--r--letsencryptauthorityx2.pem32
4 files changed, 108 insertions, 13 deletions
diff --git a/acme-slave b/acme-slave
index ee39f8d..5ff9834 100755
--- a/acme-slave
+++ b/acme-slave
@@ -79,7 +79,7 @@ sub request($$;$) {
# ACME client
-# https://github.com/letsencrypt/acme-spec/blob/master/draft-barnes-acme.md
+# https://tools.ietf.org/html/draft-ietf-acme-acme-01
sub acme($$) {
my ($uri, $h) = @_;
@@ -119,7 +119,7 @@ if ($COMMAND eq 'new-reg') {
acme($RES{'new-reg'}, {
resource => 'new-reg',
- contact => [ map {"mailto:$_"} split(',', @ARGV) ],
+ contact => [ map {"mailto:$_"} @ARGV ],
agreement => $uri,
});
exit;
@@ -168,7 +168,7 @@ my @domains = do {
open my $fh1, '-|', @req, '-subject' or die "Can't run req(1ssl): $!";
my $subject = <$fh1>;
close $fh1;
- $domains{$1} = 1 if $subject =~ /\Asubject=\/CN=($RE_domain)(?:,.*)?\n\z/o;
+ $domains{$1} = 1 if $subject =~ /\Asubject=.*\/CN=($RE_domain)\n\z/o;
open my $fh2, '-|', @req, '-text', '-reqopt', 'no_header,no_version,no_subject,no_pubkey,no_sigdump'
or die "Can't run req(1ssl): $!";
@@ -224,7 +224,7 @@ foreach my $domain (@domains) {
for (my $i=0;; $i++) {
my $status = request('GET' => $challenge->{uri})->{status} // 'pending';
- die "Invalid challenge for $domain" if $status eq 'invalid';
+ die "Error: Invalid challenge for $domain\n" if $status eq 'invalid';
last if $status eq 'valid';
die "Timeout exceeded while waiting for challenge to pass ($domain)\n" if $i >= $TIMEOUT;
sleep 1;
diff --git a/letsencrypt b/letsencrypt
index 50406f7..7465378 100755
--- a/letsencrypt
+++ b/letsencrypt
@@ -11,12 +11,14 @@ WWW_USER=www-data
WWW_GROUP=www-data
ACME_WEBSERVER=acme-webserver
ACME_CLIENT=acme-slave
+CAfile=/usr/share/lets-encrypt/letsencryptauthorityx1.pem
declare COMMAND ACCOUNTKEY
declare -l GENKEY
declare RUNAS QUIET= DEBUG=
-declare SRVCERT= CHAIN= CSR SRVKEY
+declare SRVCRT= CHAIN= CSR SRVKEY
+declare -i MIN_AGE=0
declare -l HASH=
declare SUBJECT=/
declare SAN=
@@ -62,6 +64,8 @@ usage() {
(default: "digitalSignature,keyEncipherment,keyCertSign")
--chain Store not only the server certificate in the file specified with --output, but
also the CA's
+ --min-age=SECONDS Don't do anything if the certificate specified by --output exists and its expiration
+ is more than SECONDS ahead.
--output=FILE Where to store the issued (signed) X.509 certificate
--notify=COMMAND Command to run upon success. (This option can be repeated.)
@@ -96,7 +100,8 @@ while [ $# -gt 0 ]; do
--quiet|-q) QUIET=1;;
--debug) DEBUG=1;;
- --output=*) SRVCERT="${1#*=}";;
+ --output=*) SRVCRT="${1#*=}";;
+ --min-age=*) MIN_AGE="${1#*=}";;
--chain) CHAIN=1;;
--csr=*) CSR="${1#*=}";;
--key=*) SRVKEY="${1#*=}";;
@@ -160,10 +165,16 @@ if [ "$COMMAND" = 'revoke-cert' ]; then
exit 1
fi
elif [ "$COMMAND" = 'new-cert' ]; then
- if [ ! "${SRVCERT:-}" ]; then
+ if [ ! "${SRVCRT:-}" ]; then
echo "Error: Missing --output" >&2
exit 1
fi
+ if [ -s "$SRVCRT" ] && [ $MIN_AGE -gt 0 ] && \
+ exp=$(openssl x509 -noout -enddate <"$SRVCRT" 2>/dev/null) && \
+ [ $(( $(date -d "${exp#*=}" +%s) - $(date +%s))) -gt $MIN_AGE ]; then
+ [ ! "$DEBUG" ] || echo "Expiration date ($(date -d"${exp#*=}")) is too far away, come back later." >&2
+ exit 0
+ fi
# Generate a Certificate Signing Request if need be
if [ ${CSR+x} ]; then
@@ -267,15 +278,35 @@ while read data; do
done >"$pipe"
if [ "$COMMAND" = 'new-cert' ]; then
- # TODO
- # Verify: dump and compare public keys
- # Valid cert, signed by the right CA
+ # https://crt.sh/?q=mail.fripost.org&iCAID=7395
+ # https://crt.sh/?spkisha1=$sha1
+
+ # Ensure the cert's pubkey matches that of the CSR, and that it's signed by the intended CA
+ if [ ! -s "$x509" ] ||
+ ! diff <(openssl req -in "$CSR" -pubkey -noout) \
+ <(openssl x509 -in "$x509" -pubkey -noout) >/dev/null ||
+ ! openssl verify -CAfile "$CAfile" -purpose sslserver -x509_strict <"$x509" >/dev/null; then
+ echo "Error: Got an invalid X.509 certificate from the ACME server!" >&2
+ exit 1
+ fi
- # Copy "$x509" to "$SRVCERT", possibly chained
- # https://crt.sh/?q=cse-fresti.cse.chalmers.se&iCAID=7395
- cp "$x509" "$SRVCERT"
+ # if it doesn't exist, create the file with mode 0644 minus the process's umask(2)
+ [ -e "$SRVCRT" ] || touch "$SRVCRT"
+ cat "$x509" >"$SRVCRT"
+ [ ! "$DEBUG" ] || openssl x509 -noout -text <"$SRVCRT"
+
+ if [ ! "$QUIET" ]; then
+ echo "X.509 certificate $SRVCRT has been updated or renewed"
+ echo
+ openssl x509 -noout \
+ -text -certopt no_header,no_version,no_pubkey,no_sigdump \
+ -fingerprint -sha256 <"$SRVCRT"
+ fi
for (( i=0; i<${#NOTIFY[@]}; i++ )); do
${NOTIFY[$i]}
done
+
+else
+ [ "$QUIET" ] || echo OK
fi
diff --git a/letsencryptauthorityx1.pem b/letsencryptauthorityx1.pem
new file mode 100644
index 0000000..22c095b
--- /dev/null
+++ b/letsencryptauthorityx1.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIRAOeTkL6SBwNJGF95dYHlyoMwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTIwMDIw
+WhcNMjAwNjA0MTIwMDIwWjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
+RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDEwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX
+NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf
+89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl
+Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc
+Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz
+uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB
+AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
+BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
+FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo
+SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
+LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
+BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
+AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
+VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
+AGvM/XGv8yafGRGMPP6hnggoI9DGWGf4l0mzjBhuCkDVqoG/7rsH1ytzteePxiA3
+7kqSBo0fXu5GmbWOw09GpwPYyAAY0iWOMU6ybrTJHS466Urzoe/4IwLQoQc219EK
+lh+4Ugu1q4KxNY1qMDA/1YX2Qm9M6AcAs1UvZKHSpJQAbsYrbN6obNoUGOeG6ONH
+Yr8KRQz5FMfZYcA49fmdDTwKn/pyLOkJFeA/dm/oP99UmKCFoeOa5w9YJr2Vi7ic
+Xd59CU8mprWhxFXnma1oU3T8ZNovjib3UHocjlEJfNbDy9zgKTYURcMVweo1dkbH
+NbLc5mIjIk/kJ+RPD+chR+gJjy3Gh9xMNkDrZQKfsIO93hxTsZMmgZQ4c+vujC1M
+jSak+Ai87YZeYQPh1fCGMSTno5III37DUCtIn8BJxJixuPeOMKsjLLD5AtMVy0fp
+d19lcUek4bjDY8/Ujb5/wfn2+Kk7z72SxWdekjtHOWBmKxqq8jDuuMw4ymg1g5n7
+R7TZ/Y3y4bTpWUDkBHFo03xNM21wBFDIrCZZeVhvDW4MtT6+Ass2bcpoHwYcGol2
+gaLDa5k2dkG41OGtXa0fY+TjdryY4cOcstJUKjv2MJku4yaTtjjECX1rJvFLnqYe
+wC+FmxjgWPuyRNuLDAWK30mmpcJZ3CmD6dFtAi4h7H37
+-----END CERTIFICATE-----
diff --git a/letsencryptauthorityx2.pem b/letsencryptauthorityx2.pem
new file mode 100644
index 0000000..612b4a5
--- /dev/null
+++ b/letsencryptauthorityx2.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIRAJY2TKc4C+SL3JDGzeC33mgwDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTIwMDMx
+WhcNMjAwNjA0MTIwMDMxWjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
+RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhJHRCe7eRMdlz/ziq2M5EXLc5
+CtxErg29RbmXN2evvVBPX9MQVGv3QdqOY+ZtW8DoQKmMQfzRA4n/YmEJYNYHBXia
+kL0aZD5P3M93L4lry2evQU3FjQDAa/6NhNy18pUxqOj2kKBDSpN0XLM+Q2lLiSJH
+dFE+mWTDzSQB+YQvKHcXIqfdw2wITGYvN3TFb5OOsEY3FmHRUJjIsA9PWFN8rPba
+LZZhUK1D3AqmT561Urmcju9O30azMdwg/GnCoyB1Puw4GzZOZmbS3/VmpJMve6YO
+lD5gPUpLHG+6tE0cPJFYbi9NxNpw2+0BOXbASefpNbUUBpDB5ZLiEP1rubSFAgMB
+AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
+BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
+FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBTF
+satOTLHNZDCTfsGEmQWr5gPiJTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
+LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
+BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
+AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
+VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
+AA4eqMjSEJKCF6XRR5pEutkS/e7xgy2vCYYbw1ospQiGQ4FO5TtbvO+5K4v7WR3b
+1peMQ03rX0Dr+ylmGNypZahNxTqDiO0X2sHBwJWj/k61+MYq3bRYxKwI6cduTDXb
+YQxilGTDNGZUIFKKIloz4zGAl68sj+8pLg534EqKgl8+rWSxclToS1KrydJezokE
+dQRXfxu79iscWA3PIj1vbaUBB16lnWJxA3LhTGhUrhZrCnFuOZ93KO8kCKPM7EVo
+7c4FCYKI8eWDsf0FF49A4xMUmxPJAPIyZkwQ8KkjpzcTHOmT4CEXUhNu9eMI9qBK
+VSFDDMifJ8HzCaVLyMvY1Kf7iR+840EkX1EGC+Z39EaK1hjm314LYpLoYGvYYLJO
+/J76XAx8ZgpofqHz1gAEfiMLMLxLQkOjKLXqoUEd5KdnzaO3aLH91gnasy8aD4D5
+9RfEO2xcaozD2rbYsoAMVzcZZHw0Smdmobaz2YazMBjFRcqGntg6s5Xqwusaleiy
+snjMCC/9mvIPqGyuVnBPTBaUDFDEhX6qD2MX4dzODL91Z0ogYDWcFLN+uLnZKHje
+4JoNuzkJ2FXWOREcsW93KXb+3T8COjhTDKvK4H6ufdrZxxusx60ajJAMBzW0XTf5
+nm2yGEDtyVoMgJLp0rkiPlormgHxSkFDOJbY94J7yxRK
+-----END CERTIFICATE----- \ No newline at end of file