How do I make an HTTPS call in a Busybox Docker container running Go?

Ismail Khan picture Ismail Khan · Jul 29, 2017 · Viewed 7.9k times · Source

I am trying to make an HTTPS call in a Docker container running a Go binary. This gives me the following error:

x509: failed to load system roots and no roots provided

Having looked this up, it seems the problem is that the BusyBox docker image does not have a root CA certificate. From other answers on StackOverflow it seems that the best approach is to mount the CA root into the /etc/ssl/certs container directory.

To test locally, it makes sense to mount the host machine's root CA certificate. When running in production (I use Google Container Engine), I'm not sure how to specify a root CA certificate. Do I need to create one myself? Or is there an existing cert in GKE that I can reuse?

Answer

Tarun Lalwani picture Tarun Lalwani · Jul 30, 2017

There are multiple options you can have

Share certificates from host

As you pointed out you can share /etc/ssl/certs from the host.

Use busybox with certificates

You can use a image like odise/busybox-curl which already has the certificates installed.

Use docker-compose and shared volumes for this

This is a better approach as it would not require you to have dependency on host

version: '2'

services:
  busybox:
    image: busybox
    command: sleep 1000
    volumes:
      - certificates:/etc/ssl/certs:ro
  certifcate_installer:
    image: alpine
    command: sh -c 'apk update && apk add ca-certificates'
    volumes:
      - certificates:/etc/ssl/certs
volumes:
  certificates:

Build it using multi-stage Dockerfile

FROM alpine as certs
RUN apk update && apk add ca-certificates

FROM busybox
COPY --from=certs /etc/ssl/certs /etc/ssl/certs

And then build it like normal file

vagrant@vagrant:~/certs$ docker build -t busyboxcerts .
Sending build context to Docker daemon  49.66kB
Step 1/4 : FROM alpine as certs
 ---> 4a415e366388
Step 2/4 : RUN apk update && apk add ca-certificates
 ---> Running in 0059f93b5fc5
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
v3.5.2-131-g833fa41a4d [http://dl-cdn.alpinelinux.org/alpine/v3.5/main]
v3.5.2-125-g9cb91a548a [http://dl-cdn.alpinelinux.org/alpine/v3.5/community]
OK: 7966 distinct packages available
(1/1) Installing ca-certificates (20161130-r1)
Executing busybox-1.25.1-r0.trigger
Executing ca-certificates-20161130-r1.trigger
OK: 5 MiB in 12 packages
 ---> 1a84422237e4
Removing intermediate container 0059f93b5fc5
Step 3/4 : FROM busybox
 ---> efe10ee6727f
Step 4/4 : COPY --from=certs /etc/ssl/certs /etc/ssl/certs
 ---> af9936f55fc4
Removing intermediate container 1af54c34a5b5
Successfully built af9936f55fc4
Successfully tagged busyboxcerts:latest
vagrant@vagrant:~/certs$ docker run busyboxcerts:latest ls /etc/ssl/certs
02265526.0
024dc131.0
03179a64.0

For more details on multistage build refer to https://docs.docker.com/engine/userguide/eng-image/multistage-build/#before-multi-stage-builds

All methods have their own pros and cons. I would prefer the last or the second last method personally