[쿠버네티스 패턴] 26장 Access Control

bocopile·2026년 1월 24일

쿠버네티스 패턴

목록 보기
24/28

0. 개념 요약

Kubernetes Access Control은 API 서버에 대한 접근을 제어하는 핵심 보안 메커니즘입니다.

단계질문실패 시담당
Authentication"당신은 누구입니까?"401 Unauthorized신원 확인
Authorization"무엇을 할 수 있습니까?"403 ForbiddenRBAC 평가
Admission Control"정책을 준수합니까?"거부 메시지Webhook/정책

1. Problem - 왜 Access Control이 중요한가

1.1 실제 보안 사고 사례

2022년 보안 연구에 따르면, 약 100만 개의 Kubernetes 인스턴스가 잘못된 설정으로 인터넷에 노출되어 있었습니다. 이는 Access Control 설정의 중요성을 보여주는 대표적인 사례입니다.

1.2 인증과 인가의 차이

구분인증 (Authentication)인가 (Authorization)
질문"당신은 누구입니까?""무엇을 할 수 있습니까?"
목적주체(Subject) 식별허용된 작업 결정
실패 시401 Unauthorized403 Forbidden
예시OIDC 토큰 검증, 인증서 확인Pod 생성 권한, Secret 읽기 권한

실무 예시: 개발자 Alice가 kubectl로 Pod를 생성하려 할 때

  1. 인증: Alice의 OIDC 토큰이 유효한지 확인
  2. 인가: Alice가 해당 namespace에서 Pod를 생성할 권한이 있는지 확인

2. Solution - API 서버 요청 처리 단계

2.1 3단계 보안 파이프라인

모든 Kubernetes API 요청은 반드시 3단계를 순차적으로 통과해야 합니다. 각 단계는 순차적으로 실행되며, 하나라도 실패하면 요청이 거부됩니다.

2.2 단계별 상세 설명

  1. Authentication (인증)

    • 요청자의 신원을 확인합니다
    • OIDC 토큰, X.509 인증서, Webhook 등 다양한 방식 지원
    • 실패 시: 401 Unauthorized 반환
    • 여러 인증 플러그인을 동시에 사용 가능하며, 하나라도 성공하면 통과
  2. Authorization (인가)

    • 인증된 사용자가 요청한 작업을 수행할 권한이 있는지 확인합니다
    • 주로 RBAC(Role-Based Access Control) 사용
    • 실패 시: 403 Forbidden 반환
    • Role/ClusterRole에 정의된 규칙과 요청을 매칭

    3. Admission Control (승인 제어)

    • 인가를 통과한 요청에 대해 추가 정책 검증 및 리소스 수정을 수행합니다
    • Mutating Webhook: 리소스에 기본값 주입, 사이드카 자동 추가 등
    • Validating Webhook: 정책 위반 검사 (예: privileged 컨테이너 차단)
    • 실패 시: 정책 위반 사유와 함께 거부

예시: kubectl create pod를 실행하면

  1. 인증: kubeconfig의 토큰/인증서로 사용자 신원 확인
  2. 인가: 해당 namespace에서 pods 리소스에 대한 create 권한 확인
  3. 승인: Pod 스펙이 조직 정책(리소스 제한, 이미지 레지스트리 등)을 준수하는지 확인

3. Authentication (인증)

3.1 Kubernetes 인증 전략

Kubernetes는 다양한 플러그인 방식의 인증 전략을 제공합니다.

여러 전략을 동시에 사용할 수 있으며, 하나의 전략이 성공하면 다음 단계로 진행됩니다.

3.2 인증 전략 비교

전략설명적합한 환경
Bearer Tokens (OIDC)OAuth2 기반 OIDC 토큰 사용기업 환경, SSO 연동
Client Certificates (X.509)TLS 인증서 기반 인증서비스 간 통신, CI/CD
Authenticating Proxy프록시를 통한 인증 위임레거시 시스템 연동
Webhook Token외부 서비스로 토큰 검증 위임커스텀 인증 로직

