본문 바로가기
Security/Keycloak

Keycloak을 활용한 Kubernetes 사용자 인증 및 권한 관리

by wlsdn3004 2023. 12. 19.
728x90
반응형

 

개요

Kubernetes를 사용하는 환경에서는 사용자 인증 및 권한 관리 측면에서 어려움을 겪는다. 

 

일반적으로 하나의 "kubeconfig" 파일을 통한 접근을 제공하지만 해당 파일을 여러 사용자가 공유하여 사용하는 것은 보안상 매우 취약하고 부적절한 방법이다. 따라서 사용자 인증 및 권한을 분리하여 관리해야 하며, 이를 위해서는 추가적인 설정이 필요하다. 또한 이를 팀 또는 프로젝트 단위로 효과적으로 관리하기에는 어려움이 존재한다.

 

이번 글에서는 이러한 어려움을 개선하고자 Keycloak을 통한 사용자 인증 및 권한 관리에 대한 내용을 다루며, 실제 구성 실습을 통해 동작을 이해하고자 한다.

 

 

Keycloak + Kubernetes 통합을 통해 오는 장점은 여러 가지가 있겠지만 실제 사용하면서 느낀 장점은 다음과 같다.

  • 그룹 및 권한 관리의 용이성 향상
    Keycloak의 그룹 권한 관리 기능을 활용하여 팀 또는 프로젝트 단위로 사용자를 그룹화하고, 각 그룹에 필요한 권한을 부여하여 효과적으로 권한을 관리할 수 있다.
  • 보안성 향상
    사용자 토큰의 만료기간을 설정하여 사용자가 사용하는 시간 외에는 토큰을 사용할 수 없도록 하여 보안성이 향상된다.

 

Keycloak과 Kubernetes 간 인증 워크플로우는 다음과 같다.

  1. Token 요청
    사용자는 Kubernetes 클러스터 리소스에 접근하기 위해 Keycloak에게 Token을 요청한다.
  2. Token 발급
    Keycloak 서버는 사용자의 요청을 확인 후 Token을 발급한다.
  3. API 요청
    사용자는 발급받은 Token을 이용하여 Kubernetes API 서버에게 요청한다.
  4. 검증
    Kubernetes API 서버는 요청된 Token을 Keycloak 서버를 통해 검증을 진행한다. 이때, 서명 및 유효 기간 등의 검증이 이루어진다.
  5. 응답
    Kubernetes API 서버는 Token이 유효하고 권한이 부여된 경우, 적절한 응답을 생성하여 사용자의 요청에 응답한다. 만약, 검증이 실패하면 적절한 오류 응답을 반환한다.

 

 

전제 조건

설치 환경

  • AWS EKS : v1.24.17
  • Helm : v3.8.2

설치 버전

  • Keycloak Chart 버전 : 14.0.0
  • Keycloak APP 버전 : 21.0.2

 

1. Keycloak K8S Clinets 생성 및 설정


1-1. Keycloak K8S Clinets 생성

Keycloak에 접속하여 K8S에서 사용할 신규 realm을 생성한다.

  • Create Realm

 

Realm name 입력 후 Create로 생성한다.

 

위에서 생성한 "k8s-realm"에 K8S에서 사용할 Client를 생성한다.

 

Client ID에 "k8s-client"로 입력 후 Next를 누른다.

 

Client authentication을 "On"으로 활성화 후 Next를 누른다

 

Save로 위에서 설정한 client를 생성 & 저장한다.

1-2. Keycloak K8S Clinets Role 생성

위에서 생성한 Clients에 들어가 아래와 같이 role을 생성한다.

  • Roles > Create role

 

k8s-role 이름으로 role을 생성한다.

1-3. Keycloak K8S Clinets Mapper 설정

위에서 생성한 Clients에 들어가 아래와 같이 Mapper를 설정한다.

  • Client scopes > k8s-client-dedicated 클릭

 

  • Scope > Full scope allowed 비활성화

 

  • Mappers > Add mapper > By configuration > User Client Role

 

아래와 같이 Mapper 정보를 추가 후 Save로 저장한다.

 

2. Keycloak Group 생성 및 설정


2-1. Keyclaok Group 생성

권한을 부여할 Keycloak Group을 생성한다.

  • Groups > Create group > "Name" 입력 > Create

2-2. Keyclaok Group Role mapping

admin 이름으로 생성된 Group 확인 후 admin을 클릭한다

 

  • Role mapping > Assign role

 

  • Filter bt clients > k8s-client에서 생성한 "k8s-role" 선택 > Assign

 

admin이라는 그룹에 k8s-client의 k8s-role이 성공적으로 mapping 되었는지 확인한다.

 

3. Keycloak User 생성 및 설정


admin group에 포함시킬 user를 생성한다.

  • Users > Add user

 

여기서는 "k8s-user"라는 이름으로 생성한다.

 

생성된 User로 접근하여 비밀번호를 설정한다.

  • Users > Credentials > Set password > "비밀번호 입력" > Save > Save password

 

