ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kubernetes SSO with OIDC and Keycloak
    IT 2021. 8. 20. 15:16
    반응형

    Kubernetes Cluster의 사이즈가 일정 수준 이상을 넘어서면 여러명의 개발자와 운영자가 같은 Cluster에 접근하게 된다. 여러명의 개발자/관리자 접근이 필요한 Cluster를 안전하게 운영하기 위해서는 복잡한 ACL 설정과 로깅은 필수이며 이는 운영에 많은 노력을 필요로하게 된다. 레거시 시스템에서는 이러한 문제를 해결하기 위해서 LDAP을 연동하여 문제를 해결해 왔다. 하지만 LDAP은 인증시마다 매번 아이디와 패스워드 입력이 필요하며 그룹을 이용한 정책을 기본으로 하기 때문에 최신 OIDC에서 제공하는 RBAC(Role Base Access Control)은 사용이 불가능하다. 이 글에서는 Keycloak의 OIDC 기능을 이용하여 Kubernetes에 접근하는 개발자 또는 운영자에게 LDAP을 기본으로 ROLE을 분리 운영하는 방법을 설명하고자 한다.

     

    Prerequists 

    - Keycloak >= 14.0.0 (User Federation with LDAP)

    - Kubernetes Cluster

    - LDAP

     

    Keycloak Application 생성

    Keycloak에서 kubernetes application을 생성한다. application 생성은 configure -> clients에서 생성 가능하다. 여기에서는 편의성을 위해 application 이름을 ai-cluster로 설정하였다.

    client 생성이 완료되면 Access Type을 Confidential 그리고 Access Token Lifespan을 1시간 이상으로 설정하도록 한다.(Test를 위한 설정이다. 운영 환경에서는 적절한 설정값을 검토하도록 한다.)

     

    Keycloak Role 생성

    application이 생성되면 clients -> ai-cluster -> Add Role 버튼을 클릭하여 Role을 생성한다. Role이름은 devops로 지정하였으며 keycloak에서의 role은 어떠한 역할을 수행한다기 보다는 Client에서의 ACL 구분을 위한 목적으로 사용되므로 여기서는 특별한 이벤트가 발생하지 않는다.

     

    LDAP Group 매핑

    사용자에 대한 인증은 최종적으로 LDAP에서 이루어지므로 LDAP의 그룹과 ROLE을 매핑하도록 한다. 그룹과 Role 매핑은 Groups -> {group} -> edit -> Role Mappings -> Client Roles 에서 매핑 가능하다. Application 내에서 Role을 생성하였으므로 ai-cluster를 선택하고 devops role을 assign 하도록 한다.

     

    Kuberntes API Server OIDC 적용

    OIDC를 이용하여 kubernetes에 로그인하기 위해서는 API Server에서 OIDC 서버를 인지할 수 있도록 관련 속성을 입력해주어야 한다. kubernetes master 서버의 api server 설정 파일에 수정해주어야하며 api server는 daemon 으로 실행되므로 파일 수정 후 자동으로 자동으로 재시작하게 된다. 입력해주어야 하는 속성은 5가지로 각각의 Sample은 아래와 같다.

    - Path : /etc/kubernetes/manifests/kube-apiserver.yaml

    - Value

      spec:
        containers:
        - command:
          - kube-apiserver

        - --oidc-client-id=ai-cluster
        - --oidc-groups-claim=groups
        - --oidc-issuer-url=https://keycloak.homelab.com/auth/realms/homelab
        - --oidc-username-claim=preferred_username
        - --oidc-username-prefix=-

     

     

    Keycloak Token Key 받아 오기

    oidc를 kubernetes에 연동하기 위해서는 먼저 id_token을 받아와야 한다. 아래 REST API sample을 이용하여 id_token을 받아오도록 하자.

    $ curl -s -X POST https://keycloak.homelab.com/auth/realms/homelab/protocol/openid-connect/token \
      --header 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode 'grant_type=password' \
      --data-urlencode 'client_id=ai-cluster' \
      --data-urlencode 'client_secret=8cbe5231-9ff7-4b60-9b04-4863b080cf8a'  \
      --data-urlencode 'username=user' \
      --data-urlencode 'password=ChangeMe' \
      --data-urlencode 'scope=openid' \
      --data-urlencode 'response_type=id_token' | jq .id_token
    "eyJhbGciOiJSUzI1NiIsInR5c....."
    $

    받아온 token에 대한 세부 정보는 jwt.io 사이트에서 확인 가능하다.

     

    credential 설정

    받아온 token을 값을 kubectl에서 사용하는 config 파일에 설정하도록 하자. config 파일은 $KUBECONFIG 환경 변수 또는 $HOME/.kube/config 위치에 존재하면 직접적인 수정보다는 명령어를 사용하여 수정하는걸 추천한다.

    $ kubectl config get-users
    NAME
    kubernetes-admin
    $ kubectl config set-credentials "test" \
      --auth-provider=oidc \
      --auth-provider-arg=idp-issuer-url="https://keycloak.homelab.com/auth/realms/homelab" \
      --auth-provider-arg=client-id="ai-cluster" \
      --auth-provider-arg=id-token="eyJhbG......" \
      --auth-provider-arg=refresh-token="eyJhb....."
    $ kubectl config get-users
    NAME
    awslife@homelab
    kubernetes-admin
    $

     

    context 추가 설정

    credential 설정이 완료되면 credential 정보를 context로 설정해준다. 여기에서는 특별한 namespace를 설정하지 않으므로 kube-system으로 지정하였다.

    $ kubectl config get-contexts
    CURRENT   NAME                       CLUSTER   AUTHINFO           NAMESPACE
    *         kubernetes-admin@homelab   homelab   kubernetes-admin   kube-system
    $ kubectl config set-context awslife@homelab --user=awslife@homelab --cluster=homelab --namespace=kube-system
    Context "awslife@homelab" created.
    $ kubectl config get-contexts
    CURRENT   NAME                       CLUSTER   AUTHINFO           NAMESPACE
              awslife@homelab            homelab   awslife@homelab    kube-system
    *         kubernetes-admin@homelab   homelab   kubernetes-admin   kube-system
    $

     

    context 지정

    context를 추가하였지만 kubectl 명령어 사용시 현재는 kubernetes-admin@homelab context를 사용하도록 설정되어 있으므로 추가한 awslife@homelab을 사용하도록 변경해주어야 한다. 변경은 use-context로 설정 가능하다.

    $ kubectl config get-contexts
    CURRENT   NAME                       CLUSTER   AUTHINFO           NAMESPACE
              awslife@homelab            homelab   awslife@homelab    kube-system
    *         kubernetes-admin@homelab   homelab   kubernetes-admin   kube-system
    $ kubectl config use-context awslife@homelab
    Switched to context "awslife@homelab".
    $ kubectl config get-contexts
    CURRENT   NAME                       CLUSTER   AUTHINFO           NAMESPACE
    *         awslife@homelab            homelab   awslife@homelab    kube-system
              kubernetes-admin@homelab   homelab   kubernetes-admin   kube-system
    $

     

    Pods 조회

    설정한 context로 pods 조회를 해보도록 하자. 당연하게도 조회되지 않는다. 이는 사용자만 할당 하였을 뿐 사용자의 ROLE에 권한을 매핑하지 않았기 때문이다. Keycloak에서는 단순히 Role 이름만 제공해줄 뿐 Role에 대응하는 권한은 Kubernetes에서 설정해주어야 한다.

    $ kubectl get pods
    Error from server (Forbidden): pods is forbidden: User "awslife" cannot list resource "pods" in API group "" in the namespace "kube-system"
    $

     

    Role & Role binding 설정

    Keycloak의 group에 대응되는 role과 role binding을 설정하도록 한다. 위에서 설정한 그룹은 devops 이므로 대응되는 그룹도 devops로 설정하도록 하자. 

    $ kubectl config set-context kubernetes-admin@homelab
    Context "kubernetes-admin@homelab" modified.
    $ cat << EOF | kubectl apply -f -
    > apiVersion: rbac.authorization.k8s.io/v1
    > kind: Role
    > metadata:
    >   namespace: kube-system
    >   name: role-admin
    > rules:
    > - apiGroups: [""]
    >   resources: ["pods"]
    >   verbs: ["get", "watch", "list", "create", "delete"]
    > ---
    > apiVersion: rbac.authorization.k8s.io/v1
    > kind: RoleBinding
    > metadata:
    >   namespace: kube-system
    >   name: rolebinding-admin
    > subjects:
    > - kind: Group
    >   name: devops
    >   apiGroup: rbac.authorization.k8s.io
    > roleRef:
    >   kind: Role
    >   name: role-admin
    >   apiGroup: rbac.authorization.k8s.io
    > EOF
    role.rbac.authorization.k8s.io/role-admin created
    rolebinding.rbac.authorization.k8s.io/rolebinding-admin created
    $ kubectl get roles role-admin
    NAME         CREATED AT
    role-admin   2021-08-20T06:09:43Z
    $ kubectl get rolebindings rolebinding-admin
    NAME                ROLE              AGE
    rolebinding-admin   Role/role-admin   60s
    $

     

    Pod 조회

    모든 설정이 완료되었다. 이제 Pod 조회를 해보도록 하자. kube-system에 생성되어 있는 pods가 조회되면 제대로 설정된것이다.

    $ kubectl config set-context awslife@homelab
    Context "awslife@homelab" modified.
    $ kubectl get pods
    NAME                                               READY   STATUS    RESTARTS   AGE
    cilium-bk9wh                                       1/1     Running   3          5d16h
    cilium-hq7fh                                       1/1     Running   3          5d16h
    cilium-hwrwj                                       1/1     Running   3          5d16h
    cilium-kf45f                                       1/1     Running   3          5d16h
    cilium-operator-58c4888784-4z24w                   1/1     Running   5          5d16h
    cilium-operator-58c4888784-7sqnh                   1/1     Running   3          5d16h
    cilium-pv8wh                                       1/1     Running   3          5d16h
    coredns-74ff55c5b-4j67q                            1/1     Running   3          5d16h
    coredns-74ff55c5b-w89p9                            1/1     Running   3          5d16h
    etcd-k8smaster1                                    1/1     Running   3          5d16h
    etcd-k8smaster2                                    1/1     Running   3          5d16h
    etcd-k8smaster3                                    1/1     Running   3          5d16h
    ingress-nginx-controller-b65df6fbb-ztk5d           1/1     Running   3          5d16h
    kube-apiserver-k8smaster1                          1/1     Running   3          5d15h
    kube-apiserver-k8smaster2                          1/1     Running   3          5d15h
    kube-apiserver-k8smaster3                          1/1     Running   3          5d15h
    kube-controller-manager-k8smaster1                 1/1     Running   5          5d16h
    kube-controller-manager-k8smaster2                 1/1     Running   5          5d16h
    kube-controller-manager-k8smaster3                 1/1     Running   3          5d16h
    kube-proxy-6v89f                                   1/1     Running   3          5d16h
    kube-proxy-7dsjl                                   1/1     Running   3          5d16h
    kube-proxy-d98sk                                   1/1     Running   3          5d16h
    kube-proxy-hx287                                   1/1     Running   3          5d16h
    kube-proxy-l4d6c                                   1/1     Running   3          5d16h
    kube-scheduler-k8smaster1                          1/1     Running   4          5d16h
    kube-scheduler-k8smaster2                          1/1     Running   5          5d16h
    kube-scheduler-k8smaster3                          1/1     Running   3          5d16h
    nfs-subdir-external-provisioner-6587c94ddf-sl4nm   1/1     Running   4          5d16h
    $

     

    References

    - https://www.getambassador.io/docs/edge-stack/latest/howtos/auth-kubectl-keycloak/

     

    Kubernetes SSO with OIDC and Keycloak | Ambassador

    9 min • read Kubernetes SSO with OIDC and Keycloak Developers use kubectl to access Kubernetes clusters. By default kubectl uses a certificate to authenticate to the Kubernetes API. This means that when multiple developers need to access a cluster, the c

    www.getambassador.io

    - https://www.keycloak.org/getting-started/getting-started-kube

    반응형
Designed by Tistory.