跨命名空间共享秘密

226

在Kubernetes中是否有一种方法可以跨命名空间共享机密?

我的使用情况是:我为所有命名空间使用相同的私有注册表,并且我想避免为每个命名空间创建相同的机密。


2
这个程序可以自动化秘密分享:https://github.com/zakkg3/ClusterSecret - NicoKowe
您可以通过适当的RBAC权限从不同命名空间访问机密。请参见此链接:https://dev59.com/dMT5oIgBc1ULPQZFthsO#73419051 - Hem
20个回答

189

秘密API对象存储在命名空间中。它们只能被相同命名空间的Pod引用。基本上,您需要为每个命名空间创建该密钥。

有关更多详细信息,请参见此处: Kubernetes文档/概念/配置/Secrets


7
同样适用于ConfigMaps。https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#restrictions - Breedly
1
这是正确的答案,值得一提的是,您可以通过 kubectl + sed 克隆到另一个命名空间,所有操作都在一行中完成,请参见下面的答案。 - NicoKowe
@NicoKowe,使用kubectl和一些shell魔法复制秘密是可能的,但如果您想更新秘密,这将变得令人兴奋(与无聊相反)。 - guettli
@guettli 是的,在这种情况下,我建议使用 ClusterSecret 操作符。 - NicoKowe
为什么这个回答被接受了呢?我认为其他大多数回答都提供了解决楼主问题的方法。 - undefined

159

它们只能被同一命名空间中的 pod 引用。但您可以将 secret 从一个命名空间复制到另一个命名空间。以下是将 default 命名空间中的 localdockerreg 密钥复制到 dev 命名空间的示例:

它们只能被同一命名空间中的 pod 引用。但您可以将 secret 从一个命名空间复制到另一个命名空间。以下是将 default 命名空间中的 localdockerreg 密钥复制到 dev 命名空间的示例:

kubectl get secret localdockerreg --namespace=default --export -o yaml | kubectl apply --namespace=dev -f -

###更新### 在 Kubernetes v1.14 中,--export 标志已被弃用。因此,使用 -oyaml 标志的以下命令将在即将推出的版本中正常工作,无需警告。

kubectl get secret localdockerreg --namespace=default -oyaml | kubectl apply --namespace=dev -f -
如果源命名空间不一定是默认值,则为下面或以下。
kubectl get secret localdockerreg --namespace=default -oyaml | grep -v '^\s*namespace:\s' | kubectl apply --namespace=dev -f -

4
如果您导出的机密不在默认命名空间中,则此方法将无法正常工作。 - gerasalus
1
在v1.13上,我可以跨任意两个命名空间使用它。 - Kshitij Saraogi
8
当我使用第二个命令(没有 --export 标志)时,出现错误提示“所提供选项的命名空间不匹配”。kubectl 版本为 1.15。我认为你可能需要在这两个 kubectl 命令之间使用 sed 或其他工具来从输出的 YAML 中删除命名空间。 - Matt Dodge
17
精确来说,您需要从中间的YAML中删除源命名空间: $ kubectl get secret <SECRET> --namespace <NS-SRC> -oyaml | grep -v '^\s*namespace:\s' | kubectl apply --namespace <NS-DST> -f -附注:未测试其他对象类型,但应该可以正常工作。另外,如果您要移动,请不要忘记删除源。 - Costa Shapiro
如果在不同的集群之间切换,请使用 kubectl use context 更改上下文,以确保工作正常。 - Vincent Gerris

63

接受的答案是正确的:Secrets 只能被同一命名空间中的 pod 引用。如果您想自动化“同步”或仅在命名空间之间复制 secret,这里有一个提示。

自动化(操作员)

要自动化跨命名空间共享或同步 secret,请使用 ClusterSecret 操作员:

https://github.com/zakkg3/ClusterSecret

使用 sed:

kubectl get secret <secret-name> -n <source-namespace> -o yaml \
| sed s/"namespace: <source-namespace>"/"namespace: <destination-namespace>"/\
| kubectl apply -n <destination-namespace> -f -

使用 jq

如果您安装了 jq,我们可以使用 @Evans Tucker 的解决方案。

kubectl get secret cure-for-covid-19 -n china -o json \
 | jq 'del(.metadata["namespace","creationTimestamp","resourceVersion","selfLink","uid"])' \
 | kubectl apply -n rest-of-world -f -

在移植密钥时应删除某些元数据,请参见Evans答案 - Matt
为此,您必须安装jq,在某些情况下可能无法实现。但是我也添加了这个选项,谢谢Matt! - NicoKowe
没关系,我只是想为那些先看到这个答案的人突出两个答案之间的功能差异。 - Matt
同时,我的评论应该是“可能要删除”,因为保留源元数据也可能很有用。 - Matt

31

