Kubernetes 이야기

Impersonation (Kubernetes Authentication) 본문

Kubernetes/일반

Impersonation (Kubernetes Authentication)

kmaster 2023. 1. 26. 14:28
반응형

Kubernetes에는 서비스 계정과 일반 사용자라는 두 가지 사용자 범주가 있다. 서비스 계정은 Kubernetes API에서 관리하는 사용자를 의미하고, 일반 사용자는 개인키, Keystone 또는 Google 계정과 같은 사용자 저장소, ID/Password 등의 계정을 의미한다.

Kubernetes에서는 일반 사용자 계정을 나타내는 객체가 없다.

 

일반 사용자 계정을 통해 Kubernetes API Server에 접근해야 하는 경우가 있다. 이렇게 하려면 일반 사용자 계정을 Kubernetes의 인증과 연동할 수 있는 방법이 필요하다.

 

이번에는 사용자 인증 방식 중 Impersonation이라는 방식을 알아보자.

 

먼저 Kubernetes API Server에 접근하여 Pod정보를 조회하는 방법을 살펴보자.

curl -XGET https://localhost:6443/api/v1/pods --insecure

이 명령을 실행하면 다음과 같이 권한 오류가 발생한다.

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403
}

anonymous 사용자로는 Cluster에 배포된 Pod정보를 조회할 수 없다.

Kubernetes의 서비스 계정을 만들어 조회해 보자.

 

먼저 Service Account를 만들어보자.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: pod-viewer
  namespace: demo

이제 서비스 계정에 Cluster Pod를 조회할 수 있는 Cluster Role을 만들어보자.

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pods-view-role
rules:
- apiGroups: ["", "extensions", "apps"]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

pod-viewer라는 SA에 권한을 할당한다.

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pods-view-rolebinding
subjects:
- kind: ServiceAccount
  name: pod-viewer
  namespace: demo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pods-view-role

 

이제 Service Account Token을 생성하자.

apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: pod-viewer
  namespace: demo
  annotations:
    kubernetes.io/service-account.name: "pod-viewer"

 

서비스 계정의 Token정보를 조회해 보자.

# kubectl get secret -n demo pod-viewer -o jsonpath='{$.data.token}' | base64 --decode
eyJhbGciOiJSUzI1NiIsImtpZCI6ImRELTV0T1hZUTR0OTV6....

 

이 서비스 계정의 token을 이용하여 Kuberntes API Server에 접근해 보자.

curl -XGET -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRELTV0T1h...J_bqiv4oGxlPQAxXy1sOtYlE8zlDDg69v4s2A" https://localhost:6443/api/v1/pods --insecure

[실행결과]

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "217893655"
  },
  "items": [
    {
      "metadata": {
        "name": "xxx-58cfb95d5b-t5vzz",
        "generateName": "xxx-58cfb95d5b-",
        "namespace": "xxx",
        "uid": "98c626ef-3f64-4f2c-ac0e-4dc30e7d1c5a",
        "resourceVersion": "21609324",
        "creationTimestamp": "2022-10-25T08:28:57Z",
        "labels": {
          "app": "xxx",
          "pod-template-hash": "58cfb95d5b"
        },
        "annotations": {
          "cni.projectcalico.org/containerID": "22f44e1bfa199219490e4f2c94a160f44b71b55586e19041ed984533ab05c807",
          "cni.projectcalico.org/podIP": "192.168.166.165/32",
          "cni.projectcalico.org/podIPs": "192.168.166.165/32"
        },
        "ownerReferences": [
...
}

만약 잘못된 Token으로 조회하면 다음과 같다.

# curl -XGET -H "Authorization: Bearer 123" https://localhost:6443/api/v1/deployments --insecure
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

에러 코드를 보면 401이다. 401 에러는 인증에 실패한 것이고 403에러는 인가에 실패했다는 의미이다.

 

생성된 Token으로 deployment를 조회해 보자.

curl -XGET -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRELTV0T1h...J_bqiv4oGxlPQAxXy1sOtYlE8zlDDg69v4s2A" https://localhost:6443/api/v1/deployments --insecure

[실행결과]

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "deployments is forbidden: User \"system:serviceaccount:demo:pod-viewer\" cannot list resource \"deployments\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "deployments"
  },
  "code": 403
}

 

Impersonation 

 

Kubernetes의 가장(Impersonation)은 사용자 또는 서비스 계정이 Kubernetes API 서버에 API 요청을 할 때 다른 사용자 또는 서비스 계정의 ID를 가장하는 기능을 의미한다.

이는 일반적으로 사용자 또는 서비스 계정에 특정 작업을 수행하는 데 필요한 권한이 없지만 다른 사용자 또는 서비스 계정에는 있는 상황에서 사용된다. 또는 관리자는 이 기능을 사용하여 일시적으로 다른 사용자를 가장하고 요청이 거부되었는지 확인하여 권한 부여 정책을 디버깅할 수도 있다.

 

가장 요청은 먼저 요청한 사용자로 인증한 다음 가장된 사용자 정보로 전환한다.

  • 사용자는 자격 증명 및 가장 헤더 를 사용하여 API를 호출한다.
  • API 서버가 사용자를 인증한다.
  • API 서버는 인증된 사용자에게 가장 권한이 있는지 확인한다.
  • 요청 사용자 정보는 가장 값으로 대체된다.

가장 요청을 수행하는 데 다음 HTTP 헤더를 사용할 수 있다.

  • Impersonate-User: 역할을 할 사용자 이름.
  • Impersonate-Group: 활동할 그룹명. 여러 그룹을 설정하기 위해 여러 번 제공될 수 있다. "Impersonate-User"가 필요한다.
  • Impersonate-Extra-( extra name ): 추가 필드를 사용자와 연결하는 데 사용되는 동적 헤더. "Impersonate-User"가 필요하다. 
  • Impersonate-Uid: 가장 중인 사용자를 나타내는 고유 식별자.  "Impersonate-User"가 필요합니다. Kubernetes는 이 문자열에 형식 요구 사항을 부과하지 않는다.

[예제]

Impersonate-User: jane.doe@example.com
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b

 

Kubernetes에서 다음과 같은 몇 가지 방법으로 Impersonation을 수행할 수 있다.

 

1) kubectl 명령줄 도구를 사용하면 --as 또는 --as-user 플래그를 사용하여 사용자 또는 서비스 계정을 가장할 수 있다. 예를 들어 다음 명령을 사용하면 사용자 "jane"으로 kubectl get pods 명령을 실행할 수 있다.

