1 前言
默认 Secrets 对象的值是 base64 编码的内容,这个可以反编码得到原文的,不能起到加密重要密文的作用。
解决方法是使用开源的 Sealed Secrets
2 认识 Sealed Secrets
github 地址: https://github.com/bitnami-labs/sealed-secrets
有两部分总成:
- 集群里的: controller / operator
- 客户端程序: kubeseal
kubeseal 程序使用非对称加密来加密,只有控制器才能解密。
这些加密的秘密被编码在SealedSecret资源中,您可以将其视为创建秘密的配方。
它的 YAML 资源文件大概是这样的:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:name: mysecretnamespace: mynamespace
spec:encryptedData:foo: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq.....
经过 kubernetes 已安装的 controller 解密后相当于下面的内容:
apiVersion: v1
kind: Secret
metadata:name: mysecretnamespace: mynamespace
data:foo: YmFy # <- base64 编码的 "bar"
当这个 SealedSecret 的 secret 被创建的几秒钟之后,它会出现在 kubernetes 中;随后你可以像使用 kubernetes 原生的 secret 一样使用它。例如,从 Pod 引用它。
部署
获取最新的版本 https://github.com/bitnami-labs/sealed-secrets/releases
选择自己喜欢的部署方式即可。
这里选择手动非工具部署
需要下载
- controller.yml 集群端 控制器
- kubeseal 客户端程序
controller.yml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:annotations: {}labels:name: sealed-secrets-service-proxiername: sealed-secrets-service-proxiernamespace: kube-system
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: sealed-secrets-service-proxier
subjects:
- apiGroup: rbac.authorization.k8s.iokind: Groupname: system:authenticated
---
apiVersion: v1
kind: Service
metadata:annotations: {}labels:name: sealed-secrets-controller-metricsname: sealed-secrets-controller-metricsnamespace: kube-system
spec:ports:- port: 8081targetPort: 8081selector:name: sealed-secrets-controllertype: ClusterIP
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:annotations: {}labels:name: sealed-secrets-controllername: sealed-secrets-controller
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: secrets-unsealer
subjects:
- kind: ServiceAccountname: sealed-secrets-controllernamespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:annotations: {}labels:name: secrets-unsealername: secrets-unsealer
rules:
- apiGroups:- bitnami.comresources:- sealedsecretsverbs:- get- list- watch
- apiGroups:- bitnami.comresources:- sealedsecrets/statusverbs:- update
- apiGroups:- ""resources:- secretsverbs:- get- list- create- update- delete- watch
- apiGroups:- ""resources:- eventsverbs:- create- patch
- apiGroups:- ""resources:- namespacesverbs:- get
---
apiVersion: v1
kind: ServiceAccount
metadata:annotations: {}labels:name: sealed-secrets-controllername: sealed-secrets-controllernamespace: kube-system
---
apiVersion: v1
kind: Service
metadata:annotations: {}labels:name: sealed-secrets-controllername: sealed-secrets-controllernamespace: kube-system
spec:ports:- port: 8080targetPort: 8080selector:name: sealed-secrets-controllertype: ClusterIP
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:annotations: {}labels:name: sealed-secrets-service-proxiername: sealed-secrets-service-proxiernamespace: kube-system
rules:
- apiGroups:- ""resourceNames:- sealed-secrets-controllerresources:- servicesverbs:- get
- apiGroups:- ""resourceNames:- 'http:sealed-secrets-controller:'- http:sealed-secrets-controller:http- sealed-secrets-controllerresources:- services/proxyverbs:- create- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:annotations: {}labels:name: sealed-secrets-controllername: sealed-secrets-controllernamespace: kube-system
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: sealed-secrets-key-admin
subjects:
- kind: ServiceAccountname: sealed-secrets-controllernamespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:annotations: {}labels:name: sealed-secrets-key-adminname: sealed-secrets-key-adminnamespace: kube-system
rules:
- apiGroups:- ""resources:- secretsverbs:- create- list
---
apiVersion: apps/v1
kind: Deployment
metadata:annotations: {}labels:name: sealed-secrets-controllername: sealed-secrets-controllernamespace: kube-system
spec:minReadySeconds: 30replicas: 1revisionHistoryLimit: 10selector:matchLabels:name: sealed-secrets-controllerstrategy:rollingUpdate:maxSurge: 25%maxUnavailable: 25%type: RollingUpdatetemplate:metadata:annotations: {}labels:name: sealed-secrets-controllerspec:containers:- args: []command:- controllerenv: []image: docker.io/bitnami/sealed-secrets-controller:0.26.1imagePullPolicy: IfNotPresentlivenessProbe:httpGet:path: /healthzport: httpname: sealed-secrets-controllerports:- containerPort: 8080name: http- containerPort: 8081name: metricsreadinessProbe:httpGet:path: /healthzport: httpsecurityContext:allowPrivilegeEscalation: falsecapabilities:drop:- ALLreadOnlyRootFilesystem: truestdin: falsetty: falsevolumeMounts:- mountPath: /tmpname: tmpimagePullSecrets: []initContainers: []securityContext:fsGroup: 65534runAsNonRoot: truerunAsUser: 1001seccompProfile:type: RuntimeDefaultserviceAccountName: sealed-secrets-controllerterminationGracePeriodSeconds: 30volumes:- emptyDir: {}name: tmp
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:name: sealedsecrets.bitnami.com
spec:group: bitnami.comnames:kind: SealedSecretlistKind: SealedSecretListplural: sealedsecretssingular: sealedsecretscope: Namespacedversions:- name: v1alpha1schema:openAPIV3Schema:description: SealedSecret is the K8s representation of a "sealed Secret" -a regular k8s Secret that has been sealed (encrypted) using the controller'skey.properties:apiVersion:description: 'APIVersion defines the versioned schema of this representationof an object. Servers should convert recognized schemas to the latestinternal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'type: stringkind:description: 'Kind is a string value representing the REST resource thisobject represents. Servers may infer this from the endpoint the clientsubmits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'type: stringmetadata:type: objectspec:description: SealedSecretSpec is the specification of a SealedSecretproperties:data:description: Data is deprecated and will be removed eventually. Useper-value EncryptedData instead.format: bytetype: stringencryptedData:additionalProperties:type: stringtype: objectx-kubernetes-preserve-unknown-fields: truetemplate:description: Template defines the structure of the Secret that willbe created from this sealed secret.properties:data:additionalProperties:type: stringdescription: Keys that should be templated using decrypted datanullable: truetype: objectimmutable:description: Immutable, if set to true, ensures that data storedin the Secret cannot be updated (only object metadata can bemodified). If not set to true, the field can be modified atany time. Defaulted to nil.type: booleanmetadata:description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'nullable: trueproperties:annotations:additionalProperties:type: stringtype: objectfinalizers:items:type: stringtype: arraylabels:additionalProperties:type: stringtype: objectname:type: stringnamespace:type: stringtype: objectx-kubernetes-preserve-unknown-fields: truetype:description: Used to facilitate programmatic handling of secretdata.type: stringtype: objectrequired:- encryptedDatatype: objectstatus:description: SealedSecretStatus is the most recently observed status ofthe SealedSecret.properties:conditions:description: Represents the latest available observations of a sealedsecret's current state.items:description: SealedSecretCondition describes the state of a sealedsecret at a certain point.properties:lastTransitionTime:description: Last time the condition transitioned from one statusto another.format: date-timetype: stringlastUpdateTime:description: The last time this condition was updated.format: date-timetype: stringmessage:description: A human readable message indicating details aboutthe transition.type: stringreason:description: The reason for the condition's last transition.type: stringstatus:description: 'Status of the condition for a sealed secret. Validvalues for "Synced": "True", "False", or "Unknown".'type: stringtype:description: 'Type of condition for a sealed secret. Valid value:"Synced"'type: stringrequired:- status- typetype: objecttype: arrayobservedGeneration:description: ObservedGeneration reflects the generation most recentlyobserved by the sealed-secrets controller.format: int64type: integertype: objectrequired:- spectype: objectserved: truestorage: truesubresources:status: {}
基本使用
以某种方式创建一个json/yaml编码的Secret:
echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o yaml >mysecret.yml
内容如下:
apiVersion: v1
data:foo: YmFy
kind: Secret
metadata:creationTimestamp: nullname: mysecret
生成加密的 secret 资源文件
kubeseal -f mysecret.yml -w mysealedsecret.yml
数据被加密后的资源文件如下:
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:creationTimestamp: nullname: mysecretnamespace: default
spec:encryptedData:foo: AgA4KaUIHoNRNv8ZN1H1+dZfRyHFahVcTMMgRjoTkbL8dYmImstmw9oxrahaL8OOji5inMZ+AYP2UGJjUzflWN5SNOWpFGLN5LGHG2tinp1TWt14wCI20XqbVkGsbEwiQeh8Nudb32EgCJ0/Z4dYsa3zBR1jAbklyNv3s8sUnvDN/yvijMJYqxU3kbnHqQhiwt9SohTw445GCv1YlOe7ZqmB1MR2uK+3tRx7WUWpGe8A84uxZEJYMps/S5gxbGUMp5uvIm55S3fEYsWxqus6ssVMUoBAyCaVKCkftfRQBBd2bpBTiiQ8cnAw2BRVaGtDCHKqbZ/FY/uhllUOHxfQxzWwtDh1k3hQgXLnvG8krJytZ4OqZmkWYsOYPgakwZcZeLpBryEJv5CuwG/5RCOM5Cxjlwqph2Kfah0kULNdoAe4GbwvW1bK0PI/9SKZbrhiQ/o6GfR30W9aos3ucNbZihRA9P6er8sWbguzN3qYvdqbrSdrJ+VAyjty7lEBR/BTlUo5NdjbLuYTOhGbhcvCi7TYXThklRjisNzbS1pd/CMIm8difwEudnXmx44+hsqoK8MLgYXXEdSriJuHMeql881pxP2HQjXabr4MJkTKO7hznc4/vNnP+QtNbvjL01f1XDxIUrzsv1D3T45pBYYIfvJGb9rcskyunjJvhyJ2LUAlWY6f3uKUTwBwSIcoEX4V6WuuKYc=template:metadata:creationTimestamp: nullname: mysecretnamespace: default
创建资源对象到集群中
kubectl apply -f mysealedsecret.yml
查看资源对象
kubectl get secret mysecret
输出如下内容:
NAME TYPE DATA AGE
mysecret Opaque 1 17m
查看对象详情
kubectl describe secret mysecret
输出如下内容:
Name: mysecret
Namespace: default
Labels: <none>
Annotations: <none>Type: OpaqueData
====
foo: 3 bytes
请注意,SealedSecret和Secret必须具有相同的命名空间和名称。这是一个防止同一集群上的其他用户重新使用您的密封机密的功能。
在 Pod 中使用
busybox.yml
文件内容:
apiVersion: v1
kind: Pod
metadata:name: mypod
spec:containers:- name: mypodimage: busyboxcommand:- cat- "/etc/mysecret/foo"volumeMounts:- name: foomountPath: "/etc/mysecret"readOnly: truevolumes:- name: foosecret:secretName: mysecret