秘密是有命名空间的资源,但您可以使用Kubernetes扩展来复制它们。我们使用这个方法自动将存储在秘密中的凭据或证书传播到所有命名空间,并保持同步(修改源并更新所有副本)。 请参阅 Kubernetes Reflector (https://github.com/EmberStack/kubernetes-reflector)。

该扩展允许您通过注释自动复制和同步跨命名空间的秘密:

在源机密上添加注释:

 annotations:
   reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"

这将在所有命名空间中创建秘钥的副本。您可以使用以下方法限制创建副本的命名空间:

reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "namespace-1,namespace-2,namespace-[0-9]*"

该扩展支持ConfigMaps和cert-manager证书。免责声明:我是Kubernetes Reflector扩展的作者。


25

--export已经被废弃。

sed不适合用于编辑YAML或JSON。

以下是使用 jq 删除我们不需要的命名空间和其他元数据的示例:

kubectl get secret cure-for-covid-19 -n china -o json \
 | jq 'del(.metadata["namespace","creationTimestamp","resourceVersion","selfLink","uid"])' \
 | kubectl apply -n rest-of-world -f -

1
这应该是第二个答案,紧接着对命名空间的使用和技术解释的第一个答案。如果您来到这里是为了将秘密复制到新的命名空间,请使用此选项。 - Null Value
1
最近在尝试使用一个秘密时,还有另一个需要删除的部分:ownerReferences。因此,新的 jq 字符串看起来像这样: jq 'del(.metadata["namespace","creationTimestamp","resourceVersion","selfLink","uid", "ownerReferences"])' - phyatt

9

使用RBAC授权服务账户在原始命名空间中使用密钥。但是,不建议在命名空间之间共享密钥。


1
这是唯一正确的答案,并带有正确的警告。请参考此示例https://dev59.com/dMT5oIgBc1ULPQZFthsO#73419051 - P....

8

他们并没有特别推荐 kubed,而是将其列为三个推荐之一。 - Mohammed Noureldin

4

基于@Evans Tucker的回答,但使用白名单而不是在jq过滤器中删除来仅保留我们想要的内容。

kubectl get secret cure-for-covid-19 -n china -o json | jq '{apiVersion,data,kind,metadata,type} | .metadata |= {"annotations", "name"}' | kubectl apply -n rest-of-world -f -

本质上相同,但保留标签。

kubectl get secret cure-for-covid-19 -n china -o json | jq '{apiVersion,data,kind,metadata,type} | .metadata |= {"annotations", "name", "labels"}' | kubectl apply -n rest-of-world -f -


4

我想帮助澄清其他答案中发现的两个错误信息。

RBAC和ServiceAccount不能用于在命名空间之间共享imagePullSecret

当然,RBAC可以允许在Pod中运行的应用程序使用kubernetes API或客户端库从另一个命名空间读取Secret。(请参见下面奖励部分中的最小工作示例。)

但是,当共享要在Pod规范的imagePullSecret字段中使用的Secret时,此方法不起作用。原因如下:

  1. 使用Secret创建Pod的应用程序是kubelet,它本身并未在Pod中运行,也没有关联的ServiceAccount。
  2. imagePullSecret只能引用同一命名空间内的Secret。请参阅API参考。

只需使用kubectl即可将Secret从一个命名空间复制到另一个命名空间。(无需使用jqsed。)

诀窍是模拟应用JSON补丁以将命名空间编辑到现有Secret上,以获取新的Secret文档。

kubectl patch secret \
  -n SOURCE-NAMESPACE SECRET-NAME \
  --type=json -p='[{"op": "replace", "path": "/metadata/namespace", "value": "DESTINATION-NAMESPACE"}]'  \
  -o yaml --dry-run=client |
  kubectl apply -f -

奖励:跨命名空间共享秘密并使用 Kubernetes Python 客户端访问的最小工作示例。

我不会进一步解释这个示例,因为它不适用于 OP 所谈论的那种秘密。然而,我认为对于那些像我一样被 RBAC 解决方案误导的人来说,这将是有帮助的。

apiVersion: v1
kind: Namespace
metadata:
  name: secret-owner-namespace
---
apiVersion: v1
kind: Namespace
metadata:
  name: secret-reader-namespace
---
apiVersion: v1
kind: Secret
metadata:
  name: example-secret
  namespace: secret-owner-namespace
type: Opaque
data:
  # echo -n secret-value | base64
  SECRET_KEY: c2VjcmV0LXZhbHVl
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader-role
  # NOTE: Reader's role is defined inside owner's namespace.
  # That is how `resourcesNames` below can be resolved.
  namespace: secret-owner-namespace
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["example-secret"]
  verbs: ["get", "watch", "list"]
---
# Allow pods in secret-reader-namespace to read the example-secret by:
# binding the default ServiceAccount of the secret-reader-namespace
# to the Role of secret-reader-role.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: secret-reader-rolebinding
  namespace: secret-owner-namespace
subjects:
- kind: ServiceAccount
  name: default
  apiGroup: ""
  namespace: secret-reader-namespace
roleRef:
  kind: Role
  name: secret-reader-role
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: secret-reading-code-configmap
  namespace: secret-reader-namespace
data:
  read-secret.py: |
    from kubernetes import client, config

    config.load_incluster_config()
    with client.ApiClient() as api_client:
        api_instance = client.CoreV1Api(api_client)
        name = 'example-secret'
        namespace = 'secret-owner-namespace'
        api_response = api_instance.read_namespaced_secret(name, namespace)

    print(api_response)
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secret-reader-deployment
  namespace: secret-reader-namespace
  labels:
    app: secret-reader-example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: secret-reader-example
  template:
    metadata:
      labels:
        app: secret-reader-example
    spec:
      containers:
      - name: secret-reading-container 
        image: python
        command: ["/bin/sh","-c"]
        args: ["pip install --no-cache-dir --upgrade kubernetes ; python read-secret.py ; sleep infinity"]
        workingDir: /opt/code
        volumeMounts:
        - name: secret-reading-code-volume
          mountPath: "/opt/code"
          readOnly: true
      volumes:
      - name: secret-reading-code-volume
        configMap:
          name: secret-reading-code-configmap

3

@NicoKowe改进

一行代码将一个命名空间中的所有密钥复制到另一个命名空间

$ for i in `kubectl get secrets | awk '{print $1}'`; do  kubectl get secret $1 -n <source-namespace> -o yaml | sed s/"namespace: <source-namespace>"/"namespace: <target-namespace>"/ | kubectl apply -n <target-namespace> -f -  ; done

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