Security - 정리(2)

2026. 1. 2. 21:21·CKA

(API Group → Authorization/RBAC → ServiceAccount/Token → Image/Runtime/Network Security → Context 도구 → CRD/Controller/Operator)

아래 내용은 자막 스크립트에서 다룬 흐름을 중심으로, 우리가 다뤘던 주제들을 한 번에 이어지도록 재구성한 정리글입니다. (중간중간 실무에서 자주 막히는 포인트만 최소한으로 덧붙였습니다.)


1) Kubernetes API와 API Group: “무엇을” 제어하는가의 지도

Kubernetes에서 우리가 하는 거의 모든 일은 결국 kube-apiserver와의 상호작용입니다.

  • 버전 확인: API 서버의 /version
  • 파드 목록: /api/v1/pods
  • 기타 리소스: /apis/<group>/<version>/...

Kubernetes API는 목적에 따라 큰 덩어리로 나뉘는데, 이 강의에서는 “클러스터 기능을 담당하는 API”에 집중합니다.

Core Group vs Named Group

  • Core Group: 네임스페이스, 파드, 서비스, ConfigMap, Secret, PV/PVC 등 “기본 리소스”가 있는 곳
    • URL 경로가 보통 /api/v1 형태 (그룹 이름을 명시하지 않는 특수 케이스)
  • Named Group: apps, networking, storage, rbac, auth 등 기능별로 체계적으로 정리된 그룹
    • URL 경로가 보통 /apis/<group>/<version>

리소스와 Verb(동사)

각 API 그룹 아래에 리소스가 있고, 리소스마다 가능한 동작(verb)이 있습니다.

  • 예: deployments에 대해 list/get/create/update/delete/watch 등

API Group 확인 방법 (실습/디버깅용)

  • API 서버 루트로 접근하면(인증 필요) 사용 가능한 그룹이 나열됩니다.
  • 인증 없이 curl로 때리면 /version 같은 일부만 되고 대부분은 막힙니다.
  • 그래서 kubectl proxy를 쓰면 편해집니다.
    • 로컬 127.0.0.1:8001로 프록시를 띄우고, kubeconfig 자격증명으로 API 서버에 대신 요청해줍니다.
  • 주의: kube-proxy(데이터 플레인 네트워킹)와 kubectl proxy(API 접근용 HTTP 프록시)는 전혀 다른 것

2) Authorization(인가): “누가 들어왔는지” 다음엔 “무엇을 하게 할지”

인증(Authentication)은 “누구냐”를 증명하는 단계이고,
인가(Authorization)는 “무엇을 할 수 있냐”를 결정하는 단계입니다.

클러스터에는 관리자뿐 아니라 개발자/테스터/모니터링 도구/CI-CD(Jenkins 등)도 들어오게 됩니다.
모두에게 관리자급 권한을 줄 수 없으니 인가가 필요합니다.

Kubernetes가 지원하는 대표 인가 메커니즘

  • Node Authorizer
  • ABAC(Attribute-Based Access Control)
  • RBAC(Role-Based Access Control)
  • Webhook Authorization(외부 정책 엔진, 예: OPA 등)
  • AlwaysAllow / AlwaysDeny 같은 모드도 존재

여러 인가 모드의 체인 동작

API 서버의 --authorization-mode에 Node,RBAC,Webhook처럼 콤마로 여러 개를 줄 수 있고,
요청은 순서대로 평가됩니다.

  • 어떤 모듈이 “승인”하면 그 시점에 끝
  • 어떤 모듈이 “거부/처리 불가”하면 다음 모듈로 넘어감

3) RBAC 핵심: Role/RoleBinding (네임스페이스 범위)

RBAC는 “사용자/그룹을 권한에 직접 연결”하는 대신:

  1. Role(권한 규칙 묶음)을 만들고
  2. RoleBinding으로 사용자(또는 SA)를 Role에 연결합니다.

이게 ABAC(정책 파일 직접 편집 + API 서버 재기동)보다 관리가 훨씬 쉽습니다.

