如何使用 Kubernetes 使用自签名证书访问私有 Docker 注册表?

jjN*_*ord 9 cloud infrastructure docker google-cloud-platform kubernetes

目前,在使用自签名证书进行身份验证的内部网络上运行私有 Docker 注册表(Artifactory)。

当 Kubernetes 启动一个新节点时,它无法使用私有 Docker 注册表进行身份验证,因为这个新节点没有自签名证书。

任何帮助将非常感激。谢谢!

mol*_*oly 11

最新版本的 kubernetes 现在使用 containerd 而不是 docker 来拉取镜像,因此其他答案将不再有效。kubectl get nodes -o wide您可以通过运行并查看“CONTAINER-RUNTIME”来检查您的节点正在使用哪些节点。

目前,我能找到的与containerd一起使用的唯一方法是将证书添加到主机的根存储中,然后重新启动containerd服务。通过 DaemonSet 执行此操作需要使用特权容器和 nsenter,以便我们可以在主机上运行 shell。此配置适用于我的 Ubuntu 主机(首先使用您的证书创建密钥):

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: registry-ca
  namespace: kube-system
  labels:
    k8s-app: registry-ca
spec:
  selector:
    matchLabels:
      name: registry-ca
  template:
    metadata:
      labels:
        name: registry-ca
    spec:
      hostPID: true
      hostNetwork: true
      initContainers:
      - name: registry-ca
        image: busybox
        securityContext:
          privileged: true
        command: [ 'sh' ]
        args:
          - -c
          - |
            cp /home/core/registry-ca /usr/local/share/ca-certificates/registry-ca.crt
            nsenter --mount=/proc/1/ns/mnt -- sh -c "update-ca-certificates && systemctl restart containerd"
        volumeMounts:
        - name: usr-local-share-certs
          mountPath: /usr/local/share/ca-certificates
        - name: ca-cert
          mountPath: /home/core
      terminationGracePeriodSeconds: 30
      volumes:
      - name: usr-local-share-certs
        hostPath:
          path: /usr/local/share/ca-certificates
      - name: ca-cert
        secret:
          secretName: registry-ca
      containers:
        - name: wait
          image: k8s.gcr.io/pause:3.1
Run Code Online (Sandbox Code Playgroud)

目前有一个针对 containerd 的开放合并请求,该请求应该允许在不重新启动的情况下添加证书,因此希望我们可以使用类似于Bichon 的更简单答案的方法,该答案很快不需要主机访问解决方法。

  • 现在应该将其标记为正确答案,因为 K8s 1.24+ 已从 Docker 转移到 containerd,因此当前的任何工作都需要此解决方案 (3认同)

Bic*_*hon 6

CoreOS 在本指南中建议了我在广泛搜索后找到的最简单的解决方案:https : //github.com/coreos/tectonic-docs/blob/master/Documentation/admin/add-registry-cert.md

它包括创建一个包含您的证书的密钥和一个 DaemonSet,以将其填充到/etc/docker/certs.d/my-private-insecure-registry.com/ca.crt集群的所有节点上。

我认为这回答了您的问题,因为在添加新节点时,会自动在其上执行 DaemonSet。

我在下面给出了详细的解决方案,但所有的功劳都归功于 Kyle Brown (kbrwn) 的非常酷的指南(参见上面的链接)。

Kubernetes 1.16+详细解决方案

假设您的证书是ca.crt在您的工作目录中命名的文件。从此文件内容创建一个秘密:

kubectl create secret generic registry-ca --namespace kube-system --from-file=registry-ca=./ca.crt
Run Code Online (Sandbox Code Playgroud)

然后,使用以下 DaemonSet 将证书挂载为文件/home/core/registry-ca并将其复制到所需位置:/etc/docker/certs.d/reg.example.com/ca.crt.

只需替换my-private-insecure-registry.com为您的容器注册表的主机名。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: registry-ca
  namespace: kube-system
  labels:
    k8s-app: registry-ca
