目录

Kubernetes-08 RBAC 与安全

Kubernetes RBAC 与安全

系列第八篇。安全是 K8s 生产运维的重要课题。本篇讲解 RBAC 权限模型、安全上下文、Pod 安全策略和零信任网络。


目录

  1. K8s 安全概览
  2. 认证(Authentication)
  3. RBAC 授权
  4. ServiceAccount
  5. 安全上下文(Security Context)
  6. Pod Security Standards
  7. 镜像安全
  8. Go Operator 的 RBAC 实践
  9. 安全检查清单
  10. 小结

1. K8s 安全概览

K8s 的安全模型分为四层(4C 安全模型):

Cloud(云/数据中心)
  └── Cluster(K8s 集群)
        └── Container(容器)
              └── Code(代码)

每层都需要独立的安全措施,本篇重点讲 Cluster 和 Container 层。

K8s 安全三大核心:

  1. 认证(Authentication):你是谁?
  2. 授权(Authorization):你能做什么?
  3. 准入控制(Admission Control):你的请求合规吗?

2. 认证(Authentication)

K8s 支持多种认证方式:

认证方式 适用场景
客户端证书(X.509) 组件间通信、管理员 kubectl
Bearer Token ServiceAccount、OIDC
OIDC(OpenID Connect) 集成 GitHub/Google/Dex 登录
Webhook Token 自定义认证服务

2.1 kubeconfig 文件

kubectl 通过 ~/.kube/config 管理集群连接信息:

apiVersion: v1
kind: Config
clusters:
- name: production
  cluster:
    server: https://api.k8s.company.com:6443
    certificate-authority-data: <base64-ca-cert>
- name: staging
  cluster:
    server: https://api.staging.k8s.company.com:6443
    certificate-authority-data: <base64-ca-cert>

users:
- name: admin-user
  user:
    client-certificate-data: <base64-cert>
    client-key-data: <base64-key>
- name: dev-user
  user:
    token: <bearer-token>

contexts:
- name: prod-admin
  context:
    cluster: production
    user: admin-user
    namespace: production
- name: staging-dev
  context:
    cluster: staging
    user: dev-user
    namespace: development

current-context: staging-dev
# 查看所有 context
kubectl config get-contexts

# 切换 context
kubectl config use-context prod-admin

# 查看当前 context
kubectl config current-context

# 合并多个 kubeconfig
export KUBECONFIG=~/.kube/config:~/.kube/config-staging
kubectl config view --merge --flatten > ~/.kube/config-merged

3. RBAC 授权

RBAC(Role-Based Access Control,基于角色的访问控制) 是 K8s 默认的授权机制。

3.1 RBAC 核心概念

资源(Resource)   = 什么:Pod、Deployment、Secret、ConfigMap...
操作(Verb)       = 怎么做:get/list/watch/create/update/patch/delete
角色(Role)       = 权限集合(允许对哪些资源执行哪些操作)
主体(Subject)    = 谁:User、Group、ServiceAccount
角色绑定(RoleBinding)= 将角色授予主体

3.2 Role vs ClusterRole

Role ClusterRole
作用范围 单个 Namespace 整个集群
绑定对象 RoleBinding RoleBinding 或 ClusterRoleBinding
能访问的资源 Namespace 级别资源 集群级别 + Namespace 级别资源

3.3 常用 Verbs

get          → 查看单个资源
list         → 列出资源
watch        → 监听资源变化
create       → 创建
update       → 全量更新(PUT)
patch        → 部分更新(PATCH)
delete       → 删除
deletecollection → 批量删除

3.4 Role 示例

只读 Role(适合监控/审计):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
- apiGroups: [""]          # "" 表示 core API 组
  resources: ["pods", "pods/log", "pods/exec"]
  verbs: ["get", "list", "watch"]

- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch"]

- apiGroups: [""]
  resources: ["services", "endpoints"]
  verbs: ["get", "list", "watch"]

开发者 Role(可以部署和调试):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: development
rules:
# 管理工作负载
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]

# 查看 Pod、日志、进入容器
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch", "delete"]
- apiGroups: [""]
  resources: ["pods/exec", "pods/portforward"]
  verbs: ["create"]

