본문 바로가기
데브옵스 이야기/Kubernetes

kubernetes 인증/인가 - OIDC (AWS EKS)

by lakescript 2026. 4. 3.
728x90

 

앞서 작성한 글인 인증/인가에 대하여..OIDC(로컬환경실습)에서 인증/인가와 `OIDC`의 개념에 대해서는 충분히 알아보았습니다.

하지만 앞에서 진행한 실습이 local 환경이다보니 실무에서 사용하듯이 외부 `idP`를 통한 인증/인가가 불가능했는데요. 이번 포스팅에서는 `AWS EKS` 환경에서 `OIDC`를 설정하여 사용자(개발자)가 `kubectl` 명령어로 `EKS Cluster`에 접근할 때에 허용된 리소스에 접근할 수 있는 실습을 진행해보겠습니다.

 

실습 

실습 환경 다이어그램

전체적인 다이어그램은 위와 같습니다.

 

  1. `kubectl get nodes` 실행
  2. `kubelogin`이 `Dex`에 `Authorization Request` 전송 (`client_id`, `redirect_uri`, `scope`)
  3. `Dex`가 `GitHub OAuth`로 리다이렉트
  4. 개발자가 `GitHub` 로그인 & 권한 승인
  5. `Dex`가 `Authorization Code` + `org/team` 정보 수신
  6. `Dex`가 `ID Token(JWT)` 발급 -> `sub`, `email`, `groups=[org:team]` 포함
  7. `kubelogin`이 `Bearer Token`을 포함해 `EKS API Server`에 요청
  8. `EKS`가 `OIDC Discovery` (`/.well-known/openid-configuration`) 조회
  9. `JWKS`(공개키) 수신
  10. `Token` 서명 검증
  11. `groups=[org:team]` 정보를 `RBAC`에 전달
  12. `ClusterRoleBinding` 조회
  13. 권한 확인 완료
  14. 응답 반환

  15. `kubectl get nodes` 결과 출력

 

AWS VPC 및 EKS Cluster 생성

 

VPC는 서울 리전에 `192.168.0.0/16` 대역으로 생성하며, 2개 가용 영역(2a, 2c)에 Public과 Private 서브넷을 각각 구성하고, public subnet에 ALB를 배치했습니다.

 

EKS는 1.32 버전을 사용하였고, 워커노드는 `t3.medium` 2대로 구성하였습니다. 

 

kubeconfig 등록

aws eks update-kubeconfig --region ap-northeast-2 --name temp-eks

 

생성된 cluster 확인

kubectl get po -A

 

kubectl get node

 

`pod 조회` 및 `node 조회` 명령어를 통해 `EKS cluster`가 생성되었는지 확인합니다.

 

 

 AWS Load Balancer Controller 설치

`Dex`는 외부에서 `HTTPS`로 접근 가능해야 합니다. 그렇기 떄문에 `Load Balancer Controller`를 통해 `ALB Ingress`를 생성하여 `Dex`를 외부에 노출합니다.

 

Helm repo 추가

helm repo add eks https://aws.github.io/eks-charts
helm repo update

 

배포

helm upgrade --install aws-load-balancer-controller eks/aws-load-balancer-controller \
  --namespace kube-system \
  --values values.yaml \
  --wait \
  --timeout 3m

배포 확인

kubectl get po -n kube-system

 

 

 

 

Github OAuth 설정

Github Organization 생성 및 Team 생성

OIDC 실습을 위해 인증으로 활용할 Github Organization을 생성하고, 권한에 따른 인가 실습을 위해 `devops` 팀과 `backend` 팀을 생성합니다. (현재는 `devops`팀으로 설정했습니다.)

 

Github OAuth App 생성

github organization에서 `/settings/applications`에 접근한 뒤 OAuth APP을 생성합니다.

이때, `Authorization callback UR` 에 접근할 `Dex`의 `URL`을 입력합니다. 

 

 

생성이 완료되면 `ClientID`와 `ClientSecretID`를 얻을 수 있습니다. 

Github OAuth ClientID와 ClientSecretID도 다시 사용하니 저장해두세요!

 

Dex 배포

secret 및 pod 배포

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: dex-github-secret
  namespace: dex
type: Opaque
stringData:
  GITHUB_CLIENT_ID: # 실제 GITHUB OAuth CLIENT ID
  GITHUB_CLIENT_SECRET: #실제 GITHUB OAuth CLIENT SECRET ID

 

먼저 dex용 secret을 먼저 생성합니다. 여기에는 위에서 생성한 Github OAuth의 clientID와 ClientSecretID를 넣어줍니다. 

