本指南演示如何使用 Kubernetes Secrets 存储 Kyverno 镜像签名验证的公钥,相较于直接在策略中嵌入密钥,提供了更好的安全性和密钥管理。
使用 Kubernetes Secrets 存储公钥具有以下优势:
# 生成 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创建 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.ioapiVersion: 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# 签名镜像
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# 从已有 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=kyvernoapiVersion: 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简单场景,一个团队管理所有镜像签名:
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不同团队拥有各自的签名密钥和 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高安全环境,多个团队必须对关键镜像签名:
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在隔离环境中使用 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