Role 정의(개념)

  • apiGroups: Core면 [""] 또는 빈 값(스크립트에서는 “코어 그룹은 비워도 된다”는 뉘앙스)
  • resources: 예) pods, configmaps
  • verbs: 예) get, list, create, delete
  • (옵션) resourceNames: 특정 리소스 이름만 허용(예: blue/orange 파드만)

예시(개념형):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","list","create","delete"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["create"]

RoleBinding 정의(개념)

  • subjects: 사용자/그룹/서비스어카운트 지정
  • roleRef: 어떤 Role(또는 ClusterRole)에 붙일지 지정
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: devuser-developer-binding
  namespace: default
subjects:
- kind: User
  name: dev-user
roleRef:
  kind: Role
  name: developer
  apiGroup: rbac.authorization.k8s.io

확인 명령(스크립트 흐름)

  • kubectl get role, kubectl describe role <name>
  • kubectl get rolebinding, kubectl describe rolebinding <name>
  • 권한 점검:
    • kubectl auth can-i <verb> <resource> --as <user>
    • 네임스페이스도 함께 테스트 가능

4) ClusterRole/ClusterRoleBinding (클러스터 범위) + PV/PVC 정리

Role/RoleBinding은 기본적으로 네임스페이스 범위입니다.
하지만 어떤 리소스는 네임스페이스에 속하지 않습니다.

클러스터 범위 리소스 예

  • Node
  • PersistentVolume(PV)
  • Namespace
  • CertificateSigningRequest(CSR)
  • ClusterRole / ClusterRoleBinding
  • (등)

이런 리소스 권한을 주려면 ClusterRole + ClusterRoleBinding을 씁니다.

중요한 포인트(스크립트에도 나오는 주의)

  • ClusterRole은 “클러스터 범위 리소스 전용”만은 아닙니다.
    네임스페이스 리소스(pods 등) 에 대해서도 ClusterRole을 만들 수 있고,
    이 경우 “모든 네임스페이스에 걸친 권한”이 됩니다.

PV/PVC가 뭐고, “어떤 클러스터”에 속하나?

  • PV(PersistentVolume)
    • 클러스터 범위(Cluster-scoped) 리소스
    • “클러스터가 제공할 수 있는 스토리지 덩어리(볼륨)”를 미리 등록해두거나(정적 프로비저닝), StorageClass로 동적 생성되게 함
  • PVC(PersistentVolumeClaim)
    • 네임스페이스 범위(Namespaced) 리소스
    • 파드/앱이 “이만큼의 스토리지가 필요해요”라고 요청(Claim) 하는 것

그리고 질문했던 핵심:

  • PV/PVC의 바인딩은 오직 ‘그 Kubernetes 클러스터 내부’에서만 일어납니다.
    • PV는 클러스터 전역 리소스
    • PVC는 특정 네임스페이스의 요청
    • 둘이 바인딩되면 그 관계는 그 클러스터 내부의 etcd에 저장된 상태입니다.
  • “다른 클러스터의 PV를 이 클러스터 PVC가 쓴다” 같은 형태는 Kubernetes 리소스 바인딩 관점에서는 불가(클러스터가 다르면 API/etcd 자체가 다름)

5) ServiceAccount와 Token: “RBAC만 있으면 되지 않나?”의 답

스크립트의 흐름은 다음입니다.

  • Kubernetes 계정은 크게 2종:
    • User Account: 사람이 쓰는 계정(관리자/개발자)
    • ServiceAccount(SA): 애플리케이션(기계)이 쓰는 계정(Prometheus, Jenkins 등)

RBAC vs Token: 역할이 다르다

  • RBAC: “무엇을 할 수 있는지(인가)” 규칙
  • Token(또는 인증서): “누구인지(인증)” 증명 수단

즉 “RBAC가 있는데 왜 토큰이 필요?”에 대한 답은:

