OAuth2 Proxy는 Keycloak, Google, GitHub 등 외부 OAuth2 또는 OpenID Connect 제공자를 통해 사용자 인증을 수행하고, 인증된 사용자만 웹 애플리케이션이나 API에 접근할 수 있도록 보호해주는 인증 프록시입니다.
주로 NGINX, Istio, Envoy, Kong과 같은 리버스 프록시나 Ingress Controller와 함께 사용되며, 애플리케이션이 별도의 인증 로직을 구현하지 않아도 외부에서 손쉽게 인증 기능을 구성할 수 있게 해줍니다.
또한 OAuth2 Proxy는 인증된 사용자 정보를 공통된 세션 또는 쿠키 기반으로 처리하기 때문에, 동일한 OAuth2 Proxy 설정을 사용하는 애플리케이션 간에는 인증 세션이 자동으로 공유됩니다. 이를 통해 사용자 입장에서는 한 번의 로그인만으로 여러 서비스에 접근할 수 있는 SSO 환경을 구현할 수 있습니다.
이 글에서는 Kubernetes 환경에서 OAuth2 Proxy를 하나만 구성하여 모든 서비스가 이를 공유해 사용자 인증을 수행하는 구조를 설명합니다. OAuth2 Proxy와 Keycloak 간의 연동을 사전에 설정하고, 각 애플리케이션의 인그레스(Ingress)에서 OAuth2 Proxy를 통해 인증이 적용되도록 구성합니다.
※ 애플리케이션별로 OAuth2 Proxy를 구성하는 방법은 이 글의 마자막에 있는 참고자료를 보세요.
1. 설치 환경
OAuth2 Proxy 설치를 위해 사전에 준비되어 있어야 하는 환경 구성 정보입니다.
- Kubernetes v1.30
- Keycloak 25.0.4
- Kustomize v1.30.0
- Helm v3.17.1
이 환경은 arm64(aarch64) 아키텍처를 기준으로 구성되어 있습니다.
Keycloak 설치 및 GitOps 배포 방식은 아래 블로그를 참고하세요.
2. OAuth2 Proxy 설치
Keycloak Admin Console에서 cnap realm으로 이동하여 OAuth2 Proxy를 위한 oauth2-proxy 클라이언트를 생성합니다.
- Client ID: oauth2-proxy
- Client Authentication: On
- Authentication Flow:
Standard Flow On,
Direct Access Grants On - Home URL: https://oauth2-proxy.cnap.local
- Redirect URI: https://oauth2-proxy.cnap.dev/oauth2/callback
- Valid post logout redirect URIs: +
- Web Origins: https://oauth2-proxy.cnap.dev
Kubernetes 환경에 OAuth2 Proxy를 배포하기 위해, GitHub의 gitops-demo 저장소를 클론합니다. 여기서 사용된 OAuth2 Proxy Helm Chart, OAuth2 Proxy 버전은 다음과 같습니다.
- OAuth2 Proxy Helm Chart 7.14.1
- OAuth2 Proxy v7.10.0
git clone https://github.com/cnapcloud/gitops-demo.git
cd gitops-demo/oauth2-proxy
OAuth2 Proxy와 Keycloak과 연동하기 위해, 앞서 생성한 oauth2-proxy 클라이언트 정보를 사용하여 Dev Overlay의 Helm values 파일(dev/helm/values.yaml)에서 다음 속성값을 찾아 설정합니다.
config:
# oauth2_proxy secret
clientID: "oauth2-proxy"
clientSecret: "p9J8xZTsBXGu4gex4V63PqMiF7fDCiym"
# oauth2_proxy.cfg
configFile: |-
# cookie
cookie_domains: ".cnap.dev"
# only users with this domain will be let in
whitelist_domains=[".cnap.dev"]
# redirect_url, logout_url
redirect_url: "http://oauth2-proxy.cnap.dev/oauth2/callback"
backend_logout_url: "https://keycloak.cnap.dev/realms/cnap/protocol/openid-connect/logout?client_id=oauth2-proxy&id_token_hint={id_token}"
# login_url, redeem_url, and oidc_jwks_url
oidc_issuer_url: "https://keycloak.cnap.dev/realms/cnap"
provider: "oidc"
provider_display_name: "Keycloak"
# access token scope
scope="openid profile email"
OAuth2 Proxy에서 Keycloak에 접근하기 위해 Dev Overlay의 patches/add-host-alias.yaml 파일에 Keycloak 도메인과 해당 호스트의 IP를 추가해야 합니다. 이 설정은 OAuth2 Proxy Pod의 /etc/hosts 파일에 해당 도메인을 수동으로 매핑하기 위해 사용됩니다. DNS 서버에 Keycloak 도메인이 등록이 되어 있는 경우, 이 설정은 건너뛰어도 됩니다.
- op: add
path: /spec/template/spec/hostAliases
value:
- ip: "192.168.64.2"
hostnames:
- "keycloak.cnap.dev"
이제 security namespace에 Oauth2 Proxy를 배포합니다.
make namespace
make oauth2-proxy
kubectl 명령어로 OAuth2 Proxy와 관련 리소스가 정상적으로 배포되었는지 확인하고 /etc/hosts 파일에 인그레스 도메인을 등록합니다.
# 설치 확인
kubectl -n gateway get deploy,pod,ingress
# /etc/hosts
127.0.0.1 oauth2-proxy.cnap.dev
127.0.0.1 auth-httpbin.cnap.dev auth-httpbin-api.cnap.dev
이 설치에는 OAuth2 Proxy를 사용하여 HttpBin 애플리케이션에 대한 인증이 인그레스에 구성되어 있습니다
4. 인증 구성
OAuth2 Proxy를 사용하여 Ingress에 인증 기능을 손쉽게 추가할 수 있습니다. 주로 NGINX Ingress Controller와 함께 사용되며, 특정 리소스에 접근하기 전에 사용자 인증을 요구하도록 구성할 수 있습니다. 이때 Ingress 리소스에 nginx.ingress.kubernetes.io/* 형태의 애노테이션을 추가하여 인증 흐름을 제어합니다.
인그레스 어노테이션
- nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy.cnap.dev/oauth2/start
인증되지 않은 사용자가 접근할 경우 리디렉션할 URL을 지정합니다.
일반적으로 OAuth2 Proxy의 로그인 시작 URL(/oauth2/start)을 지정하여, 인증 프로세스를 시작하도록 유도합니다.
이 글의 구성에서는 https://oauth2-proxy.cnap.dev/oauth2/start로 리디렉션되어 Keycloak 로그인 페이지로 안내됩니다. - nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.security.svc.cluster.local/oauth2/auth
인증 여부를 확인하기 위한 엔드포인트를 지정합니다.
NGINX는 이 URL을 통해 현재 요청이 인증되었는지를 판단하며, 2xx 응답이면 요청을 통과시키고, 그렇지 않으면 auth-signin에서 지정한 경로로 리디렉션합니다.
내부 DNS로 접근 가능한 oauth2-proxy 서비스의 /oauth2/auth 경로를 지정하는 것이 일반적입니다. - nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email
인증이 완료된 후, NGINX가 oauth2-proxy로부터 전달받은 사용자 정보를 백엔드 애플리케이션에 전달할지 여부를 결정합니다. - nginx.ingress.kubernetes.io/proxy-buffer-size: “32k”
nginx.ingress.kubernetes.io/proxy-buffers-number: “8”
nginx.ingress.kubernetes.io/proxy-buffering: “on”
쿠키 기반 세션 저장소는 쿠키 크기 증가로 인해 발생하는 NGINX 버퍼 부족 문제를 해결하기 위해 사용합니다. 하지만 본 설치에서는 세션 저장소를 Redis로 변경하여 쿠키 용량 문제를 근본적으로 해결했으므로, 이 설정은 생략 가능합니다. - nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $access_token $upstream_http_x_auth_request_access_token;
proxy_set_header Authorization “Bearer $access_token”;
브라우저 기반 인증에서 업스트림으로 Authorization 헤더를 보냅니다.
브라우저 기반 인증 구성
OAuth2 Proxy는 Ingress에 인증 흐름을 추가함으로써 웹 애플리케이션에 대한 보호를 간편하게 설정할 수 있습니다. 사용자가 보호된 리소스(https://auth-httpbin.cnap.dev)에 접근하면, NGINX Ingress는 먼저 auth-url을 통해 인증 여부를 확인하고, 인증되지 않은 경우 auth-signin에서 지정한 경로(/oauth2/start)로 리디렉션합니다. 이 과정에서 사용자는 Keycloak 로그인 화면으로 이동하게 되며, 로그인이 성공하면 다시 원래 요청한 리소스로 리디렉션되어 접근이 허용됩니다.
아래는 HttpBin 애플리케이션에 대한 브라우저 기반으 인증을 위한 설정입니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
annotations:
nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
nginx.ingress.kubernetes.io/proxy-buffering: "on"
nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy.cnap.dev/oauth2/start
nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.security.svc.cluster.local/oauth2/auth
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Access-Token
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $access_token $upstream_http_x_auth_request_access_token;
proxy_set_header Authorization "Bearer $access_token";
rules:
- host: auth-httpbin.cnap.dev
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: httpbin
port:
number: 80
브라우저에서 https://auth-httpbin.cnap.dev에 접속하면 Keycloak 로그인 페이지로 리디렉션됩니다. 인증이 완료되면 OAuth2 Proxy가 사용자 정보 헤더와 토큰을 포함해 요청을 auth-httpbin.cnap.dev로 전달합니다.
API 인증 구성 (Access Token 전달)
OAuth2 Proxy는 브라우저 기반 인증뿐만 아니라 API 요청에 대해서도 인증을 적용할 수 있습니다. 이 경우, 브라우저 기반 인증과는 달리 nginx.ingress.kubernetes.io/auth-url 애노테이션은 사용되지 않습니다. 대신, 인증된 사용자에게 발급된 Access Token 또는 ID Token을 백엔드 애플리케이션(upstream)으로 전달하여, API 수준에서 세밀한 권한 제어가 가능해집니다. 이를 위해 NGINX Ingress에서는 configuration-snippet과 auth-snippet 애노테이션을 활용하여, 인증 토큰을 헤더에 포함시키는 방식으로 설정을 확장할 수 있습니다.
아래는 HttpBin 애플리케이션에 대한 API 접근 제어를 위한 Ingress 설정 예시입니다. OAuth2 Proxy를 통해 API 요청에 포함된 인증 토큰을 검증하고, 인증이 성공하면 해당 토큰을 백엔드 API 호출 시 헤더에 포함하여 전달합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin-api
annotations:
nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.security.svc.cluster.local/oauth2/auth
spec:
rules:
- host: auth-httpbin-api.cnap.dev
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: httpbin
port:
number: 80
OAuth2 Proxy가 적용된 Ingress를 테스트하기 위해, curl 명령어로 Access Token을 Authorization 헤더에 포함하여 HttpBin API에 요청을 보냅니다.
# 토큰 발급
export KEYCLOAK_URL=https://keycloak.cnap.dev
export CLIENT=oauth2-proxy
export CLIENT_SECRET=h2QxtQFCnOjuRqqfwvkbBPZcsnJyyJHn
export USER=admin
export PASSWORD=password
export ACCESS_TOKEN=$(curl -sk -X POST "${KEYCLOAK_URL}/realms/${REALM}/protocol/openid-connect/token" \
-d grant_type=password \
-d client_id=${CLIENT} \
-d client_secret=${CLIENT_SECRET} \
-d request_token_type=urn:ietf:params:oauth:token_type:access_token \
-d username=${USER} \
-d password=${PASSWORD} \
-d scope='openid profile email' \
| jq -r .access_token)
echo "ACCESS_TOKEN: ${ACCESS_TOKEN}"
# httpbin 서비스 조회
curl -sk -H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "accept: application/json" \
-X GET "https://auth-httpbin-api.cnap.dev/ip"
API 기반으로 인증을 진행할 경우는 단순히 Access Token만 Authorization 헤더에 담아 보낼 수 있습니다. 반면 브라우저 기반에서는, 앞서 설정한 것처럼 NGINX가 auth-response-headers를 통해 X-Auth-Request-User, X-Auth-Request-Email 같은 사용자 정보 헤더도 백엔드에 함께 전달할 수 있습니다.
이처럼 OAuth2 Proxy로 인증을 하고 사용자 정보와 Access Token을 헤더로 백엔드에 전달되면, 백엔드는 이를 정보를 활용해 사용자 확인, 인증 상태 확인, 권한 권리와 같은 보다 정교한 후속 처리를 수행할 수 있습니다.
인증 제외 경로 구성
OAuth2 Proxy는 특정 경로에 대해 인증을 제외할 수 있는 방법으로 skip_auth_routes 옵션을 제공하고 있습니다. 이 설정은 Helm Chart의 values.yaml에 oauth2_proxy.cfg configFile에 추가할 수 있습니다.
skip_auth_routes=[
"GET=^/public",
"^/actuator",
"^/status"
]
모든 서비스가 하나의 OAuth2 Proxy 인스턴스를 공유하는 구성에서는 이 설정이 전체 서비스에 동일하게 적용됩니다.
서비스별로 개별적으로 인증 제외 경로를 지정하기 위해서 인그레스에 auth-snippet 어노테이션을 활용할 수 있습니다.
nginx.ingress.kubernetes.io/auth-snippet: |
if ( $request_uri = "/public" ) {
return 200;
}
5. 마무리
이번 글에서는 OAuth2 Proxy를 Kubernetes 환경에 배포하고 Keycloak과 연동하여,
브라우저 및 API 요청에 대한 인증을 Ingress로 구성하는 방법을 살펴보았습니다.
- 브라우저 기반 인증: Keycloak 로그인 페이지로 리디렉션하여 사용자 인증 수행
- API 인증: 인증 토큰을 검증하여 요청 처리
OAuth2 Proxy는 애플리케이션에 별도의 인증 로직을 구현하지 않고 IdP와 연동하여
간편하게 인증 기능을 추가할 수 있는 도구입니다.
운영 환경에서 인증 방식을 표준화하고 보안을 강화하기 위해 널리 사용됩니다.
참고자료
Kubernetes — Ingress External Authentication with OAuth2 Proxy and Keycloak