kubectl --as=jane get pods
kubectl에서는 추가 필드 또는 UID를 가장할 수 없다.

 

2) 가장은 API 요청에서 Impersonate-User 및 Impersonate-Group 헤더를 사용하여 수행할 수 있다. 이러한 헤더를 사용하면 요청을 수행해야 하는 사용자 및 그룹을 지정할 수 있다. 예를 들어 다음 명령을 사용하면 사용자 "jane" 및 그룹 "developers"로 포드를 가져올 수 있다.

curl -H "Impersonate-User: jane" -H "Impersonate-Group: developers" https://<k8s-apiserver-url>/api/v1/pods

 

3) 포드 및 배포와 같은 일부 Kubernetes 리소스는 컨테이너를 실행하는 동안 사용자를 가장하는 데 사용할 수 있는 사양 섹션의 runAsUser 및 fsGroup 필드를 설정할 수도 있다.

 

가장하려는 사용자 또는 서비스 계정에 해당 작업을 수행하는 데 필요한 권한이 있는 경우에만 가장이 작동한다는 점에 유의해야 한다. 또한 가장은 리소스에 대한 액세스 권한을 얻거나 가장하는 사용자 또는 서비스 계정이 일반적으로 액세스할 수 없는 작업을 수행하는 데 사용될 수 있으므로 가장은 주의해서 사용해야 한다.

 

테스트

 

john이라는 사용자 계정에 pod의 조회 권한을 줘 보도록 하자.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: john-binding
subjects:
- kind: User
  name: john
  apiGroup: rbac.authorization.k8s.io
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: john-role
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: john-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]

 

admin 계정인 kubeconfig를 이용하여 pod정보를 조회해 보자.

# kubectl get pods --as=john -A
NAMESPACE                       NAME                                                          READY   STATUS      RESTARTS        AGE
cert-manager                    cert-manager-66bd77df8f-qm2jg                                 1/1     Running     15 (8d ago)     92d
cert-manager                    cert-manager-cainjector-6495667ff4-bt5bw                      1/1     Running     67 (8d ago)     92d
...

 

service 정보를 조회해 보자.

# kubectl get svc --as=john -A
Error from server (Forbidden): services is forbidden: User "john" cannot list resource "services" in API group "" at the cluster scope

 

can-i로도 확인이 가능하다.

# kubectl auth can-i create pods --as john
no
# kubectl auth can-i get pods --as john
yes

 

Impersonation을 사용하면 외부 인증서버 ( 예:Keycloak 등 ) 의 Account 및 Group정보를 이용하여 Kubernetes의 인증과 연동할 수 있고, 다수의 Kubernetes Cluster를 중앙에서 인증/인가를 할 수 있는 Gateway를 만들면 멀티클러스터 관리를 손쉽게 할 수 있다.

 

참고

https://kubernetes.io/docs/reference/access-authn-authz/authentication/

반응형

'Kubernetes > 일반' 카테고리의 다른 글

Chaos Mesh 에서 Physical Machines 카오스 엔지니어링  (0) 2023.02.08
KubeVela  (0) 2023.01.26
GitOps의 장단점  (0) 2023.01.10
Ingress vs Service Mesh  (0) 2022.12.28
Kubernetes와 Edge computing  (0) 2022.12.09
Comments