LESSON 40分

「Kubernetes のセキュリティは多層防御だ。認証、認可、Admission Control、Pod Security、ネットワークポリシー、シークレット管理。どれか一つが欠けても穴になる」と佐藤CTOが警告した。

推定読了時間: 40分


セキュリティの多層モデル

graph TB
    External["外部アクセス"]:::external
    Auth["Authentication<br/>(認証)"]:::auth
    Authz["Authorization<br/>(RBAC)"]:::authz
    Admission["Admission Control"]:::admission
    PodSec["Pod Security"]:::podsec
    NetPol["Network Policy"]:::netpol

    External ==> Auth
    Auth ==> Authz
    Authz ==> Admission
    Admission ==> PodSec
    PodSec ==> NetPol

    classDef external fill:#8b0000,stroke:#ff4444,color:#ffffff,stroke-width:3px
    classDef auth fill:#b8860b,stroke:#ffd700,color:#ffffff,stroke-width:2px
    classDef authz fill:#0f3460,stroke:#4a90d9,color:#ffffff,stroke-width:2px
    classDef admission fill:#533483,stroke:#9b59b6,color:#ffffff,stroke-width:2px
    classDef podsec fill:#1a5276,stroke:#2e86c1,color:#ffffff,stroke-width:2px
    classDef netpol fill:#006400,stroke:#00ff00,color:#ffffff,stroke-width:2px

RBAC (Role-Based Access Control)

基本概念

リソーススコープ説明
RoleNamespaceNamespace 内のリソースへのアクセス権
ClusterRoleClusterクラスタ全体のリソースへのアクセス権
RoleBindingNamespaceRole をユーザー/グループに紐付け
ClusterRoleBindingClusterClusterRole をユーザー/グループに紐付け

実践的な RBAC 設定

# 開発者向け Role(Namespace スコープ)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: development
rules:
  # Pod の読み取りとログ閲覧
  - apiGroups: [""]
    resources: ["pods", "pods/log", "pods/status"]
    verbs: ["get", "list", "watch"]
  # Deployment の管理
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  # ConfigMap / Secret の読み取り
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]
  # Secret は名前を指定して制限
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["app-config"]
    verbs: ["get"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-binding
  namespace: development
subjects:
  - kind: Group
    name: developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io
# SRE 向け ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: sre-role
rules:
  - apiGroups: [""]
    resources: ["nodes", "namespaces", "persistentvolumes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods", "services", "configmaps", "secrets"]
    verbs: ["*"]
  - apiGroups: ["apps"]
    resources: ["deployments", "statefulsets", "daemonsets"]
    verbs: ["*"]
  - apiGroups: ["batch"]
    resources: ["jobs", "cronjobs"]
    verbs: ["*"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["networkpolicies", "ingresses"]
    verbs: ["*"]

ServiceAccount

# アプリケーション専用の ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: api-server
  namespace: production
  annotations:
    # AWS IRSA (IAM Roles for Service Accounts)
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/api-server-role
automountServiceAccountToken: false  # デフォルトではマウントしない
---
# Deployment で ServiceAccount を指定
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  template:
    spec:
      serviceAccountName: api-server
      automountServiceAccountToken: true  # 必要な場合のみ有効化
      containers:
        - name: api
          image: my-registry/api-server:v1.0.0

IRSA(IAM Roles for Service Accounts)

// Pod 内から AWS サービスにアクセス
// IRSA により Pod に IAM Role が割り当てられる
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

// 認証情報は自動的に Pod の ServiceAccount から取得
const s3 = new S3Client({ region: "ap-northeast-1" });
const response = await s3.send(new GetObjectCommand({
  Bucket: "my-bucket",
  Key: "config.json",
}));
# Terraform での IRSA 設定
module "irsa_api_server" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
  version = "5.33.0"

  role_name = "api-server-role"

  oidc_providers = {
    main = {
      provider_arn               = module.eks.oidc_provider_arn
      namespace_service_accounts = ["production:api-server"]
    }
  }

  role_policy_arns = {
    s3_read = aws_iam_policy.s3_read.arn
    sqs     = aws_iam_policy.sqs_access.arn
  }
}

Pod Security Standards

Kubernetes 1.25+ で Pod Security Admission が GA。

レベル説明用途
Privileged制限なしシステムレベルのワークロード
Baseline既知の権限昇格を防止一般的なワークロード
Restricted最も厳格なセキュリティセキュリティ重視のワークロード
# Namespace に Pod Security Standards を適用
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
# Restricted レベルに準拠した Pod
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: my-registry/app:v1.0.0
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsUser: 1000
        runAsGroup: 1000
        capabilities:
          drop:
            - ALL
      volumeMounts:
        - name: tmp
          mountPath: /tmp
  volumes:
    - name: tmp
      emptyDir:
        sizeLimit: 100Mi

Secrets 管理

Kubernetes Secret の限界

「Kubernetes の Secret は base64 エンコードされているだけで暗号化されていない。本番では外部シークレット管理サービスとの連携が必須だ」

External Secrets Operator

# SecretStore(AWS Secrets Manager と連携)
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets
            namespace: external-secrets
---
# ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: production/database
        property: username
    - secretKey: password
      remoteRef:
        key: production/database
        property: password

Sealed Secrets

# SealedSecret(Git にコミット可能な暗号化された Secret)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: api-keys
  namespace: production
spec:
  encryptedData:
    API_KEY: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...
    DB_PASSWORD: AgAR0x4+mIWcQJ0qfk5H3FZ8...
  template:
    metadata:
      name: api-keys
      namespace: production
    type: Opaque

OPA / Gatekeeper(ポリシーエンジン)

# ConstraintTemplate: コンテナイメージのレジストリを制限
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sallowedrepos
spec:
  crd:
    spec:
      names:
        kind: K8sAllowedRepos
      validation:
        openAPIV3Schema:
          type: object
          properties:
            repos:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sallowedrepos

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          satisfied := [good | repo = input.parameters.repos[_]; good = startswith(container.image, repo)]
          not any(satisfied)
          msg := sprintf("container <%v> has an invalid image repo <%v>, allowed repos are %v", [container.name, container.image, input.parameters.repos])
        }
---
# Constraint: 許可されたレジストリのみ
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allowed-repos
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces: ["production"]
  parameters:
    repos:
      - "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/"
      - "gcr.io/distroless/"
追加のOPAポリシー例
# リソースリクエストの必須化
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.requests
          msg := sprintf("container <%v> must have resource requests", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.requests.cpu
          msg := sprintf("container <%v> must have CPU request", [container.name])
        }

        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not container.resources.requests.memory
          msg := sprintf("container <%v> must have memory request", [container.name])
        }

まとめ

セキュリティレイヤーツール/機能目的
認証x509, OIDC, ServiceAccount身元の確認
認可RBACアクセス権の制御
AdmissionOPA/Gatekeeper, Webhookポリシーの強制
Pod SecurityPod Security Standardsコンテナの権限制限
シークレットExternal Secrets, Sealed Secrets秘密情報の安全な管理
ネットワークNetworkPolicy通信の制御

チェックリスト

  • RBAC の Role/ClusterRole/Binding を設計できる
  • ServiceAccount と IRSA を設定できる
  • Pod Security Standards の3レベルを理解している
  • External Secrets Operator でシークレットを外部管理できる
  • OPA/Gatekeeper でポリシーを定義できる
  • 最小権限の原則に基づいたセキュリティ設計ができる

次のステップへ

Step 2 の演習とクイズで、Kubernetes 本番運用の知識を確認しましょう。