# 管理 Service
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

# 管理 ConfigMap
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

# 只能查看 Secret(不能查看内容)
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["list"]

# 查看事件
- apiGroups: [""]
  resources: ["events"]
  verbs: ["get", "list", "watch"]

ClusterRole(集群管理员):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-viewer
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
  resources: ["nodes", "pods"]
  verbs: ["get", "list"]

3.5 RoleBinding 示例

# 将 pod-reader Role 授予用户 alice
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: alice-pod-reader
  namespace: production
subjects:
# 可以绑定多种主体
- kind: User
  name: alice                    # 用户名(来自认证信息)
  apiGroup: rbac.authorization.k8s.io

- kind: Group
  name: dev-team                 # 用户组
  apiGroup: rbac.authorization.k8s.io

- kind: ServiceAccount
  name: monitoring-sa            # ServiceAccount
  namespace: monitoring          # ServiceAccount 所在 Namespace

roleRef:
  kind: Role                     # Role 或 ClusterRole
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding:

# 将 ClusterRole 授予集群范围
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-admin-binding
subjects:
- kind: User
  name: admin@company.com
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin            # K8s 内置的超级管理员角色
  apiGroup: rbac.authorization.k8s.io

3.6 内置 ClusterRole

ClusterRole 描述
cluster-admin 超级管理员,所有权限
admin 命名空间管理员,不能修改 Namespace 本身
edit 可以修改大多数资源,不能修改 Role/RoleBinding
view 只读(不包括 Secret)
# 快速给用户只读权限
kubectl create clusterrolebinding view-binding \
  --clusterrole=view \
  --user=alice

# 给 Namespace 管理员权限
kubectl create rolebinding admin-binding \
  --clusterrole=admin \
  --user=alice \
  --namespace=development

3.7 权限检查

# 检查当前用户是否有权限
kubectl auth can-i create pods
kubectl auth can-i get secrets -n production
kubectl auth can-i list deployments --all-namespaces

# 检查其他用户的权限
kubectl auth can-i create pods --as=alice
kubectl auth can-i create pods --as=system:serviceaccount:default:my-sa

# 查看用户的所有权限
kubectl auth can-i --list
kubectl auth can-i --list --as=alice

# 查看所有 ClusterRole 和 RoleBinding
kubectl get clusterrole,clusterrolebinding
kubectl get role,rolebinding -n production

4. ServiceAccount

ServiceAccount 是 Pod 的"身份证",让 Pod 能够调用 K8s API。

4.1 默认 ServiceAccount

每个 Namespace 都有一个 default ServiceAccount。如果不指定,Pod 使用 default ServiceAccount。

kubectl get serviceaccount
kubectl describe serviceaccount default

Token 自动挂载:

kubectl exec -it <pod> -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt   namespace   token

4.2 创建自定义 ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  name: go-operator-sa
  namespace: production
  annotations:
    # AWS IRSA(IAM Roles for Service Accounts)
    eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/my-app-role"
---
# 给 ServiceAccount 赋予权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: go-operator-role
  namespace: production
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "secrets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: go-operator-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: go-operator-sa
  namespace: production
roleRef:
  kind: Role
  name: go-operator-role
  apiGroup: rbac.authorization.k8s.io

4.3 Pod 使用 ServiceAccount

spec:
  serviceAccountName: go-operator-sa    # 指定 ServiceAccount
  automountServiceAccountToken: false    # 不自动挂载 Token(不需要 API 访问时禁用)

  containers:
  - name: go-app
    ...

4.4 Go 应用使用 ServiceAccount 调用 K8s API

package main

import (
    "context"
    "fmt"
    "log"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
)

func main() {
    // InCluster 配置:自动读取 ServiceAccount Token
    config, err := rest.InClusterConfig()
    if err != nil {
        log.Fatalf("Failed to get InCluster config: %v", err)
    }

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        log.Fatalf("Failed to create clientset: %v", err)
    }

    // 列出当前 Namespace 的 Pod
    namespace := getCurrentNamespace()
    pods, err := clientset.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{})
    if err != nil {
        log.Fatalf("Failed to list pods: %v", err)
    }

    fmt.Printf("Found %d pods in namespace %s:\n", len(pods.Items), namespace)
    for _, pod := range pods.Items {
        fmt.Printf("  - %s (%s)\n", pod.Name, pod.Status.Phase)
    }
}

