개요
Kubernetes를 사용하는 환경에서는 사용자 인증 및 권한 관리 측면에서 어려움을 겪는다.
일반적으로 하나의 "kubeconfig" 파일을 통한 접근을 제공하지만 해당 파일을 여러 사용자가 공유하여 사용하는 것은 보안상 매우 취약하고 부적절한 방법이다. 따라서 사용자 인증 및 권한을 분리하여 관리해야 하며, 이를 위해서는 추가적인 설정이 필요하다. 또한 이를 팀 또는 프로젝트 단위로 효과적으로 관리하기에는 어려움이 존재한다.
이번 글에서는 이러한 어려움을 개선하고자 Keycloak을 통한 사용자 인증 및 권한 관리에 대한 내용을 다루며, 실제 구성 실습을 통해 동작을 이해하고자 한다.
Keycloak + Kubernetes 통합을 통해 오는 장점은 여러 가지가 있겠지만 실제 사용하면서 느낀 장점은 다음과 같다.
- 그룹 및 권한 관리의 용이성 향상
Keycloak의 그룹 권한 관리 기능을 활용하여 팀 또는 프로젝트 단위로 사용자를 그룹화하고, 각 그룹에 필요한 권한을 부여하여 효과적으로 권한을 관리할 수 있다. - 보안성 향상
사용자 토큰의 만료기간을 설정하여 사용자가 사용하는 시간 외에는 토큰을 사용할 수 없도록 하여 보안성이 향상된다.
Keycloak과 Kubernetes 간 인증 워크플로우는 다음과 같다.
- Token 요청
사용자는 Kubernetes 클러스터 리소스에 접근하기 위해 Keycloak에게 Token을 요청한다. - Token 발급
Keycloak 서버는 사용자의 요청을 확인 후 Token을 발급한다. - API 요청
사용자는 발급받은 Token을 이용하여 Kubernetes API 서버에게 요청한다. - 검증
Kubernetes API 서버는 요청된 Token을 Keycloak 서버를 통해 검증을 진행한다. 이때, 서명 및 유효 기간 등의 검증이 이루어진다. - 응답
Kubernetes API 서버는 Token이 유효하고 권한이 부여된 경우, 적절한 응답을 생성하여 사용자의 요청에 응답한다. 만약, 검증이 실패하면 적절한 오류 응답을 반환한다.
전제 조건
- AWS EKS 클러스터
- Helm CLI 도구
- Keycloak 설치
설치 환경
- 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 생성
1-2. Keycloak K8S Clinets Role 생성
1-3. Keycloak K8S Clinets Mapper 설정
2. Keycloak Group 생성 및 설정
2-1. Keyclaok Group 생성
2-2. Keyclaok Group Role mapping
3. Keycloak User 생성 및 설정
4. EKS OIDC 설정
5. Token을 이용한 EKS 권한 획득
6. KUBECONFIG 자동 생성 쉘스크립트 작성
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 클러스터를 운영하고 안전하게 사용자를 관리할 수 있다.
'Security > Keycloak' 카테고리의 다른 글
Keycloak + Terraform Cloud SAML SSO 로그인 연동 (0) | 2024.02.19 |
---|---|
Keycloak + saml2aws로 AWS 임시 자격 증명 얻기 (0) | 2023.05.09 |
Keycloak + AWS saml SSO 로그인 연동 (2) | 2023.05.09 |
Keycloak + Vault 연동 SSO(Single Sign-On) 구현하기 (2) | 2023.04.12 |
Keycloak 이란? 개념과 설치 실습 (0) | 2023.04.08 |
댓글