CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서     CNAP 클라우드 플랫폼 GitOps 대시보드를 직접 체험해보세요  —  실시간 CI/CD, 모니터링, 보안 관제를 한 곳에서    

OAuth2-Proxy

Keycloak OIDC와 연동하여 Kubernetes Ingress에 인증 레이어를 제공하는 리버스 프록시입니다.


1. 개요

OAuth2-Proxy는 Nginx Ingress의 auth-url annotation과 연동하여 업스트림 서비스에 대한 인증 게이트웨이 역할을 합니다. 트래픽을 직접 프록시하지 않고(upstreams = ["file:///dev/null"]) 인증 여부만 판단하는 reverse proxy 모드로 동작합니다.

OIDC 프로바이더로 Keycloak(keycloak.cnapcloud.com/realms/cnap)을 사용하며, 브라우저 기반 인증(로그인 리다이렉트)과 API 기반 인증(Bearer token 검증)을 모두 지원합니다. 세션은 외부 Redis HA(redis-ha-haproxy.database.svc)에 저장합니다.


2. 사전 요구사항

  • Keycloak: cnap realm에 oauth2-proxy client 생성 완료, client scopes에 groups 포함 (Keycloak 가이드)
  • Redis HA: database 네임스페이스에 배포 완료 (Redis HA 가이드)
  • TLS 인증서: cnapcloud.com-tls Secret이 security 네임스페이스에 복제되어 있어야 함 (reflector 설정 필요)
  • DNS 등록:
    • oauth2-proxy.cnapcloud.com
    • httpbin.cnapcloud.com
    • httpbin-api.cnapcloud.com

3. 디렉터리 구조

oauth2-proxy/
├── Makefile                                # pull / preview / apply / delete 자동화
└── kustomize/
    ├── base/
    │   └── helm/
    │       └── oauth2-proxy/               # Helm Chart v7.14.1 (make pull로 다운로드)
    └── overlays/
        └── dev/
            ├── kustomization.yaml          # namespace, generators, resources, images 지정
            ├── helm/
            │   ├── helm-chart.yaml         # HelmChartInflationGenerator 설정
            │   └── values.yaml             # OAuth2-Proxy 커스텀 값
            ├── patches/
            │   └── add-host-alias.yaml     # Keycloak DNS 해소용 hostAlias 패치 (선택)
            └── resources/
                └── httpbin/                # 인증 테스트용 httpbin 리소스
                    ├── deployment.yaml
                    ├── service.yaml
                    ├── serviceaccount.yaml
                    ├── ingress.yaml        # 브라우저 인증 테스트용 (auth-signin 포함)
                    └── ingress-api.yaml    # API 인증 테스트용 (Bearer token)

4. 사전 설정

4.1 Keycloak Client 설정

Keycloak Admin Console(https://keycloak.cnapcloud.com)에서 cnap realm에 아래 설정으로 Client를 생성합니다.

항목
Client IDoauth2-proxy
Client authenticationOn
Valid redirect URIshttps://oauth2-proxy.cnapcloud.com/oauth2/callback
Client scopesopenid, profile, email, groups

Client 생성 후 Credentials 탭에서 Client Secret을 확인합니다.

4.2 cookieSecret 생성

32바이트 랜덤 값을 생성합니다.

python3 -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'

5. 배포

5.1 Namespace 생성

make namespace DEPLOY_ENV=dev

5.2 Helm Chart 다운로드

make pull DEPLOY_ENV=dev

kustomize/base/helm/oauth2-proxy/ 디렉터리에 Chart v7.14.1이 다운로드됩니다.

5.3 배포 설정

values.yaml 주요 설정

kustomize/overlays/dev/helm/values.yaml의 핵심 항목을 환경에 맞게 수정합니다.

config:
  clientID: "oauth2-proxy"
  clientSecret: "<keycloak-client-secret>"
  cookieSecret: "<32바이트-base64-인코딩값>"

  configFile: |-
    # 쿠키 설정
    cookie_domains=".cnapcloud.com"
    cookie_expire="0s"
    cookie_refresh="3m"
    cookie_secure=false
    whitelist_domains=[".cnapcloud.com"]
    email_domains=["*"]
    insecure_oidc_allow_unverified_email=true

    # 세션 스토리지
    session_store_type="redis"

    # OIDC 프로바이더
    provider="oidc"
    provider_display_name="Keycloak"
    oidc_issuer_url="https://keycloak.cnapcloud.com/realms/cnap"
    redirect_url="https://oauth2-proxy.cnapcloud.com/oauth2/callback"
    backend_logout_url="https://keycloak.cnapcloud.com/realms/cnap/protocol/openid-connect/logout?client_id=oauth2-proxy&id_token_hint={id_token}"
    scope="openid profile email groups"

    # 리버스 프록시 모드 (트래픽 프록시 없이 인증만 처리)
    reverse_proxy=true
    upstreams=["file:///dev/null"]

    # 인증 헤더 전달
    set_xauthrequest=true
    set_authorization_header=true
    pass_access_token=true
    pass_authorization_header=true
    pass_user_headers=true

    # OIDC 클레임 매핑
    oidc_email_claim="email"
    oidc_groups_claim="groups"

    # Bearer token 검증 위임 (API 인증용)
    skip_jwt_bearer_tokens=true
    oidc_extra_audiences=["account"]

    # 인증 제외 경로
    skip_auth_routes=["GET=^/api/token", "^/actuator", "^/status"]

sessionStorage:
  type: redis
  redis:
    password: "<redis-password>"
    clientType: standalone
    standalone:
      connectionUrl: redis://redis-ha-haproxy.database.svc

redis:
  enabled: false   # 외부 Redis HA 사용, 내장 Redis 비활성화

hostAlias 패치 (선택)

내부 Ingress Controller를 통해 Keycloak에 접근하는 경우 patches/add-host-alias.yaml에 ingress-nginx-controller의 ClusterIP를 지정합니다.

# ingress-nginx-controller ClusterIP 확인
kubectl get svc ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.clusterIP}'
# patches/add-host-alias.yaml
- op: add
  path: /spec/template/spec/hostAliases
  value:
    - ip: "<ingress-nginx-controller-cluster-ip>"
      hostnames:
        - "keycloak.cnapcloud.com"