func getCurrentNamespace() string {
    // 从 ServiceAccount 挂载的文件中读取当前 Namespace
    data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
    if err != nil {
        return "default"
    }
    return string(data)
}

5. 安全上下文(Security Context)

Security Context 定义容器的权限和访问控制设置,实现最小权限原则。

5.1 Pod 级别安全上下文

spec:
  securityContext:
    runAsUser: 1000           # 以用户 ID 1000 运行(非 root)
    runAsGroup: 3000          # 主组 ID
    fsGroup: 2000             # 挂载 Volume 的组 ID(写入文件时的 group)
    runAsNonRoot: true        # 强制非 root(如果镜像是 root 则启动失败)
    seccompProfile:           # Seccomp 安全计算模式
      type: RuntimeDefault    # 使用运行时默认配置(限制危险系统调用)
    sysctls:
    - name: net.core.somaxconn
      value: "1024"

5.2 容器级别安全上下文

spec:
  containers:
  - name: go-app
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      runAsGroup: 1000
      readOnlyRootFilesystem: true    # 根文件系统只读(防止容器写文件)
      allowPrivilegeEscalation: false  # 禁止提权(sudo、SUID 等)
      capabilities:                    # Linux Capabilities
        drop:
        - ALL                          # 删除所有 Capabilities
        add:
        - NET_BIND_SERVICE             # 只添加需要的(绑定 < 1024 端口)
      seccompProfile:
        type: RuntimeDefault

5.3 Linux Capabilities 常用列表

Capability 描述 是否需要
NET_BIND_SERVICE 绑定低于 1024 的端口 Web 服务监听 80/443
SYS_PTRACE 调试其他进程 通常不需要
SYS_ADMIN 各种系统管理操作 几乎不需要
NET_ADMIN 网络管理 CNI 插件需要
CHOWN 修改文件所有者 通常不需要
SETUID 修改进程 UID 通常不需要

最佳实践:drop ALL,只 add 需要的。

5.4 只读根文件系统 + 临时写目录

spec:
  containers:
  - name: go-app
    securityContext:
      readOnlyRootFilesystem: true   # 根文件系统只读
    volumeMounts:
    - name: tmp
      mountPath: /tmp                # 允许写 /tmp
    - name: logs
      mountPath: /var/log/app        # 允许写日志目录

  volumes:
  - name: tmp
    emptyDir: {}
  - name: logs
    emptyDir: {}

6. Pod Security Standards

K8s 1.25+ 弃用 PodSecurityPolicy,改用 Pod Security Standards(PSS)

6.1 三种安全级别

级别 描述
privileged 无限制(等同于 root 在宿主机上)
baseline 防止已知权限提升,允许默认配置
restricted 最严格,遵循 Pod 安全最佳实践

6.2 为 Namespace 设置安全策略

通过 Namespace 的 Label 控制:

# 对 production Namespace 强制执行 restricted 策略
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=v1.29 \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted       # 不满足则拒绝
    pod-security.kubernetes.io/warn: restricted          # 不满足则警告
    pod-security.kubernetes.io/audit: restricted         # 不满足则审计日志

restricted 级别要求(部分):

  • runAsNonRoot: true
  • allowPrivilegeEscalation: false
  • readOnlyRootFilesystem: true
  • seccompProfile.type: RuntimeDefaultLocalhost
  • capabilities.drop: [ALL]
  • 不允许 hostNetwork、hostPID、hostIPC

7. 镜像安全

7.1 使用私有镜像仓库

# 创建镜像仓库认证 Secret
kubectl create secret docker-registry registry-secret \
  --docker-server=myregistry.io \
  --docker-username=myuser \
  --docker-password=mypassword
spec:
  imagePullSecrets:
  - name: registry-secret
  containers:
  - name: go-app
    image: myregistry.io/go-app:v1.0.0

