aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog59
-rw-r--r--Makefile14
-rw-r--r--certs-staging/letsencrypt-stg-int-e1.pem19
-rw-r--r--certs-staging/letsencrypt-stg-int-e2.pem19
-rw-r--r--certs-staging/letsencrypt-stg-int-r3.pem31
-rw-r--r--certs-staging/letsencrypt-stg-int-r4.pem31
-rw-r--r--certs/lets-encrypt-e1.pem17
-rw-r--r--certs/lets-encrypt-e2.pem17
-rw-r--r--certs/lets-encrypt-r3-cross-signed.pem26
-rw-r--r--certs/lets-encrypt-r3.pem30
-rw-r--r--certs/lets-encrypt-r4-cross-signed.pem26
-rw-r--r--certs/lets-encrypt-r4.pem30
-rw-r--r--certs/lets-encrypt-x1-cross-signed.pem27
-rw-r--r--certs/lets-encrypt-x2-cross-signed.pem27
-rw-r--r--certs/lets-encrypt-x3-cross-signed.pem27
-rw-r--r--certs/lets-encrypt-x4-cross-signed.pem27
-rw-r--r--certs/letsencryptauthorityx1.pem32
-rw-r--r--certs/letsencryptauthorityx2.pem32
-rw-r--r--certs/letsencryptauthorityx3.pem32
-rw-r--r--certs/letsencryptauthorityx4.pem32
-rwxr-xr-xclient52
-rw-r--r--config/lacme-certs.conf4
-rw-r--r--debian/changelog52
-rw-r--r--debian/control13
-rw-r--r--debian/copyright1
-rwxr-xr-xdebian/lacme.postrm15
-rw-r--r--debian/salsa-ci.yml4
-rwxr-xr-xlacme197
-rwxr-xr-xlacme-accountd71
-rw-r--r--lacme.8.md12
-rwxr-xr-xtest9
-rw-r--r--tests/account-encrypted-gpg2
-rw-r--r--tests/account-encrypted-openssl3
-rw-r--r--tests/accountd7
-rw-r--r--tests/accountd-kid14
-rw-r--r--tests/accountd-remote2
-rw-r--r--tests/accountd-validate36
-rw-r--r--tests/cert-extensions10
-rw-r--r--tests/cert-install149
-rw-r--r--tests/cert-revoke4
-rw-r--r--tests/cert-verify22
-rw-r--r--tests/drop-privileges18
-rw-r--r--tests/old-accountd3
-rw-r--r--tests/old-lacme10
44 files changed, 511 insertions, 754 deletions
diff --git a/Changelog b/Changelog
index 9f12237..5c91365 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,60 @@
+lacme (0.8.3) upstream;
+
+ + Fix post-issuance validation logic. We avoid pining the
+ intermediate certificates in the bundle and instead validate the
+ leaf certificate with intermediates supplied during issuance as
+ untrusted (used for chain building only). Only the root
+ certificates are used as trust anchor. Not pining intermediate
+ certificates is in line with Let's Encrypt's latest recommendations.
+ + Pass `-in /dev/stdin` option to openssl(1) to avoid warning with
+ OpenSSL 3.2 or later.
+ + Fix test suite.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Thu, 13 Jun 2024 17:39:34 +0200
+
+lacme (0.8.2) upstream;
+
+ + client: Handle "ready" → "processing" → "valid" status change during
+ newOrder, instead of just "ready" → "valid". The latter may be what
+ we observe when the server is fast enough, but according to RFC 8555
+ sec. 7.1.6 the state actually transitions via "processing" state and
+ we need to account for that.
+ - Test suite: Point stretch's archive URL to archive.d.o.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Tue, 25 Apr 2023 20:06:22 +0200
+
+lacme (0.8.1) upstream;
+
+ + lacme-accountd: improve log messages and refactor logging logic.
+ + lacme-accountd: refuse to sign JWS with an invalid Protected Header.
+ + lacme: don't write certificate(-chain) file on chown/chmod failure.
+ + lacme: default mode for certificate(-chain) creation is 0644 minus
+ umask restrictions. Also, always spawn the client with umask 0022 so
+ a starting lacme(8) with a restrictive umask doesn't impede serving
+ challenge files.
+ + lacme: add 'owner' resp. 'mode' as (prefered) alias for 'chown' resp.
+ 'chmod'.
+ + lacme: split certificates using Net::SSLeay::PEM_* instead of calling
+ openssl.
+ + lacme: pass a temporary JSON file with the client configuration to
+ the internal client, so it doesn't have to parse the INI file again.
+ - lacme: in the [accountd] config, let lacme-accountd(1) do the
+ %-expansion for 'config', not lacme(8) when building the command.
+ - lacme-accountd: don't log debug messages unless --debug is set.
+ - lacme: when getpwnam()/getgrnam()'s errno is 0, exclude it from error
+ messages.
+ - tests/drop-privileges: ensure failure to drop privileges yields an
+ error instead of retaining root priviliges.
+ - tests/cert-install: include tests for failing chown(2) due to unknown
+ user/group name.
+ - lacme: ignore empty values in settings 'chown', 'chmod', 'certificate'
+ and 'certificate-chain'.
+ - lacme: return an error when the 'mode'/'chown' isn't a number.
+ - Makefile: replace '$(dir $@)' with '$(@D)'.
+ - Test suite: Adjust against current Let's Encrypt staging environment.
+
+ -- Guilhem Moulin <guilhem@fripost.org> Wed, 25 Jan 2023 03:23:51 +0100
+
lacme (0.8.0) upstream;
* Breaking change: 'challenge-directory' now needs to be set to an
@@ -218,6 +275,8 @@ lacme (0.2) upstream;
directories. New default "lacme-certs.conf lacme-certs.conf.d/".
- Minor manpage fixes
- More useful message upon Validation Challenge failure.
+ - If restricting access via umask() fails, don't include errno in the
+ error message as it's not set on failure.
-- Guilhem Moulin <guilhem@guilhem.org> Sat, 03 Dec 2016 16:40:56 +0100
diff --git a/Makefile b/Makefile
index 16ac04e..cb2f4ed 100644
--- a/Makefile
+++ b/Makefile
@@ -16,18 +16,14 @@ $(MANUAL_FILES): $(BUILDDIR)/%: $(BUILDDIR)/%.md
# used for validation, see https://letsencrypt.org/certificates/
$(BUILDDIR)/certs/ca-certificates.crt: \
certs/isrgrootx1.pem \
- certs/isrg-root-x2.pem \
- certs/lets-encrypt-r[34].pem \
- certs/lets-encrypt-e[12].pem
- mkdir -pv -- $(dir $@)
+ certs/isrg-root-x2.pem
+ mkdir -pv -- $(@D)
cat -- $^ >$@
# Staging Environment for tests, see https://letsencrypt.org/docs/staging-environment/
$(BUILDDIR)/certs-staging/ca-certificates.crt: \
- certs-staging/letsencrypt-stg-root-x[12].pem \
- certs-staging/letsencrypt-stg-int-r[34].pem \
- certs-staging/letsencrypt-stg-int-e[12].pem
- mkdir -pv -- $(dir $@)
+ certs-staging/letsencrypt-stg-root-x[12].pem
+ mkdir -pv -- $(@D)
cat -- $^ >$@
prefix ?= $(DESTDIR)
@@ -52,7 +48,7 @@ lacme_client_group ?= nogroup
acmeapi_server ?= https://acme-v02.api.letsencrypt.org/directory
$(BUILDDIR)/%: %
- mkdir -pv -- $(dir $@)
+ mkdir -pv -- $(@D)
cp --no-dereference --preserve=mode,links,xattr -vfT -- "$<" "$@"
sed -i "s#@@bindir@@#$(bindir)#g; \
s#@@sbindir@@#$(sbindir)#g; \
diff --git a/certs-staging/letsencrypt-stg-int-e1.pem b/certs-staging/letsencrypt-stg-int-e1.pem
deleted file mode 100644
index 2d6290c..0000000
--- a/certs-staging/letsencrypt-stg-int-e1.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDCzCCApGgAwIBAgIRALRY4992FVxZJKOJ3bpffWIwCgYIKoZIzj0EAwMwaDEL
-MAkGA1UEBhMCVVMxMzAxBgNVBAoTKihTVEFHSU5HKSBJbnRlcm5ldCBTZWN1cml0
-eSBSZXNlYXJjaCBHcm91cDEkMCIGA1UEAxMbKFNUQUdJTkcpIEJvZ3VzIEJyb2Nj
-b2xpIFgyMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowVTELMAkGA1UE
-BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSQwIgYDVQQD
-ExsoU1RBR0lORykgRXJzYXR6IEVkYW1hbWUgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
-IgNiAAT9v/PJUtHOTk28nXCXrpP665vI4Z094h8o7R+5E6yNajZa0UubqjpZFoGq
-u785/vGXj6mdfIzc9boITGusZCSWeMj5ySMZGZkS+VSvf8VQqj+3YdEu4PLZEjBA
-ivRFpEejggEQMIIBDDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH
-AwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFOv5JcKA
-KGbibQiSMvPC4a3D/zVFMB8GA1UdIwQYMBaAFN7Ro1lkDsGaNqNG7rAQdu+ul5Vm
-MDYGCCsGAQUFBwEBBCowKDAmBggrBgEFBQcwAoYaaHR0cDovL3N0Zy14Mi5pLmxl
-bmNyLm9yZy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3N0Zy14Mi5jLmxlbmNy
-Lm9yZy8wIgYDVR0gBBswGTAIBgZngQwBAgEwDQYLKwYBBAGC3xMBAQEwCgYIKoZI
-zj0EAwMDaAAwZQIwXcZbdgxcGH9rTErfSTkXfBKKygU0yO7OpbuNeY1id0FZ/hRY
-N5fdLOGuc+aHfCsMAjEA0P/xwKr6NQ9MN7vrfGAzO397PApdqfM7VdFK18aEu1xm
-3HMFKzIR8eEPsMx4smMl
------END CERTIFICATE-----
diff --git a/certs-staging/letsencrypt-stg-int-e2.pem b/certs-staging/letsencrypt-stg-int-e2.pem
deleted file mode 100644
index 931ff9b..0000000
--- a/certs-staging/letsencrypt-stg-int-e2.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDCjCCApCgAwIBAgIQQuJJzkyQeLKT5OSWP41qRTAKBggqhkjOPQQDAzBoMQsw
-CQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3VyaXR5
-IFJlc2VhcmNoIEdyb3VwMSQwIgYDVQQDExsoU1RBR0lORykgQm9ndXMgQnJvY2Nv
-bGkgWDIwHhcNMjAwOTA0MDAwMDAwWhcNMjUwOTE1MTYwMDAwWjBVMQswCQYDVQQG
-EwJVUzEgMB4GA1UEChMXKFNUQUdJTkcpIExldCdzIEVuY3J5cHQxJDAiBgNVBAMT
-GyhTVEFHSU5HKSBFcnNhdHogRWRhbWFtZSBFMjB2MBAGByqGSM49AgEGBSuBBAAi
-A2IABEpsxJnmT3EQu6hL6LeYyvVggZd1aOj6QepgX+mdhOYxCgAvb4etuL80y7EP
-sUUJh3Y20WhgXsZW21jukrL+PzdKfctcRoOM7CFBBk+09Ubalyys69O99+B6doRx
-GYWWp6OCARAwggEMMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcD
-AgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUbPiNRb5I
-GjoDUBH28pJdQ2OphogwHwYDVR0jBBgwFoAU3tGjWWQOwZo2o0busBB2766XlWYw
-NgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRwOi8vc3RnLXgyLmkubGVu
-Y3Iub3JnLzArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vc3RnLXgyLmMubGVuY3Iu
-b3JnLzAiBgNVHSAEGzAZMAgGBmeBDAECATANBgsrBgEEAYLfEwEBATAKBggqhkjO
-PQQDAwNoADBlAjEAv19ESEwzY8fAt1WkE4Nkm6bJxQEJZwILGNnvPuEmAKlngKov
-dm1feBw0q45Fl8MEAjA24IoWYt7txJSbPQpxETJfsjO8aLWxedQpqHWS1x0zEB4L
-K5uFc99+L56DIgmqjKM=
------END CERTIFICATE-----
diff --git a/certs-staging/letsencrypt-stg-int-r3.pem b/certs-staging/letsencrypt-stg-int-r3.pem
deleted file mode 100644
index 0282fc1..0000000
--- a/certs-staging/letsencrypt-stg-int-r3.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFWzCCA0OgAwIBAgIQTfQrldHumzpMLrM7jRBd1jANBgkqhkiG9w0BAQsFADBm
-MQswCQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3Vy
-aXR5IFJlc2VhcmNoIEdyb3VwMSIwIAYDVQQDExkoU1RBR0lORykgUHJldGVuZCBQ
-ZWFyIFgxMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowWTELMAkGA1UE
-BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSgwJgYDVQQD
-Ex8oU1RBR0lORykgQXJ0aWZpY2lhbCBBcHJpY290IFIzMIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEAu6TR8+74b46mOE1FUwBrvxzEYLck3iasmKrcQkb+
-gy/z9Jy7QNIAl0B9pVKp4YU76JwxF5DOZZhi7vK7SbCkK6FbHlyU5BiDYIxbbfvO
-L/jVGqdsSjNaJQTg3C3XrJja/HA4WCFEMVoT2wDZm8ABC1N+IQe7Q6FEqc8NwmTS
-nmmRQm4TQvr06DP+zgFK/MNubxWWDSbSKKTH5im5j2fZfg+j/tM1bGaczFWw8/lS
-nukyn5J2L+NJYnclzkXoh9nMFnyPmVbfyDPOc4Y25aTzVoeBKXa/cZ5MM+WddjdL
-biWvm19f1sYn1aRaAIrkppv7kkn83vcth8XCG39qC2ZvaQIDAQABo4IBEDCCAQww
-DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAS
-BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTecnpI3zHDplDfn4Uj31c3S10u
-ZTAfBgNVHSMEGDAWgBS182Xy/rAKkh/7PH3zRKCsYyXDFDA2BggrBgEFBQcBAQQq
-MCgwJgYIKwYBBQUHMAKGGmh0dHA6Ly9zdGcteDEuaS5sZW5jci5vcmcvMCsGA1Ud
-HwQkMCIwIKAeoByGGmh0dHA6Ly9zdGcteDEuYy5sZW5jci5vcmcvMCIGA1UdIAQb
-MBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCN
-DLam9yN0EFxxn/3p+ruWO6n/9goCAM5PT6cC6fkjMs4uas6UGXJjr5j7PoTQf3C1
-vuxiIGRJC6qxV7yc6U0X+w0Mj85sHI5DnQVWN5+D1er7mp13JJA0xbAbHa3Rlczn
-y2Q82XKui8WHuWra0gb2KLpfboYj1Ghgkhr3gau83pC/WQ8HfkwcvSwhIYqTqxoZ
-Uq8HIf3M82qS9aKOZE0CEmSyR1zZqQxJUT7emOUapkUN9poJ9zGc+FgRZvdro0XB
-yphWXDaqMYph0DxW/10ig5j4xmmNDjCRmqIKsKoWA52wBTKKXK1na2ty/lW5dhtA
-xkz5rVZFd4sgS4J0O+zm6d5GRkWsNJ4knotGXl8vtS3X40KXeb3A5+/3p0qaD215
-Xq8oSNORfB2oI1kQuyEAJ5xvPTdfwRlyRG3lFYodrRg6poUBD/8fNTXMtzydpRgy
-zUQZh/18F6B/iW6cbiRN9r2Hkh05Om+q0/6w0DdZe+8YrNpfhSObr/1eVZbKGMIY
-qKmyZbBNu5ysENIK5MPc14mUeKmFjpN840VR5zunoU52lqpLDua/qIM8idk86xGW
-xx2ml43DO/Ya/tVZVok0mO0TUjzJIfPqyvr455IsIut4RlCR9Iq0EDTve2/ZwCuG
-hSjpTUFGSiQrR2JK2Evp+o6AETUkBCO1aw0PpQBPDQ==
------END CERTIFICATE-----
diff --git a/certs-staging/letsencrypt-stg-int-r4.pem b/certs-staging/letsencrypt-stg-int-r4.pem
deleted file mode 100644
index 7e482dc..0000000
--- a/certs-staging/letsencrypt-stg-int-r4.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFWzCCA0OgAwIBAgIQaCYQ95QBw3BbcmLyhdXHzjANBgkqhkiG9w0BAQsFADBm
-MQswCQYDVQQGEwJVUzEzMDEGA1UEChMqKFNUQUdJTkcpIEludGVybmV0IFNlY3Vy
-aXR5IFJlc2VhcmNoIEdyb3VwMSIwIAYDVQQDExkoU1RBR0lORykgUHJldGVuZCBQ
-ZWFyIFgxMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2MDAwMFowWTELMAkGA1UE
-BhMCVVMxIDAeBgNVBAoTFyhTVEFHSU5HKSBMZXQncyBFbmNyeXB0MSgwJgYDVQQD
-Ex8oU1RBR0lORykgQXJ0aWZpY2lhbCBBcHJpY290IFI0MIIBIjANBgkqhkiG9w0B
-AQEFAAOCAQ8AMIIBCgKCAQEA6J8Tmlh6z62axF2+KzRgHHmxf0c5LHGA+wVx9ukJ
-nB9zkqdG+gyGfYBnwPlxny0JWBS/1/wu7ry+IhtYOqar1Rg+f+gD0+SqOYmngNW8
-IIw0WTjHhqYB2d2Fxsr9bPIpDwpHRbgE8HkozAKwrWs5xDthZlaMZfEyAKzdx8mC
-PttZzKW4ubSptmNMoGHx5t/pBWrNGz5EFuTYcy0DkknMvKedkVJn+jJBxVQ/ef/y
-Gep7+1WjpW/UQvwJ5H2sm6UtBRAfos5U2TubN7fiI9OGsRmIp73BP9TsyP0Mi1rZ
-kVAfMEU6pI5dJXTNt6hmzuE6frt+NlHaC9yrs9iEG1m6DQIDAQABo4IBEDCCAQww
-DgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAS
-BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSa7sAVo3Q7x+zxBWCmwpOn+U8a
-NTAfBgNVHSMEGDAWgBS182Xy/rAKkh/7PH3zRKCsYyXDFDA2BggrBgEFBQcBAQQq
-MCgwJgYIKwYBBQUHMAKGGmh0dHA6Ly9zdGcteDEuaS5sZW5jci5vcmcvMCsGA1Ud
-HwQkMCIwIKAeoByGGmh0dHA6Ly9zdGcteDEuYy5sZW5jci5vcmcvMCIGA1UdIAQb
-MBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCw
-o/xtzOLhliWC6F6XTdgCqt2FrjLQAXRUnCwnwg44oVbD+Gh9qXREpdpRPVrlIS7T
-tobw8Z329nsLZ+wiGvm3wkC3Ka3RWbThLE/MuwVMysaTi5jXLJcQLvHRlW00jHgM
-/V8vtJPr5s443KuOX1TFUV5Z/0ZilrBY3sAbgVk2n/fgyonX/JCdiGXt3HODpBh3
-eJH1kKM/EIrVVZwxHCwG1x/LuKpcUpbelF+NDI11neL+AzhU6wmBhgplg4OxzYWD
-Xg0LCi6W0/t73HnG8SaRiCAcPyJOGBZtLQLUgfVKSd6DtRMhVcTzM6EjFiJYZuv4
-JHj65p1yFh8+kgJL/kyIWp2+mHgj/QCDaQEJccmWl+dpXm0jyyNBbjKG7oOlCzvH
-HEBmrYSlzWI4XcK5C2+1SmSQqXv7vPo8jEVXSftg4Z6mEA8e2S6dt/rvreiQ+fDm
-gixNiQRd7lkUqPv1EZhiGMYxhW52taj9A3xlcnD9/tfD7BKFe38ilVblYTU423WY
-zAslpz6oDEiftKCZ2VmaUzEsS6Hma/r/SkF5oorVlaz7hE1qcu9HLkRohXPJtw3k
-XJcK1hT3rITPasaSTREfwKBQS+y6guxv+IpkZftMEiOUix2cRoICJesxsYDE5tei
-Pwrakf/zlLfF4WDZVqrYKsddVqddAoa64LfXxS/B4Q==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-e1.pem b/certs/lets-encrypt-e1.pem
deleted file mode 100644
index 2a19d41..0000000
--- a/certs/lets-encrypt-e1.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICxjCCAk2gAwIBAgIRALO93/inhFu86QOgQTWzSkUwCgYIKoZIzj0EAwMwTzEL
-MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo
-IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjAwOTA0MDAwMDAwWhcN
-MjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3MgRW5j
-cnlwdDELMAkGA1UEAxMCRTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQkXC2iKv0c
-S6Zdl3MnMayyoGli72XoprDwrEuf/xwLcA/TmC9N/A8AmzfwdAVXMpcuBe8qQyWj
-+240JxP2T35p0wKZXuskR5LBJJvmsSGPwSSB/GjMH2m6WPUZIvd0xhajggEIMIIB
-BDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
-MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFFrz7Sv8NsI3eblSMOpUb89V
-yy6sMB8GA1UdIwQYMBaAFHxClq7eS0g7+pL4nozPbYupcjeVMDIGCCsGAQUFBwEB
-BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gyLmkubGVuY3Iub3JnLzAnBgNVHR8E
-IDAeMBygGqAYhhZodHRwOi8veDIuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYG
-Z4EMAQIBMA0GCysGAQQBgt8TAQEBMAoGCCqGSM49BAMDA2cAMGQCMHt01VITjWH+
-Dbo/AwCd89eYhNlXLr3pD5xcSAQh8suzYHKOl9YST8pE9kLJ03uGqQIwWrGxtO3q
-YJkgsTgDyj2gJrjubi1K9sZmHzOa25JK1fUpE8ZwYii6I4zPPS/Lgul/
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-e2.pem b/certs/lets-encrypt-e2.pem
deleted file mode 100644
index 0fd9f40..0000000
--- a/certs/lets-encrypt-e2.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICxjCCAkygAwIBAgIQTtI99q9+x/mwxHJv+VEqdzAKBggqhkjOPQQDAzBPMQsw
-CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
-R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw0y
-NTA5MTUxNjAwMDBaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNy
-eXB0MQswCQYDVQQDEwJFMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABCOaLO3lixmN
-YVWex+ZVYOiTLgi0SgNWtU4hufk50VU4Zp/LbBVDxCsnsI7vuf4xp4Cu+ETNggGE
-yBqJ3j8iUwe5Yt/qfSrRf1/D5R58duaJ+IvLRXeASRqEL+VkDXrW3qOCAQgwggEE
-MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw
-EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUbZkq9U0C6+MRwWC6km+NPS7x
-6kQwHwYDVR0jBBgwFoAUfEKWrt5LSDv6kviejM9ti6lyN5UwMgYIKwYBBQUHAQEE
-JjAkMCIGCCsGAQUFBzAChhZodHRwOi8veDIuaS5sZW5jci5vcmcvMCcGA1UdHwQg
-MB4wHKAaoBiGFmh0dHA6Ly94Mi5jLmxlbmNyLm9yZy8wIgYDVR0gBBswGTAIBgZn
-gQwBAgEwDQYLKwYBBAGC3xMBAQEwCgYIKoZIzj0EAwMDaAAwZQIxAPJCN9qpyDmZ
-tX8K3m8UYQvK51BrXclM6WfrdeZlUBKyhTXUmFAtJw4X6A0x9mQFPAIwJa/No+KQ
-UAM1u34E36neL/Zba7ombkIOchSgx1iVxzqtFWGddgoG+tppRPWhuhhn
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-r3-cross-signed.pem b/certs/lets-encrypt-r3-cross-signed.pem
deleted file mode 100644
index 1d82449..0000000
--- a/certs/lets-encrypt-r3-cross-signed.pem
+++ /dev/null
@@ -1,26 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
-MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
-AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
-jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
-Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
-U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
-gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
-/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
-oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
-BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
-ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
-p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
-AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
-Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
-LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
-r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
-AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
-ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
-S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
-qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
-O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
-UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-r3.pem b/certs/lets-encrypt-r3.pem
deleted file mode 100644
index 43b222a..0000000
--- a/certs/lets-encrypt-r3.pem
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
-WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
-RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
-R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
-sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
-NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
-Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
-/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
-AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
-Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
-FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
-AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
-Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
-gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
-PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
-ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
-CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
-lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
-avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
-yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
-yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
-hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
-HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
-MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
-nLRbwHOoq7hHwg==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-r4-cross-signed.pem b/certs/lets-encrypt-r4-cross-signed.pem
deleted file mode 100644
index f0ed3cd..0000000
--- a/certs/lets-encrypt-r4-cross-signed.pem
+++ /dev/null
@@ -1,26 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEZTCCA02gAwIBAgIQQAF1BIMlO+Rkt3exI9CKgjANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0NVoXDTIxMDkyOTE5MjE0NVow
-MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
-AlI0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyjcdynT55G+87cK
-AMf78lULJSJjUzav6Qgg3w2vKD7NxqtXtp2kJRml0jJtSaYIuccvoZuTxSBAa4Qx
-IKKOMGAlYO/ZGok/H2lxstrqP3NBxJBvZv19nljYd8/NWXVEyaEKe58/Gw46Zm+2
-dc+Ly6+dwHDF/9KCCq9dzeLonIWUpOYANeh+TjmBxyGJYHfqHZbyi4N7R8RtMsBS
-fiMeRbVx7qPvF8IDqZOJ3fWf27rx2uB+l4dxgR4aglbkPnwYogjlFl+o+qjgSFFN
-GBSgDKPltsqztVUSa3LHWn87jPnn2dGOEk0zMwMq8RPhQjzCLllgLm3gB0czZd/S
-Z8pNhQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
-BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
-ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
-p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
-AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
-Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
-LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFDadPuCxQPYnLHy/jZ0x
-ivZUpkYmMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
-AQsFAAOCAQEAN4CpgPmK2C5pq/RdV9gEdWcvPnPfT9ToucrAMTcn//wyWBWF2wG4
-hvPBQxxuqPECZsi4nLQ45VJpyC1NDd0GqGQIMqNdC4N4TLDtd7Yhy8v5JsfEMUbb
-6xW4sKeeeKy3afOkel60Xg1/7ndSmppiHqdh+TdJML1hptRgdxGiB8LMpHuW/oM8
-akfyt4TkBhA8+Wu8MM6dlJyJ7nHBVnEUFQ4Ni+GzNC/pQSL2+Y9Mq4HHIk2ZFy0W
-B8KsVwdeNrERPL+LjhhLde1Et0aL9nlv4CqwXHML2LPgk38j/WllbQ/8HRd2VpB+
-JW6Z8JNhcnuBwATHMCeJVCFapoZsPfQQ6Q==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-r4.pem b/certs/lets-encrypt-r4.pem
deleted file mode 100644
index 578b3bd..0000000
--- a/certs/lets-encrypt-r4.pem
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFFjCCAv6gAwIBAgIRAIp5IlCr5SxSbO7Pf8lC3WIwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
-WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
-RW5jcnlwdDELMAkGA1UEAxMCUjQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQCzKNx3KdPnkb7ztwoAx/vyVQslImNTNq/pCCDfDa8oPs3Gq1e2naQlGaXS
-Mm1Jpgi5xy+hm5PFIEBrhDEgoo4wYCVg79kaiT8faXGy2uo/c0HEkG9m/X2eWNh3
-z81ZdUTJoQp7nz8bDjpmb7Z1z4vLr53AcMX/0oIKr13N4uichZSk5gA16H5OOYHH
-IYlgd+odlvKLg3tHxG0ywFJ+Ix5FtXHuo+8XwgOpk4nd9Z/buvHa4H6Xh3GBHhqC
-VuQ+fBiiCOUWX6j6qOBIUU0YFKAMo+W2yrO1VRJrcsdafzuM+efZ0Y4STTMzAyrx
-E+FCPMIuWWAubeAHRzNl39Jnyk2FAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
-AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
-Af8CAQAwHQYDVR0OBBYEFDadPuCxQPYnLHy/jZ0xivZUpkYmMB8GA1UdIwQYMBaA
-FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
-AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
-Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
-gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCJbu5CalWO+H+Az0lmIG14DXmlYHQE
-k26umjuCyioWs2icOlZznPTcZvbfq02YPHGTCu3ctggVDULJ+fwOxKekzIqeyLNk
-p8dyFwSAr23DYBIVeXDpxHhShvv0MLJzqqDFBTHYe1X5X2Y7oogy+UDJxV2N24/g
-Z8lxG4Vr2/VEfUOrw4Tosl5Z+1uzOdvTyBcxD/E5rGgTLczmulctHy3IMTmdTFr0
-FnU0/HMQoquWQuODhFqzMqNcsdbjANUBwOEQrKI8Sy6+b84kHP7PtO+S4Ik8R2k7
-ZeMlE1JmxBi/PZU860YlwT8/qOYToCHVyDjhv8qutbf2QnUl3SV86th2I1QQE14s
-0y7CdAHcHkw3sAEeYGkwCA74MO+VFtnYbf9B2JBOhyyWb5087rGzitu5MTAW41X9
-DwTeXEg+a24tAeht+Y1MionHUwa4j7FB/trN3Fnb/r90+4P66ZETVIEcjseUSMHO
-w6yqv10/H/dw/8r2EDUincBBX3o9DL3SadqragkKy96HtMiLcqMMGAPm0gti1b6f
-bnvOdr0mrIVIKX5nzOeGZORaYLoSD4C8qvFT7U+Um6DMo36cVDNsPmkF575/s3C2
-CxGiCPQqVxPgfNSh+2CPd2Xv04lNeuw6gG89DlOhHuoFKRlmPnom+gwqhz3ZXMfz
-TfmvjrBokzCICA==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-x1-cross-signed.pem b/certs/lets-encrypt-x1-cross-signed.pem
deleted file mode 100644
index 8a92a0b..0000000
--- a/certs/lets-encrypt-x1-cross-signed.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIRAJgT9HUT5XULQ+dDHpceRL0wDQYJKoZIhvcNAQELBQAw
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzAeFw0xNTEwMTkyMjMzMzZaFw0yMDEwMTkyMjMzMzZa
-MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
-ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBAJzTDPBa5S5Ht3JdN4OzaGMw6tc1Jhkl4b2+NfFwki+3uEtB
-BaupnjUIWOyxKsRohwuj43Xk5vOnYnG6eYFgH9eRmp/z0HhncchpDpWRz/7mmelg
-PEjMfspNdxIknUcbWuu57B43ABycrHunBerOSuu9QeU2mLnL/W08lmjfIypCkAyG
-dGfIf6WauFJhFBM/ZemCh8vb+g5W9oaJ84U/l4avsNwa72sNlRZ9xCugZbKZBDZ1
-gGusSvMbkEl4L6KWTyogJSkExnTA0DHNjzE4lRa6qDO4Q/GxH8Mwf6J5MRM9LTb4
-4/zyM2q5OTHFr8SNDR1kFjOq+oQpttQLwNh9w5MCAwEAAaOCAZIwggGOMBIGA1Ud
-EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAy
-BggrBgEFBQcwAYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5j
-b20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMv
-ZHN0cm9vdGNheDMucDdjMB8GA1UdIwQYMBaAFMSnsaR7LHH62+FLkHX/xBVghYkQ
-MFQGA1UdIARNMEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUH
-AgEWImh0dHA6Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUw
-MzAxoC+gLYYraHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JM
-LmNybDATBgNVHR4EDDAKoQgwBoIELm1pbDAdBgNVHQ4EFgQUqEpqYwR93brm0Tm3
-pkVl7/Oo7KEwDQYJKoZIhvcNAQELBQADggEBANHIIkus7+MJiZZQsY14cCoBG1hd
-v0J20/FyWo5ppnfjL78S2k4s2GLRJ7iD9ZDKErndvbNFGcsW+9kKK/TnY21hp4Dd
-ITv8S9ZYQ7oaoqs7HwhEMY9sibED4aXw09xrJZTC9zK1uIfW6t5dHQjuOWv+HHoW
-ZnupyxpsEUlEaFb+/SCI4KCSBdAsYxAcsHYI5xxEI4LutHp6s3OT2FuO90WfdsIk
-6q78OMSdn875bNjdBYAqxUp2/LEIHfDBkLoQz0hFJmwAbYahqKaLn73PAAm1X2kj
-f1w8DdnkabOLGeOVcj9LQ+s67vBykx4anTjURkbqZslUEUsn2k5xeua2zUk=
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-x2-cross-signed.pem b/certs/lets-encrypt-x2-cross-signed.pem
deleted file mode 100644
index c152625..0000000
--- a/certs/lets-encrypt-x2-cross-signed.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIRAMODTJjAvWslLKN5tm+lKw4wDQYJKoZIhvcNAQELBQAw
-PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
-Ew5EU1QgUm9vdCBDQSBYMzAeFw0xNTEwMTkyMjM1MDFaFw0yMDEwMTkyMjM1MDFa
-MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
-ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMjCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBAOEkdEJ7t5Ex2XP/OKrYzkRctzkK3ESuDb1FuZc3Z6+9UE9f
-0xBUa/dB2o5j5m1bwOhAqYxB/NEDif9iYQlg1gcFeJqQvRpkPk/cz3cviWvLZ69B
-TcWNAMBr/o2E3LXylTGo6PaQoENKk3Rcsz5DaUuJIkd0UT6ZZMPNJAH5hC8odxci
-p93DbAhMZi83dMVvk46wRjcWYdFQmMiwD09YU3ys9totlmFQrUPcCqZPnrVSuZyO
-707fRrMx3CD8acKjIHU+7DgbNk5mZtLf9Wakky97pg6UPmA9Skscb7q0TRw8kVhu
-L03E2nDb7QE5dsBJ5+k1tRQGkMHlkuIQ/Wu5tIUCAwEAAaOCAZIwggGOMBIGA1Ud
-EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAy
-BggrBgEFBQcwAYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5j
-b20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMv
-ZHN0cm9vdGNheDMucDdjMB8GA1UdIwQYMBaAFMSnsaR7LHH62+FLkHX/xBVghYkQ
-MFQGA1UdIARNMEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUH
-AgEWImh0dHA6Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUw
-MzAxoC+gLYYraHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JM
-LmNybDATBgNVHR4EDDAKoQgwBoIELm1pbDAdBgNVHQ4EFgQUxbGrTkyxzWQwk37B
-hJkFq+YD4iUwDQYJKoZIhvcNAQELBQADggEBAAcSAhaE7rvHxyUnhgkEpMR56o2I
-IH+mlw5kknjhAuvaBIAM59MZkFbFg5CrNWt8K+G3UoxJgFwv7HvJJxqwgPpNgXC/
-uT3prkvwt+2lvzKJKbqdH+lo40P8EuSyyJOz2hjrRzNMHbJHYDS9OhF5WC5LOQQa
-ydgLZ/JHxXgJypEZqcmVgQ+yYBs0XPwXjE7OE8vbx5REwu7gToMIqAoWRoWW2MxS
-g28RGPVnHzHk2XV1nZGy9T+NYQ91vWWJr1pzNEFZ0cnA2xGwTeJ+zZ3URCfw3Z1U
-+YAL3YUmrvdoRBlASOTmNJmXSo9qvMYPa3DEomAPoFQFZqsSN6kuqDEIqMA=
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-x3-cross-signed.pem b/certs/lets-encrypt-x3-cross-signed.pem
deleted file mode 100644
index 0002462..0000000
--- a/certs/lets-encrypt-x3-cross-signed.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
-SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
-GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
-q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
-SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
-Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
-a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
-/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
-CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
-bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
-c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
-VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
-ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
-MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
-Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
-AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
-uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
-wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
-X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
-PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
-KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
------END CERTIFICATE-----
diff --git a/certs/lets-encrypt-x4-cross-signed.pem b/certs/lets-encrypt-x4-cross-signed.pem
deleted file mode 100644
index c0b4eb6..0000000
--- a/certs/lets-encrypt-x4-cross-signed.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc6bLEeMfizANBgkqhkiG9w0BAQsFADA/
-MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
-DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDEwMloXDTIxMDMxNzE2NDEwMlow
-SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
-GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFg0MIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEA4SR0Qnu3kTHZc/84qtjORFy3OQrcRK4NvUW5lzdnr71QT1/T
-EFRr90HajmPmbVvA6ECpjEH80QOJ/2JhCWDWBwV4mpC9GmQ+T9zPdy+Ja8tnr0FN
-xY0AwGv+jYTctfKVMajo9pCgQ0qTdFyzPkNpS4kiR3RRPplkw80kAfmELyh3FyKn
-3cNsCExmLzd0xW+TjrBGNxZh0VCYyLAPT1hTfKz22i2WYVCtQ9wKpk+etVK5nI7v
-Tt9GszHcIPxpwqMgdT7sOBs2TmZm0t/1ZqSTL3umDpQ+YD1KSxxvurRNHDyRWG4v
-TcTacNvtATl2wEnn6TW1FAaQweWS4hD9a7m0hQIDAQABo4IBfTCCAXkwEgYDVR0T
-AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
-CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
-bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
-c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
-VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
-ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
-MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
-Y3JsMB0GA1UdDgQWBBTFsatOTLHNZDCTfsGEmQWr5gPiJTANBgkqhkiG9w0BAQsF
-AAOCAQEANlaeSdstfAtqFN3jdRZJFjx9X+Ob3PIDlekPYQ1OQ1Uw43rE1FUj7hUw
-g2MJKfs9b7M0WoQg7C20nJY/ajsg7pWhUG3J6rlkDTfVY9faeWi0qsPYXE6BpBDr
-5BrW/Xv8yT8U2BiEAmNggWq8dmFl82fghmLzHBM8X8NZ3ZwA1fGePA53AP5IoD+0
-ArpW8Ik1sSuQBjZ8oQLfN+G8OoY7MNRopyLyQQCNy4aWfE+xYnoVoa5+yr+aPiX0
-7YQrY/cKawAn7QB4PyF5//IKSAVs7mAuB68wbMdE3FKfOHfJ24W4z/bIJTrTY8Y5
-Sr4AUhtzf8oVDrHZYWRrP4joIcOu/Q==
------END CERTIFICATE-----
diff --git a/certs/letsencryptauthorityx1.pem b/certs/letsencryptauthorityx1.pem
deleted file mode 100644
index 0a9a3ce..0000000
--- a/certs/letsencryptauthorityx1.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------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/certs/letsencryptauthorityx2.pem b/certs/letsencryptauthorityx2.pem
deleted file mode 100644
index 3a8e77c..0000000
--- a/certs/letsencryptauthorityx2.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------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-----
diff --git a/certs/letsencryptauthorityx3.pem b/certs/letsencryptauthorityx3.pem
deleted file mode 100644
index 4e82cb5..0000000
--- a/certs/letsencryptauthorityx3.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1
-WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
-RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi
-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
-ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx
-A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM
-UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2
-DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1
-eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu
-OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw
-p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY
-2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0
-ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR
-PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b
-rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt
------END CERTIFICATE-----
diff --git a/certs/letsencryptauthorityx4.pem b/certs/letsencryptauthorityx4.pem
deleted file mode 100644
index 34064da..0000000
--- a/certs/letsencryptauthorityx4.pem
+++ /dev/null
@@ -1,32 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFjTCCA3WgAwIBAgIRAJObmZ6kjhYNW0JZtD0gE9owDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0NDM0
-WhcNMjExMDA2MTU0NDM0WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
-RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDQwggEi
-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
-AF4tI1yGjZgld9lP01+zftU3aSV0un0d2GKUMO7GxvwTLWAKQz/eT+u3J4+GvpD+
-BMfopIxkJcDCzMChjjZtZZwJpIY7BatVrO6OkEmaRNITtbZ/hCwNkUnbk3C7EG3O
-GJZlo9b2wzA8v9WBsPzHpTvLfOr+dS57LLPZBhp3ArHaLbdk33lIONRPt9sseDEk
-mdHnVmGmBRf4+J0Wy67mddOvz5rHH8uzY94raOayf20gzzcmqmot4hPXtDG4Y49M
-oFMMT2kcWck3EOTAH6QiGWkGJ7cxMfSL3S0niA6wgFJtfETETOZu8AVDgENgCJ3D
-S0bz/dhVKvs3WRkaKuuR/W0nnC2VDdaFj4+CRF8LGtn/8ERaH48TktH5BDyDVcF9
-zfJ75Scxcy23jAL2N6w3n/t3nnqoXt9Im4FprDr+mP1g2Z6Lf2YA0jE3kZalgZ6l
-NHu4CmvJYoOTSJw9X2qlGl1K+B4U327rG1tRxgjM76pN6lIS02PMECoyKJigpOSB
-u4V8+LVaUMezCJH9Qf4EKeZTHddQ1t96zvNd2s9ewSKx/DblXbKsBDzIdHJ+qi6+
-F9DIVM5/ICdtDdulOO+dr/BXB+pBZ3uVxjRANvJKKpdxkePyluITSNZHbanWRN07
-gMvwBWOL060i4VrL9er1sBQrRjU9iNpZQGTnLVAxQVFu
------END CERTIFICATE-----
diff --git a/client b/client
index fdef865..2a5ee3c 100755
--- a/client
+++ b/client
@@ -43,7 +43,7 @@ use warnings;
# instance own by another user and created with umask 0177) is not a
# problem since SOCKET_FD can be bound as root prior to the execve(2).
-our $VERSION = '0.8.0';
+our $VERSION = '0.8.3';
my $PROTOCOL_VERSION = 1;
my $NAME = 'lacme-client';
@@ -56,8 +56,6 @@ use Date::Parse ();
use LWP::UserAgent ();
use JSON ();
-use Config::Tiny ();
-
# Clean up PATH
$ENV{PATH} = join ':', qw{/usr/bin /bin};
delete @ENV{qw/IFS CDPATH ENV BASH_ENV/};
@@ -87,13 +85,13 @@ do {
if (defined (my $extra = $2)) {
my $h = eval { JSON::->new->decode($extra) };
if ($@ or !defined $h) {
- print STDERR "WARN: Ignoring extra greeting data from accountd \"$extra\"\n";
+ print STDERR "Warning: Ignoring extra greeting data from accountd \"$extra\"\n";
} else {
print STDERR "Received extra greeting data from accountd: $extra\n" if $ENV{DEBUG};
($JWK_thumbprint, $ALG, $KID) = @$h{qw/jwk-thumbprint alg kid/};
}
}
- my $jwk_str = $S->getline() // die "ERROR: No JWK from lacme-accountd\n";
+ my $jwk_str = $S->getline() // die "Error: No JWK from lacme-accountd\n";
$JWK = JSON::->new->decode($jwk_str);
$JWK_thumbprint //= encode_base64url(sha256(json()->encode($JWK))); # SHA-256 is hardcoded, see RFC 8555 sec. 8.1
$ALG //= "RS256";
@@ -107,11 +105,7 @@ do {
my $CONFIG = do {
my $conf = do { local $/ = undef; <$CONFFILE> };
- close $CONFFILE or die "close: $!";
- my $h = Config::Tiny::->read_string($conf) or die Config::Tiny::->errstr()."\n";
- $h->{_} //= {};
- $h->{client}->{$_} //= $h->{_}->{$_} foreach keys %{$h->{_}}; # add defaults
- $h->{client};
+ JSON::->new->decode($conf);
};
my $UA = do {
my %args = %$CONFIG;
@@ -210,7 +204,7 @@ sub acme2($$;$) {
$payload = defined $payload ? encode_base64url(json()->encode($payload)) : "";
$S->printflush($protected, ".", $payload, "\r\n");
- my $sig = $S->getline() // die "ERROR: No response from lacme-accountd\n";
+ my $sig = $S->getline() // die "Error: lost connection with lacme-accountd\n";
$sig =~ s/\r\n\z// or die;
undef $NONCE; # consume the nonce
@@ -249,7 +243,7 @@ sub acme_resource($%) {
if ($r eq "newAccount" or ($r eq "revokeCert" and !defined $KID)) {
# per RFC 8555 sec. 6.2 these requests MUST have a JWK
- print STDERR "WARNING: lacme-accountd supplied an empty JWK; try removing 'keyid' ",
+ print STDERR "Warning: lacme-accountd supplied an empty JWK; try removing 'keyid' ",
"setting from lacme-accountd.conf if the ACME resource request fails.\n"
unless %$JWK;
return acme2($uri, {jwk => $JWK}, \%payload);
@@ -338,11 +332,12 @@ elsif ($COMMAND eq 'newOrder') {
my $keyAuthorization = $challenge->{token}.'.'.$JWK_thumbprint;
# serve $keyAuthorization at http://$domain/.well-known/acme-challenge/$challenge->{token}
- if (sysopen(my $fh, $challenge->{token}, O_CREAT|O_EXCL|O_WRONLY, 0644)) {
+ if (sysopen(my $fh, $challenge->{token}, O_CREAT|O_EXCL|O_WRONLY)) {
+ # note: the file is created mode 0666 minus umask restrictions
$fh->print($keyAuthorization);
$fh->close() or die "close: $!";
} elsif ($! == EEXIST) {
- print STDERR "WARNING: File exists: $challenge->{token}\n";
+ print STDERR "Warning: File exists: $challenge->{token}\n";
} else {
die "open($challenge->{token}): $!";
}
@@ -351,11 +346,12 @@ elsif ($COMMAND eq 'newOrder') {
}
# poll the order URL (to get the status of all challenges at once)
- # until the status become 'valid'
+ # until the status become 'valid'; see RFC 8555 sec. 7.1.6 for the
+ # the status change flow
my $orderstr = join(', ', map {uc($_->{type}) .":". $_->{value}} @identifiers);
my $certuri;
- for (my $i = 0;;) {
- my $r = acme($orderurl);
+ for (my $i = 0, my $url = $orderurl, my $payload;;) {
+ my $r = acme($url => $payload);
my $resp = request_json_decode($r);
if (defined (my $problem = $resp->{error})) { # problem document (RFC 7807)
my $msg = $problem->{status};
@@ -366,19 +362,21 @@ elsif ($COMMAND eq 'newOrder') {
my $status = $resp->{status};
if (!defined $status or $status eq "invalid") {
die "Error: Invalid order $orderstr\n";
- }
- elsif ($status eq "ready") {
- my $r = acme($order->{finalize}, {csr => encode_base64url($csr)});
- my $resp = request_json_decode($r);
- $certuri = $resp->{certificate};
- last;
- }
- elsif ($status eq "valid") {
+ } elsif ($status eq "pending") {
+ # keep retrying
+ } elsif ($status eq "ready") {
+ $url = $order->{finalize};
+ $payload = {csr => encode_base64url($csr)};
+ # retry after moving to "processing" or "valid" state
+ next;
+ } elsif ($status eq "processing") {
+ $url = $orderurl;
+ undef $payload;
+ } elsif ($status eq "valid") {
$certuri = $resp->{certificate} //
die "Error: Missing \"certificate\" field in \"valid\" order\n";
last;
- }
- elsif ($status ne "pending" and $status ne "processing") {
+ } else {
warn "Unknown order status: $status\n";
}
diff --git a/config/lacme-certs.conf b/config/lacme-certs.conf
index 5259690..4af5652 100644
--- a/config/lacme-certs.conf
+++ b/config/lacme-certs.conf
@@ -52,11 +52,11 @@
# username[:groupname] to chown the issued certificate and
# certificate-chain with.
#
-#chown = root:root
+#owner = root:root
# Octal mode to chmod the issued certificate and certificate-chain with.
#
-#chmod = 0644
+#mode = 0644
# Command to pass the the system's command shell ("/bin/sh -c") after
# successful installation of the certificate and/or certificate-chain.
diff --git a/debian/changelog b/debian/changelog
index f359a55..ca3e7b3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,55 @@
+lacme (0.8.3-1) unstable; urgency=high
+
+ * New upstream bugfix release.
+ + Fix post-issuance validation logic. We avoid pinning the intermediate
+ certificates in the bundle and instead validate the leaf certificate
+ with intermediates supplied during issuance as untrusted (used for chain
+ building only). Only the root certificates are used as trust anchor.
+ Not pinning intermediate certificates is in line with Let's Encrypt's
+ latest recommendations.
+ Closes: #1072847
+ + Pass `-in /dev/stdin` option to openssl(1) to avoid warning with OpenSSL
+ 3.2 or later.
+ + Fix test suite to account for Let's Encrypt's (staging) ACME server
+ changes.
+ * d/control: Update Standards-Version to 4.7.0 (no changes necessary).
+
+ -- Guilhem Moulin <guilhem@debian.org> Thu, 13 Jun 2024 17:56:33 +0200
+
+lacme (0.8.2-1) unstable; urgency=medium
+
+ * New upstream bugfix release.
+ + client: Handle "ready" → "processing" → "valid" status change during
+ newOrder, instead of just "ready" → "valid". The latter may be what we
+ observe when the server is fast enough, but according to RFC 8555 sec.
+ 7.1.6 the state actually transitions via "processing" state and we need
+ to account for that. Closes: #1034834.
+ + Test suite: Point stretch's archive URL to archive.d.o.
+
+ -- Guilhem Moulin <guilhem@debian.org> Tue, 25 Apr 2023 20:08:21 +0200
+
+lacme (0.8.1-1) unstable; urgency=medium
+
+ [ Guilhem Moulin ]
+ * New upstream bugfix release.
+ * Salsa CI: Remove default configuration file.
+ * d/control: Improve long package descriptions.
+ * Set field Upstream-Name in debian/copyright.
+ * Update standards version to 4.6.2, no changes needed.
+
+ [ Debian Janitor ]
+ * d/control: Add 'Multi-Arch: foreign' mark.
+
+ -- Guilhem Moulin <guilhem@debian.org> Wed, 25 Jan 2023 03:33:11 +0100
+
+lacme (0.8.0-2) unstable; urgency=medium
+
+ * d/lacme.postrm: Don't delete system users on purge. There might be files
+ on disk owned by _lacme-client when 'challenge-directory' is set in the
+ configuration (closes: #988032).
+
+ -- Guilhem Moulin <guilhem@debian.org> Tue, 04 May 2021 01:37:13 +0200
+
lacme (0.8.0-1) unstable; urgency=low
* New upstream release (closes: #970458, #970800, #972456).
diff --git a/debian/control b/debian/control
index be18eac..cf41ba9 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
Maintainer: Guilhem Moulin <guilhem@debian.org>
Build-Depends: debhelper-compat (= 13), jq, pandoc (>= 2.1~)
Rules-Requires-Root: no
-Standards-Version: 4.5.1
+Standards-Version: 4.7.0
Homepage: https://git.guilhem.org/lacme/about/
Vcs-Git: https://salsa.debian.org/debian/lacme.git -b debian/latest
Vcs-Browser: https://salsa.debian.org/debian/lacme
@@ -22,7 +22,9 @@ Depends: adduser,
${perl:Depends}
Recommends: lacme-accountd (>= 0.8.0), liblwp-protocol-https-perl
Description: ACME client written with process isolation and minimal privileges in mind
- lacme is divided into four components, each with its own executable:
+ lacme is an ACME client which can be used to request X.509 certificates from
+ ACME service providers such as Let's Encrypt or ZeroSSL. The architecture is
+ divided into four components, each with its own executable:
.
* A process to manage the account key and issue SHA-256 signatures needed for
each ACME command. (This process binds to a UNIX-domain socket to reply to
@@ -56,9 +58,12 @@ Architecture: all
Depends: libconfig-tiny-perl, libjson-perl, ${misc:Depends}, ${perl:Depends}
Recommends: libcrypt-openssl-rsa-perl
Suggests: gpg, openssl
+Multi-Arch: foreign
Description: lacme account key manager
- lacme is an ACME client written with process isolation and minimal privileges
- in mind. It is divided into four components, each with its own executable:
+ lacme is an ACME client which can be used to request X.509 certificates from
+ ACME service providers such as Let's Encrypt or ZeroSSL. The architecture is
+ designed with process isolation and minimal privileges in mind, and is divided
+ into four components:
.
* A process to manage the account key and issue SHA-256 signatures needed for
each ACME command. (This process binds to a UNIX-domain socket to reply to
diff --git a/debian/copyright b/debian/copyright
index b2b94c4..1e7760e 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,5 +1,6 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https://git.guilhem.org/lacme/
+Upstream-Name: lacme
Files: *
Copyright: © 2015-2021 Guilhem Moulin <guilhem@fripost.org>
diff --git a/debian/lacme.postrm b/debian/lacme.postrm
deleted file mode 100755
index c52a198..0000000
--- a/debian/lacme.postrm
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-set -e
-
-if [ "$1" = "purge" ]; then
- if getent passwd _lacme-www >/dev/null; then
- deluser --quiet --system _lacme-www
- fi
- if getent passwd _lacme-client >/dev/null; then
- deluser --quiet --system _lacme-client
- fi
-fi
-
-#DEBHELPER#
-exit 0
diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml
deleted file mode 100644
index 33c3a64..0000000
--- a/debian/salsa-ci.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-include:
- - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
- - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
diff --git a/lacme b/lacme
index 731535f..37d3a51 100755
--- a/lacme
+++ b/lacme
@@ -22,7 +22,7 @@ use v5.14.2;
use strict;
use warnings;
-our $VERSION = '0.8.0';
+our $VERSION = '0.8.3';
my $NAME = 'lacme';
use Errno 'EINTR';
@@ -37,13 +37,14 @@ use Socket 1.95 qw/AF_UNIX AF_INET AF_INET6 PF_UNIX PF_INET PF_INET6 PF_UNSPEC
use Config::Tiny ();
use Date::Parse ();
-use Net::SSLeay ();
+use JSON ();
+use Net::SSLeay 1.46 ();
# Clean up PATH
$ENV{PATH} = join ':', qw{/usr/bin /bin};
delete @ENV{qw/IFS CDPATH ENV BASH_ENV/};
-my ($COMMAND, %OPTS, $CONFFILE, $CONFIG, @CLEANUP);
+my ($COMMAND, %OPTS, $CONFIG, @CLEANUP);
$SIG{$_} = sub() { exit 1 } foreach qw/INT TERM/; # run the END block upon SIGINT/SIGTERM
@@ -99,14 +100,12 @@ sub spec_expand($) {
return $str;
}
-sub set_FD_CLOEXEC($$);
my $CONFFILENAME = spec_expand($OPTS{config} // "%E/lacme/$NAME.conf");
do {
print STDERR "Using configuration file: $CONFFILENAME\n" if $OPTS{debug};
- open $CONFFILE, '<', $CONFFILENAME or die "Can't open $CONFFILENAME: $!\n";
- my $conf = do { local $/ = undef; <$CONFFILE> };
- # don't close $CONFFILE so we can pass it to the client
- set_FD_CLOEXEC($CONFFILE, 1);
+ open my $fh, '<', $CONFFILENAME or die "Can't open $CONFFILENAME: $!\n";
+ my $conf = do { local $/ = undef; <$fh> };
+ close $fh or die "close: $!";
my $h = Config::Tiny::->read_string($conf) or die Config::Tiny::->errstr()."\n";
my $defaults = delete $h->{_} // {};
@@ -185,7 +184,7 @@ sub gen_csr(%) {
push @args, "-$args{hash}" if defined $args{hash};
push @args, '-subj', $args{subject}, '-config', $config->filename(), qw/-reqexts v3_req/;
- open my $fh, '-|', qw/openssl req -outform DER/, @args or die "fork: $!";
+ open my $fh, '-|', qw{openssl req -outform DER}, @args or die "fork: $!";
my $csr = do { local $/ = undef; <$fh> };
close $fh or $! ? die "close: $!" : return;
@@ -196,7 +195,7 @@ sub gen_csr(%) {
unless ($pid) {
open STDIN, '<&', $rd or die "dup: $!";
open STDOUT, '>&', \*STDERR or die "dup: $!";
- exec qw/openssl req -noout -text -inform DER/ or die;
+ exec qw{openssl req -in /dev/stdin -inform DER -noout -text} or die;
}
$rd->close() or die "close: $!";
$wd->print($csr);
@@ -240,7 +239,7 @@ sub drop_privileges($$$) {
# set effective and real gid; also set the list of supplementary gids to that single gid
if ($group ne '') {
- my $gid = getgrnam($group) // die "getgrnam($group): $!";
+ my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n");
$) = "$gid $gid";
die "setgroups: $!" if $@;
POSIX::setgid($gid) or die "setgid: $!";
@@ -249,7 +248,7 @@ sub drop_privileges($$$) {
# set effective and real uid
if ($user ne '') {
- my $uid = getpwnam($user) // die "getpwnam($user): $!";
+ my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n");
POSIX::setuid($uid) or die "setuid: $!";
die "Couldn't setuid/seteuid" unless $< == $uid and $> == $uid; # safety check
}
@@ -351,7 +350,7 @@ sub spawn_webserver() {
my $tmpdir = File::Temp::->newdir(CLEANUP => 1, TMPDIR => 1, TEMPLATE => "acme-challenge.XXXXXXXXXX") // die;
chmod 0755, $tmpdir or die "chmod: $!";
if ((my $username = $CONFIG->{client}->{user}) ne '') {
- my $uid = getpwnam($username) // die "getpwnam($username): $!";
+ my $uid = getpwnam($username) // die "getpwnam($username)", ($! ? ": $!" : "\n");
chown($uid, -1, $tmpdir) or die "chown: $!";
}
@@ -376,14 +375,14 @@ sub spawn_webserver() {
if ($domain == AF_UNIX) {
# bind(2) with a loose umask(2) to allow anyone to connect
- my $umask = umask(0111) // die "umask: $!";
+ my $umask = umask(0111) // die;
my $path = Socket::unpack_sockaddr_un($sockaddr);
bind($sock, $sockaddr) or die "Couldn't bind to $p: $!";
push @CLEANUP, sub() {
print STDERR "Unlinking $path\n" if $OPTS{debug};
unlink $path or warn "Warning: Couldn't unlink $path: $!";
};
- umask($umask) // die "umask: $!";
+ umask($umask) // die;
}
else {
bind($sock, $sockaddr) or die "Couldn't bind to $p: $!";
@@ -536,7 +535,7 @@ sub acme_client($@) {
my ($cmd, @args) = split(/\s+/, $accountd->{command}) or die "Empty accountd command\n";
$_ = spec_expand($_) foreach ($cmd, @args); # expand %-specifiers after privilege drop and whitespace split
push @args, '--stdio';
- push @args, '--config='.spec_expand($accountd->{config}) if $accountd->{config} ne '';
+ push @args, '--config='.$accountd->{config} if $accountd->{config} ne '';
push @args, '--privkey='.$accountd->{privkey} if $accountd->{privkey} ne ''; # XXX deprecated in 0.8.0
push @args, '--quiet' unless lc $accountd->{quiet} eq 'no';
push @args, '--debug' if $OPTS{debug};
@@ -573,18 +572,26 @@ sub acme_client($@) {
die "connect: $!";
}
}
+ set_FD_CLOEXEC($client, 1);
+
+ my $client_config;
+ do {
+ my $tmp = File::Temp::->new(TMPDIR => 1, TEMPLATE => "lacme-client.conf.json-XXXXXXXXXX", UNLINK => 1) // die;
+ print $tmp JSON::->new->encode($conf);
+ open $client_config, "<", $tmp->filename() or die "open: $!";
+ };
# use execve(2) rather than a Perl pseudo-process to ensure that the
# child doesn't have access to the parent's memory
my ($cmd, @args2) = split(/\s+/, $conf->{command}) or die "Empty client command\n";
- my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($CONFFILE, $client); # untaint fileno
- set_FD_CLOEXEC($client, 1);
+ my @fileno = map { fileno($_) =~ /^(\d+)$/ ? $1 : die } ($client_config, $client); # untaint fileno
my $rv = spawn({in => $args->{in}, out => $args->{out}, child => sub() {
drop_privileges($conf->{user}, $conf->{group}, $args->{chdir} // '/');
- set_FD_CLOEXEC($_, 0) foreach ($CONFFILE, $client);
- seek($CONFFILE, SEEK_SET, 0) or die "seek: $!";
+ umask(0022) // die;
+ set_FD_CLOEXEC($_, 0) for ($client_config, $client);
$ENV{DEBUG} = $OPTS{debug} // 0;
}}, $cmd, @args2, $COMMAND, @fileno, @args);
+ close $client_config or die "close: $!\n";
if (defined $cleanup) {
@CLEANUP = grep { $_ ne $cleanup } @CLEANUP;
@@ -657,44 +664,43 @@ sub spawn($@) {
#############################################################################
# Install the certificate (optionally excluding the chain of trust)
#
-sub install_cert($$;$) {
- my ($filename, $chain, $leafonly) = @_;
+sub install_cert($$%) {
+ my ($path, $content, %args) = @_;
- my ($dirname, $basename) =
- $filename =~ /\A(.*)\/([^\/]+)\z/ ? ($1, $2) : ('.', $filename);
- my $fh = File::Temp::->new(UNLINK => 0, DIR => $dirname,
- TEMPLATE => "$basename.XXXXXX") // die;
+ my $fh = File::Temp::->new(TEMPLATE => "$path.XXXXXXXXXX", UNLINK => 0) // die;
+ my $path_tmp = $fh->filename();
eval {
- my $umask = umask() // die "umask: $!";
- chmod(0644 &~ $umask, $fh) or die "chmod: $!";
- if ($leafonly) {
- # keep only the leaf certificate
- pipe my $rd, my $wd or die "pipe: $!";
- my $pid = fork // die "fork: $!";
- unless ($pid) {
- open STDIN, '<&', $rd or die "dup: $!";
- open STDOUT, '>&', $fh or die "dup: $!";
- exec qw/openssl x509 -outform PEM/ or die;
- }
- $rd->close() or die "close: $!";
- $wd->print($chain);
- $wd->close() or die "close: $!";
+ $fh->print($content) or die "print: $!";
- waitpid $pid => 0;
- die $? if $? > 0;
+ my $mode;
+ if ((my $m = $args{mode}) ne "") {
+ die "Not an octal string: $m\n" unless $m =~ /^[0-9]+$/;
+ $mode = oct($m);
} else {
- $fh->print($chain) or die "print: $!";
+ my $umask = umask() // die;
+ $mode = 0644 &~ $umask;
}
+ chmod($mode, $fh) or die "chown: $!";
+
+ if ((my $owner = $args{owner}) ne "") {
+ my ($user, $group) = split /:/, $owner, 2;
+ my $uid = getpwnam($user) // die "getpwnam($user)", ($! ? ": $!" : "\n");
+ my $gid = getgrnam($group) // die "getgrnam($group)", ($! ? ": $!" : "\n") if defined $group;
+ chown($uid, $gid // -1, $fh) or die "chown: $!";
+ }
+
$fh->close() or die "close: $!";
};
- my $path = $fh->filename();
+
if ($@) {
- print STDERR "Unlinking $path\n" if $OPTS{debug};
- unlink $path or warn "unlink($path): $!";
+ print STDERR "Unlinking $path_tmp\n" if $OPTS{debug};
+ unlink $path_tmp or warn "unlink($path_tmp): $!";
die $@;
+ } else {
+ # atomically replace $path if it exists
+ rename($path_tmp, $path) or die "rename($path_tmp, $path): $!";
}
- rename($path, $filename) or die "rename($path, $filename): $!";
}
@@ -743,7 +749,8 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
my $def = delete $h->{_} // {};
$defaults{$_} = $def->{$_} foreach keys %$def;
my @valid = qw/certificate certificate-chain certificate-key min-days CAfile
- hash keyUsage subject subjectAltName tlsfeature chown chmod notify/;
+ hash keyUsage subject subjectAltName tlsfeature
+ owner chown mode chmod notify/;
foreach my $s (keys %$h) {
$conf->{$s} = { map { $_ => delete $h->{$s}->{$_} } @valid };
die "Unknown option(s) in [$s]: ".join(', ', keys %{$h->{$s}})."\n" if %{$h->{$s}};
@@ -766,15 +773,15 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
print STDERR " $_ = $conf->{$_}\n" foreach grep { defined $conf->{$_} } (sort keys %$conf);
}
- my $cert = $conf->{'certificate-chain'} // $conf->{'certificate'};
- unless (defined $cert) {
+ my @certpaths = grep {defined $_ and $_ ne ""} @$conf{qw/certificate-chain certificate/};
+ unless (@certpaths) {
print STDERR "[$s] Warning: Missing 'certificate' and 'certificate-chain', skipping\n";
$rv = 1;
next;
}
# skip certificates that expire at least $conf->{'min-days'} days in the future
- if (-f $cert and defined (my $t = x509_enddate($cert))) {
+ if (-f $certpaths[0] and defined (my $t = x509_enddate($certpaths[0]))) {
my $d = $OPTS{'min-days'} // $conf->{'min-days'} // 21;
if ($d >= 0 and $t - time > $d*86400) {
my $d = POSIX::strftime('%Y-%m-%d %H:%M:%S UTC', gmtime($t));
@@ -807,18 +814,47 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
}
}
- my ($x509, $csr_pubkey, $x509_pubkey);
+ my $chain;
print STDERR "[$s] Will request authorization for: ".join(", ", @authz), "\n" if $OPTS{debug};
- if (acme_client({chdir => $challenge_dir, in => $csr, out => \$x509}, @authz)) {
+ if (acme_client({chdir => $challenge_dir, in => $csr, out => \$chain}, @authz)) {
print STDERR "[$s] Error: Couldn't issue X.509 certificate!\n";
$rv = 1;
next;
}
+ my @chain;
+ eval {
+ my $mem = Net::SSLeay::BIO_s_mem() or die;
+ my $bio = Net::SSLeay::BIO_new($mem) or die;
+ die "incomplete write" unless
+ Net::SSLeay::BIO_write($bio, $chain) == length($chain);
+
+ my $sk_x509_info = Net::SSLeay::PEM_X509_INFO_read_bio($bio);
+
+ my $n = Net::SSLeay::sk_X509_INFO_num($sk_x509_info);
+ for (my $i = 0; $i < $n; $i++) {
+ my $x509_info = Net::SSLeay::sk_X509_INFO_value($sk_x509_info, $i);
+ my $x509 = Net::SSLeay::P_X509_INFO_get_x509($x509_info);
+ my $cert = Net::SSLeay::PEM_get_string_X509($x509);
+ push @chain, $cert;
+ }
+
+ Net::SSLeay::BIO_free($bio) or die;
+ };
+ if ($@ or !@chain) {
+ print STDERR "[$s] Error: Received bogus X.509 certificate from ACME server!\n";
+ $rv = 1;
+ next;
+ }
+ my $cert = shift @chain; # leave only the intermediate in @chain
+
# extract pubkeys from CSR and cert, and ensure they match
- spawn({in => $csr, out => \$csr_pubkey }, qw/openssl req -inform DER -noout -pubkey/);
- spawn({in => $x509, out => \$x509_pubkey}, qw/openssl x509 -inform PEM -noout -pubkey/);
- unless (defined $x509_pubkey and defined $csr_pubkey and $x509_pubkey eq $csr_pubkey) {
+ # XXX would be nice to use X509_get_X509_PUBKEY and X509_REQ_get_X509_PUBKEY here,
+ # or EVP_PKEY_cmp(), but unfortunately Net::SSLeay 1.88 doesn't support these
+ my ($cert_pubkey, $csr_pubkey);
+ spawn({in => $cert, out => \$cert_pubkey}, qw{openssl x509 -in /dev/stdin -inform PEM -noout -pubkey});
+ spawn({in => $csr, out => \$csr_pubkey }, qw{openssl req -in /dev/stdin -inform DER -noout -pubkey});
+ unless (defined $cert_pubkey and defined $csr_pubkey and $cert_pubkey eq $csr_pubkey) {
print STDERR "[$s] Error: Received bogus X.509 certificate from ACME server!\n";
$rv = 1;
next;
@@ -826,9 +862,15 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
# verify certificate validity against the CA bundle
if ((my $CAfile = $conf->{CAfile} // '@@datadir@@/lacme/ca-certificates.crt') ne '') {
- my %args = (in => $x509);
+ my $chain_tmp = File::Temp::->new(SUFFIX => '.crt', TMPDIR => 1) // die;
+ $chain_tmp->say($_) foreach @chain;
+ $chain_tmp->flush();
+
+ my %args = (in => $cert);
$args{out} = \*STDERR if $OPTS{debug};
- my @options = ('-trusted', $CAfile, '-purpose', 'sslserver', '-x509_strict');
+ my @options = ('-trusted', $CAfile);
+ push @options, '-untrusted', $chain_tmp->filename() if @chain;
+ push @options, ('-purpose', 'sslserver', '-x509_strict');
push @options, '-show_chain' if $OPTS{debug};
if (spawn(\%args, 'openssl', 'verify', @options)) {
print STDERR "[$s] Error: Received invalid X.509 certificate from ACME server!\n";
@@ -838,34 +880,23 @@ elsif ($COMMAND eq 'newOrder' or $COMMAND eq 'new-cert') {
}
# install certificate
- if (defined $conf->{'certificate'}) {
- print STDERR "Installing X.509 certificate $conf->{'certificate'}\n";
- install_cert($conf->{'certificate'}, $x509, 1);
- }
- if (defined $conf->{'certificate-chain'}) {
- print STDERR "Installing X.509 certificate chain $conf->{'certificate-chain'}\n";
- install_cert($conf->{'certificate-chain'}, $x509);
- }
-
- if (defined $conf->{chown}) {
- my ($user, $group) = split /:/, $conf->{chown}, 2;
- my $uid = getpwnam($user) // die "getpwnam($user): $!";
- my $gid = defined $group ? (getgrnam($group) // die "getgrnam($group): $!") : -1;
- foreach (grep defined, @$conf{qw/certificate certificate-chain/}) {
- chown($uid, $gid, $_) or die "chown: $!";
- }
+ my %install_opts = (
+ mode => $conf->{mode} // $conf->{chmod} // "",
+ owner => $conf->{owner} // $conf->{chown} // ""
+ );
+ if ((my $path = $conf->{'certificate'} // "") ne "") {
+ print STDERR "Installing X.509 certificate $path\n";
+ install_cert($path => $cert, %install_opts);
}
- if (defined $conf->{chmod}) {
- my $mode = oct($conf->{chmod}) // die;
- foreach (grep defined, @$conf{qw/certificate certificate-chain/}) {
- chmod($mode, $_) or die "chown: $!";
- }
+ if ((my $path = $conf->{'certificate-chain'} // "") ne "") {
+ print STDERR "Installing X.509 certificate chain $path\n";
+ install_cert($path => $chain, %install_opts);
}
my @certopts = join ',', qw/no_header no_version no_pubkey no_sigdump/;
- open my $fh, '|-', qw/openssl x509 -noout -fingerprint -sha256 -text -certopt/, @certopts
+ open my $fh, '|-', qw{openssl x509 -in /dev/stdin -noout -fingerprint -sha256 -text -certopt}, @certopts
or die "fork: $!";
- print $fh $x509;
+ print $fh $cert;
close $fh or die $! ?
"close: $!" :
"Error: x509(1ssl) exited with value ".($? >> 8)."\n";
@@ -894,14 +925,14 @@ elsif ($COMMAND eq 'revokeCert' or $COMMAND eq 'revoke-cert') {
print STDERR "Revoking $filename\n";
# conversion PEM -> DER
- open my $fh, '-|', qw/openssl x509 -outform DER -in/, $filename or die "fork: $!";
+ open my $fh, '-|', qw{openssl x509 -in}, $filename, qw{-outform DER} or die "fork: $!";
my $der = do { local $/ = undef; <$fh> };
close $fh or die $! ?
"close: $!" :
"Error: x509(1ssl) exited with value ".($? >> 8)."\n";
my @certopts = join ',', qw/no_header no_version no_pubkey no_sigdump no_extensions/;
- open my $fh2, '|-', qw/openssl x509 -inform DER -noout -fingerprint -sha256 -text -certopt/, @certopts
+ open my $fh2, '|-', qw{openssl x509 -in /dev/stdin -inform DER -noout -fingerprint -sha256 -text -certopt}, @certopts
or die "fork: $!";
print $fh2 $der;
close $fh2 or die $! ?
diff --git a/lacme-accountd b/lacme-accountd
index 0f5deb2..5bc8b5f 100755
--- a/lacme-accountd
+++ b/lacme-accountd
@@ -23,7 +23,7 @@ use v5.14.2;
use strict;
use warnings;
-our $VERSION = '0.8.0';
+our $VERSION = '0.8.3';
my $PROTOCOL_VERSION = 1;
my $NAME = 'lacme-accountd';
@@ -64,18 +64,21 @@ sub usage(;$$) {
usage(1) unless GetOptions(\%OPTS, qw/config=s privkey=s socket=s stdio quiet|q debug help|h/);
usage(0) if $OPTS{help};
-my $LOG;
+my ($LOG, $LOGLEVEL);
+my ($LOG_INFO, $LOG_VERBOSE, $LOG_DEBUG) = (0,1,2);
sub logmsg($@) {
- my $lvl = shift // "all";
- if (defined $LOG) {
+ my $lvl = shift;
+ if (defined $LOG and ($lvl <= $LOGLEVEL or $lvl <= $LOG_VERBOSE)) {
+ # --quiet flag hides verbose-level messages from the standard
+ # error but we add them to the logfile nonetheless
my $now = localtime;
$LOG->printflush("[", $now, "] ", @_, "\n") or warn "print: $!";
}
- unless (($lvl eq "debug" and !$OPTS{debug}) or ($lvl eq "noquiet" and $OPTS{quiet})) {
+ if ($lvl <= $LOGLEVEL) {
print STDERR @_, "\n" or warn "print: $!";
}
}
-sub info(@) { logmsg(all => @_); }
+sub info(@) { logmsg($LOG_INFO => @_); }
sub error(@) {
my @msg = ("Error: ", @_);
info(@msg);
@@ -83,7 +86,8 @@ sub error(@) {
}
sub panic(@) {
my @loc = caller;
- my @msg = (@_, " at line $loc[2] in $loc[1]");
+ my @msg = ("PANIC at line $loc[2] in $loc[1]");
+ push @msg, ": ", @_ if @_;
info(@msg);
exit 255;
}
@@ -133,7 +137,7 @@ do {
print STDERR "Ignoring missing configuration file at default location $conffile\n" if $OPTS{debug};
}
- $OPTS{quiet} = 0 if $OPTS{debug};
+ $LOGLEVEL = $OPTS{debug} ? $LOG_DEBUG : $OPTS{quiet} ? $LOG_INFO : $LOG_VERBOSE;
error("'privkey' is not specified") unless defined $OPTS{privkey};
};
@@ -211,9 +215,9 @@ unless (defined $OPTS{stdio}) {
my @stat = stat($dirname) or error("stat($dirname): $!");
error("Insecure permissions on $dirname") if ($stat[2] & 0022) != 0;
- my $umask = umask(0177) // panic("umask: $!");
+ my $umask = umask(0177) // panic();
- logmsg(noquiet => "Starting lacme Account Key Manager at $sockname");
+ logmsg($LOG_VERBOSE => "Starting lacme Account Key Manager at $sockname");
socket(my $sock, PF_UNIX, SOCK_STREAM, 0) or panic("socket: $!");
my $sockaddr = Socket::sockaddr_un($sockname) // panic();
bind($sock, $sockaddr) or panic("bind: $!");
@@ -221,7 +225,7 @@ unless (defined $OPTS{stdio}) {
($SOCKNAME, $S) = ($sockname, $sock);
listen($S, 1) or panic("listen: $!");
- umask($umask) // panic("umask: $!");
+ umask($umask) // panic();
};
@@ -234,26 +238,43 @@ sub conn($$$) {
$out->printflush( "$PROTOCOL_VERSION OK ", $EXTRA_GREETING_STR, "\r\n",
$JWK_STR, "\r\n" ) or warn "print: $!";
- # sign whatever comes in
while (defined (my $data = $in->getline())) {
$data =~ s/\r\n\z// or panic();
+ # validate JWS Signing Input from RFC 7515:
+ # ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload))
my ($header, $payload) = split(/\./, $data, 2);
- unless (defined $header and $header =~ /\A[A-Za-z0-9\-_]+\z/) {
- info("[$id] >>> Error: Refusing to sign request: Malformed protected header");
+ if (defined $header and $header =~ /\A[A-Za-z0-9\-_]+\z/) {
+ $header = decode_base64url($header);
+ } else {
+ info("[$id] NOSIGN [malformed JWS Protected Header]");
last;
}
- unless (defined $payload and $payload =~ /\A[A-Za-z0-9\-_]*\z/) {
- # POST-as-GET yields an empty payload
- info("[$id] >>> Error: Refusing to sign request: Malformed payload");
+ if (defined $payload and $payload =~ /\A[A-Za-z0-9\-_]*\z/) {
+ # empty payloads are valid, and used for POST-as-GET (RFC 8555 sec. 6.3)
+ $payload = decode_base64url($payload);
+ } else {
+ info("[$id] NOSIGN [malformed JWS Payload]");
last;
}
- logmsg(noquiet => "[$id] >>> OK signing request: ",
- "header=base64url(", decode_base64url($header), "); ",
- "playload=base64url(", decode_base64url($payload), ")");
+ my $req = "header=base64url($header) playload=base64url($payload)";
+
+ eval { $header = JSON::->new->decode($header); };
+ if ($@ or # couldn't decode (parse error)
+ # RFC 7515: not a JSON object
+ !defined($header) or ref($header) ne "HASH" or
+ # RFC 8555 sec. 6.2: the protected Header MUST include all these fields
+ grep !defined, @$header{qw/alg nonce url/} or
+ # RFC 8555 sec. 6.2: the protected header MUST include any of these fields
+ !grep defined, @$header{qw/jwk kid/}) {
+ info("[$id] NOSIGN [invalid JWS Protected Header] ", $req);
+ last;
+ }
- my $sig = $SIGN->($data);
+ my $sig = eval { $SIGN->($data) };
+ panic($@) if $@ or !defined $sig;
+ logmsg($LOG_VERBOSE => "[$id] SIGNED ", $req);
$out->printflush( encode_base64url($sig), "\r\n" ) or warn "print: $!";
}
}
@@ -267,9 +288,9 @@ if (defined $OPTS{stdio}) {
next if $! == EINTR; # try again if accept(2) was interrupted by a signal
panic("accept: $!");
};
- logmsg(noquiet => "[$count] >>> Accepted new connection");
+ logmsg($LOG_VERBOSE => "[$count] Accepted new connection");
conn($conn, $conn, $count);
- logmsg(noquiet => "[$count] >>> Connection terminated");
+ logmsg($LOG_VERBOSE => "[$count] Connection terminated");
$conn->close() or warn "close: $!";
}
}
@@ -279,11 +300,11 @@ if (defined $OPTS{stdio}) {
#
END {
if (defined $SOCKNAME and -S $SOCKNAME) {
- logmsg(debug => "Unlinking $SOCKNAME");
+ logmsg($LOG_DEBUG => "Unlinking $SOCKNAME");
unlink $SOCKNAME or info("Error: unlink($SOCKNAME): $!");
}
if (defined $S) {
- logmsg(noquiet => "Shutting down and closing lacme Account Key Manager");
+ logmsg($LOG_VERBOSE => "Shutting down and closing lacme Account Key Manager");
shutdown($S, SHUT_RDWR) or info("Error: shutdown: $!");
close $S or info("Error: close: $!");
}
diff --git a/lacme.8.md b/lacme.8.md
index ad6dab6..65f1c36 100644
--- a/lacme.8.md
+++ b/lacme.8.md
@@ -322,9 +322,8 @@ UNIX-domain socket.
*config*
-: Path to the [`lacme-accountd`(1)] configuration file. The value is
- subject to [%-specifier expansion](#percent-specifiers) _after_
- privilege drop.
+: Path to the [`lacme-accountd`(1)] configuration file. Note that the
+ value might be subject to %-expansion by [`lacme-accountd`(1)].
*quiet*
@@ -422,15 +421,16 @@ Valid settings are:
See [`x509v3_config`(5ssl)] for a list of possible values. Note
that the ACME server might override the value provided here.
-*chown*
+*owner*, *chown*
: An optional `username[:groupname]` to chown the issued *certificate*
and *certificate-chain* to.
-*chmod*
+*mode*, *chmod*
: An optional octal mode to chmod the issued *certificate* and
- *certificate-chain* to.
+ *certificate-chain* to. By default the files are created with mode
+ 0644 minus umask restrictions.
*notify*
diff --git a/test b/test
index 81d910c..1606151 100755
--- a/test
+++ b/test
@@ -34,6 +34,7 @@ usage() {
# must be routed to this machine.
# This can be done with a wildcard DNS record and opening tcp/80 in firewall.
DOMAINNAME="lacme-test.guilhem.org"
+ACMEAPI_SERVER="https://acme-staging-v02.api.letsencrypt.org/directory"
MODE="dev"
DISTRIBUTION="sid"
@@ -62,10 +63,11 @@ if [ $# -eq 0 ]; then
done
else
for t in "$@"; do
+ t="${t#tests/}"
if [ -f "tests/$t" ]; then
TESTS+=( "$t" )
else
- echo "Error: '$1': no such test" >&2
+ echo "Error: '$t': no such test" >&2
exit 1
fi
done
@@ -116,7 +118,7 @@ run() {
lacme_www_group=nogroup \
lacme_client_user=_lacme-client \
lacme_client_group=nogroup \
- acmeapi_server="https://acme-staging-v02.api.letsencrypt.org/directory"
+ acmeapi_server="$ACMEAPI_SERVER"
CHROOT="$(schroot -c "$DISTRIBUTION-$ARCH-sbuild" -b)"
rootdir="/run/schroot/mount/$CHROOT"
@@ -167,8 +169,7 @@ run() {
sudo install -oroot -groot -m0644 -vT "$BUILDDIR/certs-staging/ca-certificates.crt" \
"$rootdir/usr/share/lacme/ca-certificates.crt"
sudo schroot -d"/" -c "$CHROOT" -r -- \
- sed -ri '0,/^#?server\s*=.*/ {s||server = https://acme-staging-v02.api.letsencrypt.org/directory|}' \
- /etc/lacme/lacme.conf
+ sed -ri "0,/^#?server\\s*=.*/ {s||server = $ACMEAPI_SERVER|}" /etc/lacme/lacme.conf
# install account key and configure lacme accordingly
sudo install -oroot -groot -m0600 -vT -- "$BUILDDIR/account.key" \
diff --git a/tests/account-encrypted-gpg b/tests/account-encrypted-gpg
index fd1e4ac..7cb978d 100644
--- a/tests/account-encrypted-gpg
+++ b/tests/account-encrypted-gpg
@@ -9,7 +9,7 @@ keyid="$(gpg --list-secret-key --with-colons | grep -m1 ^fpr: | cut -sd: -f10)"
gpg --encrypt -r "$keyid" /etc/lacme/account.key
sed -ri '0,\|^#?privkey\s*=.*| {s||privkey = gpg:/etc/lacme/account.key.gpg|}' /etc/lacme/lacme-accountd.conf
-export GPG_TTY="$(tty)"
+export GPG_TTY="$(tty)" TERM="linux"
lacme account
# vim: set filetype=sh :
diff --git a/tests/account-encrypted-openssl b/tests/account-encrypted-openssl
index e79a528..1f97fd0 100644
--- a/tests/account-encrypted-openssl
+++ b/tests/account-encrypted-openssl
@@ -2,9 +2,10 @@
PASSPHRASE="test"
-openssl rsa -aes128 -passout pass:"$PASSPHRASE" </etc/lacme/account.key >/etc/lacme/account.enc.key
+openssl rsa -in /etc/lacme/account.key -out /etc/lacme/account.enc.key -aes128 -passout pass:"$PASSPHRASE"
sed -ri '0,\|^#?privkey\s*=.*| {s||privkey = file:/etc/lacme/account.enc.key|}' /etc/lacme/lacme-accountd.conf
+export TERM="linux"
lacme account
# vim: set filetype=sh :
diff --git a/tests/accountd b/tests/accountd
index a603c16..433f8ad 100644
--- a/tests/accountd
+++ b/tests/accountd
@@ -65,6 +65,7 @@ grep -F "Error: " ~lacme-account/.local/share/lacme/accountd.log
# rotate the log and start accountd
rm -f ~lacme-account/.local/share/lacme/accountd.log
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# run lacme(8) multiple times using that single lacme-accountd(1) instance
lacme --socket="$SOCKET" --debug account 2>"$STDERR" || fail
@@ -79,9 +80,9 @@ wait
# ensure signature requests are logged
grep -Fq "Starting lacme Account Key Manager at /home/lacme-account/S.lacme" ~lacme-account/.local/share/lacme/accountd.log
-grep -Fq "[0] >>> Accepted new connection" ~lacme-account/.local/share/lacme/accountd.log
-grep -Fq "[1] >>> Accepted new connection" ~lacme-account/.local/share/lacme/accountd.log
+grep -Fq "[0] Accepted new connection" ~lacme-account/.local/share/lacme/accountd.log
+grep -Fq "[1] Accepted new connection" ~lacme-account/.local/share/lacme/accountd.log
grep -Fq "Shutting down and closing lacme Account Key Manager" ~lacme-account/.local/share/lacme/accountd.log
-grep -F ">>> OK signing request:" ~lacme-account/.local/share/lacme/accountd.log
+grep -F "] SIGNED header=base64url({" ~lacme-account/.local/share/lacme/accountd.log
# vim: set filetype=sh :
diff --git a/tests/accountd-kid b/tests/accountd-kid
index e1bd63d..8a4b53c 100644
--- a/tests/accountd-kid
+++ b/tests/accountd-kid
@@ -23,13 +23,14 @@ EOF
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# newAccount resource fails as per RFC 8555 sec. 6.2 it requires a JWK
! lacme --socket="$SOCKET" account 2>"$STDERR" || fail
-grepstderr -Fxq "WARNING: lacme-accountd supplied an empty JWK; try removing 'keyid' setting from lacme-accountd.conf if the ACME resource request fails."
+grepstderr -Fxq "Warning: lacme-accountd supplied an empty JWK; try removing 'keyid' setting from lacme-accountd.conf if the ACME resource request fails."
grepstderr -Fxq "400 Bad Request (Parse error reading JWS)"
-! grep -F ">>> OK signing request: header=" ~lacme-account/.local/share/lacme/accountd.log | \
- grep -vF ">>> OK signing request: header=base64url({\"alg\":\"RS256\",\"jwk\":{}," || exit 1
+grep -F "] SIGNED header=base64url({" ~lacme-account/.local/share/lacme/accountd.log >/tmp/signed
+! grep -vF "] SIGNED header=base64url({\"alg\":\"RS256\",\"jwk\":{}," </tmp/signed
# rotate log and restart accountd
kill $PID
@@ -37,6 +38,7 @@ wait
rm ~lacme-account/.local/share/lacme/accountd.log
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" --quiet & PID=$!
+sleep 1
# newOrder works fine without JWK
lacme --socket="$SOCKET" newOrder
@@ -46,14 +48,14 @@ test /etc/lacme/simpletest.rsa.crt -nt /etc/lacme/simpletest.rsa.key
lacme --socket="$SOCKET" revokeCert /etc/lacme/simpletest.rsa.crt
! lacme --socket="$SOCKET" revokeCert /etc/lacme/simpletest.rsa.crt 2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.rsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.rsa.crt"
kill $PID
wait
# make sure all signing requests have a KID
-! grep -F ">>> OK signing request: header=" ~lacme-account/.local/share/lacme/accountd.log | \
- grep -vF ">>> OK signing request: header=base64url({\"alg\":\"RS256\",\"kid\":\"$keyid\"," || exit 1
+grep -F "] SIGNED header=base64url({" ~lacme-account/.local/share/lacme/accountd.log >/tmp/signed
+! grep -vF "] SIGNED header=base64url({\"alg\":\"RS256\",\"kid\":\"$keyid\"," </tmp/signed
# vim: set filetype=sh :
diff --git a/tests/accountd-remote b/tests/accountd-remote
index 9e7f812..ce2b54e 100644
--- a/tests/accountd-remote
+++ b/tests/accountd-remote
@@ -50,6 +50,6 @@ lacme newOrder
test /etc/lacme/simpletest.rsa.crt -nt /etc/lacme/simpletest.rsa.key
# ensure signature requests are logged
-grep -F ">>> OK signing request:" ~lacme-account/.local/share/lacme/accountd.log
+grep -F "] SIGNED header=base64url({" ~lacme-account/.local/share/lacme/accountd.log
# vim: set filetype=sh :
diff --git a/tests/accountd-validate b/tests/accountd-validate
new file mode 100644
index 0000000..d4be5ee
--- /dev/null
+++ b/tests/accountd-validate
@@ -0,0 +1,36 @@
+# JWS Signing Input (RFC 7515) validation
+
+# missing or empty protected header
+printf "\\r\\n" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -Fq "] NOSIGN [malformed JWS Protected Header]"
+printf ".foo\\r\\n" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -Fq "] NOSIGN [malformed JWS Protected Header]"
+
+# invalid base64url-encoded protected header
+printf "foo/bar.baz\\r\\n" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -Fq "] NOSIGN [malformed JWS Protected Header]"
+
+# missing payload
+printf "foo\\r\\n" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -Fq "] NOSIGN [malformed JWS Payload]"
+
+# invalid base64url-encoded payload
+printf "foo.bar/baz\\r\\n" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -Fq "] NOSIGN [malformed JWS Payload]"
+
+# invalid JWS Protected Header: not a JSON object; missing fields "alg",
+# "nonce", "url", or either "jwk" or "kid"
+for s in "null" "\"str\"" "{}" "{\"alg\":\"\",\"nonce\":\"\",\"url\":\"\"}" "{\"jwk\":{}}"; do
+ s="$(printf "%s" "$s" | base64 -w0 | sed "s/=*$//" | tr "+/" "-_")"
+ printf "%s.\\r\\n" "$s" | lacme-accountd --stdio 2>"$STDERR"
+ grepstderr -F "] NOSIGN [invalid JWS Protected Header]"
+done
+
+# valid JWS Protected Header and Payload
+h="{\"alg\":\"\",\"nonce\":\"\",\"url\":\"\",\"jwk\":{}}"
+s="$(printf "%s" "$h" | base64 -w0 | sed "s/=*$//" | tr "+/" "-_")"
+p="$(printf "%s" "JWS Payload" | base64 -w0 | sed "s/=*$//" | tr "+/" "-_")"
+printf "%s.%s\\r\\n" "$s" "$p" | lacme-accountd --stdio 2>"$STDERR"
+grepstderr -F "] SIGNED header=base64url($h) playload=base64url(JWS Payload)"
+
+# vim: set filetype=sh :
diff --git a/tests/cert-extensions b/tests/cert-extensions
index a397ee5..d7e7855 100644
--- a/tests/cert-extensions
+++ b/tests/cert-extensions
@@ -4,13 +4,13 @@ x509_check() {
local cert="$1" ext out
out="$(mktemp --tmpdir)"
ext="basicConstraints,subjectAltName,keyUsage,extendedKeyUsage,tlsfeature"
- openssl x509 -noout -subject -ext "$ext" -nameopt compat <"$cert" >"$out"
+ openssl x509 -in "$cert" -noout -subject -ext "$ext" -nameopt compat >"$out"
diff --unified --color=auto -b --label="a/${cert#/}" --label="b/${cert#/}" -- - "$out"
}
# default settings (the ACME server adds a subjectAltName with the Common Name)
openssl genpkey -algorithm RSA -out /etc/lacme/test1.key
-commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "[A-Z]" "[a-z]").$DOMAINNAME"
+commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z" "a-z").$DOMAINNAME"
cat >"/etc/lacme/lacme-certs.conf.d/test1.conf" <<- EOF
[test1]
certificate-key = /etc/lacme/test1.key
@@ -34,10 +34,10 @@ EOF
# subjectAltName
openssl genpkey -algorithm RSA -out /etc/lacme/test2.key
-commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "[A-Z]" "[a-z]").$DOMAINNAME"
+commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z" "a-z").$DOMAINNAME"
subjectAltName=""
for i in $(seq 1 8); do
- subjectAltName="${subjectAltName:+"$subjectAltName "}$(head -c10 /dev/urandom | base32 -w0 | tr "[A-Z]" "[a-z]").$DOMAINNAME"
+ subjectAltName="${subjectAltName:+"$subjectAltName "}$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z" "a-z").$DOMAINNAME"
done
cat >"/etc/lacme/lacme-certs.conf.d/test2.conf" <<- EOF
[test2]
@@ -63,7 +63,7 @@ EOF
# tlsfeature
openssl genpkey -algorithm RSA -out /etc/lacme/test3.key
-commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "[A-Z]" "[a-z]").$DOMAINNAME"
+commonName="$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z" "a-z").$DOMAINNAME"
cat >"/etc/lacme/lacme-certs.conf.d/test3.conf" <<- EOF
[test3]
certificate-key = /etc/lacme/test3.key
diff --git a/tests/cert-install b/tests/cert-install
index f2147d2..279309f 100644
--- a/tests/cert-install
+++ b/tests/cert-install
@@ -28,9 +28,58 @@ EOF
grepstderr -Fxq "[bad3] Warning: Couldn't generate CSR, skipping"
+check_spki() {
+ local p1="$1" p2="$2" s1 s2
+ s1="$(openssl x509 -in "$p1" -noout -pubkey \
+ | openssl pkey -pubin -outform DER \
+ | openssl dgst -sha256 \
+ | sed 's/.*=\s*//')"
+ s2="$(openssl pkey -in "$p2" -pubout -outform DER \
+ | openssl dgst -sha256 \
+ | sed 's/.*=\s*//')"
+ if [ -n "$s1" ] && [ "$s1" = "$s2" ]; then
+ return 0
+ else
+ printf "%s != %s\\n" "$s1" "$s2" >&2
+ return 1
+ fi
+}
+check_chain() {
+ local priv="$1" chain="$2" leaf="${3-}" pem0
+
+ csplit -f "${chain%.crt}.chain.pem" "$chain" \
+ "/-----BEGIN CERTIFICATE-----/" "{*}"
+
+ pem0="${chain%.crt}.chain.pem00"
+ if [ ! -s "$pem0" ]; then
+ # 00 is empty, leaf cert is at 01
+ rm -f -- "$pem0"
+ pem0="${chain%.crt}.chain.pem01"
+ fi
+ test -s "$pem0" || return 1
+ check_spki "$pem0" "$priv"
+
+ if [ -n "$leaf" ]; then
+ diff --ignore-blank-lines --unified "$pem0" "$leaf" || return 1
+ fi
+
+ leaf="${chain%.crt}.leaf.pem"
+ mv -T -- "$pem0" "$leaf"
+
+ intermediates="${chain%.crt}.intermediates.pem"
+ sed "/^$/d" "${chain%.crt}.chain.pem"[0-9]* >"$intermediates"
+ test -s "$intermediates" || return 1 # ensure there is at least one intermediate
+
+ openssl verify -trusted /usr/share/lacme/ca-certificates.crt \
+ -untrusted "$intermediates" \
+ -purpose sslserver -x509_strict \
+ -show_chain \
+ -- "$leaf" || return 1
+}
+
# 'certificate' installs only the leaf certificate
openssl genpkey -algorithm RSA -out /etc/lacme/test1.key
-subject="/CN=$(head -c10 /dev/urandom | base32 -w0).$DOMAINNAME"
+subject="/CN=$(head -c10 /dev/urandom | base32 -w0 | tr "A-Z" "a-z").$DOMAINNAME"
cat >"/etc/lacme/lacme-certs.conf.d/test1.conf" <<- EOF
[test1]
certificate-key = /etc/lacme/test1.key
@@ -42,23 +91,9 @@ lacme newOrder test1 2>"$STDERR" || fail newOrder test1
test /etc/lacme/test1.crt -nt /etc/lacme/test1.key
sed -n "0,/^-----END CERTIFICATE-----$/ p" /etc/lacme/test1.crt >/etc/lacme/test1.pem
diff --unified /etc/lacme/test1.crt /etc/lacme/test1.pem
+check_spki /etc/lacme/test1.crt /etc/lacme/test1.key
-check_hash() {
- local p1="$1" p2 s1 s2
- s1="$(openssl x509 -noout -hash <"$p1")"
- for p2 in /usr/share/lacme/ca-certificates.pem.*; do
- s2="$(openssl x509 -noout -hash <"$p2")"
- if [ "$s1" = "$s2" ]; then
- return 0
- fi
- done
- return 1
-}
-csplit -f /usr/share/lacme/ca-certificates.pem. /usr/share/lacme/ca-certificates.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-rm -f /usr/share/lacme/ca-certificates.pem.00
-
# 'certificate-chain' appends the chain of trust
openssl genpkey -algorithm RSA -out /etc/lacme/test2.key
cat >"/etc/lacme/lacme-certs.conf.d/test2.conf" <<- EOF
@@ -70,16 +105,7 @@ EOF
lacme newOrder test2 2>"$STDERR" || fail newOrder test2
test /etc/lacme/test2.crt -nt /etc/lacme/test2.key
-csplit -f /etc/lacme/test2.chain.pem /etc/lacme/test2.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-test -s /etc/lacme/test2.chain.pem01 # leaf cert (00 is empty)
-rm -f /etc/lacme/test2.chain.pem0[01]
-test -s /etc/lacme/test2.chain.pem02 # depth 1
-
-# all certificates at depth >=1 must be in our CA bundle
-for p in /etc/lacme/test2.chain.pem*; do
- check_hash "$p"
-done
+check_chain /etc/lacme/test2.key /etc/lacme/test2.crt
# 'certificate' + 'certificate-chain'
openssl genpkey -algorithm RSA -out /etc/lacme/test3.key
@@ -94,83 +120,110 @@ EOF
lacme newOrder test3 2>"$STDERR" || fail newOrder test3
test /etc/lacme/test3.pem -nt /etc/lacme/test3.key
test /etc/lacme/test3.crt -nt /etc/lacme/test3.key
-csplit -f /etc/lacme/test3.chain.pem /etc/lacme/test3.crt \
- "/-----BEGIN CERTIFICATE-----/" "{*}"
-sed -i "/^$/d" /etc/lacme/test3.chain.pem*
-diff -q /etc/lacme/test3.chain.pem01 /etc/lacme/test3.pem
+check_chain /etc/lacme/test3.key /etc/lacme/test3.crt /etc/lacme/test3.pem
+
st="$(stat -c "%U:%G %#a" /etc/lacme/test3.pem)"
[ "$st" = "root:root 0644" ]
st="$(stat -c "%U:%G %#a" /etc/lacme/test3.crt)"
[ "$st" = "root:root 0644" ]
-# chmod user
+# owner user
openssl genpkey -algorithm RSA -out /etc/lacme/test4.key
cat >"/etc/lacme/lacme-certs.conf.d/test4.conf" <<- EOF
[test4]
certificate-key = /etc/lacme/test4.key
certificate = /etc/lacme/test4.pem
certificate-chain = /etc/lacme/test4.crt
- chown = nobody
+ owner = nonexistent-user
subject = $subject
EOF
+! lacme newOrder test4 2>"$STDERR" || fail newOrder test4
+grepstderr -Fxq "getpwnam(nonexistent-user)"
+! test -e /etc/lacme/test4.pem
+! test -e /etc/lacme/test4.crt
+
+sed -ri "s/^owner\\s*=.*/owner = nobody/" /etc/lacme/lacme-certs.conf.d/test4.conf
lacme newOrder test4 2>"$STDERR" || fail newOrder test4
st="$(stat -c "%U:%G %#a" /etc/lacme/test4.pem)"
[ "$st" = "nobody:root 0644" ]
st="$(stat -c "%U:%G %#a" /etc/lacme/test4.crt)"
[ "$st" = "nobody:root 0644" ]
-# chmod user:group
+# owner user:group
openssl genpkey -algorithm RSA -out /etc/lacme/test5.key
cat >"/etc/lacme/lacme-certs.conf.d/test5.conf" <<- EOF
[test5]
certificate-key = /etc/lacme/test5.key
certificate = /etc/lacme/test5.pem
certificate-chain = /etc/lacme/test5.crt
- chown = nobody:nogroup
+ owner = nobody:nonexistent-group
subject = $subject
EOF
+! lacme newOrder test5 2>"$STDERR" || fail newOrder test5
+grepstderr -Fxq "getgrnam(nonexistent-group)"
+! test -e /etc/lacme/test5.pem
+! test -e /etc/lacme/test5.crt
+
+sed -ri "s/^owner\\s*=.*/owner = nobody:nogroup/" /etc/lacme/lacme-certs.conf.d/test5.conf
lacme newOrder test5 2>"$STDERR" || fail newOrder test5
st="$(stat -c "%U:%G %#a" /etc/lacme/test5.pem)"
[ "$st" = "nobody:nogroup 0644" ]
st="$(stat -c "%U:%G %#a" /etc/lacme/test5.crt)"
[ "$st" = "nobody:nogroup 0644" ]
-# chown
+# umask restrictions (also test empty values)
openssl genpkey -algorithm RSA -out /etc/lacme/test6.key
cat >"/etc/lacme/lacme-certs.conf.d/test6.conf" <<- EOF
[test6]
certificate-key = /etc/lacme/test6.key
- certificate = /etc/lacme/test6.pem
certificate-chain = /etc/lacme/test6.crt
- chmod = 0400
+ certificate =
+ mode =
+ owner =
subject = $subject
EOF
-lacme newOrder test6 2>"$STDERR" || fail newOrder test6
-st="$(stat -c "%U:%G %#a" /etc/lacme/test6.pem)"
-[ "$st" = "root:root 0400" ]
+( umask 0077 && lacme newOrder test6 2>"$STDERR" || fail newOrder test6 )
+! test -e /etc/lacme/test6.pem
st="$(stat -c "%U:%G %#a" /etc/lacme/test6.crt)"
-[ "$st" = "root:root 0400" ]
+[ "$st" = "root:root 0600" ]
-# post-issuance notification
+# mode
openssl genpkey -algorithm RSA -out /etc/lacme/test7.key
cat >"/etc/lacme/lacme-certs.conf.d/test7.conf" <<- EOF
[test7]
certificate-key = /etc/lacme/test7.key
+ certificate = /etc/lacme/test7.pem
certificate-chain = /etc/lacme/test7.crt
+ mode = 0400
subject = $subject
- notify = touch /tmp/test7.notify
EOF
lacme newOrder test7 2>"$STDERR" || fail newOrder test7
-grepstderr -Fxq "Running notification command \`touch /tmp/test7.notify\`"
-test -e /tmp/test7.notify
+st="$(stat -c "%U:%G %#a" /etc/lacme/test7.pem)"
+[ "$st" = "root:root 0400" ]
+st="$(stat -c "%U:%G %#a" /etc/lacme/test7.crt)"
+[ "$st" = "root:root 0400" ]
-rm -f /tmp/test7.notify
-lacme newOrder test7 2>"$STDERR" || fail newOrder test7
+# post-issuance notification
+openssl genpkey -algorithm RSA -out /etc/lacme/test8.key
+cat >"/etc/lacme/lacme-certs.conf.d/test8.conf" <<- EOF
+ [test8]
+ certificate-key = /etc/lacme/test8.key
+ certificate-chain = /etc/lacme/test8.crt
+ subject = $subject
+ notify = touch /tmp/test8.notify
+EOF
+
+lacme newOrder test8 2>"$STDERR" || fail newOrder test8
+grepstderr -Fxq "Running notification command \`touch /tmp/test8.notify\`"
+test -e /tmp/test8.notify
+
+rm -f /tmp/test8.notify
+lacme newOrder test8 2>"$STDERR" || fail newOrder test8
ngrepstderr -Fq "Running notification command"
-! test -e /tmp/test7.notify
+! test -e /tmp/test8.notify
# vim: set filetype=sh :
diff --git a/tests/cert-revoke b/tests/cert-revoke
index f3d585e..179ccba 100644
--- a/tests/cert-revoke
+++ b/tests/cert-revoke
@@ -18,7 +18,7 @@ test /etc/lacme/simpletest.ecdsa.crt -nt /etc/lacme/simpletest.ecdsa.key
lacme revokeCert /etc/lacme/simpletest.ecdsa.crt
! lacme revokeCert /etc/lacme/simpletest.ecdsa.crt 2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.ecdsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.ecdsa.crt"
# and the RSA certificate using the service key
@@ -26,7 +26,7 @@ mv -vfT /etc/lacme/simpletest.rsa.key /etc/lacme/account.key
lacme revokeCert /etc/lacme/simpletest.rsa.crt
! lacme revokeCert /etc/lacme/simpletest.rsa.crt 2>"$STDERR" || fail
grepstderr -Fxq "Revoking /etc/lacme/simpletest.rsa.crt"
-grepstderr -Fxq "400 Bad Request (Certificate already revoked)"
+grepstderr -Fq "400 Bad Request (unable to revoke"
grepstderr -Fxq "Warning: Couldn't revoke /etc/lacme/simpletest.rsa.crt"
# vim: set filetype=sh :
diff --git a/tests/cert-verify b/tests/cert-verify
index 49629f2..a6cd336 100644
--- a/tests/cert-verify
+++ b/tests/cert-verify
@@ -8,31 +8,19 @@ for ca in /usr/share/lacme/letsencrypt-stg-root-*.pem; do
done
update-ca-certificates
-# test (modified) trust store for intermediate certificates
-openssl verify -no-CAfile -CApath /etc/ssl/certs -show_chain /usr/share/lacme/letsencrypt-stg-int-*.pem
-openssl verify -no-CApath -CAfile /etc/ssl/certs/ca-certificates.crt -show_chain /usr/share/lacme/letsencrypt-stg-int-*.pem
+# test (modified) trust store
+openssl verify -no-CAfile -CApath /etc/ssl/certs -show_chain /usr/share/lacme/letsencrypt-stg-root-x1.pem
+openssl verify -no-CApath -CAfile /etc/ssl/certs/ca-certificates.crt -show_chain /usr/share/lacme/letsencrypt-stg-root-x1.pem
mv /usr/share/lacme/ca-certificates.crt /usr/share/lacme/ca-certificates.crt.back
! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "Can't open /usr/share/lacme/ca-certificates.crt for reading, No such file or directory"
+grepstderr -Fq "Could not open file or uri for loading trusted certificates from /usr/share/lacme/ca-certificates.crt:"
grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate from ACME server!"
# verification error for unrelated CA bundle
cat /etc/ssl/certs/ssl-cert-snakeoil.pem >/usr/share/lacme/ca-certificates.crt
! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 20 at 0 depth lookup: unable to get local issuer certificate"
-grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate from ACME server!"
-
-# verification error when the CA bundle contains only the root certificates
-cat /usr/share/lacme/letsencrypt-stg-root-*.pem >/usr/share/lacme/ca-certificates.crt
-! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 20 at 0 depth lookup: unable to get local issuer certificate"
-grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate from ACME server!"
-
-# verification error when the CA bundle contains only the intermediate certificates
-cat /usr/share/lacme/letsencrypt-stg-int-*.pem >/usr/share/lacme/ca-certificates.crt
-! lacme newOrder 2>"$STDERR" || fail
-grepstderr -Fxq "error 2 at 1 depth lookup: unable to get issuer certificate"
+grepstderr -Fxq "error 20 at 1 depth lookup: unable to get local issuer certificate"
grepstderr -Fxq "[simpletest-rsa] Error: Received invalid X.509 certificate from ACME server!"
# use saved bundle as custom CAfile
diff --git a/tests/drop-privileges b/tests/drop-privileges
index 0596e31..8deb8f1 100644
--- a/tests/drop-privileges
+++ b/tests/drop-privileges
@@ -1,6 +1,17 @@
# Check privilige drop: UID/GID changes, chdir, environment, and file
# descriptors
+# ensure failure to drop privileges doesn't retain root privileges
+sed -ri 's/^#(user|group)\s*=\s*$/\1 = nonexistent-\1/' /etc/lacme/lacme.conf
+! lacme account 2>"$STDERR" || fail
+grepstderr -Fxq "getgrnam(nonexistent-group)"
+grepstderr -Fxq "Error: Invalid client version"
+
+sed -ri 's/^group\s*=\s*nonexistent.*/#&/' /etc/lacme/lacme.conf
+! lacme account 2>"$STDERR" || fail
+grepstderr -Fxq "getpwnam(nonexistent-user)"
+grepstderr -Fxq "Error: Invalid client version"
+
# create wrapper to inspect processes
STATUSDIR="/dev/shm/lacme-wrap"
install -oroot -groot -m0755 /dev/stdin /run/lacme-wrap <<-EOF
@@ -24,8 +35,7 @@ adduser --system --group \
--home /nonexistent --no-create-home \
--gecos "lacme account user" \
--quiet lacme-account
-sed -ri 's|^#user\s*=\s*$|user = lacme-account|' /etc/lacme/lacme.conf
-sed -ri 's|^#group\s*=\s*$|group = lacme-account|' /etc/lacme/lacme.conf
+sed -ri 's/^#?(user|group)\s*=\s*nonexistent.*/\1 = lacme-account/' /etc/lacme/lacme.conf
chown lacme-account: /etc/lacme/account.key
install -oroot -groot -dm0755 -- "$STATUSDIR"
@@ -113,8 +123,8 @@ check_client() {
grep -Exq "[0-9]+ 0700 $UID:$GID socket:\[[0-9]+\]" "$prefix/fd" || return 1
sed -ri '0,\#^[0-9]+ .* socket:\[[0-9]+\]$# {//d}' "$prefix/fd"
- grep -Exq "[0-9]+ 0500 $UID:$GID /etc/lacme/lacme\.conf" "$prefix/fd" || return 1
- sed -ri '0,\#^[0-9]+ .* /etc/lacme/lacme\.conf$# {//d}' "$prefix/fd"
+ grep -Eq "^[0-9]+ 0500 $UID:$GID /tmp/lacme-client.conf\.json-" "$prefix/fd" || return 1
+ sed -ri '0,\#^[0-9]+ .* /tmp/lacme-client.conf\.json-# {//d}' "$prefix/fd"
! test -s "$prefix/fd" || return 1
}
check_webserver() {
diff --git a/tests/old-accountd b/tests/old-accountd
index b44f7ec..3ad4b31 100644
--- a/tests/old-accountd
+++ b/tests/old-accountd
@@ -12,7 +12,7 @@ cat >~lacme-account/.config/lacme/lacme-accountd.conf <<-EOF
privkey = file:/etc/lacme/account.key
EOF
-echo "deb http://deb.debian.org/debian stretch main" >>/etc/apt/sources.list
+echo "deb http://archive.debian.org/debian stretch main" >>/etc/apt/sources.list
DEBIAN_FRONTEND="noninteractive" apt update
DEBIAN_FRONTEND="noninteractive" apt install -y --no-install-recommends \
--reinstall --allow-downgrades \
@@ -21,6 +21,7 @@ DEBIAN_FRONTEND="noninteractive" apt install -y --no-install-recommends \
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" & PID=$!
+sleep 1
lacme --socket="$SOCKET" account
lacme --socket="$SOCKET" newOrder
diff --git a/tests/old-lacme b/tests/old-lacme
index fa7d827..278a705 100644
--- a/tests/old-lacme
+++ b/tests/old-lacme
@@ -1,5 +1,6 @@
-# IPC test between recent lacme-accountd(1) and ancient lacme(8) 0.5 from Debian buster
-# (we don't try earlier versions as we need v2 support of the ACME API)
+# IPC test between recent lacme-accountd(1) and ancient lacme(8) 0.8 from Debian Bullseye
+# (we don't try earlier versions as we need v2 support of the ACME API
+# and non-pinned intermediates)
adduser --disabled-password \
--home /home/lacme-account \
@@ -14,18 +15,19 @@ cat >~lacme-account/.config/lacme/lacme-accountd.conf <<-EOF
privkey = file:/etc/lacme/account.key
EOF
-echo "deb http://deb.debian.org/debian buster main" >>/etc/apt/sources.list
+echo "deb http://deb.debian.org/debian bullseye main" >>/etc/apt/sources.list
DEBIAN_FRONTEND="noninteractive" apt update
DEBIAN_FRONTEND="noninteractive" apt install -y --no-install-recommends \
--reinstall --allow-downgrades \
-oDPkg::Options::="--force-confdef" -oDPkg::Options::="--force-overwrite" \
- lacme/buster
+ lacme/bullseye
# restore staging environment
mv -f /usr/share/lacme/ca-certificates.crt.back /usr/share/lacme/ca-certificates.crt
SOCKET=~lacme-account/S.lacme
runuser -u lacme-account -- lacme-accountd --socket="$SOCKET" & PID=$!
+sleep 1
sed -ri "s/^\[accountd]$/#&/" /etc/lacme/lacme.conf # https://bugs.debian.org/955767
lacme --socket="$SOCKET" account
lacme --socket="$SOCKET" newOrder