config:
  issuer: "https://dex.lakescript.net"

  storage:
    type: kubernetes
    config:
      inCluster: true

  web:
    http: 0.0.0.0:5556

  oauth2:
    skipApprovalScreen: true
    responseTypes:
      - code

  connectors:
  - type: github
    id: github
    name: GitHub
    config:
      clientID: $GITHUB_CLIENT_ID
      clientSecret: $GITHUB_CLIENT_SECRET
      redirectURI: "https://dex.lakescript.net/callback"
      orgs:
      - name: eks-oidc

  staticClients:
  - id: kubectl
    name: kubectl
    public: true
    redirectURIs:
    - http://localhost:8000
    - http://localhost:18000

  enablePasswordDB: false

# GitHub OAuth 자격증명은 Secret으로 주입
envFrom:
- secretRef:
    name: dex-github-secret

# ALB를 통해 외부 노출 → ClusterIP로 변경
service:
  type: ClusterIP
  ports:
    http:
      port: 5556

# ALB Ingress
ingress:
  enabled: true
  className: alb
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
    alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
  hosts:
  - host: dex.lakescript.net
    paths:
    - path: /
      pathType: Prefix

 

`Dex` 의 공식 helm을 참고하여 `values.yaml`을 작성하는데, 위에서 생성한 `secret`을 참고하여 `GithubOAuth`에서 발급받은  `ClientID`와 `SecretID`를 가져오게 됩니다. 

또한 `Dex issuer URL`은 반드시 `HTTPS`여야 합니다. `EKS`가 `OIDC Provider`를 등록할 때 `issuer URL`의 `TLS` 인증서를 검증하기 때문에, `ACM`에서 `dex.lakescript.net`에 대한 인증서를 발급받아 `ARN`에 지정합니다. (후에 `Route53`에서 해당 경로에 대한 `record`를 추가해주셔야 합니다.)

 

 

dex 배포 확인

kubectl get po,secret,ing -n dex

 

생성된 `pod`와 `secret`, `ingress`를 확인합니다.

 

 

브라우저에서 dex 접근

위와 같이 생성한 `ingress`으로 접근했을 때 정상적으로 접근이 되는 것을 확인하실 수 있습니다.

 

EKS 설정

EKS OIDC 설정

`Dex` 배포를 완료했고, 접근까지 확인했으면 이제 `EKS Cluster`에 `OIDC` 설정을 진행합니다.

variable "oidc_client_id" {
  type        = string
  default     = "kubectl"
  description = "Dex static client ID"
}

resource "aws_eks_identity_provider_config" "dex" {
  cluster_name = var.cluster_name

  oidc {
    identity_provider_config_name = "dex"
    issuer_url                    = "https://dex.lakescript.net"
    client_id                     = var.oidc_client_id

    username_claim  = "email"
    username_prefix = "oidc:"

    groups_claim  = "groups"
    groups_prefix = "oidc:"
  }
}

 

위의 `tf` 값을 통해 `Dex`용 `EKS OIDC Identity Provider`를 등록합니다.

 

그 후 `terraform apply` 하게 되면 위와 같이 정상적으로 `EKS`에 `OIDC` 설정이 완료된 것을 확인하실 수 있습니다.

 

RBAC 설정

이제 `Github`의 팀별로 권한을 다르게 설정하기 위해 `RBAC`를 설정하도록 하겠습니다.

 

# devops 팀
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: oidc-github-team-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: Group
  name: "oidc:eks-oidc:devops"
  apiGroup: rbac.authorization.k8s.io

 

`devops` 팀의 경우 `admin` 권한을 부여하는 `RBAC`를 생성합니다.

 

# backend 팀
apiVersion: v1
kind: Namespace
metadata:
  name: backend
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer
  namespace: backend
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["pods", "pods/log", "deployments", "services", "configmaps", "replicasets"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-team-binding
  namespace: backend
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: developer
subjects:
- kind: Group
  name: "oidc:eks-oidc:backend"
  apiGroup: rbac.authorization.k8s.io

 

`backend` 팀의 경우 `backend` 네임스페이스에서만 조회할 수 있는 권한만 부여하도록 `RBAC`를 생성합니다.

 

kubeconfig 생성

일반적으로 기본 `kubeconfig`는 `AWS IAM`을 통해 인증합니다. 하지만 OIDC로 인증하려면 `Dex issuer`와 `kubelogin`을 사용하는 별도의 `kubeconfig`가 필요합니다. 

 

CLUSTER_NAME="temp-eks"
REGION="ap-northeast-2"
OIDC_ISSUER="https://dex.lakescript.net"
CLIENT_ID="kubectl"
OUTPUT="oidc-kubeconfig.yaml"

 

먼저 원활하게 명령어를 실행할 수 있게 환경변수를 등록합니다.

 

# EKS 클러스터 정보 조회
EKS_API=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${REGION} \
  --query "cluster.endpoint" --output text)