RBAC는 인증이 끝난 ‘주체(identity)’가 전제다.
토큰/인증서는 그 주체를 증명하기 위한 인증 수단이다.

SA 토큰은 어떻게 파드에 들어오나

  • 기본적으로 파드가 생성되면 기본 ServiceAccount가 연결됩니다.
  • kubelet이 Projected Volume 형태로 토큰을 파드 내부에 마운트합니다.
    • 경로 예: /var/run/secrets/kubernetes.io/serviceaccount
  • 애플리케이션은 그 토큰을 읽어 API 서버에 Bearer 토큰으로 요청할 수 있습니다.

토큰 자동 주입 끄기

  • automountServiceAccountToken: false
    • ServiceAccount 수준 또는 Pod 수준에서 설정 가능

“SA에 파드 생성 권한이 있고, SA가 파드에 달려 있으면 그 파드가 파드를 만들 수 있나?”

가능합니다. 파드 안의 애플리케이션이:

  1. 마운트된 SA 토큰을 사용해 API 서버에 인증하고
  2. RBAC가 pods.create를 허용한다면
  3. API 서버에 “파드 생성” 요청을 할 수 있습니다.

그래서 SA 권한은 최소 권한(least privilege) 로 주는 게 매우 중요합니다.

SA를 Pod에 설정 vs Deployment에 설정 차이

  • 실제로 권한이 적용되는 단위는 Pod입니다.
  • Deployment에 SA를 “설정”한다는 건, 정확히는 Deployment의 Pod Template(spec.template.spec.serviceAccountName) 에 넣는 것입니다.
    • 결과적으로 “그 Deployment가 만들어내는 모든 Pod”에 같은 SA가 붙습니다.

6) Image Security: 프라이빗 레지스트리 이미지 가져오기

이미지는 보통 다음 규칙으로 해석됩니다.

  • nginx 라고만 쓰면 사실상 docker.io/library/nginx
    • library는 도커 공식 이미지 계정(공식 검증/관리)
  • 레지스트리를 명시하면 예: gcr.io/..., private.registry.com/...

프라이빗 레지스트리 이미지를 쓰려면 노드의 런타임이 이미지를 pull할 때 인증이 필요합니다.
Kubernetes에서는 이를 imagePullSecrets로 해결합니다.

  1. Docker registry 타입 Secret 생성
  2. Pod spec에 imagePullSecrets로 참조

개념 예시:

kubectl create secret docker-registry regcred \
  --docker-server=private.registry.com \
  --docker-username=USER \
  --docker-password=PASS \
  --docker-email=you@example.com
spec:
  imagePullSecrets:
  - name: regcred

여기서의 Secret은 “레지스트리 인증” 목적이고, SA 토큰과 목적이 다릅니다(혼동 포인트).


7) Security Context: Docker 보안 개념이 Kubernetes로 올라오는 지점

스크립트는 Docker 보안 기본을 먼저 다룹니다.

  • 컨테이너는 VM처럼 완전 분리라기보다 커널 공유
  • Namespace로 프로세스 격리
  • 기본적으로 컨테이너 프로세스는 root로 실행될 수 있지만,
    Docker는 Linux Capabilities로 root 권한을 “풀 권한이 아니라 제한된 권한 묶음”으로 제어합니다.
  • --cap-add, --cap-drop, --privileged 같은 옵션

Kubernetes SecurityContext 적용 원칙

  • 컨테이너는 Pod 안에 들어가므로
    • Pod 수준 securityContext(모든 컨테이너에 적용)
    • Container 수준 securityContext(해당 컨테이너에만 적용)
  • 둘 다 있으면 컨테이너 설정이 Pod 설정을 override 합니다.

예: runAsUser, capabilities.add 등

실습에서 겪은 에러 포인트(중요)

에러:

unknown field "spec.securityContext.capabilities"

이유: capabilities는 Pod-level securityContext에 둘 수 없고, Container-level securityContext에 둬야 합니다.

올바른 형태(컨테이너 아래로 이동):