운영 팁: Static Token Files는 보안 취약점이 많아 프로덕션에서 사용을 권장하지 않습니다.

3.3 OIDC 인증 흐름 예시

kubeconfig 설정 예시

users:
- name: alice
  user:
    auth-provider:
      name: oidc
      config:
        idp-issuer-url: <https://keycloak.example.com/realms/k8s>
        client-id: kubernetes
        refresh-token: <refresh-token>
        id-token: <id-token>

4. Subject (주체)

4.1 Subject의 두 가지 유형

유형관리 방식인증 방법사용 사례
User외부 IdP (Keycloak, Okta)OIDC, X.509kubectl, 관리자 접근
ServiceAccountKubernetes API자동 발급 토큰Pod, Operator, CI/CD

4.2 User 인증 후 표현 형식

인증이 완료되면 사용자 정보는 다음과 같은 형식으로 표현됩니다

alice,4bc01e30-406b-4514,"system:authenticated,developers","scopes:openid"
필드설명
Usernamealice사용자 이름
UID4bc01e30-406b-4514고유 사용자 ID
Groupssystem:authenticated,developers소속 그룹 목록
Extrascopes:openid추가 정보 (인증 방식 등)

system: 접두사가 붙은 사용자/그룹은 Kubernetes 내부용이므로, 직접 생성하지 않습니다.

4.3 ServiceAccount 정의

ServiceAccount는 Pod의 워크로드 신원(workload identity)을 나타내는 Kubernetes 리소스입니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: production
automountServiceAccountToken: false  # 보안 권장: 필요 없으면 비활성화
imagePullSecrets:                    # 프라이빗 레지스트리 인증
  - name: my-registry-secret
secrets:                             # 마운트 가능한 Secret 제한
  - name: my-app-secret

ServiceAccount 사용자 이름 형식

system:serviceaccount:<namespace>:<name>

예시:

system:serviceaccount:production:my-app-sa

4.4 ServiceAccount 추가 기능

imagePullSecrets

  • 프라이빗 레지스트리 인증 정보를 ServiceAccount에 연결
  • 해당 SA를 사용하는 모든 Pod에 자동으로 주입
  • Pod마다 개별 설정할 필요 없이 중앙 관리 가능

mountable secrets (secrets 필드)

  • kubernetes.io/enforce-mountable-secrets: "true" 어노테이션과 함께 사용
  • 해당 SA를 사용하는 Pod가 지정된 Secret만 마운트하도록 제한
  • 보안 강화를 위한 추가 레이어
apiVersion: v1
kind: ServiceAccount
metadata:
  name: restricted-sa
  namespace: production
  annotations:
    kubernetes.io/enforce-mountable-secrets: "true"  # 제한 활성화
secrets:
  - name: allowed-secret-1
  - name: allowed-secret-2
# 이 SA를 사용하는 Pod는 allowed-secret-1, allowed-secret-2만 마운트 가능

4.3 ServiceAccount Token 마운트

Pod가 생성되면 연결된 ServiceAccount의 토큰이 자동으로 마운트됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  serviceAccountName: my-app-sa
  containers:
    - name: app
      image: my-app:latest
      # 토큰은 아래 경로에 자동 마운트됨
      # /var/run/secrets/kubernetes.io/serviceaccount/token

Kubernetes 1.24+ 변경사항
Projected Volume 토큰이 기본값이 되었습니다.
토큰이 Pod 수명과 연동되어 자동 회전되며, 만료 시간 설정이 가능합니다.

4.4 기본 시스템 사용자와 그룹

시스템 사용자

Username용도
system:anonymous익명 요청
system:apiserverAPI 서버 자체
system:kube-controller-manager컨트롤러 매니저
system:kube-scheduler스케줄러

시스템 그룹