spec:
  selector:
    matchLabels:
      name: registry-ca
  template:
    metadata:
      labels:
        name: registry-ca
    spec:
      containers:
      - name: registry-ca
        image: busybox
        command: [ 'sh' ]
        args: [ '-c', 'cp /home/core/registry-ca /etc/docker/certs.d/my-private-insecure-registry.com/ca.crt && exec tail -f /dev/null' ]
        volumeMounts:
        - name: etc-docker
          mountPath: /etc/docker/certs.d/my-private-insecure-registry.com
        - name: ca-cert
          mountPath: /home/core
      terminationGracePeriodSeconds: 30
      volumes:
      - name: etc-docker
        hostPath:
          path: /etc/docker/certs.d/my-private-insecure-registry.com
      - name: ca-cert
        secret:
          secretName: registry-ca
Run Code Online (Sandbox Code Playgroud)

将文件另存为registry-ca-ds.yaml,然后创建 DaemonSet :

kubectl create -f registry-ca-ds.yaml
Run Code Online (Sandbox Code Playgroud)

您现在可以检查您的应用程序是否正确地从您的私有自签名注册表中提取。

如前所述,证书将由registry-caDaemonSet以自动方式添加到新节点的docker。如果你想避免这种情况,只需删除 DaemonSet :

kubectl delete ds registry-ca --namespace kube-system
Run Code Online (Sandbox Code Playgroud)

我认为这比设置insecure-registriesdocker 守护进程的标志更安全。此外,它对新节点具有弹性。

Kubernetes 1.16 之前的原始解决方案

正如@MarcusMaxwell 所建议的,这个答案已经过编辑,以考虑到extensions/v1beta1Kubernetes 1.16+ 集群的 API 的弃用。如果您仍然使用 1.16 之前的版本运行 Kubernetes 集群,则应改用以下代码:

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: registry-ca
  namespace: kube-system
  labels:
    k8s-app: registry-ca
spec:
  template:
    metadata:
      labels:
        name: registry-ca
    spec:
      containers:
      - name: registry-ca
        image: busybox
        command: [ 'sh' ]
        args: [ '-c', 'cp /home/core/registry-ca /etc/docker/certs.d/my-private-insecure-registry.com/ca.crt && exec tail -f /dev/null' ]
        volumeMounts:
        - name: etc-docker
          mountPath: /etc/docker/certs.d/my-private-insecure-registry.com
        - name: ca-cert
          mountPath: /home/core
      terminationGracePeriodSeconds: 30
      volumes:
      - name: etc-docker
        hostPath:
          path: /etc/docker/certs.d/my-private-insecure-registry.com
      - name: ca-cert
        secret:
          secretName: registry-ca
Run Code Online (Sandbox Code Playgroud)

已知限制(编辑 2021)

根据这些 Kubernetes对相关 github 问题的回答(此处此处),Kubernetes 卷路径不能包含冒号。因此,此解决方案对于使用特定端口(例如 5000)上的自签名证书进行安全通信的注册表无效。

在这种情况下,请参阅 Gary Plattenburg 的回答以在 shell 命令中创建目录,而不是在挂载期间使用 Kubernetes 来处理它。

  • 虽然这是比“不安全注册表”更好的选择,但首选方法应该是在主机上正确安装 CA。不过,如果您只想让 EKS 托管节点组通过最少的修改与 AWS 提供的 AMI 配合使用,那么这是一个不错的选择。请注意,对于 Artifactory,您需要提供注册表的完整长路径,my-local.artifactory.mydomain.com 而不仅仅是artifactory.mydomain.com (2认同)

小智 0

您可以在 $HOME/.dockercfg 或 $HOME/.docker/config.json 中访问私有 docker 注册表的密钥。如果将其添加到这些搜索路径之一,kubelet 应该在拉取镜像时将其用作凭证。

  • {--root-dir:-/var/lib/kubelet}/config.json
  • {kubelet 的 cwd}/config.json
  • ${HOME}/.docker/config.json
  • /.docker/config.json
  • {--root-dir:-/var/lib/kubelet}/.dockercfg
  • {kubelet 的 cwd}/.dockercfg
  • ${HOME}/.dockercfg
  • /.dockercfg

https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry

“配置节点以向私有注册表进行身份验证”部分逐步指导您如何执行此操作。