Dex

6 분 소요

Dex - A federated OpenID Connect provider


dex는 기본적으로 OpenID Connect를 사용하여 다른 애플리케이션의 인증을 하게 해주는 identity 서비스다.

https://github.com/coreos/dex

Dex는 “connectors”를 통해 다른 identity provider의 포털(게이트웨이, 중계자) 역할을 한다. 이를 통해 LDAP, SAML또는 GitHub, Google, AD(Active Directory)와 같은 기존 인증을 dex에게 위임할 수 있다. 클라이언트는 일단 인증 로직을 작성하여 dex와 통신하면 dex는 주어진 백엔드(여기서는 예시로 kubernetes 클러스터)에 대한 프로토콜을 처리하게 된다.

하지만 향후 Cloud Native 인증 카탈로그 중 하나로서 생각하고 있는 오픈소스이다 보니 테스트는 진행해봐야 할 것 같다.

OpenID Connect

기본적으로 Dex는 OIDC(OpenID Connect)를 사용한다.
그러므로 kubernetes 클러스터 사용자 인증용도로 Dex를 붙일수 있는 것이다.
또한 위에서 말한것과 같이 다른 identity provider를 사용하여 사용자를 인증하는데 아래 그림과 같이 connectors를 사용한다.

dex_image

위 그림과 같이 “connectors”는 다른 identity provider를 통해 사용자를 인증하기 위해 dex에서 사용하는 기본 도구다. 결국 Dex는 기존에 활용하던 GitHub, LinkedIn 및 Microsoft AD와 같은 벤더 플랫폼이나 LDAP 및 SAML과 같은 기존 프로토콜 방식을 사용하여 kubernetes 클러스터 사용자 인증을 쉽게 구현할 수 있는 것이다. 큰 회사??들은 OpenLDAP이나 Microsoft Active Directory와 같은 사내 디렉토리 서비스를 운용하거나 내가 있는 작은 규모의 팀들은 대부분 Github이나 Google 계정은 가지고 있을 것이기 때문에 Dex와 같은 오픈소스를 사용함으로써 기존 identity provider 와 kubernetes를 쉽게 통합할 수 있다고 본다.

dex_kube

Kubernetes OIDC

아래 그림처럼 Identity Provider 위치에 Dex를 구성하면 된다.

kube_openid

public managed kubernetes 환경에서는 현실적으로 kubeapi-server를 컨트롤하기 어렵기 때문에 일단 Native 클러스터에 구성을 하도록 한다.

Github 연동하기

테스트로 Github을 연동해보자. 일단 아래와 같이 인증서를 생성하는 스크립트를 하나만든다.
아래에서 DNS.1 부분은 /etc/hosts로 테스트로 등록할 도메인으로 가상으로 만들었다. (generator.sh)

#!/bin/bash

mkdir -p ssl

cat << EOF > ssl/req.cnf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = dex.newtech.academy
EOF

openssl genrsa -out ssl/ca-key.pem 2048
openssl req -x509 -new -nodes -key ssl/ca-key.pem -days 10 -out ssl/ca.pem -subj "/CN=kube-ca"

openssl genrsa -out ssl/key.pem 2048
openssl req -new -key ssl/key.pem -out ssl/csr.pem -subj "/CN=kube-ca" -config ssl/req.cnf
openssl x509 -req -in ssl/csr.pem -CA ssl/ca.pem -CAkey ssl/ca-key.pem -CAcreateserial -out ssl/cert.pem -days 10 -extensions v3_req -extfile ssl/req.cnf

Namespace도 하나 만들자. (dex-ns.yaml)

apiVersion: v1
kind: Namespace
metadata:
  name: dex

kubernetest 클러스터용 인증서를 생성한다.

$ ./generator.sh
$ kubectl create -f dex-ns.yaml
$ kubectl create secret tls dex.newtech.academy.tls -n dex --cert=ssl/cert.pem --key=ssl/key.pem
$ mkdir pki
$ cp ssl/ca.pem pki/openid-ca.pem