Group용도
system:unauthenticated인증되지 않은 모든 요청
system:authenticated인증된 사용자
system:masters클러스터 무제한 접근 권한
system:serviceaccounts모든 ServiceAccount
system:serviceaccounts:<ns>특정 네임스페이스의 ServiceAccount

5. Role-Based Access Control (RBAC)

5.1 RBAC 핵심 구조

RBAC는 "누가(Subject) 무엇을(Resource) 어떻게(Verb) 할 수 있는가"를 정의합니다.

핵심 관계:

  • Subject : Role = M : N (다대다 관계)
  • RoleBinding이 Subject와 Role을 연결합니다

5.2 Role 정의

Role은 특정 네임스페이스 내에서 허용되는 작업을 정의합니다.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production  # Role은 항상 네임스페이스에 종속됨
rules:
  - apiGroups: [""]      # 코어 API 그룹 (빈 문자열)
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get"]

5.3 Rule 구성 요소

필드설명예시
apiGroupsAPI 그룹"" (코어), apps, networking.k8s.io
resources리소스 종류pods, deployments, secrets
verbs허용 작업get, list, create, delete
resourceNames특정 리소스 이름 (선택)["my-config"]

Verb → HTTP 메서드 매핑:

VerbsHTTP설명
getGET단일 리소스 조회
listGET리소스 목록 조회
watchGET (streaming)변경 이벤트 감시
createPOST리소스 생성
updatePUT리소스 전체 수정
patchPATCH리소스 부분 수정
deleteDELETE단일 리소스 삭제
deletecollectionDELETE다수 리소스 삭제

5.4 RoleBinding 정의

RoleBinding은 Subject와 Role을 연결합니다.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: production
subjects:
  - kind: User
    name: alice
    apiGroup: rbac.authorization.k8s.io
  - kind: ServiceAccount
    name: ci-bot
    namespace: ci-cd  # 다른 네임스페이스의 SA도 가능
  - kind: Group
    name: developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

5.5 ClusterRole과 ClusterRoleBinding

ClusterRole은 클러스터 전체에 적용되는 Role입니다.

사용 시나리오

시나리오Role 유형Binding 유형
특정 NS 리소스 접근RoleRoleBinding
여러 NS에서 동일 규칙 재사용ClusterRoleRoleBinding (각 NS)
클러스터 전역 리소스 (CRD, Node)ClusterRoleClusterRoleBinding
모든 NS의 특정 리소스 접근ClusterRoleClusterRoleBinding

5.6 기본 제공 ClusterRole

ClusterRole권한사용 시 주의
view대부분 읽기 (Secret 제외)안전, 개발자용
edit대부분 읽기/수정Role/RoleBinding 수정 불가
admin네임스페이스 내 완전 제어Role/RoleBinding 수정 가능
cluster-admin클러스터 전체 완전 제어⚠️ 절대 Pod에 부여 금지

5.7 ClusterRole Aggregation

여러 ClusterRole의 권한을 동적으로 결합할 수 있습니다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: custom-view
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"  # view에 자동 포함
rules:
  - apiGroups: ["mycompany.io"]
    resources: ["myresources"]
    verbs: ["get", "list", "watch"]

CRD 권한 추가 시 aggregate-to-view 레이블을 사용하면
view Role을 가진 모든 사용자에게 해당 권한이 자동 부여됩니다.

5.8 resourceNames를 통한 세밀한 접근 제어

특정 이름의 리소스에만 접근을 허용하려면 resourceNames 필드를 사용합니다.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: configmap-updater
  namespace: production
rules:
  # 특정 ConfigMap만 수정 가능
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config", "feature-flags"]  # 이 두 개만 허용
    verbs: ["get", "update", "patch"]
  # 모든 ConfigMap 읽기는 허용
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]

[주의사항]
resourceNames는 create verb와 함께 사용할 수 없습니다.
생성 시점에는 리소스 이름이 아직 존재하지 않기 때문입니다.

