103

I am running an ASP.NET Core 1.1 Web API in a Docker 1.13.1 container on Ubuntu 14.04.

When the code attempts to retrieve some data from an HTTPS server, I get this certificate authentication error:

 An error occurred while sending the request. ---> System.Net.Http.CurlException: Peer certificate cannot be authenticated with given CA certificates
   at System.Net.Http.CurlHandler.ThrowIfCURLEError(CURLcode error)
   at System.Net.Http.CurlHandler.MultiAgent.FinishRequest(StrongToWeakReference`1 easyWrapper, CURLcode messageResult)

The HTTPS server is internal with certificate signed by our corporate CA, so am aware that I may need to register the internal CA.

Everything I've found so far about this error and Docker talks to getting docker itself running, connecting to repos etc. My Docker is working fine, and the Web API runs on the Ubuntu server outside of the container without a problem.

1) Do I need to add a CA root certificate inside a docker image?

2) If so, how do I do it?

3) If not, how do I fix this?

Joel Harkes
  • 10,975
  • 3
  • 46
  • 65
Peter
  • 5,455
  • 7
  • 46
  • 68

7 Answers7

163

The task itself is not specific to docker as you would need to add that CA on a normal system too. There is an answer on the askubuntu community on how to do this.

So in a Dockerfile you would do the following (don't forget chmod in case you're running the container with a user other than root):

ADD your_ca_root.crt /usr/local/share/ca-certificates/foo.crt
RUN chmod 644 /usr/local/share/ca-certificates/foo.crt && update-ca-certificates
GMartinez
  • 301
  • 4
  • 9
cebe
  • 3,610
  • 1
  • 23
  • 37
  • 1
    Thanks, I'll try that. I have successfully added the CA on my servers, but the commands I ran to achieve that are not working when I logon to the container. – Peter Feb 17 '17 at 08:17
  • Thanks, this worked great, though I used `curl` to pull the certificates from our repo. – Peter Feb 17 '17 at 09:02
  • 8
    Just a note to suggest using COPY rather than ADD. See https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy – unigeek Apr 05 '21 at 00:57
  • the solution is great. however, it exposes private certificate into image. how to keep it only in build time? – Doz Parp Oct 28 '21 at 02:08
  • 3
    @DozParp there is no such thing as a private certificate. The private key is what you probably don't want to have in the image. What are you trying to achieve? – cebe Oct 28 '21 at 06:23
  • `private certificate` means that it uses merely in your network but should not publish to outside. if you do `ADD` the cert to image, it will pollute the cert pool and user can detect this as a malware (trusted in your env but not users) – Doz Parp Oct 29 '21 at 01:20
  • 1
    @DozParp If you want to keep the certificate out of the final image, use a [multi-stage Docker build](https://docs.docker.com/develop/develop-images/multistage-build/). Pull the certificate into the first stage, do what you need with it, and move only the results into your final stage. – phoenix May 05 '22 at 17:34
  • @DozParp it's a certificate, it doesn't matter at all if anyone else has your CA cert – IanNorton Jul 30 '22 at 14:28
  • @DozParp PKI's trust mecanisms does not rely on where X.509 certificates are stored or if is well-knowed by others users; it relies on how secretly your private key is. There is not reverse engineering to get the private key knowing the public key or it's certificates. As more the certificate were well-knowed, more easy and efficient PKI will be. You cannot supplant an identity just using its public key and X.509 certificates. – José Pulido Nov 11 '22 at 22:19
45

To simplify/standardise all container builds, we now host our certificates on a central HTTPS server and build them into our containers like this:

# Debian stretch based container
RUN curl -ks 'https://cert.host.server/ssl_certs/EnterpriseRootCA.crt' -o '/usr/local/share/ca-certificates/EnterpriseRootCA.crt'
RUN /usr/sbin/update-ca-certificates

Alpine-based containers don't have the tools immediately available so require a bit more work to achieve the same:

# Alpine based containers
RUN apk update && apk add curl
WORKDIR /usr/local/share/ca-certificates
RUN curl -ks 'https://cert.host.server/ssl_certs/EnterpriseRootCA.crt' -o '/usr/local/share/ca-certificates/EnterpriseRootCA.crt'
RUN /usr/sbin/update-ca-certificates

If you also want to update your Java truststore (same as on any computer):

RUN keytool -keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias EnterpriseRootCA -file EnterpriseRootCA.crt
Peter
  • 5,455
  • 7
  • 46
  • 68
  • _Oracle_ Java needs to update separately; the OpenJDK packages _from Debian/Ubuntu/etc_ already use the 'systemwide' `update-ca-certificates` data. – dave_thompson_085 Oct 28 '21 at 03:54
35

It's also worth noting that this definitely needs to use the .crt extension. I initially tried this with a .pem cert file (I thought they were interchangeable, so others might also), which is not linked by update-ca-certificates.

damiankloip
  • 494
  • 4
  • 4
  • 3
    Oh wow, thanks for that note. For some reason, the certificates I had were `.pem` and it totally didn't see them. The hint I had was that the `update-ca-certificates` command had the following output: `Updating certificates in /etc/ssl/certs... 0 added, 0 removed; done.` Once fixed, I had `Updating certificates in /etc/ssl/certs... 4 added, 0 removed; done.`. – GabLeRoux Jul 18 '19 at 15:05
  • 1
    I'd like to add that .crt binary after .pem to crt conversion (using openssl x509 -outform der) did NOT work. Maybe my .pem was not really PEM? However, just renaming my .pem to .crt did the trick. i.e. keeping the TEXT "BEGIN CERTIFICATE...etc" did the trick – Danilo Ramirez Sep 28 '21 at 04:43
11

Installing ca-certificates locate cert_file_name.crt file in the same directory as Dockerfile.

# Install ca-certificates
# Please locate cert_file_name.crt file in the same directory as Dockerfile.
COPY cert_file_name.crt /usr/share/ca-certificates/
RUN echo cert_file_name.crt >> /etc/ca-certificates.conf
RUN update-ca-certificates

This will update certificates in the Dockerfile.

banoth ravinder
  • 1,314
  • 15
  • 18
  • 2
    So this `*.crt` file can be check-in to the code repository? – alltej Feb 07 '20 at 04:05
  • @alltej Yes. If you're intending to use these commands as part of your build, then you *should* check-in the `*.crt` file to your code repository. – Jim G. Jul 15 '20 at 14:42
4

Another option is to use OpenSSL. Replace domain_name with the URL you can retrieve the CA

RUN openssl s_client -connect <domain_name>:443 -showcerts </dev/null 2>/dev/null | sed -e '/-----BEGIN/,/-----END/!d' | tee "/usr/local/share/ca-certificates/ca.crt" >/dev/null && \
update-ca-certificates
Jobin James
  • 916
  • 10
  • 13
  • Not Found The requested URL was not found on this server. – Julien Baldy Mar 30 '22 at 15:14
  • Nice, works for me. Very elegant – Gregor Müllegger Aug 31 '22 at 08:19
  • 5
    I don't recommend this solution at all. You're basically saying that whatever site you connect to you will trust, no matter who the certificate issuer is. Much better to separate installing a certificate that you trust, and then using that to verify the site you're connecting to. If you stumble upon a site that has a rogue certificate, you will get a failure, which is probably what you want. – Dave MacLean Nov 11 '22 at 19:50
0

If u are like me and dont really want to include the root-ca inside a build docker image

U can mount the cert on runtime as a file and just pass the mounted ca-cert file path as a parameter for whatever service u where about to access

e.g. with curls inside e kubernetes pod it looks like

curl --cacert path/to/ca-root.pem https://<service>.<namespace>.svc.cluster.local:4000/
Gringo
  • 1
  • 1
  • 4
-2

sudo apt -y install ca-certificates curl gnupg lsb-release

curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg apt update

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt -y install docker-ce docker-ce-cli containerd.io

sudo systemctl enable --now docker

Dockefile:

FROM nginx:latest COPY index.html /usr/share/nginx/html/index.html

sudo docker build -t mywebapp .

sudo docker run -d -p 80:80 mywebapp

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – LinFelix Jul 07 '23 at 11:02