
최근에 쿠버네티스 클러스터 마이그레이션을 진행했다. 클러스터 마이그레이션을 진행하기 이전에는 rancher 기반으로 클러스터를 설치하고 관리했었으나, 새롭게 옮긴 클러스터는 k3s를 직접 설치해서 사용하고 있었기에, 보다 간소화된 기능으로 쿠버네티스 클러스터 모니터링 대시보드가 있으면 좋겠다는 생각을 하곤 했다.
그렇게 찾아보게 된 것이 headlamp라는 오픈소스이다. 쿠버네티스 클러스터를 모니터링하는 오픈소스 솔루션이며, 나쁘지 않은 UI를 가지고 있었기에 본격적으로 설치하기 시작했다.
- [headlamp] : https://headlamp.dev/
Headlamp
Desktop and Web It can be run as a web app, desktop app, or both.
headlamp.dev
다른 addons들과 똑같이 손쉽게 계정 컨트롤이나 접근을 설정하기 위해서 keycloak을 통해서 시도해보았으나, 401과 403 에러를 마주쳤다. 로그를 보니, 당연히 해야 하는 설정들을 하지 않아서 발생한 것이기 때문에 손쉽게 해결할 수 있었으나, 너무나 당연하게 keycloak과 headlamp를 연동하는 과정에서 발생한 에러를 좀 더 분석해서 포스팅하면 keycloak에 대한 보완 설명이 좋을 것 같아서 포스팅을 작성하게 되었다.
먼저, keycloak에 대해서 간단하게 설명하는 이전 포스팅을 참고하면 더욱 도움이 될 것이다.
- [tistory] Keycloak 및 인증 시스템에 대해서 : https://marsboy.tistory.com/103
Keycloak 및 인증 시스템에 대해서
시대생팀에서 쿠버네티스 클러스터를 옮기는 일을 하면서 새롭게 인프라를 세팅해야 하는 일이 있었다. Grafana나 Kiali 등을 온프레미즈에 다시 띄우면서 새롭게 세팅을 해야 했고, 우리의 인증을
marsboy.tistory.com
이번 포스팅은 앞서 포스팅한 Keycloak에 대한 부연 설명을 위해 작성했기 때문에 상황이 조금 한정적이다. 다음과 같은 환경을 기준으로 본 포스팅을 작성했다.
- k3s / 컨트롤 플레인 1대, 워커 플레인 2대
- keycloak 기반의 로그인 및 권한 관리 중
이제 본론으로 넘어간다.
headlamp란?

우선 headlamp에 대해서 짤막하게 짚고 넘어가자면, headlamp는 Kubernetes를 UI를 통해 볼 수 있도록 지원하는 오픈소스다. 다양한 UI를 지원하면서 RBAC도 지원한다. 뿐만 아니라, headlmap의 다양한 플러그인 중에 AI Assistant 기능이 개발되면서, LLM과의 상호작용을 통해서 쿠버네티스 클러스터의 트러블슈팅을 할 수 있도록 한다.
아래의 깃허브에서 다양한 플러그인들을 확인할 수 있으며, ai-assistant 외에도 다양한 플러그인을 지원한다.
- [github] headlamp-k8s 플러그인 : https://github.com/headlamp-k8s/plugins
GitHub - headlamp-k8s/plugins: Official plugins of the Headlamp project
Official plugins of the Headlamp project. Contribute to headlamp-k8s/plugins development by creating an account on GitHub.
github.com
다음으로는 keycloak OIDC를 통해서 headlamp를 설치하는 방법을 알아보자.
Helm Chart Vendoring
보통의 개발 환경에서 패키지 등의 다운로드를 도와주는 패키지 매니저를 일반적으로 사용하는데, 이런 식으로 외부에서 의존성을 가져오는게 아니라, 레포 안에 미리 가져다 두는 경우가 있다. 이를 벤더링(Vendoring)이라고 표현한다.
보안 상의 이유도 있을 수 있고, 혹은 버전 문제가 있을 수 있다. 이는 helm chart 뿐만 아니라, 다른 언어에서 자주 쓰이는 프레임워크도 해당한다. 예를 들어서 텐서플로우를 직접 fork 해서 커스터마이즈 해서 사용한다거나 그런 것들이 해당한다. 쿠버네티스에서는 helm chart를 기반으로 설치하는 솔루션들을 직접 디렉터리에 가져와서 설정하는 방법 등이 있다. 이렇게 함으로써 DevOps 진영에서 중요시되는 gitOps 전략을 챙길 수 있으며, 워크로드를 선언형으로 관리할 수 있다.
headlamp를 helm chart vendoring을 통해서 설치하는 명령어는 다음과 같다.
helm add
Keycloak OIDC 설정
headlamp의 helm chart를 밴더링한 다음에, Keycloak을 통해서 접근할 수 있도록 해보자. keycloak에서 headlamp라는 client를 생성하면 Client Secret이 생기는 것을 볼 수 있는데, 이를 headlamp에 등록하는 것으로 headlamp에서 oidc 로그인을 적용할 수 있다.