EKS_CA=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${REGION} \
  --query "cluster.certificateAuthority.data" --output text)

 

`EKS API Server` 주소와 `CA` 인증서를 자동으로 조회하여 각각 `EKS_API`와 `EKS_CA` 변수에 저장합니다.

 

cat > oidc-kubeconfig.yaml << EOF
apiVersion: v1
kind: Config
clusters:
- name: ${CLUSTER_NAME}-oidc
  cluster:
    server: ${EKS_API}
    certificate-authority-data: ${EKS_CA}
users:
- name: oidc-user
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: kubectl
      args:
      - oidc-login
      - get-token
      - --oidc-issuer-url=${OIDC_ISSUER}
      - --oidc-client-id=${CLIENT_ID}
      - --oidc-extra-scope=groups
      - --oidc-extra-scope=email
contexts:
- name: ${CLUSTER_NAME}-oidc
  context:
    cluster: ${CLUSTER_NAME}-oidc
    user: oidc-user
current-context: ${CLUSTER_NAME}-oidc
EOF

 

그 후 지정한 변수들을 인증에 사용하기 위한 `OIDC Kubeconfig` 파일을 생성합니다.

인증/인가 확인

kubeconfig 환경변수 등록

export KUBECONFIG=~/.kube/config:oidc-kubeconfig.yaml

 

실습을 위해 임시로 `KUBECONFIG` 환경변수에 위에서 생성한 `oidc-kubeconfig`를 지정합니다.

 

kubectl config use-context temp-eks-oidc

 

이때 `aws eks update-kubeconfig`로 만든 기본 `context` 이름이 `temp-eks` 이기 때문에 원활한 실습을 위해 `temp-eks-oidc`로 `context`를 지정합니다.

 

kubectl 명령 실행

kubectl get po

 

이제 `kubectl` 명령어를 실행합니다.

위와 같이 인증을 하는  브라우저가 보이게 됩니다.

 

 time=2026-04-02T06:39:37.760Z level=INFO msg="login successful" connector_id=github username=lake preferred_username=leehosu email=hosu4549@gmail.com groups=[eks-oidc:devops[] request_id=ba6f508d-7b94-442d-bd08-a089b1cf48a3

 

인증을 완료하고 dex의 로그를 확인하면 위와 같이 확인할 수 있습니다.

groups_prefix("oidc:") + GitHub Org("eks-oidc") + Team("devops")
→ "oidc:eks-oidc:devops"

 

즉,  `devops` 팀으로 매핑된 것을 확인할 수 있습니다.

 

 

그 후 `kubectl` 을 통해 조회가 완료된 것을 알 수 있습니다.

 

 

추가적으로 pod를 생성해봤을 때 현재 devops 팀은 admin 권한이기 때문에 pod 생성도 완료되는 것을 확인할 수 있습니다.

 

팀 변경

이제 backend 팀으로 팀을 변경합니다. 

 

rm -rf ~/.kube/cache/oidc-login/

 

그 후 `kubelogin`이 토큰을 캐싱하고 있어서 기존 토큰에는 변경 전 그룹 정보가 담겨 있기에 위의 명령어를 통해 캐시를 지우고 다시 인증합니다.

 

kubectl get po

 

다시 pod 조회 명령을 실행해보겠습니다.

 

 

이번엔 인가 결과가 실패했다는 `Forbidden`에러가 발생한 것을 확인할 수 있습니다.

 

 time=2026-04-02T06:47:51.235Z level=INFO msg="login successful" connector_id=github username=lake preferred_username=leehosu email=hosu4549@gmail.com groups=[eks-oidc:backend[] request_id=391a5247-5563-4b53-9841-0c30071e19b1

 

그 후 `Dex` 로그를 확인해보면 아까와 달리 `backend` 팀으로 매핑된 것을 알 수 있습니다. 

 

kubectl get po -n backend

 

 

`backend` 네임스페이스 내의 pod 목록을 조회하는 명령어는 정상적으로 실행되는것을 알 수 있습니다.

 

kubectl run nginx --image=nginx -n backend

 

추가로 `backend` 네임스페이스에서도 `create` 되지 않는 것을 확인할 수 있습니다. (의도한대로!)

 

728x90