지난 글에서는 k8s의 인증(Authentication)과 인가(Authorization)에 대해 개념을 공부하고 실습을 진행했습니다.
하지만 실제 업무에서는 개발자가 `kubectl`로 pod에 접근하거나 Devops 엔지니어가 `cluster` 상태를 확인한다거나, 여러 `cluster`를 관리한다거나 처럼 사람이 k8s에 접근하는 경우가 많습니다.
이때, k8s는 OIDC(OpenID Connect)를 이용하여 외부 인증 시스템과 연동할 수 있습니다.
OIDC(OpenID Connect)

OIDC는 OAuth 2.0 기반의 인증 프로토콜 입니다.
인증 흐름
User
↓
IdP Login
↓
JWT Token 발급
↓
kubectl
↓
Authorization: Bearer <token>
↓
kube-apiserver
↓
Authentication (OIDC)
↓
Authorization (RBAC)
↓
etcd
여기서 중요한 점은 k8s가 직접 로그인을 처리하지 않는다는 것입니다.
로그인 → IdP
권한 → Kubernetes
즉, k8s는 IdP를 통해 발급된 JWT 토큰을 검증하는 역할만 합니다.
토큰 정보(claim)
{
"iss": "https://auth.example.com",
"sub": "user1",
"email": "user1@example.com",
"groups": ["dev"]
}
위와 같은 형식으로 token이 발행되는데, 이를 `claim`이라고 합니다.
OIDC 도구
대표적인 OIDC 도구들은 아래와 같습니다.
| 도구 | 설명 |
| Dex | Kubernetes 환경에서 많이 사용하는 경량 OIDC Provider |
| Keycloak | RedHat에서 개발한 오픈소스 IdP (SSO, MFA 등 다양한 기능 제공) |
| Okta | SaaS 형태의 상용 IdP 서비스 |
| Auth0 | 개발 친화적인 클라우드 기반 인증 서비스 |
| Google / Azure AD | 클라우드 계정 기반 OIDC 인증 제공 |
실습 - Dex를 활용한 OIDC 인증
Kind로 Cluster 생성
kind 설정 파일
cat > kind-oidc.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
image: kindest/node:v1.34.0
- role: worker
image: kindest/node:v1.34.0
EOF
kind cluster 생성
kind create cluster --config kind-oidc.yaml --name oidc-study
node 확인
kubectl get nodes

plugin 설치
OIDC 로그인은 `kubectl` 기본 기능이 아니라 외부 인증 플러그인 방식으로 동작하기 때문에 `external credential plugin` 이 필요합니다.
인증 방식
k8s에서 `kubectl`이 `api server` 에 요청을 할 때는 반드시 인증 정보가 필요합니다.
| 인증 방식 | 동작 |
| client certificate | kubeconfig에 인증서 저장 |
| serviceaccount token | token 사용 |
| static token | kubeconfig token |
| OIDC | external credential plugin 필요 |
OIDC의 경우 사용자가 IdP에 로그인해야 JWT 토큰을 발급받을 수 있기 때문에 `kubectl` 내부에서 직접 처리하지 않습니다. 그렇기 때문에 `external credential plugin` 이 필요합니다.
oidc-login 플러그인 다운로드
OIDC 로그인은 일반적으로 `웹 로그인`, `브라우저 redirect`, `token refresh`등의 기능이 필요합니다. 이 기능들은 k8s core 범위를 벗어나기 때문에 해당 plugin을 설치해야 합니다.
curl -LO https://github.com/int128/kubelogin/releases/latest/download/kubelogin_linux_arm64.zip
unzip kubelogin_darwin_arm64.zip
kubectl plugin 형태로 설치
mv kubelogin kubectl-oidc_login
kubectl plugin으로 인식되도록 이름을 바꿉니다.
sudo mv kubectl-oidc_login /usr/local/bin/
chmod +x /usr/local/bin/kubectl-oidc_login
그 후 PATH에 있는 위치로 이동합니다.
plugin 확인
kubectl plugin list

Dex 생성
namespace 생성
kubectl create namespace dex
Dex config 파일 생성
cat > dex-config.yaml << 'EOF'
issuer: http://127.0.0.1:5556
storage:
type: memory
web:
http: 0.0.0.0:5556
enablePasswordDB: true
staticClients:
- id: kubernetes
redirectURIs:
- 'http://localhost:8000'
name: 'Kubernetes'
secret: kubernetes-secret
staticPasswords:
- email: "admin@example.com"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "123"
EOF
위로 생성된 dex의 인증 정보는 아래와 같습니다.
ID : admin@example.com
PW : password
Dex configmap 생성
kubectl create configmap dex-config \
--from-file=config.yaml=dex-config.yaml \
-n dex
Dex Pod 생성
cat > dex-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: dex
namespace: dex
spec:
replicas: 1
selector:
matchLabels:
app: dex
template:
metadata:
labels:
app: dex
spec:
containers:
- name: dex
image: ghcr.io/dexidp/dex:v2.37.0
command: ["dex","serve","/etc/dex/config.yaml"]
ports:
- containerPort: 5556
volumeMounts:
- name: config
mountPath: /etc/dex
volumes:
- name: config
configMap:
name: dex-config
EOF
Dex 배포
kubectl apply -f dex-deployment.yaml

생성된 Dex Pod 확인
kubectl get pods -n dex

