Skip to content

Instantly share code, notes, and snippets.

@x-yuri
Last active June 24, 2024 01:26
Show Gist options
  • Save x-yuri/b29572952b4046e092169c7fd99fbdea to your computer and use it in GitHub Desktop.
Save x-yuri/b29572952b4046e092169c7fd99fbdea to your computer and use it in GitHub Desktop.
Let's Encrypt and Alpine Linux <= 3.8

Let's Encrypt and Alpine Linux <= 3.8

Alpine Linux contains now expired DST Root CA X3 certificate:

$ docker run --rm alpine:3.8 sh -euxc '
    grep "### Digital Signature Trust Co." -A 21 /etc/ssl/cert.pem
'
+ grep '### Digital Signature Trust Co.' -A 21 /etc/ssl/cert.pem
### Digital Signature Trust Co.

=== /O=Digital Signature Trust Co./CN=DST Root CA X3
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            44:af:b0:80:d6:a3:27:ba:89:30:39:86:2e:f8:40:6b
    Signature Algorithm: sha1WithRSAEncryption
        Validity
            Not Before: Sep 30 21:12:19 2000 GMT
            Not After : Sep 30 14:01:15 2021 GMT
        Subject: O=Digital Signature Trust Co., CN=DST Root CA X3
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Subject Key Identifier: 
                C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10
SHA1 Fingerprint=DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13
SHA256 Fingerprint=06:87:26:03:31:A7:24:03:D9:09:F1:05:E6:9B:CF:0D:32:E1:BD:24:93:FF:C6:D9:20:6D:11:BC:D6:77:07:39

Which makes websites like registry.bower.io not pass the verification:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add openssl
    openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
'
...
+ openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
...
---
Certificate chain
 0 s:/CN=bower.io
   i:/C=US/O=Let's Encrypt/CN=E6
 1 s:/C=US/O=Let's Encrypt/CN=E6
   i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
 2 s:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
...
    Verify return code: 10 (certificate has expired)
---
DONE

The natural solution is to remove the certificate:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add openssl ca-certificates
    sed -i "/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/" /etc/ca-certificates.conf
    update-ca-certificates
    openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
'
...
+ sed -i '/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/' /etc/ca-certificates.conf
+ update-ca-certificates
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
+ openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
...
---
Certificate chain
 0 s:/CN=bower.io
   i:/C=US/O=Let's Encrypt/CN=E6
 1 s:/C=US/O=Let's Encrypt/CN=E6
   i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
 2 s:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
...
    Verify return code: 10 (certificate has expired)
---
DONE

But it doesn't help, because /etc/ssl/cert.pem, which comes with libressl2.7-libcrypto:

$ docker run --rm alpine:3.8 sh -euxc '
    apk info -W /etc/ssl/cert.pem
'
+ apk info -W /etc/ssl/cert.pem
/etc/ssl/cert.pem is owned by libressl2.7-libcrypto-2.7.5-r0

is not changed by update-ca-certificates. And that is one of the locations of the system trust store, as such the file must be moved, renamed or something:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add openssl ca-certificates
    sed -i "/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/" /etc/ca-certificates.conf
    update-ca-certificates
    mv /etc/ssl/cert.pem /etc/ssl/cert.pem.bak
    openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
'
...
+ sed -i '/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/' /etc/ca-certificates.conf
+ update-ca-certificates
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
+ mv /etc/ssl/cert.pem /etc/ssl/cert.pem.bak
+ openssl s_client -connect registry.bower.io:443 -servername registry.bower.io
...
---
Certificate chain
 0 s:/CN=bower.io
   i:/C=US/O=Let's Encrypt/CN=E6
 1 s:/C=US/O=Let's Encrypt/CN=E6
   i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
 2 s:/C=US/O=Internet Security Research Group/CN=ISRG Root X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
...
    Verify return code: 0 (ok)
---

That is not needed in case of curl:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add curl
    curl -sS https://registry.bower.io
'
...
+ curl -sS https://registry.bower.io
curl: (60) SSL certificate problem: certificate has expired
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
$ docker run --rm alpine:3.8 sh -euxc '
    apk add curl
    sed -i "/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/" /etc/ca-certificates.conf
    update-ca-certificates
    curl -sS https://registry.bower.io
'
...
+ sed -i '/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/' /etc/ca-certificates.conf
+ update-ca-certificates
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
+ curl -sS https://registry.bower.io
Found. Redirecting to http://bower.io/search/

Probably because it uses libressl:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add curl
    apk info -R curl
    apk info -R libcurl
    apk info -P libressl2.7-libssl
    apk info -P libressl2.7-libcrypto
    ldd /usr/bin/curl
    apk info -W /lib/libssl.so.45
    apk info -W /lib/libcrypto.so.43
