Skip to content

techscore/experimental-openidconnect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

本リポジトリについて

OpenID Connect Provider のOSS実装である、hydra の検証のため、 ローカル環境のk8sにhydraをセットアップする

hydraの特徴

  • Certified OpenID Connect Implementations になっている。

    • kubernetes、istio との連携も可能
  • 独自のユーザ管理、ユーザ認証ロジックを採用できる。

    • OpenID Connectフローの実装を任すことで、ユーザ認証のロジックを分離できる。
    • JWT発行時に任意のclaimを設定できる。
  • 発行したアクセストークン、リフレッシュトークンを管理するストレージとして、MySQLとPostgreSQLを使うことができる。

セットアップ

前提

  • docker for mac or minikubeなど、ローカル環境でk8s環境が構築されていること
  • kustomize がインストールされていること

namespaceの作成

kubectl create ns hydra

PostgreSQLのセットアップ

cd k8s/hydra/postgres

# persistent volume の hostPathの修正
vi base/pvc.postgres.yaml
---
  hostPath:
    # TODO modified
    path: "<need modified>" # ここを変更(ホスト側で予めディレクトリ作成しておく)
---
./build.sh
kubectl apply -f generated/hydra-postgres-local.all.yaml

# connect to postgres port 30432 from host
psql -U hydra -h localhost -p 30432

nginx-ingress-controllerのセットアップ

# namespace 作成
kubectl create ns nginx-ingress
# secret 作成
cd k8s/hydra/nginx-ingress-controller
kubectl create secret tls tls-certificate --key server.key --cert server.crt -n nginx-ingress
kubectl create secret generic tls-dhparam --from-file=dhparam.pem -n nginx-ingress
# setup
./build.sh
kubectl apply -f generated/nginx-ingress-controller.local.all.yaml

hydra DB マイグレーション

cd k8s/hydra/hydra/binary
export DATABASE_URL="postgres://hydra:secret@localhost:30432/hydra?sslmode=disable"
./hydra-darwin-amd64 migrate sql $DATABASE_URL

※ mac以外の環境の場合はhydraのバイナリをビルド(要Go1.11以上)して、それを実行する
以下ビルド手順

go get -d -u github.com/ory/hydra
cd $(go env GOPATH)/src/github.com/ory/hydra
HYDRA_LATEST=$(git describe --abbrev=0 --tags)
git checkout $HYDRA_LATEST
GO111MODULE=on go install \
    -ldflags "-X github.com/ory/hydra/cmd.Version=$HYDRA_LATEST -X github.com/ory/hydra/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/hydra/cmd.GitHash=`git rev-parse HEAD`" \
    github.com/ory/hydra
git checkout master
# 確認
$GOPATH/bin/hydra help

hydraのセットアップ

cd k8s/hydra/hydra
./build.sh
kubectl apply -f generated/hydra.local.all.yaml

API call

/etc/hosts に以下を追記する

127.0.0.1 hydra-public-api.synergy-example.com
127.0.0.1 hydra-admin-api.synergy-example.com
# call public api(jwks_url)
curl -k https://hydra-public-api.synergy-example.com:30443/.well-known/jwks.json
# call admin api(get oidc client list)
curl -k https://hydra-admin-api.synergy-example.com:30443/clients

OpenID Connect(Authorization Code flow) を試す

OpenID Connect client の作成

cd k8s/hydra/hydra/binary
./hydra-darwin-amd64 clients create \
    --skip-tls-verify --endpoint https://hydra-admin-api.synergy-example.com:30443 \
    --id test-client \
    --secret test-secret \
    --response-types code,id_token \
    --grant-types refresh_token,authorization_code \
    --scope openid,offline \
    --callbacks http://localhost:4446/callback

間違ってClient作成した場合は、以下のようにして削除

./hydra-darwin-amd64 clients delete test-client \
    --skip-tls-verify --endpoint https://hydra-admin-api.synergy-example.com:30443

ログインプロバイダの起動

exampleとして、hydra-login-consent-nodeを使う(要npm)

cd k8s/login-provider/hydra-login-consent-node
npm i
# 自己署名証明書を使っているので、NODE_TLS_REJECT_UNAUTHORIZED=0をつけて起動
NODE_TLS_REJECT_UNAUTHORIZED=0 HYDRA_ADMIN_URL=https://hydra-admin-api.synergy-example.com:30443 npm start

RP起動

hydraにはテストのためのヘルパーコマンド hydra token user があり、
このコマンドは、hydraへのリダイレクトURLを生成し、認証完了のコールバックを受け取るWebサーバを立ち上げる。

cd k8s/hydra/hydra/binary
./hydra-darwin-amd64 token user \
    --skip-tls-verify --token-url https://hydra-public-api.synergy-example.com:30443/oauth2/token \
    --auth-url https://hydra-public-api.synergy-example.com:30443/oauth2/auth \
    --scope openid,offline \
    --client-id test-client \
    --client-secret test-secret \
    --redirect http://localhost:4446/callback