Dex Service 생성
cat > dex-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: dex
namespace: dex
spec:
ports:
- port: 5556
targetPort: 5556
selector:
app: dex
EOF
kubectl apply -f dex-service.yaml

fortforwad 설정
--oidc-issuer-url=http://127.0.0.1:5556
dex를 활용하여 접속하기 위해선 위와 같이 주소를 적어야 하는데, cluster 내부 DNS이기 때문에 Dex를 portforwading해서 접속해야 합니다.
kubectl -n dex port-forward svc/dex 5556:5556
OIDC 로그인
kubectl oidc-login get-token \
--oidc-issuer-url=http://127.0.0.1:5556 \
--oidc-client-id=kubernetes \
--oidc-client-secret=kubernetes-secret
위 명령어는 Dex 서버에 로그인해서 api server 인증에 사용할 jwt 토큰을 발급받는 명령입니다.
`--oidc-issuer-url=http://127.0.0.1:5556`
위 옵션은 로그인할 OIDC 서버 주소를 의미합니다. 여기서는 Dex의 port를 명시하여 실행합니다.
`--oidc-client-id=kubernetes`
OIDC에서는 사용자가 로그인할 때 어떤 애플리케이션이 인증을 요청했는지 구분해서 명시해야 합니다.
staticClients:
- id: kubernetes
redirectURIs:
- 'http://localhost:8000'
name: 'Kubernetes'
secret: kubernetes-secret
위에서 작성한 dex conifg 파일을 보면 위와 같이 `staticClients` 를 통해 클라이언트를 등록해놨었고, 옵션으로 해당 클라이언트ID를 명시했습니다.
`--oidc-client-secret=kubernetes-secret`
OIDC에서는 단순히 clientID만 전달하면 누구든지 그 ID를 사용할 수 있기 때문에 보안 문제가 발생할 수 있습니다. 그렇기 때문에 client secret을 함께 사용하여 인증을 진행합니다.
마찬가지로 위에서 설정했던 Dex Config 파일에 `secret: kubernetes-secret` 부분을 적어놨고, 옵션으로 해당 값을 입력합니다.

위와 같이 로그인 화면이 나왔다면, 위에서 생성했던 계정 정보를 통해 로그인을 합니다.

위와 같은 화면이 보일텐데, OIDC 허용에 관한 화면입니다.
즉, `kubectl oidc-login get-token`이 Dex에 로그인한 뒤 이 클라이언트(kubernetes)가 토큰을 받아도 되는지 승인받는 단계입니다.
1. Dex가 인증 코드를 발급
2. 브라우저가 http://localhost:8000 쪽으로 리다이렉트
3. kubectl oidc-login이 그 코드를 받아 토큰으로 교환
4. 터미널에 토큰이 출력되거나, 다음 단계로 진행
요약하자면, 위와 같은 흐름으로 인증이 진행됩니다.

인증이 완료되면 위와같이 `Authenticated` 화면이 보이게 됩니다.

그리고 다시 터미널로 돌아와보면 `JWT` 이 발급된것을 확인할 수 있습니다.
인가
이제 발급받은 kubectl 대신 직접 token을 사용하여 api server를 호출해보겠습니다.
토큰을 변수에 저장
TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6I...'
api server 주소 변수 저장
kubectl cluster-info

APISERVER=https://127.0.0.1:56086
CA 인증서 경로 변수 저장
CACERT=$(mktemp)
kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
| base64 -d > $CACERT
API Server는 HTTPS이므로 CA 인증서가 필요합니다.
Pod 목록 조회
curl -s -o /dev/null -w "%{http_code}\n" \
-H "Authorization: Bearer $TOKEN" \
--cacert "$CACERT" \
"$APISERVER/api/v1/namespaces/default/pods"

위와 같이 api server에 dex로 발급받은 토큰과 인증 파일로 접근하려고 하면 `401`이 발생하는 것을 알 수 있습니다.
401이 발생한 이유는 로컬 환경에서 `Dex OIDC issuer`가 `HTTPS`가 아닌 `HTTP`로 노출되어 `kube-apiserve` r가 토큰을 검증하지 못했기 때문입니다. 이를 해결하려면 `OIDC issuer`가 `HTTPS`로 제공되어야 하는데, 로컬 환경에서는 일반적으로 구성하기 어렵습니다. (`ngrok`과 같은 터널링 도구를 사용하면 임시로 HTTPS 환경을 만들 수 있습니다.)
하지만 실제 운영 환경에서는 OIDC 제공자(Dex, Keycloak, Okta 등)가 `HTTPS`로 노출된 외부 `IdP`로 구성되기 때문에 이러한 문제는 일반적으로 발생하지 않습니다.
다음 포스팅에서는 실제로 EKS 환경을 구축해보고, OIDC 기반 IdP 인증을 통해 Kubernetes 클러스터에 접근한 뒤 RBAC을 이용한 인가까지 전체 흐름을 실습해보겠습니다.
'데브옵스 이야기 > Kubernetes' 카테고리의 다른 글
| 쿠버네티스 인증인가에 대하여... (0) | 2026.03.06 |
|---|---|
| 쿠버네티스 장애 원인 분석을 Slack으로 받아보기 (feat. K8sGPT) (0) | 2026.01.10 |
| stakater/Reloader 사용시 configmap, secret 변경할 시에 감지 안되는 이슈 (0) | 2025.12.22 |
| K8s의 Memory? (RAM, Storage) (0) | 2024.08.02 |