13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] Kubernetes Security

  • [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    [k8s] AKS에서 Vault on K8s 구축: 보안 강화 및 민감 정보 관리 방안

    AKS(Azure Kubernetes Service, 애저 쿠버네티스 서비스)를 운영하다 보면 결국 부딪히는 문제가 하나 있습니다. 바로 시크릿 관리예요. 처음에는 Kubernetes Secret(쿠버네티스 시크릿)만으로도 충분하다고 느끼실 수 있는데, 저도 처음엔 그랬거든요. 그런데 운영 환경이 커지고, 애플리케이션 수가 늘고, 누가 어떤 자격 증명(credential)을 어디서 쓰는지 추적해야 하는 순간이 오면 상황이 달라져요. 그때부터는 “이걸 계속 이렇게 들고 가도 될까?” 싶은 생각이 드더라고요.

    그래서 오늘은 Vault on K8s AKS 구성을 어떻게 접근하면 좋을지, 제가 실제로 비슷한 구조를 설계하고 검증할 때 중요하게 봤던 포인트를 중심으로 정리해보려 해요. 핵심은 단순히 HashiCorp Vault(해시코프 볼트)를 올리는 게 아니라, AKS 환경에서 인증(auth), 저장(storage), 정책(policy), 주입(injection) 흐름을 어떻게 안전하게 묶을지하는 거거든요. 특히 민감 정보가 Git, 환경 변수, 컨테이너 이미지 안에 흩어지기 시작한 팀이라면 한 번쯤 진지하게 볼 만한 주제입니다.

    AKS 클러스터, Vault 서버, 애플리케이션 Pod, Kubernetes Auth 흐름을 한눈에 보여주는 전체 구조 이미지입니다.

    1. 왜 AKS에서 Vault on K8s가 필요할까요

    쉽게 말해, Vault는 시크릿을 중앙에서 관리하고 접근을 통제하는 금고 역할을 하는 거예요. Kubernetes Secret도 이름은 시크릿이지만, 그 자체만으로는 운영 통제 수준이 부족하다고 느끼는 경우가 많아요. Base64 인코딩만으로는 보안이 해결되지 않거든요. 물론 etcd 암호화(encryption at rest) 같은 보완책이 있지만, 접근 통제와 감사(audit), 동적 자격 증명(dynamic credentials)까지 생각하면 Vault가 주는 이점이 분명합니다.

    • 중앙 집중형 관리: DB 비밀번호, API 토큰, 인증서 등을 한곳에서 관리할 수 있어요.
    • 정책 기반 접근 제어: 누가 어떤 경로(path)의 시크릿을 읽을 수 있는지 세밀하게 나눌 수 있습니다.
    • 감사 추적: 접근 기록을 남겨서 운영 가시성을 확보하기 좋아요.
    • 동적 시크릿 확장 가능성: 정적 비밀번호를 오래 들고 가지 않는 구조로 발전시키기 쉬워요.

    여기서 중요한 포인트! AKS에서 Vault를 쓴다고 해서 자동으로 다 안전해지는 건 아니에요. 어디에 저장할지, 어떻게 인증할지, 어떤 네트워크 경계 안에 둘지를 같이 설계해야 의미가 있어요. 저도 처음엔 “Vault만 올리면 끝 아닌가?” 싶었는데, 실제로 해보니까 그다음이 진짜 시작이더라고요.

    2. Vault, AKS, Kubernetes Auth를 쉽게 풀어보면

    개념을 너무 어렵게 잡으면 오히려 구현이 꼬여요. 쉽게 말하면 흐름은 이렇습니다.

    1. 애플리케이션 Pod가 AKS 안에서 실행돼요.
    2. Pod는 자신의 ServiceAccount(서비스 어카운트)를 이용해 Vault에 신원을 증명합니다.
    3. Vault는 Kubernetes Auth(쿠버네티스 인증)로 이 Pod가 누구인지 확인해요.
    4. 정책에 맞으면 필요한 시크릿을 내려줍니다.

    즉, 애플리케이션 입장에서는 “내가 누구인지 증명하고, 허용된 비밀만 받아간다”는 구조예요. 이 방식의 장점은 시크릿을 애플리케이션 매니페스트에 하드코딩하지 않아도 된다는 거거든요. 운영해보신 분들은 아실 텐데, Secret YAML 파일이 여기저기 복사되기 시작하면 관리가 정말 힘들어져요.

    항목 Kubernetes Secret Vault on K8s
    저장 위치 클러스터 내부 Vault 백엔드
    접근 제어 RBAC 중심 정책 기반 세분화 가능
    감사 추적 제한적 Audit Device(감사 장치) 활용 가능
    주입 방식 환경 변수, 볼륨 Agent Injector, CSI 등 확장 가능
    운영 복잡도 낮음 상대적으로 높음

    결국 선택의 기준은 단순해요. 작고 단순한 환경이면 Kubernetes Secret만으로도 충분합니다. 반대로 권한 분리, 감사, 로테이션, 멀티 서비스 운영이 중요해지면 Vault on K8s AKS 구성이 훨씬 낫습니다.

    3. AKS에서 Vault on K8s 아키텍처를 어떻게 잡으면 좋을까

    제가 추천하는 기본 방향은 이래요. 과하게 복잡하게 시작하지 말고, 운영상 필요한 최소 안전장치를 먼저 넣는 거예요.

    • Vault는 전용 네임스페이스에 배치합니다.
    • Integrated Storage Raft(통합 스토리지 래프트)를 사용해 내부 저장소를 구성해요.
    • Kubernetes Auth로 애플리케이션 인증을 처리합니다.
    • Vault Agent Injector 또는 템플릿 렌더링 방식으로 시크릿을 Pod에 주입해요.
    • NetworkPolicy(네트워크 정책)와 Ingress(인그레스, 외부 트래픽 진입점) 또는 내부 LoadBalancer 정책을 명확히 나누어요.

    실무에서는 Vault를 외부에 완전히 공개하지 않는 편이 훨씬 나아요. 가능하면 내부 네트워크에서만 접근하게 만들고, 운영자 접근도 제한하는 쪽이 좋거든요. 홈랩에서도 이 부분을 느슨하게 두면 결국 테스트 편의성이 보안 기준을 이겨버리더라고요. 처음엔 편한데 나중에 반드시 정리해야 해요.

    AKS에서 Vault 네임스페이스와 컴포넌트 배치 구조 이미지

    Vault 전용 네임스페이스, Raft 스토리지, Agent Injector, 애플리케이션 네임스페이스 분리를 보여주는 구성 이미지입니다.

    4. 실전 구현: AKS에 Vault 배포하기

    이제 실제 흐름으로 가봅시다. 여기서는 Helm(헬름)을 이용해 Vault를 AKS에 배포하는 예시를 기준으로 설명할게요. 세부 값은 환경마다 다르니, 그대로 복붙보다 구조를 이해하고 적용하시는 게 훨씬 중요합니다.

    4-1. 네임스페이스와 Helm 저장소 준비

    kubectl create namespace vault
    helm repo add hashicorp https://helm.releases.hashicorp.com
    helm repo update

    여기까지는 크게 어렵지 않아요. 문제는 그다음 values 설정이에요. 처음엔 기본값으로 올리고 싶어지는데, 운영 목적이라면 최소한 스토리지와 UI, Injector 여부는 같이 잡아주는 게 좋습니다.

    4-2. values.yaml 예시

    server:
      enabled: true
      ha:
        enabled: true
        replicas: 3
        raft:
          enabled: true
      dataStorage:
        enabled: true
        size: 10Gi
      service:
        enabled: true
        type: ClusterIP
      ingress:
        enabled: false
      resources: {}
    
    ui:
      enabled: true
    
    injector:
      enabled: true

    여기서는 HA(고가용성)와 Raft storage를 켰어요. Vault on K8s는 단일 인스턴스로도 테스트는 되지만, 실제 운영 관점에서는 장애 대응을 생각하지 않을 수가 없거든요. 물론 노드 수, 디스크 크기, 서비스 타입은 환경에 맞춰 조정해야 합니다.

    4-3. 배포

    helm install vault hashicorp/vault \
      --namespace vault \
      --values values.yaml

    배포 후에는 Pod 상태부터 확인하세요.

    kubectl get pods -n vault
    kubectl get svc -n vault

    이 시점에서 Pod가 뜬다고 끝이 아니에요. Vault는 초기화(initialization)와 언실(unseal) 과정을 거쳐야 하거든요. 이 부분에서 처음 삽질 많이 합니다 ㅎㅎ

    4-4. 초기화와 언실

    kubectl exec -it -n vault vault-0 -- vault operator init

    이 명령을 실행하면 unseal key와 initial root token이 출력돼요. 이 정보는 정말 민감하니까요, 테스트 환경이라고 터미널 히스토리에 남기고 방치하면 나중에 곤란해져요. 저도 예전에 PoC 환경이라고 가볍게 봤다가 정리하느라 번거로웠던 적이 있어요.

    이후 각 Pod에 대해 언실을 진행합니다.

    kubectl exec -it -n vault vault-0 -- vault operator unseal
    kubectl exec -it -n vault vault-1 -- vault operator unseal
    kubectl exec -it -n vault vault-2 -- vault operator unseal

    로그인도 해두세요.

    kubectl exec -it -n vault vault-0 -- vault login

    5. Kubernetes Auth와 시크릿 엔진 구성

    이제부터가 진짜 핵심이에요. Vault 서버를 띄우는 건 절반이고, AKS 워크로드가 안전하게 시크릿을 받아가게 만드는 과정이 나머지 절반입니다.

    5-1. KV 시크릿 엔진 활성화

    kubectl exec -it -n vault vault-0 -- vault secrets enable -path=secret kv-v2
    kubectl exec -it -n vault vault-0 -- vault kv put secret/app/config username="demo-user" password="demo-pass"

    여기서는 예시로 KV v2(Key-Value Version 2) 엔진을 사용했어요. 정적 시크릿 관리의 기본 출발점으로 가장 무난하거든요.

    5-2. Kubernetes Auth 활성화

    kubectl exec -it -n vault vault-0 -- vault auth enable kubernetes

    그다음 Kubernetes API와 통신할 수 있도록 설정해야 합니다. 실제 값은 클러스터 환경에 맞게 확인이 필요해요.

    kubectl exec -it -n vault vault-0 -- sh
    
    vault write auth/kubernetes/config \
      kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

    환경에 따라 서비스 계정 토큰과 CA 인증서 경로까지 함께 지정하는 구성이 필요할 수 있어요. 이 부분은 클러스터 설정과 배포 방식에 따라 달라지니, 반드시 현재 환경 기준으로 검증해야 합니다.

    5-3. 정책 생성

    kubectl exec -it -n vault vault-0 -- sh -c 'cat > /tmp/app-policy.hcl <<EOF
    path "secret/data/app/config" {
      capabilities = ["read"]
    }
    EOF
    vault policy write app-policy /tmp/app-policy.hcl'

    정책은 정말 최소 권한(least privilege)으로 가야 해요. “일단 다 읽게 해두고 나중에 줄이자”는 접근이 제일 위험하거든요. 운영에 들어가면 나중은 잘 안 와요.

    5-4. Role 생성

    kubectl exec -it -n vault vault-0 -- vault write auth/kubernetes/role/app-role \
      bound_service_account_names=app-sa \
      bound_service_account_namespaces=default \
      policies=app-policy \
      ttl=1h

    이렇게 하면 default 네임스페이스의 app-sa 서비스 계정을 사용하는 Pod만 해당 정책으로 인증받을 수 있어요. namespace와 service account를 분리해두면 실수 범위를 줄이기 좋거든요.

    Vault on K8s AKS의 Kubernetes Auth와 정책 매핑 흐름

    ServiceAccount, Vault Role, Policy, Secret Path가 어떻게 연결되는지 보여주는 인증 및 권한 흐름 이미지입니다.

    6. 애플리케이션 Pod에 시크릿 주입하기

    실제로 써보니까 운영팀과 개발팀 모두 만족도가 올라가는 구간이 바로 여기였어요. 개발자는 애플리케이션에서 민감 정보를 직접 들고 다니지 않아도 되고, 운영자는 중앙 통제가 가능해지거든요.

    Vault Agent Injector를 사용하는 예시는 아래처럼 가져갈 수 있습니다.

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: app-sa
      namespace: default
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demo-app
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demo-app
      template:
        metadata:
          labels:
            app: demo-app
          annotations:
            vault.hashicorp.com/agent-inject: "true"
            vault.hashicorp.com/role: "app-role"
            vault.hashicorp.com/agent-inject-secret-config.txt: "secret/data/app/config"
            vault.hashicorp.com/agent-inject-template-config.txt: |
              {{- with secret "secret/data/app/config" -}}
              username={{ .Data.data.username }}
              password={{ .Data.data.password }}
              {{- end }}
        spec:
          serviceAccountName: app-sa
          containers:
            - name: app
              image: nginx
              volumeMounts: []

    이 예시는 Pod 안에 파일 형태로 시크릿을 렌더링하는 방식이에요. 환경 변수 주입만 고집하기보다, 파일 기반 주입을 먼저 고려해보세요. 이유는 단순해요. 환경 변수는 프로세스 정보나 디버깅 과정에서 노출 범위를 관리하기 까다로울 수 있거든요.

    배포 후에는 Pod 내부에서 실제로 파일이 생성됐는지 확인하세요.

    kubectl apply -f app.yaml
    kubectl get pods
    kubectl exec -it deploy/demo-app -- cat /vault/secrets/config.txt

    7. ⚠️ 운영 중 자주 만나는 문제와 트러블슈팅

    여기서는 제가 꼭 강조하고 싶은 실전 포인트를 적어볼게요. 문서대로만 하면 바로 될 것 같지만, 실제 AKS에서 붙일 때 자주 막히는 지점이 몇 군데 있어요.

    7-1. Pod는 떴는데 시크릿 파일이 안 생기는 경우

    • ServiceAccount 이름과 Vault Role의 bound_service_account_names가 일치하는지 확인하세요.
    • 네임스페이스가 Role 설정과 같은지 봐요.
    • Pod annotation 오타가 없는지 확인해요.
    • Injector Pod 로그를 확인합니다.
    kubectl logs -n vault deploy/vault-agent-injector

    이거 진짜 자주 나와요. 저도 처음엔 Vault 정책 문제인 줄 알았는데, 알고 보니 annotation 키 오타였던 적이 있었어요. 허무하죠. 근데 이런 게 제일 오래 갑니다.

    7-2. Vault 로그인 또는 Auth 설정이 실패하는 경우

    • Kubernetes API 접근 경로가 맞는지 봐요.
    • Vault 서버가 클러스터 내부 DNS와 API 서버에 접근 가능한지 확인하세요.
    • 서비스 계정 토큰과 CA 인증서 전달이 필요한 구성인지 점검해요.

    AKS는 관리형 Kubernetes라서 제어 플레인 세부 구현을 직접 만지지는 않지만, 그렇다고 인증 흐름이 자동으로 다 맞아떨어지지는 않아요. 특히 네트워크 제한이나 정책이 들어가면 더 꼼꼼히 봐야 합니다.

    7-3. Vault 자체의 고가용성은 올렸는데 운영 절차가 없는 경우

    이 부분도 많이 놓쳐요. HA로 배포했다고 해서 운영이 끝난 게 아니거든요.

    • 언실 절차를 누가 어떻게 수행할지
    • 루트 토큰 보관 정책을 어떻게 할지
    • 백업과 복구 테스트를 할지
    • 감사 로그 보존을 어디까지 할지

    보안 시스템은 설치보다 운영 절차가 훨씬 중요해요. 이건 정말 해보면 바로 체감됩니다.

    8. 검증 방법과 기대 결과

    구축이 끝났다면 그냥 “접속된다” 수준에서 멈추면 아쉬워요. 최소한 아래 항목은 꼭 검증해보세요.

    1. 권한이 있는 Pod만 시크릿을 읽을 수 있는지 확인하세요.
    2. 다른 ServiceAccount를 쓰는 Pod는 접근이 거부되는지 확인합니다.
    3. Vault 정책 변경 시 반영 범위를 확인해요.
    4. Pod 재기동 시 시크릿 주입이 일관되게 되는지 확인하세요.
    5. 감사 로그와 서버 로그에서 접근 기록을 추적할 수 있는지 봐요.

    예를 들어 권한 없는 Pod를 하나 띄워서 동일 경로를 읽어보는 식으로 검증할 수 있어요. 이런 부정 테스트(negative test)를 같이 해봐야 진짜 구성이 맞는지 보여요.

    kubectl run test-deny --rm -it --image=busybox --restart=Never -- sh

    운영 결과 관점에서 기대할 수 있는 건 명확합니다.

    • 애플리케이션 매니페스트에 민감 정보가 직접 들어가지 않아요.
    • 시크릿 접근 경로가 정책으로 정리돼요.
    • 누가 무엇을 읽는지 추적성이 좋아져요.
    • 향후 동적 시크릿이나 인증서 자동화로 확장하기 쉬워져요.

    특히 팀 규모가 커질수록 이점이 커져요. 처음엔 조금 번거롭지만, 나중에는 “왜 더 빨리 안 했지?” 싶을 때가 와요. 실제로 써보니까 시크릿이 여기저기 흩어져 있던 상태보다 운영 피로도가 훨씬 낮아지더라고요.

    AKS에서 Vault 시크릿 주입 검증 결과 이미지

    애플리케이션 Pod 내부 시크릿 파일 생성, 정책 기반 접근 허용/거부 검증 결과를 보여주는 이미지입니다.

    9. 정리와 다음 단계

    Vault on K8s AKS 구성의 핵심은 단순해요. Vault를 설치하는 것이 목적이 아니라, AKS에서 민감 정보를 안전하게 배포하고 통제하는 체계를 만드는 게 진짜 목표거든요. 처음엔 Kubernetes Secret만으로 충분해 보일 수 있어요. 저도 그렇게 시작했었는데, 서비스가 늘고 권한 관리가 복잡해지니까 한계가 분명히 보이더라고요.

    오늘 정리한 흐름을 다시 압축하면 이렇습니다.

    • Vault를 AKS에 전용 네임스페이스로 배치해요.
    • Raft 기반 저장소와 HA 구성을 검토해요.
    • Kubernetes Auth로 Pod 신원을 검증합니다.
    • 정책 기반으로 필요한 시크릿만 주입해요.
    • 운영 절차와 감사 체계를 같이 설계하세요.

    혹시 지금 HashiCorp Vault 도입을 고민 중이세요? 그렇다면 처음부터 모든 기능을 한 번에 붙이기보다, KV 시크릿 엔진 + Kubernetes Auth + 최소 정책부터 시작해보는 걸 추천해요. 그게 훨씬 덜 꼬여요. 드디어 됐다! 싶은 순간이 분명 옵니다.

    다음 글에서는 AKS에서 Vault와 외부 비밀 저장소를 어떻게 연계해서 운영 부담을 줄일지, 또는 Vault Agent Injector와 CSI 방식의 차이를 어떻게 판단하면 좋을지 이어서 다뤄볼 예정이에요. 이전 글에서 Kubernetes 기본 보안 설정을 정리했다면 그 내용과 함께 보셔도 흐름이 잘 맞을 거예요.

    FAQ: 자주 묻는 질문

    AKS에서 꼭 Vault가 필요할까요?

    작고 단순한 환경이라면 꼭 그렇지는 않아요. 다만 시크릿 관리 범위가 넓어지고 감사와 권한 분리가 중요해지면 검토 가치가 크답니다.

    Vault on K8s는 운영 난도가 높지 않나요?

    맞습니다. Kubernetes Secret보다 복잡해요. 대신 통제력과 확장성이 좋아져요. 그래서 작은 범위로 시작하는 게 중요합니다.

    시크릿은 환경 변수보다 파일 주입이 더 나은가요?

    상황에 따라 다르지만, 운영 노출 범위를 생각하면 파일 기반 주입이 더 다루기 편한 경우가 많아요.

    Vault on K8s AKS 도입 전후 비교 인포그래픽

    도입 전 분산된 시크릿 관리와 도입 후 중앙 통제 구조를 비교하는 요약 인포그래픽입니다.

  • [k8s] 쿠버네티스 보안 강화: RBAC와 네트워크 정책 실전 설정 가이드

    [k8s] 쿠버네티스 보안 강화: RBAC와 네트워크 정책 실전 설정 가이드

    쿠버네티스 보안 강화: RBAC, 네트워크 정책 설정 실전 가이드

    안녕하세요, 13년차 서버실 지킴이, 인프라 엔지니어 “13년차의 서버실”입니다. 오늘도 여러분의 튼튼한 인프라를 위한 이야기를 들고 왔습니다.

    도입부: 왜 쿠버네티스 보안이 중요할까요?

    요즘 클라우드 환경에서 쿠버네티스(Kubernetes, k8s)는 정말 대세 중의 대세죠. 저도 홈랩에서 다양한 애플리케이션들을 쿠버네티스 위에 올려서 실험하고 운영하고 있거든요. 그런데 이렇게 편리하고 강력한 도구일수록 보안(Security)은 더욱 중요해집니다.

    솔직히 처음엔 저도 쿠버네티스 보안? 그냥 방화벽 잘 치고, SSL/TLS 잘 적용하면 되는 거 아냐? 라고 쉽게 생각했었어요. 근데 이게 웬걸, 클러스터 내부의 파드(Pod)나 서비스(Service) 간의 통신, 그리고 누가 어떤 리소스(Resource)에 접근할 수 있는지 같은 복잡한 문제들이 산적해 있더라고요. 클라우드 환경은 기존 온프레미스(On-premise)와는 또 다른 공격 표면(Attack Surface)을 가지거든요. 특히나 클러스터 보안(Cluster Security)은 한번 뚫리면 전체 시스템이 위험해질 수 있어서 각별히 신경 써야 합니다.

    그래서 오늘은 쿠버네티스 보안을 강화하기 위한 핵심 두 가지, 바로 RBAC(Role-Based Access Control, 역할 기반 접근 제어)와 네트워크 정책(Network Policy) 설정에 대한 실전 가이드를 준비해 봤습니다. 끊임없이 변화하는 클라우드 환경에서 항상 최신 보안 트렌드를 반영하는 방법을 익혀두면 좋겠죠? 제가 직접 삽질하며 배운 내용들을 아낌없이 공유해 드릴게요!

    쿠버네티스 클러스터는 여러 보안 계층으로 보호됩니다. RBAC는 ‘누가 무엇을 할 수 있는가’를, 네트워크 정책은 ‘누가 누구와 통신할 수 있는가’를 제어하며 핵심적인 역할을 합니다.

    쿠버네티스 RBAC, 왜 필요할까요? (개념 설명)

    RBAC(Role-Based Access Control)는 말 그대로 ‘역할 기반 접근 제어’입니다. 쉽게 말해, “누가 쿠버네티스 클러스터 내에서 무엇을 할 수 있는가?”를 정의하는 메커니즘이에요. 예를 들어, 개발팀은 자기네 네임스페이스(Namespace)에 파드만 배포할 수 있고, 운영팀은 클러스터 전체의 모든 리소스를 관리할 수 있도록 권한을 나누는 거죠.

    RBAC의 주요 구성 요소는 다음과 같습니다:

    • Role (역할): 특정 네임스페이스 내에서 허용되는 권한 집합을 정의합니다. (예: ‘default’ 네임스페이스에서 파드를 조회, 생성할 수 있는 권한)
    • ClusterRole (클러스터 역할): 클러스터 전체에 걸쳐 허용되는 권한 집합을 정의합니다. 네임스페이스에 종속되지 않는 리소스(노드, 퍼시스턴트 볼륨 등)나 모든 네임스페이스에 대한 권한을 부여할 때 사용합니다.
    • RoleBinding (역할 바인딩): Role을 특정 사용자(User), 그룹(Group), 또는 서비스 계정(ServiceAccount)에 연결하여 해당 네임스페이스 내에서 권한을 부여합니다.
    • ClusterRoleBinding (클러스터 역할 바인딩): ClusterRole을 사용자, 그룹, 또는 서비스 계정에 연결하여 클러스터 전체에 걸쳐 권한을 부여합니다.

    이걸 왜 써야 하냐고요? 권한을 너무 많이 주면 보안 사고로 이어지기 쉽고, 너무 적게 주면 개발이나 운영이 힘들어지거든요. 최소 권한 원칙(Principle of Least Privilege)을 지키면서 효율적인 협업 환경을 만드는 데 RBAC가 필수적입니다.

    RBAC 설정, 저도 처음엔 삽질 좀 했습니다 (실전 구현)

    자, 이제 RBAC를 직접 설정해볼까요? 제가 홈랩에서 개발팀과 운영팀의 권한을 분리했던 경험을 바탕으로 설명해 드릴게요. 처음엔 Role과 ClusterRole, Binding의 차이가 헷갈려서 삽질 좀 했습니다 ㅎㅎ.

    1. 개발팀 전용 네임스페이스 생성

    먼저 개발팀이 사용할 네임스페이스를 만듭니다. 여기서는 <code>dev-team이라는 네임스페이스를 사용할게요.

    kubectl create namespace dev-team
    

    2. 개발팀 역할(Role) 정의

    dev-team 네임스페이스 내에서 파드와 디플로이먼트(Deployment)를 조회, 생성, 업데이트, 삭제할 수 있는 권한을 부여하는 Role을 만듭니다. 이 Role은 dev-team 네임스페이스에만 적용됩니다.

    # dev-team-role.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: dev-team-pod-deployment-manager
      namespace: dev-team
    rules:
    - apiGroups: ["", "apps"]
      resources: ["pods", "deployments"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    - apiGroups: [""]
      resources: ["pods/log"]
      verbs: ["get"]
    

    파일을 저장하고 적용합니다.

    kubectl apply -f dev-team-role.yaml
    

    3. 개발팀 서비스 계정(ServiceAccount) 생성 및 RoleBinding

    실제 사용자 대신 서비스 계정(ServiceAccount)을 만들어서 권한을 부여하는 게 일반적입니다. 여기서는 dev-user-sa라는 서비스 계정을 만들고, 위에서 정의한 Role을 이 서비스 계정에 바인딩(Binding)합니다.

    # dev-team-sa.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: dev-user-sa
      namespace: dev-team
    ---
    # dev-team-rolebinding.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: dev-user-pod-deployment-binding
      namespace: dev-team
    subjects:
    - kind: ServiceAccount
      name: dev-user-sa
      namespace: dev-team
    roleRef:
      kind: Role
      name: dev-team-pod-deployment-manager
      apiGroup: rbac.authorization.k8s.io
    

    파일을 저장하고 적용합니다.

    kubectl apply -f dev-team-sa.yaml
    

    이제 dev-user-sa 서비스 계정은 dev-team 네임스페이스에서 파드와 디플로이먼트를 관리할 수 있게 됩니다. 이 서비스 계정의 토큰을 활용해 개발자는 해당 권한으로 클러스터에 접근할 수 있어요. 💡 팁: 실제 사용자에게는 OIDC(OpenID Connect) 연동을 통해 사용자 인증 후 RoleBinding을 연결하는 경우가 많습니다.

    쿠버네티스 RBAC는 사용자/서비스 계정, Role/ClusterRole, 그리고 이들을 연결하는 RoleBinding/ClusterRoleBinding으로 구성되어 권한 흐름을 제어합니다.

    네트워크 정책(Network Policy), 통제된 소통의 시작 (개념 설명)

    RBAC가 “누가 무엇을 할 수 있는가”를 정의한다면, 네트워크 정책(Network Policy)은 “쿠버네티스 파드(Pod) 간에 누가 누구와 통신할 수 있는가”를 정의하는 기능입니다. 기본적으로 쿠버네티스 클러스터 내의 모든 파드는 서로 아무런 제약 없이 통신할 수 있어요. 하지만 프로덕션 환경에서는 이게 너무 위험할 수 있겠죠?

    예를 들어, 웹 프론트엔드 파드가 데이터베이스 파드에 직접 접근할 필요는 없을 거예요. API 백엔드 파드만 DB에 접근해야 하고요. 이럴 때 네트워크 정책을 사용해서 파드 간의 통신을 세밀하게 제어할 수 있습니다.

    네트워크 정책은 특정 파드(podSelector)에 적용되며, 해당 파드로 들어오는 트래픽(Ingress)과 나가는 트래픽(Egress)을 정의할 수 있습니다. 한 번 정책이 적용되면, 명시적으로 허용된 트래픽 외에는 모두 차단됩니다. 이게 핵심이에요!

    Network Policy 적용, 드디어 통신이 막히네요! (실전 구현)

    이번에는 간단한 웹 서비스 환경에서 네트워크 정책을 적용해 볼게요. 프론트엔드, 백엔드, 데이터베이스 파드가 있다고 가정해 봅시다. 목표는 다음과 같습니다:

    • 프론트엔드는 백엔드에만 접근 가능해야 합니다.
    • 백엔드는 프론트엔드와 데이터베이스에만 접근 가능해야 합니다.
    • 데이터베이스는 그 어떤 파드로부터의 Egress 트래픽도 허용하지 않습니다 (오직 백엔드로부터의 Ingress만 허용).

    먼저 예시 파드들을 생성합니다. 각각 app: frontend, app: backend, app: database 라벨을 가지고 있다고 가정할게요.

    1. 기본 차단 정책(Default Deny) (선택적)

    특정 네임스페이스의 모든 Ingress/Egress 트래픽을 기본적으로 차단하는 정책입니다. 이렇게 해두면 명시적으로 허용한 트래픽만 통과하게 되므로 보안을 강화할 수 있어요. 저는 보통 이걸 먼저 적용하고 시작합니다.

    # default-deny.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-all
      namespace: default
    spec:
      podSelector: {}
      policyTypes:
      - Ingress
      - Egress
    
    kubectl apply -f default-deny.yaml
    

    ⚠️ 주의: 이 정책을 적용하면 해당 네임스페이스의 모든 파드 통신이 끊기므로, 이후 허용 정책을 바로 적용해야 합니다!

    2. 백엔드 접근 허용 정책

    프론트엔드 파드(app: frontend)가 백엔드 파드(app: backend)에 접근하도록 허용하는 정책입니다. 백엔드 파드 입장에서는 프론트엔드로부터의 Ingress를 허용해야겠죠?

    # backend-allow-frontend.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: backend-allow-frontend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: backend
      policyTypes:
      - Ingress
      ingress:
      - from:
        - podSelector:
            matchLabels:
              app: frontend
        ports:
        - protocol: TCP
          port: 8080 # 백엔드 서비스 포트
    
    kubectl apply -f backend-allow-frontend.yaml
    

    3. 데이터베이스 접근 허용 정책

    백엔드 파드(app: backend)가 데이터베이스 파드(app: database)에 접근하도록 허용하는 정책입니다. 데이터베이스 파드 입장에서는 백엔드로부터의 Ingress를 허용합니다.

    # database-allow-backend.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: database-allow-backend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: database
      policyTypes:
      - Ingress
      ingress:
      - from:
        - podSelector:
            matchLabels:
              app: backend
        ports:
        - protocol: TCP
          port: 5432 # 데이터베이스 포트 (예: PostgreSQL)
    
    kubectl apply -f database-allow-backend.yaml
    

    이제 app: backend 파드는 app: frontend 파드와 app: database 파드에만 접근할 수 있게 됩니다. app: frontend 파드는 app: backend 파드에만 접근할 수 있고요. 다른 파드들과의 통신은 모두 차단됩니다. 드디어 통제된 소통이 시작되는 거죠!

    네트워크 정책을 통해 프론트엔드, 백엔드, 데이터베이스 파드 간의 통신 흐름을 세밀하게 제어하여 불필요한 접근을 차단하는 모습입니다.

    ⚠️ RBAC & Network Policy 설정 시 주의할 점 (트러블슈팅)

    제가 직접 겪었던 삽질 경험들을 바탕으로 몇 가지 주의사항을 알려드릴게요.

    RBAC 관련 주의사항

    • 최소 권한 원칙(Principle of Least Privilege): 항상 필요한 최소한의 권한만 부여해야 합니다. *(모든 권한)을 남발하면 안 돼요. 저도 처음에 귀찮아서 *로 줬다가 나중에 감사(Audit) 때 식은땀 좀 흘렸습니다 😅.
    • ClusterRole 오남용 금지: ClusterRole은 클러스터 전체에 영향을 미치기 때문에 정말 신중하게 사용해야 합니다. 웬만하면 Role과 RoleBinding으로 네임스페이스 단위로 권한을 관리하는 것이 좋습니다.
    • ServiceAccount 권한 관리: 파드 내부에서 쿠버네티스 API와 통신할 때 ServiceAccount가 사용됩니다. 각 파드에 적절한 ServiceAccount를 할당하고, 해당 ServiceAccount에 최소한의 권한을 가진 RoleBinding을 연결해야 합니다.
    • kubectl auth can-i 활용: 특정 서비스 계정이나 사용자가 어떤 작업을 할 수 있는지 확인하는 데 이 명령어가 정말 유용합니다. 설정 후 꼭 확인해 보세요!

    Network Policy 관련 주의사항

    • Default Deny 정책의 영향: 네임스페이스에 podSelector: {}와 policyTypes: [Ingress, Egress]가 포함된 Network Policy를 적용하면 해당 네임스페이스의 모든 파드 통신이 기본적으로 차단됩니다. 이때 시스템 파드나 kube-dns 같은 핵심 파드의 통신까지 막힐 수 있으니, 적용 전에 충분히 테스트하고 필요한 허용 정책을 바로 이어서 적용해야 합니다. 저도 이걸 모르고 적용했다가 클러스터가 먹통이 돼서 밤샘 삽질 좀 했습니다…
    • 라벨링(Labeling)의 중요성: Network Policy는 파드의 라벨을 기반으로 동작합니다. 따라서 일관성 있고 의미 있는 라벨을 파드에 잘 붙이는 것이 매우 중요합니다. 라벨링이 엉망이면 정책 적용이 어렵거나 잘못 적용될 수 있어요.
    • CNI 플러그인 확인: 모든 CNI(Container Network Interface) 플러그인이 Network Policy를 지원하는 것은 아닙니다. Calico, Cilium, Weave Net 등 대부분의 인기 있는 CNI는 지원하지만, 사용 중인 CNI가 지원하는지 확인해야 합니다.

    확인하고 넘어가시죠! (검증/결과)

    설정만 하고 끝내면 안 되겠죠? 제대로 적용되었는지 꼭 확인해야 합니다.

    RBAC 검증

    특정 서비스 계정이 특정 리소스에 대해 어떤 권한을 가지고 있는지 확인하려면 kubectl auth can-i 명령어를 사용합니다.

    # dev-team 네임스페이스에서 dev-user-sa 서비스 계정이 파드를 생성할 수 있는지 확인
    kubectl auth can-i create pods --as=system:serviceaccount:dev-team:dev-user-sa -n dev-team
    
    # dev-team 네임스페이스에서 dev-user-sa 서비스 계정이 노드를 조회할 수 있는지 확인 (권한 없음 예상)
    kubectl auth can-i get nodes --as=system:serviceaccount:dev-team:dev-user-sa -n dev-team
    

    RoleBinding이나 ClusterRoleBinding의 상세 정보를 조회하여 어떤 Role이 누구에게 바인딩되었는지도 확인할 수 있습니다.

    kubectl describe rolebinding dev-user-pod-deployment-binding -n dev-team
    

    Network Policy 검증

    먼저 적용된 네트워크 정책 목록을 확인합니다.

    kubectl get netpol -n default
    

    그리고 실제 파드에 접속해서 curl 등으로 통신 테스트를 해보는 것이 가장 확실합니다. 예를 들어, 프론트엔드 파드에서 데이터베이스 파드로 접속을 시도하면 실패해야 합니다.

    # 프론트엔드 파드에서 백엔드 파드로 요청 (성공 예상)
    k exec -it <frontend-pod-name> -- curl backend-service:8080
    
    # 프론트엔드 파드에서 데이터베이스 파드로 요청 (실패 예상)
    k exec -it <frontend-pod-name> -- curl database-service:5432
    

    이런 식으로 실제 시나리오를 만들어 검증해보면 됩니다. 🎉 드디어 됐다! 라고 외칠 때의 그 쾌감은 정말 최고죠!

    RBAC는 ‘누가 무엇을 할 수 있는가’를, 네트워크 정책은 ‘누가 누구와 통신할 수 있는가’를 제어하는 핵심적인 쿠버네티스 보안 메커니즘입니다.

    마무리하며: 안전한 쿠버네티스 클러스터를 향한 여정

    오늘은 쿠버네티스 보안(Kubernetes Security)을 강화하기 위한 핵심 요소인 RBAC 설정(Role-Based Access Control)과 네트워크 정책(Network Policy)에 대해 자세히 알아봤습니다. RBAC는 인적 오류나 악의적인 접근으로부터 클러스터 리소스를 보호하고, 네트워크 정책은 파드 간의 불필요한 통신을 차단하여 내부 공격 표면을 줄이는 데 큰 역할을 합니다.

    이 두 가지는 k8s 보안 가이드(k8s Security Guide)의 가장 기본적인 출발점이라고 할 수 있습니다. 물론 쿠버네티스 보안은 이것이 다가 아닙니다. 시크릿(Secret) 관리, Pod Security Admission(PSA), 컨테이너 이미지 보안 스캐닝, 그리고 클러스터 로깅 및 모니터링 등 고려해야 할 부분이 정말 많아요.

    하지만 오늘 다룬 RBAC와 네트워크 정책만 제대로 적용해도 여러분의 클러스터 보안(Cluster Security) 수준은 한 단계 높아질 겁니다. 제가 직접 겪어보니, 처음에는 어렵고 복잡해 보여도 하나하나 적용해 보면서 얻는 경험이 가장 중요하더라고요. 여러분도 이 가이드를 바탕으로 안전하고 튼튼한 쿠버네티스 클러스터를 만들어 가시길 응원합니다!

    다음 글에서는 쿠버네티스 시크릿 관리나 Pod Security Admission에 대해 더 깊이 다뤄볼 예정이니, 많은 관심 부탁드립니다!