I've got an application I created that hosts a web server (https on port 8090) inside a Docker container. However, when I create a self-signed certificate and add it to the trusted root it always shows up as untrusted in the browser. If I validate it inside the container it's valid, but accessing outside the container its untrusted. I assume this is because it can resolve the CA root locally but not externally.
I've spent a few days trying to figure this out - what am I doing wrong?
Dockerfile:
FROM ubuntu
EXPOSE 8090
WORKDIR /app
#install tools
RUN apt-get -y update; apt-get -y install sudo curl wget libicu-dev apt-transport-https ca-certificates
# create SSL certificate
RUN openssl req -x509 -noenc -newkey rsa:4096 -sha256 -keyout /app/Certificates/test.key -out /app/Certificates/test.crt -passin "pass:password" -subj "/O=Test/CN=localhost" -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1" -days 3650
# export certificate
RUN openssl pkcs12 -export -password "pass:password" -inkey /app/Certificates/test.key -in /app/Certificates/test.crt -out /app/Certificates/test.pfx
# trust certificate
RUN cp /app/Certificates/test.crt /usr/local/share/ca-certificates/test.crt && update-ca-certificates
RUN useradd app
USER app
ENTRYPOINT ["./WebApplication"]
Validating the certificate inside the container (OK):
$ curl -vv https://localhost:8090
* Host localhost:8090 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8090...
* connect to ::1 port 8090 from ::1 port 59964 failed: Connection refused
* Trying 127.0.0.1:8090...
* Connected to localhost (127.0.0.1) port 8090
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted h2
* Server certificate:
* subject: O=Test; CN=localhost
* start date: Mar 17 01:37:17 2025 GMT
* expire date: Mar 15 01:37:17 2035 GMT
* subjectAltName: host "localhost" matched cert's "localhost"
* issuer: O=Test; CN=localhost
* SSL certificate verify ok.
* Certificate level 0: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://localhost:8090/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: localhost:8090]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: localhost:8090
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< content-type: text/html
< date: Mon, 17 Mar 2025 01:45:32 GMT
< server: Kestrel
< accept-ranges: bytes
< etag: "1db9629b316d3e9"
< last-modified: Sun, 16 Mar 2025 04:12:58 GMT
< content-length: 4841
< x-version: 0.0.0
<
<!DOCTYPE html>
<html lang="en">
<!-- ... HTML CONTENTS ... -->
</html>
* Connection #0 to host localhost left intact
Validating the certificate outside the container (FAIL):
$ curl -vv https://localhost:8090
* Host localhost:8090 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8090...
* Connected to localhost (::1) port 8090
* schannel: disabled automatic use of client certificate
* ALPN: curl offers http/1.1
* schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.
* closing connection #0
curl: (60) schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.
More details here: .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 webpage mentioned above.
Certificate chain information using openssl. Note that using -CAfile instead of CApath shows a valid certificate, but this returns an invalid one same as external browser.
$ openssl s_client -servername localhost -connect localhost:8090 -CApath /app/Certificates
CONNECTED(00000003)
depth=0 O = Test, CN = localhost
verify error:num=18:self-signed certificate
verify return:1
depth=0 O = Binner, CN = localhost
verify return:1
---
Certificate chain
0 s:O = Test, CN = localhost
i:O = Test, CN = localhost
a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
v:NotBefore: Mar 16 15:39:02 2025 GMT; NotAfter: Mar 14 15:39:02 2035 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=O = Test, CN = localhost
issuer=O = Test, CN = localhost
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2194 bytes and written 391 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 4096 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 18 (self-signed certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: D35F37B1E33D77323D56FA4BF418459FF7C4B752F4318F5DE0CFA15BB53FEC37
Session-ID-ctx:
Resumption PSK: ...
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
...
Start Time: 1742174452
Timeout : 7200 (sec)
Verify return code: 18 (self-signed certificate)
Extended master secret: no
Max Early Data: 0
---