spec:
  containers:
  - name: ubuntu
    image: ubuntu
    command: ["sleep","4800"]
    securityContext:
      capabilities:
        add: ["SYS_TIME"]

8) Network Security: Ingress/Egress → NetworkPolicy

Ingress / Egress 개념

  • Ingress: 들어오는 트래픽(유입)
  • Egress: 나가는 트래픽(유출)

Kubernetes는 기본적으로 모든 파드가 서로 통신 가능(All Allow)인 상태로 시작합니다.
특정 파드(예: DB)를 보호하고 싶으면 NetworkPolicy를 적용합니다.

NetworkPolicy의 핵심 구조

  1. podSelector로 “어떤 파드에 정책을 적용할지” 선택
  2. policyTypes로 Ingress/Egress 중 무엇을 제어할지 지정
  3. ingress/egress 규칙으로 “허용할 트래픽”만 열기

DB 보호 시나리오(스크립트 핵심):

  • 목표: DB 파드의 3306 포트는 API 파드에서만 접근 허용
  • DB 입장에서 “API → DB”는 Ingress

그리고 중요한 문장:

  • 인그레스에서 “요청”을 허용하면 그에 대한 “응답”은 별도 규칙 없이 정상 동작(상태 기반 흐름)

셀렉터 3종(From/To)

  • podSelector: 레이블로 파드 선택
  • namespaceSelector: 레이블로 네임스페이스 선택
  • ipBlock: CIDR로 외부 IP 대역 허용

그리고 규칙 조합의 논리:

  • 같은 from: 리스트의 여러 항목은 OR
  • 한 항목 안에서 podSelector + namespaceSelector 같이 있으면 AND
  • YAML에서 -(리스트 아이템) 위치가 바뀌면 “규칙 의미”가 크게 달라질 수 있음 (스크립트가 강조한 부분)

“ingress만 적어두면 egress는 다 열려있나?”

정리만 깔끔하게 하면:

  • 특정 파드가 Ingress 정책만 적용받는다면, Egress는 기본 동작(대개 허용) 그대로인 경우가 많습니다.
  • 반대로 policyTypes: ["Egress"]를 적용하면 egress는 “명시 허용만 통과”로 바뀝니다(규칙 없으면 사실상 deny-all egress).

apply Conflict 에러(자주 만나는 실무/실습 문제)

에러:

object has been modified; please apply your changes to the latest version

의미:

  • 내가 apply하려는 사이에 리소스가 다른 곳에서 업데이트되어 resourceVersion 충돌이 난 것

해결 패턴(안전한 순서):

  1. 최신을 다시 받아서 수정 후 apply
    • kubectl get netpol internal-policy -o yaml > policy.yaml
    • 수정 → kubectl apply -f policy.yaml
  2. 실습 환경이면 삭제 후 재생성도 가능
    • kubectl delete -f policy.yaml && kubectl apply -f policy.yaml

9) kubectx / kubens, 그리고 context의 의미

실습에서는 네임스페이스/컨텍스트를 자주 바꾸는데, 프로덕션에서는 그 복잡도가 더 커집니다.
그래서 kubectx, kubens 같은 도구가 유용합니다.

  • kubectx: 컨텍스트 전환
  • kubens: 네임스페이스 전환

그리고 가장 중요한 개념:

context = (cluster + user + namespace)

  • cluster: 어느 API 서버(클러스터)에 붙을지
  • user: 어떤 자격증명/정체성으로 붙을지
  • namespace: 기본 네임스페이스(옵션)

즉 context는 “kubectl 작업 프로필”입니다.


10) CRD → Custom Controller → Operator: Kubernetes를 “도메인 플랫폼”으로 확장

CRD(CustomResourceDefinition)

  • Kubernetes에 “새 리소스 타입”을 등록하는 객체
  • CRD를 만들면:
    • kubectl apply/get/delete로 커스텀 리소스를 다룰 수 있고
    • 데이터는 etcd에 저장됨
  • 하지만 CRD만으로는 실제 동작이 없음
    • Ticket을 만들었다고 “항공권 예약 API”를 자동 호출해주지 않음

