前言
在Kubernetes CronJob 详解:配置、原理与实践指南这篇文章中,我 将监控服务费用以及 ssl 证书的脚本部署在 k8s 中,但有一个问题,脚本中需要使用到阿里云的 AK/SK,目前是硬编码到脚本中的, 为了安全起见,我计划将 AK/SK 配置在 k8s 的 secret 中。
Secret 基础概念
为什么需要 Secret
将敏感数据直接写在容器镜像或配置文件中存在严重的安全风险。 Kubernetes 提供的 Secret 资源就是为解决这个问题而设计的。
Secret 是什么?
Secret 是 Kubernetes 中专门用于存储敏感数据的资源对象,特点包括:
- 数据以 Base64 编码存储(注意:这不是加密!)
- 支持多种数据类型(Opaque、docker-registry、TLS 等)
- 可以通过卷挂载或环境变量注入到 Pod 中
Secret 与 ConfigMap 的区别
特性 | Secret | ConfigMap |
---|---|---|
数据类型 | 敏感信息 | 普通配置 |
存储方式 | Base64 编码 | 明文 |
典型用途 | 密码、密钥 | 应用配置 |
创建 Secret 的多种方式
方法一: 通过 kubectl 命令行创建
从字面值创建:
ini
kubectl create secret generic my-secret \
--from-literal=username=my-user \
--from-literal=password=my-password
这将创建一个名为 my-secret 的 Secret,其中包含两个键值对:username 和 password。
从文件创建:
ini
kubectl create secret generic tls-cert \
--from-file=tls.crt=./server.crt \
--from-file=tls.key=./server.key
这将使用文件中的内容创建一个名为 tls-cert 的 Secret。
方法二:通过 YAML 文件定义
通过创建一个 YAML 文件来定义 Secret,然后使用 kubectl apply 或 kubectl create 命令来创建
yaml
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: bXktdXNlcg== # Base64 编码的 username
password: bXktcGFzc3dvcmQ= # Base64 编码的 password
username 和 password 值是通过 base64 编码的。
你可以使用 echo -n "bXktdXNlcg==" | base64
来获取 base64 编码的值。
恢复成原始值:
bash
echo -n "bXktdXNlcg==" | base64 --decode
Secret 的使用方式
方式一:作为环境变量注入
将这些 Secret 值注入到容器的环境变量中
yaml
containers:
- name: myapp
image: myapp:latest
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: my-secret
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: password
这样,Pod 中的容器将会通过 Secret 中的 username 和 password 创建环境变量 USERNAME 和 PASSWORD。
方式二: 作为文件挂载
可以将 Secret 挂载到容器中的某个目录,容器可以通过文件访问 Secret 的内容。
yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: myimage
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-secret
my-secret 被挂载到容器的 /etc/secrets 目录下。 容器内将可以通过文件访问这些 Secret 的值(例如 /etc/secrets/username 和 /etc/secrets/password)。
方式三:拉取私有镜像的 Secret
存储 Docker registry 的凭据,可以使用 Secret 来存储 Docker 配置文件。
ini
kubectl create secret docker-registry my-registry-secret \
--docker-server=my-docker-server.com \
--docker-username=my-username \
--docker-password=my-password \
--docker-email=my-email@example.com
可以在 Kubernetes 配置中使用该 Secret 来拉取镜像:
yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
imagePullSecrets:
- name: my-registry-secret
containers:
- name: mycontainer
image: my-docker-server.com/myimage
这个配置中,Pod 使用 my-registry-secret 来从私有 Docker registry 拉取镜像。
方式四:使用 Secret 存储 TLS 证书
可以将 TLS 证书和密钥存储在 Secret 中,并在 Pod 中使用它们。
创建 TLS 证书 Secret:
css
kubectl create secret tls my-tls-secret \
--cert=/path/to/cert.crt \
--key=/path/to/cert.key
挂载到 Pod 中:
yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: myimage
volumeMounts:
- name: tls-volume
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-volume
secret:
secretName: my-tls-secret
这个配置中,Pod 将 my-tls-secret 挂载到 /etc/tls 目录下,容器可以使用证书和密钥文件。
Secret 的类型
- Opaque:默认类型,适用于存储任意敏感信息。数据以键值对的形式存储,且数据通常是 base64 编码的。
- kubernetes.io/dockerconfigjson:用于存储 Docker registry 凭证。
- kubernetes.io/basic-auth:用于存储基本身份验证凭证(用户名和密码)。
- kubernetes.io/ssh-auth:用于存储 SSH 密钥,通常用于允许 Kubernetes 集群与外部系统(例如 Git 仓库)进行 SSH 连接
- kubernetes.io/tls:用于存储 TLS 证书和密钥,通常用于处理 HTTPS 请求时的证书认证。
- kubernetes.io/service-account-token:用于存储服务账户的令牌,通常由 Kubernetes 自动管理,您不需要手动创建。
实际案例:安全使用阿里云 AK/SK
场景
前言中有提到,需要在一个监控 Pod 中使用阿里云访问凭证,但不希望将 AK/SK 硬编码。
解决方案
- 在K8s集群中创建存储密钥的Secret:
ini
kubectl create secret generic aliyun-credentials --namespace=kube-ops \
--from-literal=access_key_id='AK123456' \
--from-literal=access_key_secret='SK789012'
- 修改CronJob配置,配置CronJob引用Secret:
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
namespace: kube-ops
name: serverguard-job
spec:
schedule: "0 10 * * *"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 60
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: serverguard
image: /server-guard:latest
imagePullPolicy: Always
env:
- name: ALIYUN_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: aliyun-credentials
key: access_key_id
- name: ALIYUN_ACCESS_KEY_SECRET
valueFrom:
secretKeyRef:
name: aliyun-credentials
key: access_key_secret
restartPolicy: OnFailure
ttlSecondsAfterFinished: 100
- 修改代码读取环境变量
ini
import os # 添加os模块导入
# ... 其他现有导入保持不变 ...
class Secret(NamedTuple):
access_key_id: str
access_key_secret: str
# 从环境变量读取密钥,移除硬编码
secret = Secret(
access_key_id=os.environ.get('ALIYUN_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIYUN_ACCESS_KEY_SECRET')
)
# ... 后续代码保持不变 ...
- 检查CronJob是否正常运行 手动创建job测试:
ini
kubectl create job test-job --from=cronjob/serverguard-job -n kube-ops
这个命令是用来基于一个已经存在的 CronJob 创建一个 Job,用是从名为 serverguard-job 的 CronJob 中提取任务配置, 并在 kube-ops 命名空间中创建一个新的 Job,任务将立即执行一次,而不是定期运行。方便测试。
最后
到这里就告别了硬编码,使用 Kubernetes Secret 存储敏感数据,那现在有一个疑问,Secret 真的安全吗?
我们知道,Secret 的内容通常是以 Base64 编码存储的,因此它不能有效地防止恶意用户获取敏感数据。 如果攻击者有权限访问 Kubernetes 集群中的资源,他们可以轻松解码 Base64 编码的数据。
为了增加 Secret 的安全性,建议采取以下措施:
- 启用 etcd 加密:确保 etcd 集群中的数据以加密形式存储,防止未经授权的访问。
- 启用 RBAC 控制:通过 Role-Based Access Control(RBAC)机制,限制对 Secret 的访问权限,仅授权必要的用户或服务账号。
- 实施严格的访问控制:确保只有授权的用户或服务账号才能访问 Secret,避免泄露敏感信息。 下一篇文章,就来探索如何使用 RBAC 控制 Secret 的访问权限。