目录

Kubernetes-08 RBAC 与安全

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. 零信任:不因为在集群内就默认信任