如何在Docker镜像中添加CA根证书?

106

我正在Ubuntu 14.04上的Docker 1.13.1容器中运行ASP.NET Core 1.1 Web API。

当代码尝试从HTTPS服务器检索一些数据时,我收到了此证书认证错误:

 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)

HTTPS服务器是内部的,使用由我们公司颁发的证书,因此我意识到可能需要注册内部CA。

到目前为止,我发现关于这个错误和Docker的所有内容都涉及到运行docker本身、连接存储库等方面。我的Docker正常工作,并且Web API在Ubuntu服务器上的容器外部运行没有问题。

1)我是否需要在Docker镜像中添加CA根证书?

2)如果需要,我该如何操作?

3)如果不需要,我该如何解决这个问题?

7个回答

167

这个任务本身与Docker无关,因为您需要在普通系统上添加该证书颁发机构。在askubuntu社区上有一个关于如何执行此操作的答案。

因此,在Dockerfile中,您需要执行以下操作(如果您正在以root用户之外的用户运行容器,请不要忘记chmod):

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

1
谢谢,我会尝试一下。我已经成功地将CA添加到我的服务器上,但我登录容器时运行的命令不起作用。 - Peter
谢谢,这个方法很好用,不过我使用了 curl 从我们的仓库中拉取证书。 - Peter
8
建议使用COPY而不是ADD命令。参见https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#add-or-copy - unigeek
3
@DozParp 没有所谓的私人证书。私钥可能不应该出现在图像中。你试图达到什么目的? - cebe
1
如果您想将证书保留在最终镜像之外,请使用多阶段Docker构建。将证书拉入第一阶段,进行必要的操作,然后仅将结果移动到最终阶段。 - phoenix
显示剩余4条评论

46
为简化/标准化所有容器的构建,我们现在将证书托管在一个中央 HTTPS 服务器上,并将它们构建到我们的容器中,如下所示:

要简化/标准化所有容器的构建,我们现在将证书托管在一个中央HTTPS服务器上,并将其构建到我们的容器中,如下所示:

# 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的容器没有立即可用的工具,因此需要更多的工作才能达到相同的效果:

# 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

如果您也想更新Java信任库(与任何计算机相同):

RUN keytool -keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias EnterpriseRootCA -file EnterpriseRootCA.crt

Oracle Java 需要单独更新; Debian/Ubuntu等的 OpenJDK 包已经使用了“系统范围”的 update-ca-certificates 数据。 - dave_thompson_085

36

值得注意的是,这一定要使用 .crt 扩展名。我最初尝试使用的是 .pem 证书文件(我认为它们可以互换,所以其他人可能也会这样想),但这种文件不会被 update-ca-certificates 链接。


3
哇,谢谢你的提示。出于某种原因,我拥有的证书是 .pem 格式的,但它们完全没有被识别。给我的提示是执行 update-ca-certificates 命令后输出了以下内容:Updating certificates in /etc/ssl/certs... 0 added, 0 removed; done. 修复后,输出变成了 Updating certificates in /etc/ssl/certs... 4 added, 0 removed; done. - GabLeRoux
1
我想补充一点,将.crt二进制文件添加到crt转换后的.pem文件中(使用openssl x509 -outform der)并不起作用。也许我的.pem文件并不是真正的PEM格式?然而,只需将我的.pem文件重命名为.crt即可解决问题。即保留文本“BEGIN CERTIFICATE...等”即可。 - Danilo Ramirez

11

安装 ca-certificates 后,在与 Dockerfile 相同的目录中查找 cert_file_name.crt 文件。

# 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

这将会在Dockerfile中更新证书。

2
那么这个 *.crt 文件可以提交到代码仓库吗? - alltej
@alltej 是的。如果您打算将这些命令作为构建的一部分使用,则应该*.crt文件提交到您的代码存储库中。 - Jim G.

4
另一个选择是使用 OpenSSL。将 domain_name 替换为您可以检索 CA 的 URL。
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

请求的URL在服务器上未找到。 - Julien Baldy
不错,对我来说可行。非常优雅。 - Gregor Müllegger
5
我完全不建议使用这种解决方案。你基本上是在说,无论证书颁发者是谁,你都会信任连接到的任何网站。更好的做法是分开安装你信任的证书,然后使用它来验证你要连接的网站。如果你遇到一个有问题的证书的网站,你将会收到一个失败的提示,这也许正是你想要的。 - Dave MacLean

1
如果你和我一样,不想在构建Docker镜像中包含根证书的话,
你可以在运行时将证书挂载为文件,并将挂载的CA证书文件路径作为参数传递给即将访问的服务。
例如,在Kubernetes Pod中使用curl时,可以这样做:
curl --cacert path/to/ca-root.pem https://<service>.<namespace>.svc.cluster.local:4000/

-3

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


你的回答可以通过提供更多支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人能够确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - LinFelix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接