5.9 Subresources 접근 제어

Pod의 로그 조회, exec 실행 등은 Subresource로 별도 권한이 필요합니다.

Subresource 권한 예시

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-debugger
  namespace: production
rules:
  # Pod 조회
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]
  # 로그 조회 (kubectl logs)
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  # exec 실행 (kubectl exec) -  주의: 매우 강력한 권한
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create"]
  # port-forward (kubectl port-forward)
  - apiGroups: [""]
    resources: ["pods/portforward"]
    verbs: ["create"]

주요 Subresource 목록:

Subresource용도필요 Verb
pods/log컨테이너 로그 조회get
pods/exec컨테이너 내 명령 실행create
pods/portforward포트 포워딩create
pods/statusPod 상태 수정update, patch
deployments/scaleDeployment 스케일 조정update, patch
services/proxy서비스 프록시get, create

pods/exec과 pods/portforward는 컨테이너 내부에 직접 접근할 수 있어 매우 강력합니다.
프로덕션 환경에서는 제한적으로 부여해야 합니다.

5.10 Privilege Escalation 방지

Kubernetes는 자신이 가지지 않은 권한을 다른 사용자에게 부여하는 것을 방지합니다.

Escalation 제어 Verb

Verb설명사용 사례
escalateRole/ClusterRole 수정 시 권한 상승 허용Role 관리자
bind자신이 가지지 않은 Role도 Binding 가능RBAC 관리자

