I am using mkcert to generate a self-signed certificate and authority. When I use these files locally with ListenAndServeTLS
, I can successfully connect with cURL. My host O/S is MacOS.
However, when trying to run this Go code in a docker container, I get the following error:
x509: certificate signed by unknown authority
Many other posts suggest that the problem is ca-certificates
is not installed and that one should run: apk add ca-certificates
. I have done this, and I still have the problem.
mkcert -cert-file ./cert.pem -key-file ./key.pem localhost
This means the cert will be valid for the domain "localhost", accessible at https://localhost.
// Load cert + key.
cert, _ := tls.LoadX509KeyPair("cert.pem", "key.pem")
// Load our CA. (Mkcert also generates this btw, check the docs).
caCert, _ := ioutil.ReadFile("rootCA.pem")
// Add our CA so it's considered an acceptable rootCA.
rootCAs, _ := x509.SystemCertPool()
rootCAs.AppendCertsFromPEM(caCert)
server := http.Server{
Addr: ":1234",
Handler: router,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: rootCAs,
},
}
_ = server.ListenAndServeTLS("", "")
This runs fine and works locally.
ARG GO_VERSION=1.14
FROM golang:${GO_VERSION}-alpine AS builder
RUN apk add --no-cache ca-certificates git curl
ENV CGO_ENABLED=0
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . /app
RUN go build -o ./bin/app .
FROM alpine AS final
WORKDIR /app
COPY --from=builder /user/group /user/passwd /etc/
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/bin/app /app
ENTRYPOINT ["./app"]
my-app:
build:
context: ./
dockerfile: Dockerfile
ports:
- 1234:1234
When running the above container and connecting over TLS, I get the unknown authority error described above.
What am I missing?
The issue is with your multi-stage Dockerfile
. While you copy all the necessary artifacts (source, certs etc.) for the first-stage, the final stage you only copy the binary bin/app
:
COPY --from=builder /app/bin/app /app
You need to also copy over the PEM files that are used during the service runtime:
COPY --from=builder \
/app/cert.pem \
/app/key.pem \
/app/rootCA.pem \
/app/
No matter how trivial an operation may appear always check for errors (and boolean returns):
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatalf("failed to load server key pairs: %v", err)
}
rootCAs, err := x509.SystemCertPool()
if err != nil {
log.Fatalf("failed to load system keychain: %v", err)
}
caCert, err := ioutil.ReadFile("rootCA.pem")
if err != nil {
log.Fatalf("failed to read CA trust file rootCA.pem: %v", err)
}
ok := rootCAs.AppendCertsFromPEM(caCert)
if !ok {
log.Fatal("failed to load CA trust: bad PEM format?")
}
log.Fatalf(
"server error: %v", // e.g. "port in use"
server.ListenAndServeTLS("", ""),
)
rigid error-checking like this will catch Docker build issues like this very quickly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With