Setting up home route on http://127.0.0.1:4446/
Setting up callback listener on http://127.0.0.1:4446/callback
Press ctrl + c on Linux / Windows or cmd + c on OSX to end the process.
If your browser does not open automatically, navigate to:

	http://127.0.0.1:4446/

ブラウザへのアクセス

http://localhost:4446/ にブラウザからアクセスして、以下の画面が表示される

RP

「Authorize」のリンクをクリックすると、
hydra auth url --> hydra-login-consent-node のログイン画面(localhost:3000/login)にリダイレクトされる

Login

ログイン成功すると、
hydra login accept --> hydra-login-consent-node の同意画面(localhost:3000/consent)にリダイレクトされる

Consent

同意すると、各種トークンが発行されます。

Token

jwt.ioでid_tokenをデコードしてみると、header、payloadの中身を見ることができます。

{
  "alg": "RS256",
  "kid": "public:40557f34-cddc-4f22-bf7b-3eb89bd8ff11",
  "typ": "JWT"
}
{
  "at_hash": "IynRuB7C5UrBH3juxLueOg",
  "aud": [
    "test-client"
  ],
  "auth_time": 1552620618,
  "exp": 1552624596,
  "groups": [
    "foo",
    "bar"
  ],
  "iat": 1552620996,
  "iss": "https://hydra-public-api.synergy-example.com:30443/",
  "jti": "3fe11e9f-3ab2-4e5b-a739-2a47fd9547bb",
  "nonce": "umlyrcshrnkcpjlkrpksellk",
  "rat": 1552620612,
  "sub": "foo@bar.com"
}

mysql を使う場合

mysql setup

cd k8s/hydra/mysql
./build.sh
kubectl apply -f secret.mysql.yaml
kubectl apply -f generated/hydra-mysql.local.all.yaml

mysql migration

cd k8s/hydra/hydra/binary
export DATABASE_URL="mysql://root:password@tcp(127.0.0.1:30306)/mysql?parseTime=true"
./hydra-darwin-amd64 migrate sql $DATABASE_URL

hydra configuration

cd k8s/hydra/hydra
vi base/deployment.hydra.yaml
---
  #- name: DATABASE_URL
  #  value: postgres://hydra:secret@postgres:5432/hydra?sslmode=disable
  - name: DATABASE_URL
    value: mysql://root:password@tcp(mysql:3306)/mysql?parseTime=true
---

./build.sh
kubectl apply -f generated/hydra.local.all.yaml

k8s との連携

kubernetes(kube-apiserver) の認証はOpenID Connectに対応しており、RBAC(Role Based Access Control)を設定することでグループによるアクセス制御が可能

OpenID Connectによる認証・認可に対応するために、kube-apiserver起動時にいくつかのパラメータを指定して起動する必要がある

パラメータ名 指定する内容
--oidc-issuer-url IDトークンのiss ※ https必須
--oidc-client-id Relying Partyのclient_id
--oidc-groups-claim RBACのGroupとして扱うclaimのキー
--oidc-ca-file IdPのサーバ証明書に署名したCA証明書

docker for mac の場合、kube-apiserver は Moby VM 上でコンテナとして動作しているので、
こちらの方法で、Docker for Mac の tty に接続して、
/etc/kubernetes/manifests/kube-apiserver.yaml を直接編集した後に kubernetes を再起動する

(中略)
spec:
  containers:
  - command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    (中略)
    - --oidc-issuer-url=https://hydra-public-api.synergy-example.com:30443/
    - --oidc-client-id=test-client
    - --oidc-groups-claim=groups
    - --oidc-ca-file=/path/to/cafile
(中略)
    - mountPath: /usr/share/ca-certificates
      name: usr-share-ca-certificates
      readOnly: true
    # 追記
    - mountPath: /path/to/cafile
      name: oidc-ca-certificates
      readOnly: true
  hostNetwork: true
(中略)
  - hostPath:
      path: /usr/share/ca-certificates
      type: DirectoryOrCreate
    name: usr-share-ca-certificates
  # 追記
  - hostPath:
      path: /path/to/cafile
      type: DirectoryOrCreate
    name: oidc-ca-certificates
status: {}

発行したIDトークンをkubernetesに登録

kubectl config set-credentials synergy-admin --token=[YOUR ID TOKEN]
kubectl config set-context oidc-example --cluster=docker-desktop --user=synergy-admin

RoleとRoleBindingを登録 登録内容としては namespace が kube-system の pod に対する get/watch/list の操作を許可する内容となる

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: test-role
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-rolebinding
  namespace: kube-system
subjects:
- kind: Group
  name: foo # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: test-role # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io