Github에 가서 아래와 같이 Org OAuth App.을 신규로 생성한다.

github

만든 위 앱정보로 Github-Client Secret을 만든다.

$ export GITHUB_CLIENT_ID=<Client ID>
$ export GITHUB_CLIENT_SECRET=<Client Secret>

$ kubectl create secret \
    generic github-client \
    -n dex \
    --from-literal=client-id=$GITHUB_CLIENT_ID \
    --from-literal=client-secret=$GITHUB_CLIENT_SECRET

$ kubectl get secret
NAME                  TYPE                                  DATA      AGE
default-token-f29fb   kubernetes.io/service-account-token   3         27m
dex.newtech.academy.tls    kubernetes.io/tls                     2         27m
github-client         Opaque                                2         6s

$ kubectl get secret github-client -o yaml
apiVersion: v1
data:
  client-id: xxxxx
  client-secret: xxxxxxx
kind: Secret
metadata:
  creationTimestamp: 2018-07-24T15:14:29Z
  name: github-client
  namespace: dex
  resourceVersion: "7044721"
  selfLink: /api/v1/namespaces/dex/secrets/github-client
  uid: 42dfa0e9-8f54-11e8-8d6d-2aaad36eb114
type: Opaque

위에서 이야기 했지만 여기까지 아무생각없이 퍼블릭에서 구성하다가 생각해보니 kube-apiserver를 변경하려면 직접 클러스터를 써야한다는 이런 바보같은 실수를 또 저지르고 말았다. 그래서 다시 VM에 재구성을 ㅜㅜ

그리고 kube-apiserver manifest 파일에 아래 내용을 추가한다.
/etc/kubernetes/manifests/kube-apiserver.manifest

    - --oidc-issuer-url=https://dex.newtech.academy:32000
    - --oidc-client-id=example-app
    - --oidc-ca-file=/pki/openid-ca.pem
    - --oidc-username-claim=email
    - --oidc-groups-claim=groups

그리고 kubelet 재시작을 한다.

$ systemctl restart kubelet

테스트를 위해 dex sample app을 빌드하고 만든 인증서와 함께 실행하게 되면 아래와 같이 샘플페이지를 볼수 있다.

$ sudo apt-get install make golang-1.9
$ git clone https://github.com/coreos/dex.git
$ cd dex
$ git checkout v2.10.0
$ export PATH=$PATH:/usr/lib/go-1.9/bin
$ go get github.com/coreos/dex
$ make bin/example-app
$ export MY_IP=$(curl -s ifconfig.co)
$ ./bin/example-app --issuer https://dex.newtech.academy:32000 --issuer-root-ca /pki/openid-ca.pem --listen http://${MY_IP}:5555 --redirect-uri http://${MY_IP}:5555/callback
2018/07/25 14:37:52 listening on http://169.56.94.55:5555

dex_sample

그리고 로그인을 하게 되면 아래와 같이 Github 인증을 통해 kubernetes 클러스터에서 사용하려고 하는 Token을 획득할 수 있다.

kube_dex

dex_token

다음으로 위에서 만든 token을 사용하기 위해서 Role, RoleBinding과 실제 로그인할 github 계정을 User로 사용하도록 아래와 같이 작성한다.

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: exampleUser
  namespace: default
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: exampleUser
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: exampleUser
subjects:
- kind: User
  name: ddiiwoong@gmail.com
  namespace: default

계정생성을 하고 위에서 만든 token을 가지고 developer 사용자를 설정하고 context설정로 dev-default로 생성한다.

$ kubectl create -f user.yaml
role.rbac.authorization.k8s.io "exampleUser" created
rolebinding.rbac.authorization.k8s.io "exampleUser" created

$ export TOKEN=<dex_sample_app_token>

$ kubectl config set-credentials developer --auth-provider=oidc --auth-provider-arg=idp-issuer-url=https://dex.newtech.academy:32000 --auth-provider-arg=client-id=example-app --auth-provider-arg=idp-certificate-authority=/pki/openid-ca.pem  --auth-provider-arg=id-token=${TOKEN}
User "developer" set.

