#使用 Secrets 的镜像签名验证策略
本指南演示如何使用 Kubernetes Secrets 存储 Kyverno 镜像签名验证的公钥,相较于直接在策略中嵌入密钥,提供了更好的安全性和密钥管理。
#目录
#为什么使用 Secrets 存储公钥?
使用 Kubernetes Secrets 存储公钥具有以下优势:
- 增强安全性:密钥安全地存储在 Kubernetes Secret 存储中
- 便捷的密钥轮换:无需修改策略即可更新密钥
- 访问控制:使用 RBAC 控制谁可以访问 Secrets
#快速开始
#1. 生成并存储密钥到 Secret
# 生成 cosign 密钥对
cosign generate-key-pair
# 从公钥文件创建 Secret
kubectl create secret generic cosign-public-key \
--from-file=cosign.pub=./cosign.pub \
--namespace=kyverno
# 验证 Secret 是否创建成功
kubectl get secret cosign-public-key -n kyverno#2. 为 Kyverno 配置 RBAC
创建 Kyverno 的 Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
name: kyverno-secret-reader
namespace: kyverno创建访问 Secret 的 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: kyverno
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
resourceNames: ["cosign-public-key", "team-keys"] # 仅限特定 Secret将 Role 绑定到 Service Account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: kyverno
subjects:
- kind: ServiceAccount
name: kyverno-secret-reader
namespace: kyverno
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io#3. 使用 Secret 引用创建策略
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-with-secret
spec:
validationFailureAction: Enforce
background: false
rules:
- name: check-signatures
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "registry.company.com/*"
attestors:
- count: 1
entries:
- keys:
secret:
name: cosign-public-key
namespace: kyverno
key: cosign.pub
rekor:
url: https://rekor.sigstore.dev
mutateDigest: true#4. 测试配置
# 签名镜像
cosign sign --key cosign.key registry.company.com/app:v1.0.0
# 应用策略
kubectl apply -f verify-with-secret.yaml
# 使用已签名镜像测试(应成功)
kubectl run test --image=registry.company.com/app:v1.0.0
# 使用未签名镜像测试(应失败)
kubectl run test-fail --image=nginx:latest#Secret 创建方式
#方式一:从文件创建
# 从已有 cosign 公钥文件创建 Secret
kubectl create secret generic cosign-public-key \
--from-file=cosign.pub=./cosign.pub \
--namespace=kyverno#方式二:从字符串字面量创建
# 使用内联公钥内容创建 Secret
kubectl create secret generic cosign-public-key \
--from-literal=cosign.pub="-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----" \
--namespace=kyverno#方式三:从 YAML 清单创建
apiVersion: v1
kind: Secret
metadata:
name: cosign-public-key
namespace: kyverno
labels:
app: kyverno
component: image-verification
type: Opaque
stringData:
cosign.pub: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----kubectl apply -f cosign-secret.yaml#常见用例
#场景 1:单团队使用一个 Secret
简单场景,一个团队管理所有镜像签名:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: single-team-verification
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-team-signatures
match:
any:
- resources:
kinds: [Pod, Deployment, StatefulSet, DaemonSet]
exclude:
any:
- resources:
namespaces: [kube-system, kyverno]
verifyImages:
- imageReferences:
- "registry.company.com/*"
- "gcr.io/myproject/*"
failureAction: Enforce
attestors:
- count: 1
entries:
- keys:
secret:
name: team-cosign-key
namespace: kyverno
key: cosign.pub
rekor:
url: https://rekor.sigstore.dev
mutateDigest: true
verifyDigest: true
required: true#场景 2:多团队使用不同 Secret
不同团队拥有各自的签名密钥和 Secret:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: multi-team-verification
spec:
validationFailureAction: Enforce
background: false
rules:
# 前端团队镜像
- name: verify-frontend-images
match:
any:
- resources:
kinds: [Pod]
namespaces: [frontend-*]
verifyImages:
- imageReferences:
- "registry.company.com/frontend/*"
attestors:
- count: 1
entries:
- keys:
secret:
name: frontend-team-key
namespace: kyverno
key: cosign.pub
rekor:
url: https://rekor.sigstore.dev
mutateDigest: true
required: true
# 后端团队镜像
- name: verify-backend-images
match:
any:
- resources:
kinds: [Pod]
namespaces: [backend-*]
verifyImages:
- imageReferences:
- "registry.company.com/backend/*"
attestors:
- count: 1
entries:
- keys:
secret:
name: backend-team-key
namespace: kyverno
key: cosign.pub
rekor:
url: https://rekor.sigstore.dev
mutateDigest: true
required: true#场景 3:关键镜像需要多重签名
高安全环境,多个团队必须对关键镜像签名:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: critical-multi-signature
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-critical-images
match:
any:
- resources:
kinds: [Pod]
namespaces: [production]
verifyImages:
- imageReferences:
- "registry.company.com/critical/*"
failureAction: Enforce
attestors:
# 安全团队签名(必需)
- count: 1
entries:
- keys:
secret:
name: security-team-key
namespace: kyverno
key: security.pub
rekor:
url: https://rekor.sigstore.dev
# 开发团队签名(必需)
- count: 1
entries:
- keys:
secret:
name: dev-team-key
namespace: kyverno
key: development.pub
rekor:
url: https://rekor.sigstore.dev
# 发布团队签名(必需)
- count: 1
entries:
- keys:
secret:
name: release-team-key
namespace: kyverno
key: release.pub
rekor:
url: https://rekor.sigstore.dev
mutateDigest: true
required: true#场景 4:离线环境使用 Secrets
在隔离环境中使用 Secrets:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: offline-verification-with-secret
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-offline-images
match:
any:
- resources:
kinds: [Pod, Deployment, StatefulSet, DaemonSet]
verifyImages:
- imageReferences:
- "registry.internal.com/*"
- "airgap.company.com/*"
failureAction: Enforce
emitWarning: false
attestors:
- count: 1
entries:
- keys:
secret:
name: offline-cosign-key
namespace: kyverno
key: cosign.pub
# 离线模式配置
rekor:
url: "" # 离线模式下为空 URL
ignoreTlog: true # 忽略透明日志
ignoreSCT: true # 忽略 SCT
ctlog:
ignoreTlog: true # 忽略证书透明日志
ignoreSCT: true # 忽略 SCT
mutateDigest: true
verifyDigest: true
required: true