예시: RBAC 관리자 Role

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: rbac-manager
rules:
  # Role과 ClusterRole 관리
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["roles", "clusterroles"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  # RoleBinding과 ClusterRoleBinding 관리
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["rolebindings", "clusterrolebindings"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  # 권한 상승 허용 (매우 주의!)
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["roles", "clusterroles"]
    verbs: ["escalate"]
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["rolebindings", "clusterrolebindings"]
    verbs: ["bind"]

escalate와 bind 권한은 실질적으로 cluster-admin과 동등한 권한을 부여할 수 있으므로,
극히 제한된 관리자에게만 부여해야 합니다.


6. Admission Controllers (승인 컨트롤러)

6.1 Admission Controller 역할

인증과 인가를 통과한 요청에 대해 추가 검증 및 수정을 수행합니다.

기능설명예시
기본값 설정리소스에 기본값 자동 적용StorageClass 기본 설정
검증리소스 스펙 유효성 검사리소스 제한 검증
정책 적용조직 정책 강제특정 레지스트리만 허용

6.2 Webhook 동작 메커니즘

Admission Webhook은 API 서버가 외부 HTTP 서비스를 호출하여 요청을 검증하거나 수정하는 방식입니다.

Webhook 호출 순서

  1. Mutating Webhooks: 먼저 실행, 리소스를 수정할 수 있음
  2. Validating Webhooks: 이후 실행, 수정 불가, 검증만 수행

Mutating Webhook이 여러 개인 경우, 순서에 따라 결과가 달라질 수 있습니다.
reinvocationPolicy: IfNeeded를 설정하면 다른 Webhook의 수정 후 재호출됩니다.

6.3 ValidatingWebhookConfiguration 예시

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy-validator
webhooks:
  - name: validate.pods.example.com
    # Webhook 서비스 위치
    clientConfig:
      service:
        name: pod-validator-service
        namespace: validation-system
        path: /validate
        port: 443
      caBundle: <base64-encoded-ca-cert>  # Webhook 서버 인증서 검증용

    # 대상 리소스 지정
    rules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
        scope: Namespaced  # Namespaced 또는 Cluster

    # 네임스페이스 필터링
    namespaceSelector:
      matchExpressions:
        - key: environment
          operator: In
          values: ["production", "staging"]

    # 실패 시 동작 (Fail: 거부, Ignore: 허용)
    failurePolicy: Fail

    # 사이드이펙트 없음 선언 (Dry-run 요청도 처리)
    sideEffects: None

    # 타임아웃 (기본 10초, 최대 30초)
    timeoutSeconds: 5

    # 요청에 포함할 정보
    admissionReviewVersions: ["v1", "v1beta1"]

    # 동일 객체의 여러 변경 처리 방식
    matchPolicy: Equivalent

6.4 MutatingWebhookConfiguration 예시 (사이드카 자동 주입)

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
  - name: inject.sidecar.example.com
    clientConfig:
      service:
        name: sidecar-injector
        namespace: istio-system
        path: /inject
        port: 443
      caBundle: <base64-encoded-ca-cert>

    rules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]

    # 특정 레이블이 있는 네임스페이스만 대상
    namespaceSelector:
      matchLabels:
        sidecar-injection: enabled

    # 특정 레이블이 있는 Pod는 제외
    objectSelector:
      matchExpressions:
        - key: sidecar.istio.io/inject
          operator: NotIn
          values: ["false"]

    failurePolicy: Ignore  # 주입 실패해도 Pod 생성은 허용
    sideEffects: None
    admissionReviewVersions: ["v1"]

    # Mutating Webhook은 재호출 정책 설정 가능
    reinvocationPolicy: IfNeeded

6.5 Webhook 운영 Best Practices

항목권장이유
failurePolicyFail (중요 정책)보안 정책은 우회되면 안 됨
timeoutSeconds5초 이하API 응답 지연 방지
sideEffectsNoneDry-run 지원, 캐싱 가능
고가용성최소 2개 PodWebhook 장애 시 전체 API 차단 방지
namespaceSelectorkube-system 제외시스템 컴포넌트 영향 방지

kube-system 제외 예시

namespaceSelector:
  matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: NotIn
      values: ["kube-system", "kube-public"]

운영 주의: Webhook 서비스가 다운되면 failurePolicy: Fail인 경우
모든 관련 API 요청이 실패합니다. 반드시 고가용성을 확보하세요.

7. Kubernetes v1.35+ 최신 보안 기능

7.1 버전별 주요 변경사항

버전변경사항영향
1.24ServiceAccount Token Secret 자동 생성 제거Projected Volume 필수
1.27ValidatingAdmissionPolicy BetaCEL 기반 정책
1.30ValidatingAdmissionPolicy GA프로덕션 사용 가능
1.31Bound ServiceAccount Token 개선audience 분리 강화
1.32AuthorizeWithSelectors (Beta)필드 셀렉터 기반 인가

7.2 ValidatingAdmissionPolicy (CEL 기반)

OPA/Gatekeeper 없이 Kubernetes 네이티브로 정책을 정의할 수 있습니다.

# Privileged 컨테이너 차단 정책
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: deny-privileged
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validations:
    - expression: "!object.spec.containers.exists(c, c.securityContext.privileged == true)"
      message: "Privileged containers are not allowed"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: deny-privileged-binding
spec:
  policyName: deny-privileged
  validationActions: ["Deny"]
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: production

CEL vs OPA Gatekeeper

항목CEL (ValidatingAdmissionPolicy)OPA Gatekeeper
설치내장 (1.30+)별도 설치
언어CELRego
학습 곡선낮음높음
성능매우 빠름Webhook 오버헤드
복잡한 정책제한적강력함

7.3 Bound ServiceAccount Token + Audience 분리

외부 시스템 연동 시 토큰 재사용을 방지하기 위한 audience 분리 전략입니다.

apiVersion: v1
kind: Pod
metadata:
  name: vault-client
spec:
  serviceAccountName: vault-sa
  containers:
    - name: app
      image: my-app
      volumeMounts:
        - name: vault-token
          mountPath: /var/run/secrets/vault
  volumes:
    - name: vault-token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 600
              audience: "<https://vault.example.com>"  # Vault 전용 토큰

보안 이점: K8s API용 토큰이 탈취되어도 Vault는 audience가 다르므로 접근 차단됩니다.

7.4 Pod Security Admission (PSA)

Kubernetes v1.25+에서 GA된 Pod 보안 표준 강제 메커니즘입니다.
PodSecurityPolicy(PSP)의 대체 기능으로, 네임스페이스 레벨에서 Pod 보안 정책을 적용합니다.

flowchart TD
    subgraph Levels["보안 레벨"]
        Privileged["privileged<br/>(제한 없음)"]
        Baseline["baseline<br/>(기본 보안)"]
        Restricted["restricted<br/>(강화된 보안)"]
    end

    subgraph Modes["적용 모드"]
        Enforce["enforce<br/>(차단)"]
        Audit["audit<br/>(감사 로그)"]
        Warn["warn<br/>(경고)"]
    end

    Privileged --> Enforce
    Baseline --> Audit
    Restricted --> Warn

보안 레벨 비교

레벨설명제한 사항
privileged제한 없음, 레거시 호환없음
baseline알려진 권한 상승 방지hostNetwork, hostPID, privileged 컨테이너 차단
restricted강화된 보안 정책runAsNonRoot 필수, 특정 capabilities만 허용

네임스페이스에 PSA 적용

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # 정책 위반 시 Pod 생성 차단
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.30

    # 정책 위반 시 감사 로그 기록
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.30

    # 정책 위반 시 사용자에게 경고
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.30

restricted 레벨을 통과하는 Pod 예시

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: my-app:latest
      securityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop: ["ALL"]
        readOnlyRootFilesystem: true
        runAsNonRoot: true

7.5 ValidatingAdmissionPolicy 추가 CEL 예시

이미지 레지스트리 제한

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: restrict-image-registry
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validations:
    - expression: |
        object.spec.containers.all(c,
          c.image.startsWith('registry.company.com/') ||
          c.image.startsWith('gcr.io/google-containers/')
        )
      message: "이미지는 허용된 레지스트리에서만 가져올 수 있습니다"

리소스 제한 필수화

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-resource-limits
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validations:
    - expression: |
        object.spec.containers.all(c,
          has(c.resources) &&
          has(c.resources.limits) &&
          has(c.resources.limits.memory) &&
          has(c.resources.limits.cpu)
        )
      message: "모든 컨테이너에 CPU/메모리 limits를 설정해야 합니다"

레이블 필수화

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-labels
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"]
  validations:
    - expression: |
        has(object.metadata.labels) &&
        has(object.metadata.labels.app) &&
        has(object.metadata.labels.owner)
      message: "Deployment에는 'app'과 'owner' 레이블이 필수입니다"

유용한 CEL 표현식 패턴

패턴CEL 표현식용도
모든 항목 검증list.all(item, condition)모든 컨테이너 검증
하나라도 만족list.exists(item, condition)특정 조건 존재 확인
필드 존재 확인has(object.field)선택 필드 검증
문자열 시작string.startsWith('prefix')이미지 레지스트리 검증
문자열 포함string.contains('substr')이름 규칙 검증
정규식 매칭string.matches('regex')복잡한 패턴 검증

7.6 AuthorizeWithSelectors (Beta, v1.32+)

기존 RBAC는 리소스 종류만 제어했지만, AuthorizeWithSelectors는 필드/레이블 셀렉터 기반으로 더 세밀한 인가가 가능합니다.

flowchart LR
    subgraph Traditional["기존 RBAC"]
        T1["pods 리소스"] --> T2["모든 Pod 접근"]
    end

    subgraph New["AuthorizeWithSelectors"]
        N1["pods 리소스"] --> N2["environment=dev인<br/>Pod만 접근"]
    end

  

사용 예시 - 특정 레이블의 Pod만 접근 허용

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: dev-pod-viewer
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
    # v1.32+ Beta 기능
    fieldSelector:
      - key: metadata.namespace
        operator: In
        values: ["dev", "staging"]
    labelSelector:
      - key: environment
        operator: In
        values: ["development"]

주의: 이 기능은 v1.32에서 Beta입니다. --authorization-config 설정에서 활성화해야 합니다.

활성화 방법:

  1. Feature Gate 활성화 (v1.31 Alpha, v1.32 Beta 기본 활성화)
# kube-apiserver 시작 옵션
--feature-gates=AuthorizeWithSelectors=true
  1. Authorization Config 파일 생성
# /etc/kubernetes/authorization-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
  - type: RBAC
    name: rbac
    rbac:
      # 셀렉터 기반 인가 활성화
      allowFieldSelectors: true
      allowLabelSelectors: true
  - type: Node
    name: node
  1. API 서버에 설정 적용
# kube-apiserver 시작 옵션
--authorization-config=/etc/kubernetes/authorization-config.yaml

--authorization-config를 사용하면 기존 --authorization-mode 플래그는 무시됩니다.
모든 authorizer를 설정 파일에 명시해야 합니다.

버전별 상태

버전상태Feature Gateauthorization-config
v1.31Alpha수동 활성화 필요필수
v1.32Beta기본 활성화필수
v1.33+GA 예정기본 활성화필수

7.7 버전별 마이그레이션 가이드

v1.24 마이그레이션 - ServiceAccount Token

❌ 이전 방식 (v1.24 이전) - 더 이상 자동 생성 안 됨

apiVersion: v1
kind: Secret
metadata:
  name: my-sa-token
  annotations:
    kubernetes.io/service-account.name: my-sa
type: kubernetes.io/service-account-token

✅ 권장 방식 (v1.24+) - Projected Volume 사용

apiVersion: v1
kind: Pod
spec:
  containers:
    - name: app
      volumeMounts:
        - name: token
          mountPath: /var/run/secrets/tokens
  volumes:
    - name: token
      projected:
        sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 3600

v1.25 마이그레이션 - PSP → PSA

PSP 설정PSA 레벨조치
privileged: true 허용privileged네임스페이스 레이블 설정
hostNetwork, hostPID 차단baseline네임스페이스 레이블 설정
runAsNonRoot, capabilities 제한restrictedPod 스펙 수정 필요

마이그레이션 체크리스트

# 1. 현재 PSP 사용 현황 확인
kubectl get psp

# 2. 각 네임스페이스의 Pod 보안 수준 확인
kubectl label --dry-run=server --overwrite ns my-ns \\
  pod-security.kubernetes.io/enforce=restricted

# 3. PSA 경고 모드로 먼저 테스트
kubectl label ns my-ns \\
  pod-security.kubernetes.io/warn=restricted \\
  pod-security.kubernetes.io/warn-version=latest

# 4. 문제 없으면 enforce 모드 적용
kubectl label ns my-ns \\
  pod-security.kubernetes.io/enforce=restricted \\
  pod-security.kubernetes.io/enforce-version=v1.30

8. 운영 관점 Best Practices

8.1 RBAC 설계 체크리스트

항목권장비권장
권한 범위최소 권한 원칙와일드카드(*) 남용
ServiceAccount전용 SA 생성default SA 사용
토큰 마운트automountServiceAccountToken: false무조건 true
Binding 유형RoleBinding 우선불필요한 ClusterRoleBinding
cluster-admin관리자 전용⚠️ 절대 Pod에 부여 금지

8.2 위험한 RBAC 설정 확인

# cluster-admin 권한을 가진 ServiceAccount 확인
kubectl get clusterrolebindings -o json | \\
  jq -r '.items[] | select(.roleRef.name == "cluster-admin") | .subjects[]? | "\\(.namespace)/\\(.name)"'

# 특정 SA의 모든 권한 나열
kubectl auth can-i --list \\
  --as system:serviceaccount:production:my-sa \\
  -n production

# 특정 작업 가능 여부 확인
kubectl auth can-i create pods \\
  --as system:serviceaccount:production:my-sa \\
  -n production

8.3 403 Forbidden 트러블슈팅

8.4 RBAC 감사 도구

# rakkess - 권한 매트릭스 조회
kubectl krew install access-matrix
kubectl access-matrix --as system:serviceaccount:default:my-sa

# rbac-lookup - RBAC 관계 조회
kubectl krew install rbac-lookup
kubectl rbac-lookup alice

8.5 Role vs ClusterRole 선택 가이드

선택 기준 상세:

시나리오Role 유형Binding 유형예시
단일 NS에서만 작업RoleRoleBinding개발자의 dev NS 접근
동일 규칙을 여러 NS에 적용ClusterRoleRoleBinding (각 NS)모니터링 에이전트
모든 NS의 리소스 접근ClusterRoleClusterRoleBinding클러스터 관리자
클러스터 범위 리소스 접근ClusterRoleClusterRoleBindingNode 관리, CRD 조회

권장: 가능한 한 Role + RoleBinding을 우선 사용하세요. 권한 범위가 작을수록 보안 위험이 줄어듭니다.

8.6 Wildcard 권한의 위험성

  • (와일드카드) 권한은 현재와 미래의 모든 리소스에 대한 권한을 부여합니다.
# 위험한 예시 - 절대 사용 금지
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: dangerous-role
rules:
  - apiGroups: ["*"]     # 모든 API 그룹
    resources: ["*"]     # 모든 리소스
    verbs: ["*"]         # 모든 작업

왜 위험한가?

구체적인 위험 사례:

와일드카드 사용의도하지 않은 접근위험도
resources: ["*"]새로 설치된 CRD 리소스 자동 접근높음
verbs: ["*"]delete, deletecollection 포함높음
apiGroups: ["*"]모든 서드파티 API 접근높음
resourceNames: [] (빈 배열 아님)해당 리소스 전체 접근중간

올바른 권한 설계:

# 권장 - 명시적으로 필요한 권한만 나열
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-developer
  namespace: development
rules:
  # Pod 관리
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch", "create", "delete"]
  # Deployment 관리
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  # ConfigMap 읽기
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]
  # Secret은 의도적으로 제외