$ kubectl config set-context dev-default --cluster=kubernetes --namespace=default --user=developer
Context "dev-default" created.

위처럼 만들고 나면 현재 클러스터의 context들을 확인할수 있다. 확인후에 context를 dev-default로 바꿔보자.
새로운 context를 확인할수 있다.

$ kubectl config get-contexts
CURRENT   NAME                  CLUSTER         AUTHINFO              NAMESPACE
*         admin-cluster.local   cluster.local   admin-cluster.local   
          dev-default           kubernetes      developer             default

$ kubectl config use-context dev-default
Switched to context "dev-default".

$ kubectl config get-contexts
CURRENT   NAME                  CLUSTER         AUTHINFO              NAMESPACE
          admin-cluster.local   cluster.local   admin-cluster.local   
*         dev-default           kubernetes      developer             default

$ kubectl get pod --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
dex           dex-64c4fb5b44-42d44                    1/1       Running   0          1h
dex           dex-64c4fb5b44-r9p9d                    1/1       Running   0          1h
dex           dex-64c4fb5b44-w5s2m                    1/1       Running   0          1h
kube-system   calico-node-2bqll                       1/1       Running   0          13h
kube-system   calico-node-cljb2                       1/1       Running   0          13h
kube-system   calico-node-svh5n                       1/1       Running   0          13h
kube-system   kube-apiserver-node1                    1/1       Running   0          1h
kube-system   kube-controller-manager-node1           1/1       Running   2          13h
kube-system   kube-dns-7bd4d5fbb6-7bb2j               3/3       Running   0          13h
kube-system   kube-dns-7bd4d5fbb6-g8lz8               3/3       Running   0          13h
kube-system   kube-proxy-node1                        1/1       Running   0          13h
kube-system   kube-proxy-node2                        1/1       Running   0          13h
kube-system   kube-proxy-node3                        1/1       Running   0          13h
kube-system   kube-scheduler-node1                    1/1       Running   2          13h
kube-system   kubedns-autoscaler-679b8b455-qxlvw      1/1       Running   0          13h
kube-system   kubernetes-dashboard-55fdfd74b4-mbkm8   1/1       Running   0          13h
kube-system   nginx-proxy-node2                       1/1       Running   0          13h
kube-system   nginx-proxy-node3                       1/1       Running   0          13h
kube-system   tiller-deploy-5c688d5f9b-52tls          1/1       Running   0          13h

정리

처음에도 언급했듯이 kubernetes 인증을 위해 무거운 keycloak을 사용을 하는 경우들이 많은데 간단하게 사용할 수 있는 dex를 사용하여 여러가지 identity provider의 connector 역할로 사용하기에는 무난한것 같다.
결국 Dex를 이용하면 GitHub를 비롯해 다양한 OpenID, OAuth 2.0 인증 서비스와 Kubernetes 클러스터를 엮기 쉬울수 있다.
단, 최근 commit이 3-4달전이라는것과 처음 stable helm에 등록된 후 업데이트가 없었다는 점이 걱정되는 부분이긴 하다. (묘하게 Redhat과 합병 일정이 오버랩 되긴한다…)

거의 2주만에 포스팅인데 ‘18 Google Next 키노트에서 knative라는 현재 개발중인 프로젝트와 유사한 오픈소스가 공개되었다. 다음번엔 knative도 한번 테스트를 해봐야 할듯 하다.

퍼블릭과 프라이빗 클러스터에 대한 고민이 생기기 시작한다. 다시금 오픈소스 플랫폼에 집중하기 위해 프라이빗으로 돌아가야 하나라는 고민에도 빠져있는 상태에서 knative, GKE on Premise 같은 엔터프라이즈에서 프라이빗 구축을 위한 것들도 나오고 있어서 더더욱 고민에 빠진 요즘이다…

댓글남기기