7.2 镜像 Tag 规范

# 不要用 latest!生产环境始终使用具体版本
image: go-app:v1.2.0       # 好:版本号
image: go-app:20260316      # 好:日期
image: go-app:abc1234       # 好:Git commit hash
image: go-app:latest        # 差:无法追踪版本,缓存导致行为不一致

7.3 镜像扫描

# 使用 Trivy 扫描镜像漏洞
trivy image myregistry.io/go-app:v1.0.0

# 在 CI/CD 中集成(GitHub Actions 示例)
# - name: Run Trivy vulnerability scanner
#   uses: aquasecurity/trivy-action@master
#   with:
#     image-ref: 'myregistry.io/go-app:${{ github.sha }}'
#     format: 'sarif'
#     exit-code: '1'
#     severity: 'CRITICAL,HIGH'

7.4 Go 应用的最小化 Dockerfile

# 多阶段构建
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server

# 最终镜像:使用 distroless(无 shell,攻击面极小)
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
EXPOSE 8080
USER nonroot:nonroot  # 非 root 用户
ENTRYPOINT ["/server"]

或使用 scratch(空镜像,极度精简):

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
EXPOSE 8080
USER 65534:65534  # nobody
ENTRYPOINT ["/server"]

8. Go Operator 的 RBAC 实践

使用 controller-runtime 或 kubebuilder 开发 Operator 时的 RBAC 配置:

// 在 Controller 代码上添加标记注释,kubebuilder 自动生成 RBAC YAML
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=mygroup.io,resources=myresources/finalizers,verbs=update

func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    // Reconcile 逻辑
    return ctrl.Result{}, nil
}

生成 RBAC:

make manifests  # 生成 config/rbac/ 目录下的 YAML

9. 安全检查清单

9.1 Namespace 级别

☐ 为每个团队/应用创建独立 Namespace
☐ 设置 ResourceQuota 限制资源使用
☐ 设置 LimitRange 设置默认资源限制
☐ 应用 Pod Security Standards(至少 baseline)
☐ 配置 NetworkPolicy(默认拒绝所有)

9.2 Pod 级别

☐ 设置 requests 和 limits
☐ runAsNonRoot: true
☐ readOnlyRootFilesystem: true
☐ allowPrivilegeEscalation: false
☐ capabilities: drop [ALL]
☐ seccompProfile: RuntimeDefault
☐ 不使用 hostNetwork/hostPID/hostIPC(除非必须)
☐ imagePullPolicy: Always(或使用 digest)
☐ 设置 livenessProbe 和 readinessProbe

9.3 RBAC 级别

☐ 最小权限原则:只给必要的权限
☐ 不要直接使用 cluster-admin
☐ 为每个应用创建独立 ServiceAccount
☐ 不使用 default ServiceAccount
☐ automountServiceAccountToken: false(不需要 API 访问时)
☐ 定期审计 RBAC 配置

9.4 镜像安全

☐ 使用私有镜像仓库
☐ 固定镜像版本(不使用 latest)
☐ 定期扫描镜像漏洞(Trivy/Snyk)
☐ 使用多阶段构建减小镜像大小
☐ 使用 distroless 或 scratch 基础镜像
☐ 配置 ImagePullSecrets

10. 小结

RBAC 快速参考

# 创建 Role
kubectl create role pod-reader \
  --verb=get,list,watch \
  --resource=pods \
  -n production

# 创建 RoleBinding
kubectl create rolebinding alice-pod-reader \
  --role=pod-reader \
  --user=alice \
  -n production

# 检查权限
kubectl auth can-i list pods -n production --as=alice

# 查看谁有什么权限
kubectl get rolebinding,clusterrolebinding -A \
  -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .subjects[*]}{.kind}{"\t"}{.name}{"\n"}{end}{end}'

关键安全原则

  1. 最小权限(Least Privilege):只给必要的权限
  2. 纵深防御(Defense in Depth):多层安全防护
  3. 默认拒绝(Default Deny):NetworkPolicy 默认拒绝所有流量
  4. 不可变基础设施:容器只读文件系统,配置从外部注入
  5. 零信任:不因为在集群内就默认信任