이렇게 생성된 Client Secret을 headlamp의 values.yaml 파일에 다음과 같이 설정하면 적용할 수 있다.
config:
oidc:
secret:
create: true
name: oidc
# TODO: Keycloak에서 클라이언트 생성 후 아래 값 입력
clientID: "headlamp"
clientSecret: "<KEYCLOAK_CLIENT_SECRET>"
issuerURL: "https://auth.example.com/auth/realms/ADMIN"
scopes: "openid profile email groups"
위와 같이 설정을 마치고, helm chart를 통해서 배포하면 성공적으로 headlamp 서비스에 접근할 수 있다. 하지만 접근 자체는 되지만, headlamp가 일반적으로는 제대로 동작하지 않을 수 있다.
Headlamp OIDC 인증 문제 해결 ( 401 Unauthorized )
위와 같은 설정을 마치고 headlamp 페이지에 들어가면 oidc 로그인도 이상하고, 네트워크 탭을 열어보면 401 에러가 많이 발생하는 경우가 있다. 원인은 다른 대시보드와 다르게, headlamp는 직접 kubernetes cluster의 정보를 받아서 시각화해 주는 대시보드이기 때문에 클러스터에 대한 권한이 필요하다.
먼저, 쿠버네티스 클러스터가 어떤 namespace를 가지고 있는 지, 어떤 pod를 가지고 있는지 이러한 정보는 kube-apiserver에 접근해야 하기 때문에 이에 대한 설정을 진행해야 한다.
쿠버네티스 클러스터의 마스터 노드에 다음과 같은 설정을 추가하는 것으로 해결할 수 있다. ( k3s 기준 )
kube-apiserver-arg:
# Same issuer url in headlamp's values.yaml
- "oidc-issuer-url=https://auth.example.com/auth/realms/ADMIN"
- "oidc-client-id=headlamp"
- "oidc-username-claim=preferred_username"
- "oidc-groups-claim=groups"
여기에서 중요한 점은 kube-apiserver-arg.oidc-issuer-url을 설정할 때, headlamp에서 설정한 것과 같은 url을 설정해야 한다는 점이다! 이렇게 설정하고 다음의 명령어를 통해서 마스터 노드를 재실행하면, 401 에러가 발생하는 것을 막을 수 있다.
k3s가 아니라면? (kubeadm etc.)
kubeadm 계열의 쿠버네티스 클러스터는 전통적으로 kube-apiserver를 systemd로 띄우지만, k3s의 경우에는 프로세스가 내부적으로 컨트롤플레인 컴포넌트들을 띄우고 관리한다.
따라서, kube-apiserver 실행 커맨드라인 인자를 k3s에 전달해야 하고, 그 전달 경로가 /etc/rancher/k3s/config.yaml의 kube-apiserver-arg이다.
왜 안 됐을까?
headlamp의 OIDC 로그인은 브라우저 단계이고, 최종 판정은 kube-apiserver가 한다. 쿠버네티스 클러스터를 모니터링하는 headlamp는 그 특성상 클러스터에 대한 정보를 확인해야 하기 때문에 kube-apiserver에서 정보를 요청한다. headlamp에서 oidc로 로그인하면 그다음부터는 kubernetes API를 호출할 때, 토큰(keycloak에서 발급된)을 들고 간다.
그 토큰을 보고, 서명이 맞는지 issuer가 맞는지 등을 검증한다. 이런 것들은 kube-apiserver가 결정하는데, 위와 같이 파라미터를 넣어주지 않으면 kube-apiserver는 그 JWT를 "내가 신뢰하는 방식으로 검증할 수 없다" 그렇기에 결과적으로 401(Unauthorized) 또는 익명 사용자 처리 같은 현상이 발생한다.
Headlamp 권한 문제 해결 ( 403 Forbidden )
위와 같이 성공적으로 설정을 완료했다면, 페이지는 정상적으로 들어가지지만 403 에러가 발생할 수 있다. 성공적으로 인증(Authentication)은 완료되었으나, 클러스터에 대한 정보를 조회하는 API를 사용하기에는 인가(Authorization)가 되지 않은 것이다.
이는 쿠버네티스 클러스터를 좀 다루어봤다면 당연히 발생할만한 문제로, headlamp의 사용자가 Kubernetes API를 사용할 권한이 없기 때문이다. 이를 위해서는 RBAC를 적용해야 한다. 쿠버네티스에서는 크게 권한 정의와 주체에게 연결하는 것을 다음과 같이 다르게 분리한다.
권한을 정의하는 것
- Role : 특정 namepsace 안에서만 유효한 권한 묶음
- ClusterRole : 클러스터 전체 범위(모든 namespace 또는 cluster-scoped 리소스)까지 커버하는 권한 묶음
권한을 주체에게 연결하는 것
- RoleBinding : Role(또는 ClusterRole)을 특정 namepsace에 대해 어떤 주체에게 줄지 연결
- ClusterRoleBinding : ClusterRole을 클러스터 전체 범위로 어떤 주체에게 줄지 연결
이렇게 정리를 하면, 대략적으로 우리는 어떠한 것이 필요한지 알 수 있다. 특정 namespace에 대해서 설정하는 것이 아니기 때문에 ClusterRole과 ClusterRoleBinding이 필요하다는 것을 알 수 있다.
이때, ClusterRole은 클러스터 전체 범위를 커버하는 권한 묶음을 의미하는데, 이미 존재하는 쿠버네티스 리소스를 볼 수 있는(view) 권한을 할당하면 된다. 이는 이미 존재하기 때문에 ClusterRoleBinding을 선언해 주면 된다. 다음과 같이 설정할 수 있다.
kubectl create clusterrolebinding headlamp-viewers \
--clusterrole=view \
--group=headlamp-viewers
다만, 우리 환경에서는 gitOps 기반으로 헬름 차트를 밴더링 해서 쓰고 있기 때문에, headlamp/values.yaml에는 다음과 같이 적용하는 것을 설정할 수 있다.
# ... 중략
extraManifests:
# ClusterRoleBinding: Keycloak SRE 그룹에 cluster-admin 권한 부여
# 주의: Keycloak groups claim의 값과 정확히 일치해야 함 (대소문자 구분)
- |
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: headlamp-sre-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: SRE
# ClusterRoleBinding: Keycloak Developer 그룹에 cluster-admin 권한 부여
# 필요시 cluster-admin 대신 edit, view 등으로 변경 가능
- |
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: headlamp-developer-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: Developer
이렇게 ClusterRoleBind을 설정하고 나면, 권한 문제에서 해결되어 정상적으로 headlamp를 통해서 쿠버네티스 클러스터의 리소스를 조회할 수 있다.

여기에서 한 가지 중요한 점이 있다. ClusterRoleBinding을 하기 위해서는 Group을 설정해줘야 하는데, Keycloak을 사용하는 환경에서는 이러한 subjects(주체)를 제대로 설정해줘야 한다는 것이다.
지금 keycloak의 환경에서는 SRE이라는 인프라를 관리하는 사용자와 Developer라고 개발자 권한 2가지를 사용하고 있기 때문에, 위와 같이 2개의 권한을 가진 사용자만 접근할 수 있도록 해두었다. 만약 다른 사용자 Group을 사용하고 있다면, 다른 사용자 Group name을 설정하고 그 사용자에게 적절한 ClusterRole을 부여해야 한다.
위와 같이 kube-apiserver-arg 설정과 함께 clusterRoleBinding을 설정하면 성공적으로 리소스를 조회할 수 있게 된다. 그렇다면 keycloak에서는 대체 어떻게 페이로드를 전달하고 있기에 이런 식으로 권한 관리가 되는 걸까? keycloak에서는 evaluate라는 페이로드를 확인할 수 있는 기능을 제공한다.
Keycloak의 Payload Evaluate
Evaluate 기능은 특정 사용자가 특정 client에 접근할 때, 어떤 식으로 ID나 Access Token(JWT)을 받게 되는지 볼 수 있는 기능이다. 사용하는 방법은 다음과 같다.

clients에서 내가 특정 서비스에 대해서 로그인하거나 접근하려고 할 때 받는 페이로드를 확인하기 위해서, 해당 client를 검색한다. 현재 상황에서는 headlamp에 oidc로 로그인하는 경우 받는 JWT 페이로드를 확인하기 위해서 headlamp로 진행한다.

client scopes 탭에 들어가면 다양한 setup들을 확인할 수 있다. 내부 탭에 Setup과 Evaluate 탭이 있는 것을 볼 수 있는데, 여기에서 Evaluate를 선택한다.

위와 같은 탭을 볼 수 있는데, 여기에서 Users에 해당하는 드롭 다운에서 직접 내부 사용자의 email을 입력하면, 해당 사용자의 페이로드가 어떻게 오는지 알 수 있다. 이를 통해서 Developer 권한을 가진 사람과 SRE 권한을 가진 사람을 직접 클릭해서 각각의 경우에 어떻게 페이로드가 오는지 확인할 수 있다.

마지막으로는 내부 사용자의 이메일을 등록한 다음에 generated access token 버튼을 누르면, 해당 사용자가 oidc 로그인 후에 받게 되는 access token을 확인할 수 있다. 이런 식으로 keycloak에서 로그인 후에 주는 페이로드를 확인할 수 있다.
이 케이스에서는 headlamp 기반으로 RBAC를 다루었지만, 대부분의 서비스에서 keycloak의 페이로드를 통해서 RBAC를 연동할 수 있다. 이전 포스팅에서 다루었던 그라파나(grafana)의 경우에서는 SRE 권한을 그라파나 내부의 admin과 연동시키고, Developer 권한을 그라파나 내부의 Editor와 연동시켜서 사용하고 있다.
이런 식으로 keycloak의 Roles&Groups 시스템을 직접 다른 서비스와 연동시켜서 사용할 수 있다.
'DevOps' 카테고리의 다른 글
| Keycloak 및 인증 시스템에 대해서 (0) | 2026.01.12 |
|---|