패치를 적용하려면 kustomization.yaml에 추가합니다.

patches:
  - target:
      kind: Deployment
      name: oauth2-proxy
    path: patches/add-host-alias.yaml

5.4 배포 실행

make preview DEPLOY_ENV=dev
make apply DEPLOY_ENV=dev

6. 설치 후 검증

6.1 health check

OAuth2-Proxy의 /ping 엔드포인트로 정상 동작을 확인합니다.

curl -s https://oauth2-proxy.cnapcloud.com/ping

예상 결과

OK

6.2 브라우저 기반 인증 테스트

httpbin Ingress는 auth-signinauth-url annotation으로 OAuth2-Proxy와 연동됩니다.

# resources/httpbin/ingress.yaml
annotations:
  nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.security.svc.cluster.local/oauth2/auth
  nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy.cnapcloud.com/oauth2/start
  nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Access-Token

브라우저에서 https://httpbin.cnapcloud.com에 접근하여 아래를 확인합니다.

  • Keycloak 로그인 페이지로 리다이렉트
  • 인증 성공 후 https://httpbin.cnapcloud.com으로 복귀
  • 세션 쿠키(_oauth2_proxy) 생성

6.3 API 기반 인증 테스트

httpbin-api Ingress는 auth-url만 설정되어 Bearer token을 직접 검증합니다.

# resources/httpbin/ingress-api.yaml
annotations:
  nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.security.svc.cluster.local/oauth2/auth

1. Keycloak에서 Access Token 발급

export ACCESS_TOKEN=$(curl -sk -X POST \
  "https://keycloak.cnapcloud.com/realms/cnap/protocol/openid-connect/token" \
  -d grant_type=password \
  -d client_id=oauth2-proxy \
  -d client_secret=<keycloak-client-secret> \
  -d username=<user> \
  -d password=<password> \
  -d scope='openid profile email' \
  | jq -r .access_token)

2. Bearer token으로 API 호출

curl -sk -H "Authorization: Bearer ${ACCESS_TOKEN}" \
     -H "accept: application/json" \
     https://httpbin-api.cnapcloud.com/ip

예상 결과

{
  "origin": "10.x.x.x"
}

7. Troubleshooting

7.1 Pod 시작 실패

증상: Pod가 CrashLoopBackOff 또는 Error 상태

원인: clientSecret, cookieSecret 누락 또는 oidc_issuer_url 접근 불가

oidc_issuer_url.well-known/openid-configuration 엔드포인트 응답을 확인합니다.

kubectl exec -n security deploy/oauth2-proxy -- \
  wget -qO- https://keycloak.cnapcloud.com/realms/cnap/.well-known/openid-configuration \
  | jq .issuer

내부 DNS로 Keycloak에 접근 불가능한 경우 hostAlias 패치를 적용합니다.

7.2 인증 리다이렉트 루프

증상: 로그인 성공 후 계속 Keycloak으로 리다이렉트

원인: cookie_domains 설정 불일치 또는 cookieSecret 길이 오류

cookieSecret이 정확히 32바이트 base64url 인코딩 값인지 확인합니다.

echo -n "<cookieSecret>" | base64 -d | wc -c

출력값이 32여야 합니다.

7.3 Bearer token 인증 실패 (403)

증상: Authorization: Bearer <token>으로 API 호출 시 403 응답

원인: skip_jwt_bearer_tokens=true 미설정 또는 oidc_extra_audiences 미포함

values.yaml에 아래 설정이 있는지 확인합니다.

skip_jwt_bearer_tokens=true
oidc_extra_audiences=["account"]

Keycloak token의 aud 클레임에 account가 포함되어 있어야 합니다.

echo "${ACCESS_TOKEN}" | cut -d. -f2 | base64 -d 2>/dev/null | jq .aud

7.4 대용량 헤더로 인한 502

증상: 인증 후 upstream 호출 시 502 응답

원인: JWT 토큰 크기가 Nginx 프록시 버퍼 한도 초과

values.yaml의 Ingress annotation과 httpbin Ingress annotation에 버퍼 설정이 적용되어 있는지 확인합니다.

nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"