쿠버네티스 인증 및 인가: ServiceAccount와 RBAC
1. 쿠버네티스의 권한 인증 과정
쿠버네티스의 모든 요청은 API 서버를 통해 처리되며, 다음과 같은 단계를 거칩니다:
- 인증(Authentication): 사용자 또는 서비스가 누구인지 확인
- 인가(Authorization): 인증된 사용자/서비스가 요청한 작업을 수행할 권한이 있는지 확인
- 승인(Admission Control): 요청이 클러스터 정책에 부합하는지 확인
쿠버네티스는 다양한 인증 메커니즘을 지원합니다:
- 클라이언트 인증서(X.509)
- 베어러 토큰(Bearer Token)
- 서비스 어카운트 토큰
- OpenID Connect 토큰
- 웹훅 토큰 인증
인증이 완료되면 RBAC 시스템을 통해 인가 과정이 진행됩니다.
2. 서비스 어카운트와 롤, 클러스터 롤
서비스 어카운트(ServiceAccount)
서비스 어카운트는 파드 내에서 실행되는 프로세스에 대한 ID를 제공합니다. 이는 사람 사용자(User)와 달리 특정 네임스페이스에 속하는 쿠버네티스 리소스입니다. 모든 네임스페이스에는 기본 서비스 어카운트('default')가 자동으로 생성됩니다.
서비스 어카운트 생성 예시:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
서비스 어카운트가 생성되면 쿠버네티스는 자동으로 해당 계정을 위한 시크릿(Secret)을 생성합니다. 이 시크릿에는 JWT 토큰이 포함되어 있으며, 이 토큰은 API 서버에 대한 인증에 사용됩니다.
예를 들어, 'alicek106'이라는 서비스 어카운트를 생성하면 'alicek106-token-nrzgb'와 같은 이름의 시크릿이 자동으로 생성됩니다. 이 시크릿에 포함된 토큰을 사용하여 API 서버에 요청을 보낼 때 "나는 alicek106 서비스 어카운트입니다"라고 인증할 수 있습니다.
롤(Role)과 롤바인딩(RoleBinding)
롤은 특정 네임스페이스 내에서 수행할 수 있는 권한 집합을 정의합니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
위 예시에서는 'pod-reader'라는 롤이 'default' 네임스페이스 내의 파드를 조회(get), 감시(watch), 나열(list)할 수 있는 권한을 가집니다.
롤바인딩은 롤에 정의된 권한을 사용자, 그룹 또는 서비스 어카운트에 연결합니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
이 롤바인딩은 'my-service-account'에게 'pod-reader' 롤의 권한을 부여합니다. 이제 이 서비스 어카운트는 'default' 네임스페이스의 파드를 조회할 수 있습니다.
클러스터롤(ClusterRole)과 클러스터롤바인딩(ClusterRoleBinding)
클러스터롤은 클러스터 전체에 적용되는 권한 집합으로, 네임스페이스에 종속되지 않습니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
클러스터롤바인딩은 클러스터롤을 사용자, 그룹 또는 서비스 어카운트에 연결합니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
3. 여러개의 클러스터 롤을 조합해서 사용하기
한 서비스 어카운트에 여러 개의 롤 또는 클러스터롤을 바인딩하여 다양한 권한을 조합할 수 있습니다. 예를 들어:
# 첫 번째 RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: sa-pod-reader
namespace: default
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
---
# 두 번째 ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: sa-secret-reader
subjects:
- kind: ServiceAccount
name: my-service-account
namespace: default
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
이렇게 하면 my-service-account는 default 네임스페이스의 Pod를 볼 수 있고, 동시에 전체 클러스터의 Secret에 접근할 수 있습니다.
4. JWT(JSON Web Token) 인증 방식
- 서비스 어카운트 생성: 먼저 서비스 어카운트(예: alicek106)를 생성합니다.
- JWT 토큰 시크릿 자동 생성: 쿠버네티스는 자동으로 서비스 어카운트를 위한 시크릿(예: alicek106-token-nrzgb)을 생성합니다. 이 시크릿에는 JWT 토큰이 포함되어 있습니다.
- 롤/롤바인딩 설정: 서비스 어카운트에 필요한 권한을 부여하기 위해 롤(또는 클러스터롤)과 롤바인딩(또는 클러스터롤바인딩)을 설정합니다. 예를 들어, 파드 목록을 조회할 수 있는 권한을 부여할 수 있습니다.
- 토큰 획득: 클라이언트(사용자, 애플리케이션 또는 파드)는 서비스 어카운트의 시크릿에서 JWT 토큰을 획득합니다. 파드 내부에서는 자동으로 마운트된 토큰을 사용할 수 있습니다.
- API 서버 요청: 클라이언트는 JWT 토큰을 Authorization 헤더에 Bearer 토큰으로 포함시켜 API 서버에 요청을 보냅니다.
- 토큰 검증: API 서버는 토큰을 검증하고, 요청이 특정 서비스 어카운트(예: alicek106)에서 왔음을 확인합니다.
- 권한 확인: API 서버는 RBAC 시스템을 통해 해당 서비스 어카운트가 요청한 작업을 수행할 권한이 있는지 확인합니다.
- 요청 처리 및 응답: 인증과 인가가 성공하면 API 서버는 요청을 처리하고 응답을 반환합니다.
5. 서비스 어카운트의 시크릿을 이용해 쿠버네티스 API 서버에 접근
서비스 어카운트가 생성되면 쿠버네티스는 자동으로 해당 계정의 토큰을 담은 시크릿을 생성합니다. 이 시크릿에는 API 서버에 인증하는 데 사용할 수 있는 JWT 토큰이 포함되어 있습니다.
시크릿 확인 방법:
kubectl get serviceaccount my-service-account -o yaml
kubectl get secret $(kubectl get serviceaccount my-service-account -o jsonpath='{.secrets[0].name}') -o yaml
토큰을 추출하여 API 서버에 접근하는 예:
TOKEN=$(kubectl get secret $(kubectl get serviceaccount my-service-account -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode)
curl -X GET https://kubernetes.api.server/api/v1/namespaces/default/pods \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--insecure
이 예시에서, 추출된 JWT 토큰은 Authorization 헤더에 Bearer 토큰으로 포함되어 API 서버에 전송됩니다. API 서버는 이 토큰을 검증하고, 요청이 'my-service-account'에서 왔다는 것을 인증합니다. 그런 다음 이 서비스 어카운트에 연결된 롤 및 롤바인딩을 확인하여 요청된 작업(파드 조회)에 대한 권한이 있는지 검사합니다.
6. 클러스터 내부에서 kubernetes 서비스를 통해 API 서버에 접근
클러스터 내 모든 네임스페이스에서는 kubernetes 서비스를 통해 API 서버에 접근할 수 있습니다:
# 파드 내부에서
curl -X GET https://kubernetes.default.svc/api/v1/namespaces/default/pods \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
-H "Content-Type: application/json" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
클러스터 내 모든 파드는 자동으로 마운트된 서비스 어카운트 토큰을 이용할 수 있습니다. /var/run/secrets/kubernetes.io/serviceaccount/ 경로에 마운트된 토큰 파일에서 JWT 토큰을 읽어 API 서버에 인증할 수 있습니다.
7. 서비스 어카운트에 이미지 레지스트리 접근을 위한 시크릿 설정
프라이빗 이미지 레지스트리에서 이미지를 가져오려면 서비스 어카운트에 해당 레지스트리의 인증 정보를 포함한 시크릿을 연결해야 합니다. 이를 통해 파드가 프라이빗 레지스트리에서 이미지를 풀(pull)할 수 있습니다.
Docker Registry 시크릿 생성:
kubectl create secret docker-registry regcred \
--docker-server=<레지스트리_서버> \
--docker-username=<사용자_이름> \
--docker-password=<비밀번호> \
--docker-email=<이메일>
서비스 어카운트에 연결:
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
imagePullSecrets:
- name: regcred
8. kubeconfig 파일에 서비스 어카운트 인증 정보 설정
서비스 어카운트 토큰을 사용하여 클러스터 외부에서 API 서버에 접근하려면 kubeconfig 파일에 토큰 정보를 추가할 수 있습니다:
# 서비스 어카운트 토큰 추출
SECRET_NAME=$(kubectl get serviceaccount my-service-account -o jsonpath='{.secrets[0].name}')
TOKEN=$(kubectl get secret $SECRET_NAME -o jsonpath='{.data.token}' | base64 --decode)
# kubeconfig 업데이트
kubectl config set-credentials my-service-account --token=$TOKEN
kubectl config set-context my-service-account-context --cluster=<클러스터_이름> --user=my-service-account
kubectl config use-context my-service-account-context
9. 유저(User)와 그룹(Group)의 개념
쿠버네티스에서 '사용자(User)'는 클러스터 외부에서 접근하는 사람 또는 서비스를 나타냅니다. 쿠버네티스는 사용자 계정을 API 객체로 관리하지 않습니다. 대신, 외부 신원 공급자(Identity Provider)에 의존합니다.
그룹은 여러 사용자를 함께 관리하기 위한 개념입니다. 쿠버네티스에는 몇 가지 특별한 그룹이 있습니다:
- system:authenticated: 인증된 모든 사용자
- system:unauthenticated: 인증되지 않은 요청
- system:serviceaccounts: 모든 서비스 어카운트
- system:serviceaccounts:<namespace>: 특정 네임스페이스의 모든 서비스 어카운트
10. x509 인증서를 이용한 사용자 인증
클라이언트 인증서(x509)는 쿠버네티스 사용자를 인증하는 강력한 방법입니다. 사용자 인증서를 생성하고 API 서버가 신뢰하는 CA(Certificate Authority)로 서명함으로써 사용자를 인증할 수 있습니다.
인증서 생성 및 서명 절차:
# 개인 키 생성
openssl genrsa -out user.key 2048
# CSR(Certificate Signing Request) 생성
# CN은 사용자 이름, O는 그룹 이름으로 사용됨
openssl req -new -key user.key -out user.csr -subj "/CN=username/O=group1/O=group2"
# 쿠버네티스 CA를 사용하여 CSR 서명
openssl x509 -req -in user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out user.crt -days 365
kubeconfig에 인증서 추가:
kubectl config set-credentials username --client-certificate=user.crt --client-key=user.key
kubectl config set-context username-context --cluster=<클러스터_이름> --user=username
kubectl config use-context username-context
12. 쿠버네티스 인증 및 인가 시퀀스 흐름 상세 설명
쿠버네티스에서 요청이 처리되는 전체 흐름을 시각화하면 다음과 같습니다:
- kubectl 명령 실행: 사용자가 kubectl get pods와 같은 명령을 실행하여 쿠버네티스 클러스터와의 상호작용을 시작합니다.
- 인증 정보 요청: kubectl 클라이언트가 kubeconfig 파일에서 클러스터 정보와 인증 정보를 요청합니다.
- 인증 정보 반환: kubeconfig는 JWT 토큰, 인증서, 또는 다른 인증 방식에 대한 정보를 클라이언트에 반환합니다. 서비스 어카운트의 경우, JWT 토큰이 포함됩니다.
- API 요청: 클라이언트는 인증 정보를 Authorization 헤더에 포함시켜 HTTPS를 통해 쿠버네티스 API 서버에 요청을 보냅니다.
- 토큰 검증 요청: API 서버는 수신된 토큰을 검증하기 위해 인증 모듈에 요청을 전달합니다.
- JWT 토큰 검증: 인증 모듈은 JWT 토큰의 서명을 검증하고 내용을 확인합니다. 서비스 어카운트 토큰의 경우, 서명이 API 서버의 공개 키와 일치하는지 확인합니다.
- 사용자 신원 확인: 인증 모듈은 인증이 성공하면 사용자의 신원 정보(서비스 어카운트 이름, 그룹 등)를 API 서버에 반환합니다.
- 요청 분석: API 서버는 요청된 리소스와 작업(예: 파드 목록 조회)을 분석합니다.
- 권한 확인 요청: API 서버는 인가 모듈(RBAC 시스템)에 사용자가 해당 작업을 수행할 권한이 있는지 확인 요청을 보냅니다.
- RBAC 정책 확인: 인가 모듈은 해당 사용자나 서비스 어카운트에 연결된 롤, 클러스터롤, 롤바인딩, 클러스터롤바인딩을 검사하여 권한을 확인합니다.
- 권한 검증 결과: 인가 모듈은 요청이 허용되는지 또는 거부되는지에 대한 결과를 API 서버에 반환합니다.
- 승인 컨트롤 요청: 권한이 확인되면, API 서버는 요청을 승인 컨트롤러로 전달하여 클러스터 정책에 부합하는지 확인합니다.
- 정책 검증: 승인 컨트롤러는 요청이 클러스터 정책(예: 리소스 쿼터, 파드 보안 정책 등)에 부합하는지 검증합니다.
- 승인 결과: 승인 컨트롤러는 요청을 허용, 수정, 또는 거부하는 결과를 API 서버에 반환합니다.
- 요청 처리: 모든 검증이 통과되면, API 서버는 요청된 작업(예: 파드 목록 조회)을 처리합니다.
- 응답: 최종적으로, API 서버는 처리 결과(성공 또는 오류)를 클라이언트에 반환합니다.