Summary
To trust a self-signed certificate in a scratch image, copy the certificate at the build stage, update the trusted ca-certificates, and then copy it to the scratch image.
Prerequisites
- Docker version 20.10.17, build 100c701
- go version go1.18.1 darwin/arm64
- nginx:1.23.1
Example
Create a self-signed certificate and run a HTTPS server on nginx
First, create a self-signed certificate.
There are many ways to do this, but I prefer to do it with openssl in Docker.
$ docker container run --rm -it -v="$PWD:/workdir" -w="/workdir" --entrypoint=/bin/bash nginx:1.23.1
root@e08bb1ebe3da:/workdir# openssl version
OpenSSL 1.1.1n 15 Mar 2022
root@e08bb1ebe3da:/workdir# openssl genpkey -algorithm RSA -out server-private-key.pem
...................+++++
......................................................................................................+++++
root@e08bb1ebe3da:/workdir# openssl req -x509 -key server-private-key.pem -out server-cert.pem -addext 'subjectAltName = DNS:hogehoge.uzimihsr.com'
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:hogehoge.uzimihsr.com
Email Address []:
root@e08bb1ebe3da:/workdir#
exit
# the private key and the self-signed certificates are created on the host
$ ls
server-cert.pem server-private-key.pem
Then start a HTTPS server on nginx.
$ tree .
.
├── docker-compose.yaml
├── https.conf
├── server-cert.pem
└── server-private-key.pem
0 directories, 4 files
$ docker compose up -d
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
$ curl -I https://hogehoge.uzimihsr.com --cacert ./server-cert.pem --resolve "hogehoge.uzimihsr.com:443:127.0.0.1"
HTTP/1.1 200 OK
...
Create an application that sends HTTP requests in Go
Next, create a simple HTTP status checker in Go.
$ go run main.go https://example.com
2022/09/28 23:45:32 target: https://example.com, insecure: false
2022/09/28 23:45:33 status: 200 OK
Build and run this application with Docker.
Oops! The http-status-checker
container failed due to the certificate error.😭
$ tree .
.
├── Dockerfile
├── docker-compose.yaml
├── go.mod
├── https.conf
├── main.go
├── server-cert.pem
└── server-private-key.pem
0 directories, 7 files
$ cat go.mod
module http-status-checker
go 1.18
$ docker compose up -d
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
http-status-checker "./app https://hogeh…" http-status-checker exited (1)
$ docker compose logs http-status-checker
http-status-checker | 2022/09/28 12:17:41 target: https://hogehoge.uzimihsr.com/, insecure: false
http-status-checker | 2022/09/28 12:17:41 [ERROR] HTTP(S) request failed: Get "https://hogehoge.uzimihsr.com/": x509: certificate signed by unknown authority
Trust the certificate in a scratch image
To avoid the certificate error, the self-signed certificate should be trusted in a scratch image.
(Of course, it is possible to set tls.Config.InsecureSkipVerify as a workaround, but I have tried to trust the self-signed certificate.)
Since the golang image is Debian-based, the list of CA certificates can be updated with the update-ca-certificates command.
The Dockerfile is rewrited as follows.
The following operations are added: copy the certificate we want to trust with .crt
extension and run the update-ca-certificates command.
This allows /etc/ssl/certs/ca-certificates.crt
in the build stage to trust the specified certificate.
COPY server-cert.pem /usr/local/share/ca-certificates/server-cert.crt
RUN update-ca-certificates
Finally, rebuild and restart the container.
This time it worked fine with no certificate errors.🎉
$ docker compose up -d --remove-orphans --build
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
hogehoge.uzimihsr.com "/docker-entrypoint.…" nginx running 80/tcp, 0.0.0.0:443->443/tcp
http-status-checker "./app https://hogeh…" http-status-checker exited (0)
$ docker compose logs http-status-checker
http-status-checker | 2022/09/28 13:44:43 target: https://hogehoge.uzimihsr.com/, insecure: false
http-status-checker | 2022/09/28 13:44:43 status: 200 OK