최소 권한 원칙: "필요한 권한만, 필요한 범위에서, 필요한 기간 동안만" 부여해야 한다.

8.7 위험한 권한 조합

다음 권한 조합은 보안상 특히 주의가 필요합니다

권한 조합위험성대안
secrets + get/list모든 Secret 접근 가능resourceNames로 특정 Secret만 허용
pods/exec + create컨테이너 내부 명령 실행디버깅 용도로만 제한적 부여
* + * + *cluster-admin과 동등명시적 권한 나열
escalate / bind권한 상승 가능RBAC 관리자만 부여
impersonate다른 사용자로 가장감사/긴급 대응용으로만
# 와일드카드 권한을 가진 ClusterRole 찾기
kubectl get clusterroles -o json | \\
  jq -r '.items[] | select(.rules[]? | .resources[]? == "*" or .verbs[]? == "*") | .metadata.name'

9. 정리

핵심 내용 요약

RBAC만으로는 충분하지 않습니다

완전한 보안을 위해서는 다음이 함께 필요합니다

영역도구/정책역할
네트워크NetworkPolicyPod 간 통신 제어
컨테이너Pod Security Standards보안 컨텍스트 강제
정책ValidatingAdmissionPolicy리소스 정책 검증
런타임Falco, Sysdig런타임 위협 탐지
시크릿Vault, Sealed Secrets외부 Secret 관리

출처 및 참고자료

도서

  • Bilgin Ibryam, Roland Huß, "Kubernetes Patterns, 2nd Edition", O'Reilly Media, 2023

공식 문서

보안 관련

profile
DevOps Engineer

0개의 댓글