'
...
+ apk info -R curl
curl-7.61.1-r3 depends on:
ca-certificates
so:libcurl.so.4

+ apk info -R libcurl
libcurl-7.61.1-r3 depends on:
ca-certificates
so:libcrypto.so.43
so:libssl.so.45

+ apk info -P libressl2.7-libssl
libressl2.7-libssl-2.7.5-r0 provides:
so:libssl.so.45=45.0.1

+ apk info -P libressl2.7-libcrypto
libressl2.7-libcrypto-2.7.5-r0 provides:
so:libcrypto.so.43=43.0.1

+ ldd /usr/bin/curl
	libssl.so.45 => /lib/libssl.so.45 (0x77ac57566000)
	libcrypto.so.43 => /lib/libcrypto.so.43 (0x77ac571bb000)

+ apk info -W /lib/libssl.so.45
/lib/libssl.so.45 is owned by libressl2.7-libssl-2.7.5-r0

+ apk info -W /lib/libcrypto.so.43
/lib/libcrypto.so.43 is owned by libressl2.7-libcrypto-2.7.5-r0

But needed by nodejs:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add nodejs
    cat <<\EOF >a.js
    const https = require("https");
    https.get({host: "registry.bower.io"}, res => {
        console.log(res.statusCode);
    }).on("error", e => {
        console.log(e);
    });
EOF
    node a.js
'
...
+ node a.js
{ Error: certificate has expired
    at TLSSocket.<anonymous> (_tls_wrap.js:1116:38)
    at emitNone (events.js:106:13)
    at TLSSocket.emit (events.js:208:7)
    at TLSSocket._finishInit (_tls_wrap.js:643:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:473:38) code: 'CERT_HAS_EXPIRED' }
$ docker run --rm alpine:3.8 sh -euxc '
    apk add nodejs
    cat <<\EOF >a.js
    const https = require("https");
    https.get({host: "registry.bower.io"}, res => {
        console.log(res.statusCode);
    }).on("error", e => {
        console.log(e);
    });
EOF
    sed -i "/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/" /etc/ca-certificates.conf
    update-ca-certificates
    node a.js
'
...
+ sed -i '/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/' /etc/ca-certificates.conf
+ update-ca-certificates
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
+ node a.js
{ Error: certificate has expired
    at TLSSocket.<anonymous> (_tls_wrap.js:1116:38)
    at emitNone (events.js:106:13)
    at TLSSocket.emit (events.js:208:7)
    at TLSSocket._finishInit (_tls_wrap.js:643:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:473:38) code: 'CERT_HAS_EXPIRED' }
$ docker run --rm alpine:3.8 sh -euxc '
    apk add nodejs
    cat <<\EOF >a.js
    const https = require("https");
    https.get({host: "registry.bower.io"}, res => {
        console.log(res.statusCode);
    }).on("error", e => {
        console.log(e);
    });
EOF
    sed -i "/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/" /etc/ca-certificates.conf
    update-ca-certificates
    mv /etc/ssl/cert.pem /etc/ssl/cert.pem.bak
    node a.js
'
...
+ sed -i '/^mozilla\/DST_Root_CA_X3\.crt$/s/^/#/' /etc/ca-certificates.conf
+ update-ca-certificates
WARNING: ca-certificates.crt does not contain exactly one certificate or CRL: skipping
+ mv /etc/ssl/cert.pem /etc/ssl/cert.pem.bak
+ node a.js
302

Which uses openssl:

$ docker run --rm alpine:3.8 sh -euxc '
    apk add nodejs
    apk info -R nodejs
    apk info -P libcrypto1.0
    apk info -P libssl1.0
    ldd /usr/bin/node
    apk info -W /lib/libcrypto.so.1.0.0
    apk info -W /lib/libssl.so.1.0.0
'
...
+ apk info -R nodejs
nodejs-8.14.0-r0 depends on:
ca-certificates
so:libcrypto.so.1.0.0
so:libssl.so.1.0.0

+ apk info -P libcrypto1.0
libcrypto1.0-1.0.2u-r0 provides:
so:libcrypto.so.1.0.0=1.0.0

+ apk info -P libssl1.0
libssl1.0-1.0.2u-r0 provides:
so:libssl.so.1.0.0=1.0.0

+ ldd /usr/bin/node
	libcrypto.so.1.0.0 => /lib/libcrypto.so.1.0.0 (0x77c7c7ba3000)
	libssl.so.1.0.0 => /lib/libssl.so.1.0.0 (0x77c7c793a000)

+ apk info -W /lib/libcrypto.so.1.0.0
/lib/libcrypto.so.1.0.0 is owned by libcrypto1.0-1.0.2u-r0

+ apk info -W /lib/libssl.so.1.0.0
/lib/libssl.so.1.0.0 is owned by libssl1.0-1.0.2u-r0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment