콘텐츠로 이동
Study Note온프렘 쿠버네티스

5. 입구 ③ — 로그인을 한 곳으로

플랫폼 도구를 하나 얹을 때마다 관리자 계정이 하나씩 늘어난다 — 그게 이 장의 문제다

이 장에서 처음 나오는 말6개
IdPIdentity Provider, 신원 공급자
"이 사람이 누구인지" 확인해 주고 그 결과를 토큰으로 발급하는 쪽. 이 덱에서는 Keycloak이 그 자리다.
SSOSingle Sign-On
한 번 로그인하면 여러 앱에 다시 로그인하지 않아도 되는 것. 계정이 한 곳에만 있으니 퇴사자 차단도 한 곳에서 끝난다.
OIDCOpenID Connect
OAuth 2.0 위에 "누구인지"(신원)를 얹은 프로토콜. 앱은 ID 토큰을 받아 사용자를 안다. 상세는 [Keycloak OAuth와 OIDC](/keycloak/oauth-oidc/).
oauth2-proxy
앱 앞에 서서 로그인 여부를 대신 확인해 주는 리버스 프록시. 앱이 OIDC를 몰라도 인증을 강제할 수 있다.
claim클레임
토큰 안에 실린 사용자 정보 조각(이메일 · 그룹 · 부서). 앱은 이걸 보고 권한을 정한다.
외부 인가External Authorization
게이트웨이가 요청을 백엔드로 넘기기 전에 다른 서비스에 물어보는 기능. Gateway API 구현체에서 oauth2-proxy를 이 자리에 꽂는다.

문제 — 도구마다 계정이 생긴다

섹션 제목: “문제 — 도구마다 계정이 생긴다”

지금까지 얹은 것만 세어도 로그인 화면이 이만큼이다.

도구기본 상태그대로 두면
Grafanaadmin / admin아무나 대시보드를 고친다
Argo CDadmin + 초기 비밀번호 Secret클러스터에 무엇이든 배포할 수 있는 계정이다
Prometheus · Alertmanager인증이 아예 없다URL을 아는 사람이 전부 본다. 침묵(silence)도 걸 수 있다
MinIO 콘솔root 키오브젝트 전체 접근
Kubernetes API인증서 kubeconfig회수가 안 된다 — 유출되면 클러스터를 다시 세워야

계정이 흩어지면 세 가지가 동시에 무너진다 — 퇴사자 차단(어디를 지워야 하는지 모른다), 감사(누가 언제 했는지 도구마다 다른 로그), 비밀번호 관리(결국 공유된다).

해법은 하나다: 신원을 한 곳(Keycloak)에 두고, 각 도구는 그곳에 물어본다.

세 패턴 — 앱의 처지에 따라 갈린다

섹션 제목: “세 패턴 — 앱의 처지에 따라 갈린다”
앱이 OIDC를 지원하면 패턴 ①로 직접 Keycloak과 대화하고, 아니면 쿠버네티스 API 서버인지에 따라 패턴 ③ kubelogin과 패턴 ② oauth2-proxy로 갈리는 분기도
패턴방식장점대가
① 앱 내장 OIDC앱이 client로 등록되고 직접 로그인앱 안의 역할까지 매핑된다앱마다 설정이 다르다
② oauth2-proxy앞단 프록시가 인증을 강제앱을 안 고친다앱은 “누가 왔는지”를 헤더로만 안다
③ API 서버 OIDCkubectl이 토큰으로 인증인증서 kubeconfig를 없앤다API 서버 설정 변경(재시작)
도구패턴메모
Grafana①auth.generic_oauth. 그룹 → Grafana role 매핑까지 된다 (관측 덱 5장)
Argo CD①내장 Dex를 거치거나 Keycloak에 직접. RBAC은 그룹 claim으로
MinIO 콘솔①OIDC 지원. 다만 커뮤니티판 콘솔은 기능이 축소됐다 (6장)
Prometheus②인증 기능이 없다. 반드시 앞에 세운다
Alertmanager②침묵을 아무나 걸 수 있으면 알림 체계가 무의미해진다
Loki · Tempo—사용자에게 직접 노출하지 않는다. Grafana 뒤에 둔다
Kubernetes API③→ Keycloak Kubernetes OIDC

패턴 ② — oauth2-proxy를 어디에 꽂나

섹션 제목: “패턴 ② — oauth2-proxy를 어디에 꽂나”

oauth2-proxy는 두 가지 방식으로 배치한다. Gateway API 시대에는 ①이 기본이다.

게이트웨이가 요청을 백엔드로 넘기기 전에 oauth2-proxy에 “이 요청 통과시켜도 되나” 를 묻는다. 인증되지 않았으면 oauth2-proxy가 Keycloak 로그인으로 리다이렉트한다.

Gateway가 요청을 oauth2-proxy에 물어 미인증이면 Keycloak 로그인으로 보내고, 인증되면 사용자 헤더와 함께 Prometheus로 전달하는 흐름

구현체마다 리소스 이름이 다르다 — Envoy Gateway는 SecurityPolicy, Traefik은 미들웨어. 핵심은 같다: 백엔드는 그대로 두고 게이트웨이가 먼저 물어본다.

# Envoy Gateway 예 — 이 HTTPRoute로 오는 요청은 인가를 거친다
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
name: require-login
namespace: observability
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: prometheus
extAuth:
http:
backendRefs:
- name: oauth2-proxy
port: 4180
headersToBackend: ["x-auth-request-user", "x-auth-request-groups"]

도구를 붙이는 것보다 “누가 무엇을 할 수 있나”를 정하는 게 훨씬 오래 걸린다. 사슬은 셋이고, 어느 한 곳만 끊겨도 사용자는 “로그인은 되는데 아무것도 안 보인다”를 겪는다.

AD 그룹이 LDAP 매퍼로 Keycloak 그룹이 되고 groups claim으로 ID 토큰에 실린 뒤 도구별 매핑으로 Grafana Admin·Argo CD role:admin이 되는 권한 사슬
  1. AD 그룹을 먼저 정한다. 도구별이 아니라 역할별로 — platform-admins, platform-viewers, app-<팀>-devs 정도면 대부분 커버된다. 도구가 늘어날 때마다 그룹을 만들기 시작하면 관리가 무너진다.

  2. Keycloak에서 groups claim을 토큰에 싣는다. client scope와 mapper 설정이며, Keycloak Client와 SSO가 다룬다.

  3. 도구마다 그 claim을 자기 역할로 번역한다.

    # Grafana — 그룹에 따라 role 부여
    [auth.generic_oauth]
    role_attribute_path = contains(groups[*], 'platform-admins') && 'Admin' || 'Viewer'
    # Argo CD — argocd-rbac-cm
    g, platform-admins, role:admin
    g, platform-viewers, role:readonly
  4. claim이 실제로 도착하는지 확인한다. 안 되면 사슬 어디가 끊겼는지 순서대로 본다 — AD 매퍼 → Keycloak scope → 토큰 → 도구 설정. 진단 절차는 Keycloak Scope와 Mapper와 문제 진단에서 확인한다.

문제왜 생기나어떻게 다루나
로그아웃이 한 번에 안 된다세션이 세 층(앱 쿠키 · 프록시 쿠키 · Keycloak SSO 세션)도구 로그아웃 링크를 Keycloak의 end-session으로 보낸다 (Keycloak Session과 Logout)
Keycloak이 죽으면 아무도 못 들어온다신원을 한 곳에 모은 대가Keycloak의 저장소와 가용성을 설계하고 깨진 유리 계정을 도구마다 하나 남긴다
자동화는 사람 로그인을 못 한다CI(Continuous Integration, 지속적 통합)·스크립트에는 브라우저가 없다서비스 계정 토큰(Argo CD)·API 키를 따로 두고 감사 로그를 남긴다
터미널 창
# ① Keycloak discovery가 열리나 (여기 안 되면 나머지는 전부 무의미)
curl -s https://sso.example.internal/realms/corp/.well-known/openid-configuration | head -20
# ② 프록시를 거쳤을 때 로그인으로 튕기나
curl -sI https://prometheus.example.internal/ | grep -i location
# → Keycloak /protocol/openid-connect/auth 로 가야 정상
# ③ 토큰에 groups가 실렸나 (브라우저 개발자도구에서 ID 토큰을 꺼내 디코드)
echo "<jwt-payload>" | base64 -d | jq '.groups, .preferred_username'
# ④ 백엔드가 사용자 헤더를 받나
kubectl -n observability logs deploy/oauth2-proxy | tail -20
# ⑤ 백엔드에 직접 붙을 수 있는지 (붙으면 헤더 위조가 가능하다는 뜻)
kubectl run probe --rm -it --image=curlimages/curl:8.9.1 --restart=Never -- \
curl -s -o /dev/null -w '%{http_code}\n' http://prometheus.observability.svc:9090/
  • 도구가 늘어날수록 관리자 계정이 늘어난다 — 퇴사자 차단 · 감사 · 비밀번호가 동시에 무너진다
  • 패턴은 셋: 앱 내장 OIDC / 앞단 oauth2-proxy / API 서버 OIDC. Prometheus·Alertmanager는 인증이 없으므로 반드시 프록시 뒤에 둔다
  • 프록시는 문을 잠글 뿐 안에서의 권한은 안 나눈다. 백엔드 직접 접근도 NetworkPolicy로 막는다
  • 진짜 일은 AD 그룹 → Keycloak claim → 도구 역할 세 고리를 잇는 것이다
  • 깨진 유리 계정을 도구마다 하나 남긴다 — IdP가 죽으면 전부 잠긴다