Custom Controller

  • 루프로 돌면서 특정 리소스(Ticket)의 생성/수정/삭제 이벤트를 감시하고
  • 외부 API 호출(예약/변경/취소)을 수행하는 코드/프로세스

구현 방식 개요(스크립트):

  • Python 등으로도 만들 수 있지만 비용/구현 난도가 커질 수 있음
  • Go client 생태계(shared informer, queue, cache)를 쓰면 컨트롤러를 “올바르게” 만들기 쉬움
  • 샘플 컨트롤러 레포를 clone → 로직 커스터마이징 → 빌드/실행
  • kubeconfig로 로컬 실행도 가능
  • 보통은 Docker 이미지로 패키징해서 클러스터 안에서 Deployment로 실행

Operator(Operator Framework / OperatorHub)

  • CRD + Controller(그리고 설치에 필요한 RBAC/배포 리소스)를 하나로 패키징해서 배포/관리
  • etcd operator처럼 “설치”뿐 아니라 백업/복구 같은 운영 작업까지 자동화하는 운영자(사람 역할) 개념
  • OperatorHub에서 다양한 오퍼레이터를 찾고, OLM 기반 설치 흐름이 소개됨

마무리: 이 글의 핵심 한 줄 요약들

  • API Group: Kubernetes 기능 지도(리소스가 어디에 속하는지)
  • Authorization: 인증된 주체가 “무엇을 할 수 있는지” 결정(노드/RBAC/웹훅 등)
  • RBAC: Role/RoleBinding(네임스페이스), ClusterRole/ClusterRoleBinding(클러스터 범위)
  • PV/PVC: PV는 클러스터 범위, PVC는 네임스페이스 범위(바인딩은 같은 클러스터 안에서만)
  • ServiceAccount/Token: RBAC(인가)와 별개로 “인증 수단”이 필요하며, 파드에 토큰이 자동 마운트될 수 있음
  • ImagePullSecrets: 프라이빗 레지스트리 pull 인증
  • SecurityContext: runAsUser/capabilities 등 런타임 권한을 Pod/Container 수준에서 제어(컨테이너 설정이 우선)
  • NetworkPolicy: 기본 All-Allow에서 “선택한 파드”만 허용 규칙 기반으로 잠그는 방식
  • Context/kubectx/kubens: 멀티 클러스터/멀티 네임스페이스 운영 필수 도구
  • CRD/Controller/Operator: Kubernetes를 도메인 플랫폼으로 확장(데이터만 저장하려면 CRD, 동작하려면 컨트롤러, 패키징/운영 자동화는 오퍼레이터)

'CKA' 카테고리의 다른 글

Storage - Docker의 Storage Driver vs Volume Driver  (0) 2026.01.03
Storage - Docker의 Storage  (0) 2026.01.03
Security - Custom Controller & Operator  (0) 2026.01.02
Security - Custom Resource Definition  (0) 2026.01.02
Security - Context & 네임스페이스 전환을 빠르게 하는 도구  (0) 2026.01.02
'CKA' 카테고리의 다른 글
  • Storage - Docker의 Storage Driver vs Volume Driver
  • Storage - Docker의 Storage
  • Security - Custom Controller & Operator
  • Security - Custom Resource Definition
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (243)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (119)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      Thread
      프록시 팩토리
      빈 후처리기
      김영한
      자바
      @discriminatorcolumn
      jdk 동적 프록시
      requset scope
      단방향 맵핑
      양방향 맵핑
      @args
      reentarantlock
      typequery
      jpq
      고급
      gesingleresult
      프록시
      @discriminatorvalue
      log trace
      조회 성능 최적화
      락
      @within
      버퍼
      cglib
      스레드
      WAS
      Target
      hibernate5module
      페치 조인
      JPQL
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    Security - 정리(2)
    상단으로

    티스토리툴바