생성된 User를 위에서 생성한 admin 그룹에 포함한다.

  • Users > Groups > Join Group > admin 체크 > Join

 

4. EKS OIDC 설정


AWS Console에 들어가 Keycloak의 Token을 검증할 수 있게 EKS OIDC를 설정한다.

  • AWS Console > EKS Cluster > 액세스 > ID 제공업체 연결

 

각 입력칸에 맞게 생성 후 결과는 아래와 같다.

  • 발급자 URL: https://{Keycloak 주소}/realms/k8s-realm
  • 클라이언트 ID: k8s-client
  • 사용자 이름 클레임: preferred_username
  • 그룹 클레임: groups

 

5. Token을 이용한 EKS 권한 획득


Keycloak token을 요청하기 위해 아래 정보를 확인한다.

  • token 요청할 keycloak 주소
  • client_secret 정보

아래와 같이 token 요청할 keycloak 주소를 확인한다.

  • Realm settings > Endpoints의 OpenID Endpoint Configuration 클릭

 

위 OIDC Endpoint configuration을 열면 token_endpoint의 값이 보인다. 해당 값이 token을 요청할 Keycloak의 주소이다.

형식은 아래와 유사할 것이다.

  • https://{keycloak 주소}/realms/k8s-realm/protocol/openid-connect/token

 

아래와 같이 client_secret 정보를 확인한다.

  • 생성한 Clinets > Credentials

 

위 두 개의 값이 확인되었으면 아래 명령어로 token을 요청한다.

curl -X POST https://{keycloak 주소}/realms/k8s-realm/protocol/openid-connect/token \
-d grant_type=password -d client_id=k8s-client -d username=k8s-user -d password="{비밀번호 입력}" -d \
scope=openid -d client_secret={위에서 확인한 client_secret 입력} | jq -r '.id_token'

 

위에서 나온 token으로 jwt.io 사이트에 입력하여 decoding 하면 아래와 같이 payload에서 정보를 확인할 수 있다.

 

위 토큰으로 EKS에 접근할 수 있게 RBAC을 설정하여 권한을 부여한다.

  • namespace와 pod를 조회할 수 있는 권한을 부여한다
## rbac.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-role
rules:
  - apiGroups: [""]
    resources: ["namespaces", "pods"]
    verbs: ["get", "list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: keycloak-role
subjects:
- kind: Group
  name: k8s-role
  apiGroup: rbac.authorization.k8s.io

 

위 rbac.yaml 파일을 EKS에 적용한다.

$ kubectl apply -f rbac.yaml

 

아래 명령어로  EKS API에 default namespace 조회를 요청한다.

  • Token 기본 expire 시간은 5분이다.
$ TOKEN=$(curl -X POST https://{keycloak 주소}/realms/k8s-realm/protocol/openid-connect/token -d grant_type=password -d client_id=k8s-client -d username=k8s-user -d password="{비밀번호 입력}" -d scope=openid -d client_secret={위에서 확인한 client_secret 입력} | jq -r '.id_token')
$ curl  https://{EKS 주소}/api/v1/namespaces/default --header "Authorization: Bearer ${TOKEN}" --insecure
{
  "kind": "Namespace",
  "apiVersion": "v1",
  "metadata": {
    "name": "default",
    "uid": "022aada1-a597-4ab1-94e5-61facf07bf8e",
    "resourceVersion": "41",
    "creationTimestamp": "2023-12-05T03:03:34Z",
    "labels": {
      "kubernetes.io/metadata.name": "default"
    },
    "managedFields": [
      {
        "manager": "kube-apiserver",
        "operation": "Update",
        "apiVersion": "v1",
        "time": "2023-12-05T03:03:34Z",
        "fieldsType": "FieldsV1",
        "fieldsV1": {
          "f:metadata": {
            "f:labels": {
              ".": {},
              "f:kubernetes.io/metadata.name": {}
            }
          }
        }
      }
    ]
  },
  "spec": {
    "finalizers": [
      "kubernetes"
    ]
  },
  "status": {
    "phase": "Active"
  }
}

 

만약 RBAC 권한이 없을 경우 아래와 같은 메세지가 출력될 것이다.

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces \"default\" is forbidden: User \"https:///realms/k8s-realm#k8s-user\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "default",
    "kind": "namespaces"
  },
  "code": 403
}

 

만약 Token 정보가 잘못되었다면 아래와 같은 메세지가 출력될 것이다.

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

 

 

정상적으로 EKS API를 통해 K8S 자원이 확인되면 Token을 이용하여 kubeconfig 파일을 생성한다.

## kubeconfig-test
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: {}
    server: https://{EKS Endpoing 주소}
  name: cluster-name
contexts:
- context:
    cluster: cluster-name
    user: test
  name: test-eks
current-context: test-eks
kind: Config
preferences: {}
users:
- name: test
  user:
    token: {TOKEN 입력}

 

