Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListenAndServeTLS runs locally - x509: certificate signed by unknown authority in docker

Tags:

docker

ssl

go

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.


Generate certificates

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 the cert and authority in Go

    // 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.

Put it in a docker container

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"]

docker-compose file

    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?

like image 261
Jimbo Avatar asked Mar 03 '23 07:03

Jimbo


1 Answers

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/

Error Checking

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.

like image 104
colm.anseo Avatar answered Mar 05 '23 15:03

colm.anseo