実際に試してみると、namespace未指定の場合は pod の情報取得ができないが、kube-system を指定した場合は pod の情報取得ができるようになる

$ kubectl config use-context oidc-example
$ kubectl get pods
Error from server (Forbidden): pods is forbidden: User "https://hydra-public-api.synergy-example.com:30443/#foo@bar.com" cannot list resource "pods" in API group "" in the namespace "default"
$ kubectl get pod -n kube-system
NAME                                     READY   STATUS    RESTARTS   AGE
coredns-86c58d9df4-9sdb9                 1/1     Running   7          66d
coredns-86c58d9df4-f288f                 1/1     Running   7          66d
etcd-docker-desktop                      1/1     Running   25         66d
kube-apiserver-docker-desktop            1/1     Running   0          2m45s
kube-controller-manager-docker-desktop   1/1     Running   82         66d
kube-proxy-nswq7                         1/1     Running   7          66d
kube-scheduler-docker-desktop            1/1     Running   75         66d
tiller-deploy-dbb85cb99-bjrnr            1/1     Running   7          3d8h

istio との連携

istio はマイクロサービスのサービスメッシュを実現するオープンソースプロジェクト

OpenID Connectによる認証・認可についても、各pod で個別で行なうのは大きなコストになってしまうため、
istio によってコードに手を入れずにユーザの権限に応じて認証・認可をできるようになる

サンプルを実行する際の注意点が2点ある

  • 自己署名証明書のJWKsエンドポイントにアクセスできない istio の pilot が JWKsエンドポイントにアクセスする際、自己署名証明書を使ったURLだとinvalid扱いとなり認証できない。
    hydraはhttpのissuerで起動して、IDトークンを発行すること。
cd k8s/hydra/hydra/base
vi deployment.hydra.yaml

---
            # for kube-apiserver sample(kubeapi-server はhttpsでアクセスしないとJWT検証できない。httpはNG)
            #- name: OAUTH2_ISSUER_URL
            #  value: https://hydra-public-api.synergy-example.com:30443
            # for istio authentication sample (istio は自己署名証明書のサーバにhttpsアクセスしてJWT検証できない. httpは可能)
             - name: OAUTH2_ISSUER_URL
               value: http://hydra-public-api.synergy-example.com:30080
---
cd ../../
./build.sh
kubectl apply -f generated
  • JWKsエンドポイントが 127.0.0.1 だとアクセスできない

/etc/hosts の 以下の設定により、hydra-public-api.synergy-example.com を 127.0.0.1 と認識してしまい、pilot からJWKsエンドポイントにアクセスできない

127.0.0.1 hydra-public-api.synergy-example.com
127.0.0.1 hydra-admin-api.synergy-example.com

ホストにつなぐためには、host.docker.internal のDNS名で引けるIPでないといけないため、/etc/hostsを変更する

kubectl exec -it [pod] /bin/sh

> ping host.docker.internal
PING host.docker.internal (192.168.65.2) 56(84) bytes of data.

> exit

vi /etc/hosts

---
192.168.65.2 hydra-public-api.synergy-example.com
192.168.65.2 hydra-admin-api.synergy-example.com
---

istio のインストールは以下の手順で実施する

# helm install
$ brew install kubernetes-helm

$ NAMESPACE=istio-system
$ kubectl create ns $NAMESPACE

$ helm repo add istio.io https://storage.googleapis.com/istio-prerelease/daily-build/master-latest-daily/charts

$ cd k8s/rbac-test/istio

$ kubectl apply -f install/kubernetes/helm/helm-service-account.yaml

$ helm install install/kubernetes/helm/istio-init --name istio-init --namespace $NAMESPACE
$ helm install install/kubernetes/helm/istio --name istio --namespace $NAMESPACE
# 何故か必要なCRDがインストールされずに起動できない事象があるため、個別でCRDをインストール
# https://github.com/istio/istio/issues/12535
$ kubectl apply -f install/kubernetes/helm/istio-init/files/crd-12.yaml

httpbinサービス登録

cd k8s/rbac-test/istio
kubectl apply -f httpbin/

アクセスできるか確認

curl -i http://localhost/status/200
HTTP/1.1 200 OK が返ること

authentication policy登録

cd k8s/rbac-test/istio
kubectl apply -f authentication/

アクセス確認

curl -i http://localhost/status/200
HTTP/1.1 401 Unauthorized が返ること

curl -i -H 'Authentication: Bearer <token>' http://localhost/status/200
HTTP/1.1 200 OK が返ること

authorization policy登録

cd k8s/rbac-test/istio
kubectl apply -f authorization/

アクセス確認

curl -i http://localhost/status/200
HTTP/1.1 401 Unauthorized が返ること

curl -i -H 'Authentication: Bearer <token>' http://localhost/status/200
HTTP/1.1
HTTP/1.1 200 OK が返ること