위 kubeconfig 파일로 EKS 자원을 조회하여 정상으로 출력되는지 확인한다.

$ kubectl get ns --kubeconfig=kubeconfig-test
NAME              STATUS   AGE
default           Active   14d
observability     Active   4d1h
opentelemetry     Active   3d22h

 

RBAC이 잘 적용되었는지 service를 조회해 본다.

$ kubectl get svc --kubeconfig=kubeconfig-test
Error from server (Forbidden): services is forbidden: User "https:///realms/k8s-realm#k8s-user" cannot list resource "services" in API group "" in the namespace "default"

 

namespace와 pod 조회 권한만 주어 service는 조회가 안 되는 걸 확인할 수 있다.

 

기본 TOKEN 시간은 5분이기 때문에 해당 시간을 늘리고 싶으면 아래와 같이 값을 늘려 설정한다.

  • Realm settings > Tokens

 

6. KUBECONFIG 자동 생성 쉘스크립트 작성


위 TOKEN을 취득하여 EKS API에 접근하는 절차는 매번 하기에는 매우 번거롭기 때문에 스크립트로 관리하면 편하게 사용할 수 있다.

 

kubeconfig_create.sh

  • 맨 처음 필수 변수를 입력하고 쉘 스크립트를 실행하면 된다.
#!/bin/bash
#
# 스크립트 시작 전 필수 변수 입력!!
CLIENT_SECRET="" ## Keycloak client secret 입력
EKS_ENDPOINT=""  ## EKS Endpoing 입력 (e.g: example.eks.amazonaws.com)
CLIENT_ID=""  ## keycloak client id 입력
KEYCLOAK_URL="" ## keycloak 주소 입력 (e.g: keycloak-example.co.kr)
EKS_AUTHORITY_DATA="" ## EKS API 인증 데이터 입력
REALM=""  ## Keycloak Reaml 입력

# 변수 확인 및 검증
if [ -z "$CLIENT_SECRET" ]; then
    echo "CLIENT_SECRET이 설정되어야 합니다."
    exit 1
fi

if [ -z "$EKS_ENDPOINT" ]; then
    echo "EKS_ENDPOINT이 설정되어야 합니다."
    exit 1
fi

if [ -z "$CLIENT_ID" ]; then
    echo "CLIENT_ID가 설정되어야 합니다."
    exit 1
fi

if [ -z "$KEYCLOAK_URL" ]; then
    echo "KEYCLOAK_URL이 설정되어야 합니다."
    exit 1
fi

if [ -z "$EKS_AUTHORITY_DATA" ]; then
    echo "EKS_AUTHORITY_DATA가 설정되어야 합니다."
    exit 1
fi

if [ -z "$REALM" ]; then
    echo "Keycloak Realm이 설정되어야 합니다."
    exit 1
fi

# 스크립트 계속 진행...
echo "모든 필수 변수가 설정되었습니다."

# 사용자 입력 변수 설정
read -p "Enter Keycloak Username: " USERNAME
read -sp "Enter Keycloak Password: " PASSWORD

# 토큰 생성
TOKEN=$(curl -X POST https://${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/token \
        -d grant_type=password \
        -d client_id=${CLIENT_ID} \
        -d username=${USERNAME} \
        -d password="${PASSWORD}" \
        -d scope=openid \
        -d client_secret=${CLIENT_SECRET} | jq -r '.id_token')

# 환경 변수 검증
if [ -z "$TOKEN" ]; then
    echo "TOKEN이 존재하지 않습니다."
    exit 1
fi

# KUBECONFIG 파일 생성
cat <<EOF > kubeconfig.yaml
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: ${EKS_AUTHORITY_DATA}
    server: https://${EKS_ENDPOINT}
  name: cluster-name
contexts:
- context:
    cluster: cluster-name
    user: test
  name: test-eks
current-context: test-eks
kind: Config
preferences: {}
users:
- name: test
  user:
    token: $TOKEN
EOF

echo "KUBECONFIG 파일이 생성되었습니다."

 

아래와 같이 Keycloak user와 password를 입력하면 kubeconfig.yaml 파일이 생성된다. 이후 생성된 kubeconfig.yaml 파일로 EKS 리소스를 조회할 수 있다.

$ sh kubeconfig_create.sh
Enter Keycloak Username: k8s-user
Enter Keycloak Password:
KUBECONFIG 파일이 생성되었습니다.

## kubeconfig file 생성 확인
$ ls 
kubeconfig.yaml

$ kubectl get ns --kubeconfig=kubeconfig.yaml
NAME              STATUS   AGE
default           Active   14d
observability     Active   4d1h
opentelemetry     Active   3d22h

 

 

마치며


Keycloak을 통한 Kubernetes 사용자 인증 및 권한 관리는 기존의 어려움을 극복하고 효과적인 보안 및 사용자 관리를 가능케 한다. 이를 통해 더욱 유연하게 Kubernetes 클러스터를 운영하고 안전하게 사용자를 관리할 수 있다.

반응형

댓글