k3d でoidcを試す
目次
k3dとは
k3dはk3sをコンテナ化し、Dockerを使用してマルチノードのk3sクラスタを作れるツール
Dockerにのみ依存しローカル環境でkubernetes(k3s)を簡単に作れるので大変便利
README
構成
- Ubuntu (20.04 LTS) (IP:192.168.1.100)
- Docker(19.03.11)
- k3d
- helm
- sakura docker registry
手順
k3dでクラスタを作る
# k3d cluster create --k3s-server-arg --no-deploy --k3s-server-arg traefik --volume /root/work/istio/src/registries.yaml:/etc/rancher/k3s/registries.yaml -p 8087:80@loadbalancer -p 8088:443@loadbalancer test-oidc
–k3s-server-arg
k3sはデフォルトでtraefikを使用するが今回はnginx ingressを使うため、traefikをデプロイしないように引数を与える
参考: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
–volume /root/work/istio/src/registries.yaml:/etc/rancher/k3s/registries.yaml
sakura docker registry を使用するため、以下のregistries.yamlを記載する
参考: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
mirrors:
k3d.sakuracr.jp:
endpoint:
- https://k3d.sakuracr.jp
-p 8087:80@loadbalancer -p 8088:443@loadbalancer
k3d が外部からアクセスできるようにPortを指定する
localhost:8087 にアクセスすると、KubernetesのLoadBalancer TypeのService のPort 80にアクセスするように設定している
k3dのkubeconfigを設定
# k3d kubeconfig merge test-oidc --switch-context
# export KUBECONFIG=/root/.k3d/kubeconfig-test-oidc.yaml
podのSTATUSがRunnning
になっていることの確認
# kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system metrics-server-7566d596c8-h2fnc 1/1 Running 0 8d
kube-system local-path-provisioner-6d59f47c7-npt4h 1/1 Running 0 8d
kube-system coredns-8655855d6-qk5k4 1/1 Running 0 8d
認証をかけたいアプリケーションのDeployment, serviceの追加
apiVersion: v1
kind: Service
metadata:
name: honypot
labels:
app: honypot
service: honypot
spec:
ports:
- port: 8080
name: http
selector:
app: honypot
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-honypot
labels:
account: honypot
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: honypot-v1
labels:
app: honypot
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: honypot
version: v1
template:
metadata:
labels:
app: honypot
version: v1
spec:
serviceAccountName: bookinfo-honypot
containers:
- name: honypot
image: k3d.sakuracr.jp/test-oidc
imagePullPolicy: Always
ports:
- containerPort: 8080
# kubectl apply -f app.yml
参考:すべてOKを返すWebアプリケーション
package main
import (
"context"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
log.Println("------------------------------")
log.Printf("URL: %v", r.URL)
log.Printf("Method: %v", r.Method)
log.Printf("Header: %v", r.Header)
if body, err := ioutil.ReadAll(r.Body); err != nil {
log.Printf("ioutil.ReadAll: %v", err)
} else {
log.Println(string(body))
defer r.Body.Close()
}
w.WriteHeader(http.StatusOK)
}
func authHandler(w http.ResponseWriter, r *http.Request) {
log.Println("--------------auth----------------")
log.Printf("URL: %v", r.URL)
log.Printf("Method: %v", r.Method)
log.Printf("Header: %v", r.Header)
if body, err := ioutil.ReadAll(r.Body); err != nil {
log.Printf("ioutil.ReadAll: %v", err)
} else {
log.Println(string(body))
defer r.Body.Close()
}
w.WriteHeader(http.StatusOK)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
mux.HandleFunc("/auth", authHandler)
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
go func() {
log.Println("Start Server ....")
if err := srv.ListenAndServe(); err != nil {
log.Print(err)
}
}()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGTERM, os.Interrupt)
<-sigCh
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
if err := srv.Shutdown(ctx); err != nil {
log.Print(err)
}
}
参考:Dockerfile
FROM golang:1.14-alpine3.11 AS minimum-base
LABEL MAINTAINER 'mitsuyoshi4'
RUN set -eux && \
apk add --no-cache \
curl bash make git
WORKDIR /go/src/honeypot
COPY . /go/src/honeypot
RUN set -eux && \
go build -ldflags '-w -s'
# ---------- #
FROM alpine:3.8 AS honeypot
LABEL MAINTAINER 'mitsuyoshi4'
COPY --from=minimum-base \
/go/src/honeypot/honeypot /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/honeypot"]
# docker login k3d.sakuracr.jp
# docker build . --no-cache
# docker tag xxxxxxxxx k3d.sakuracr.jp/test-oidc:latest
# docker push k3d.sakuracr.jp/test-oidc:latest
keycloak設定
起動
# docker run -d -p 18080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin --name keycloak jboss/keycloak
relm追加、ユーザ追加、グループ追加の簡単な流れ
- HOSTのIP(192.168.1.100:18080)にアクセスしてKEYCLOAK_USER、KEYCLOAK_PASSWORDで設定したuser/passwordを入力してログインする
- relm の追加(name: demo)
- client 作成(name: test)
- client protocol を
openid-connect
に変更 - 有効なリダイレクトURLを
*
に設定 - client scopeを
api
で作成、protocolはopenid-connect
- client のclient scope に6で生成した
api
を割り当て - group 作成
- user 作成
※参考:公式サイト
keycloakの接続先等の情報
公式サイトを参考に、作成したrelmの情報を把握しておく(oauth2-proxyで使用する)
例:http://192.168.1.100:18080/auth/realms/demo/.well-known/uma2-configuration
oauth2-proxy設定
deploy, ingress
GithubのIssueを参考に、マニフェストファイルを作成する
argsの--login-url
, --redeem-url
, --validate-url
、envのOAUTH2_PROXY_CLIENT_ID
, OAUTH2_PROXY_CLIENT_SECRET
に接続するkeycloakの情報を入力する
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: oauth2-proxy
name: oauth2-proxy
spec:
replicas: 1
selector:
matchLabels:
k8s-app: oauth2-proxy
template:
metadata:
labels:
k8s-app: oauth2-proxy
spec:
containers:
- args:
- --provider=keycloak
- --email-domain=*
- --upstream=file:///dev/null
- --http-address=0.0.0.0:4180
- --login-url=http://192.168.1.100:18080/auth/realms/demo/protocol/openid-connect/auth
- --redeem-url=http://192.168.1.100:18080/auth/realms/demo/protocol/openid-connect/token
- --validate-url=http://192.168.1.100:18080/auth/realms/demo/protocol/openid-connect/userinfo
# - --ssl-insecure-skip-verify # Needed for self-signed cert
env:
- name: OAUTH2_PROXY_CLIENT_ID
value: test
- name: OAUTH2_PROXY_CLIENT_SECRET
value: 0ea6bf71-9c71-41b5-a5e9-4481439f76d1
# docker run -ti --rm python:3-alpine python -c 'import secrets,base64; print(base64.b64encode(base64.b64encode(secrets.token_bytes(16))));'
- name: OAUTH2_PROXY_COOKIE_SECRET
value: rXPajRXVoIINCXz/xJIC1w==
# my image - fork of pusher/oauth2_proxy:master + PR containing keycloak provider
image: quay.io/pusher/oauth2_proxy
imagePullPolicy: Always
name: oauth2-proxy
ports:
- containerPort: 4180
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: oauth2-proxy
name: oauth2-proxy
spec:
ports:
- name: http
port: 4180
protocol: TCP
targetPort: 4180
selector:
k8s-app: oauth2-proxy
nginx ingress のインストール
公式サイトを参考にhelm を使ってnginx ingress をインストールする
ingress service のexternalIPを設定するためにcontroller.service.externalIPs
にHostのIPアドレスを設定する
# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# helm install my-release ingress-nginx/ingress-nginx --set 'controller.service.externalIPs={192.168.1.100}'
ingress設定
nginx ingress controllerのoauth設定とGithubのIssueを参考に、マニフェストファイルを作成する
nginx.ingress.kubernetes.io/auth-url
, nginx.ingress.kubernetes.io/auth-signin
は
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: gateway-with-oidc-auth
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/auth-url: https://192.168.1.100:8088/oauth2/auth
nginx.ingress.kubernetes.io/auth-signin: https://192.168.1.100:8088/oauth2/start?rd=$escaped_request_uri
spec:
rules:
- http:
paths:
- path: /auth
backend:
serviceName: honypot
servicePort: 8080
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: oauth2-proxy
spec:
rules:
- http:
paths:
- backend:
serviceName: oauth2-proxy
servicePort: 4180
path: /oauth2
確認
ブラウザからHOSTの認証用のPath`https://192.168.1.100:8088/auth’ にアクセスするとkeycloakのログイン画面に遷移するので、
作成したユーザとパスワードを入力して、200 OK が返却されるか確認する
※認証されたセッションはブラウザのCookieとkeycloak側に保存されるので、ログアウトしたい場合はどちらも削除する
書く場所を迷った情報
- nginx ingress controller はデフォルトのTLSに
Kubernetes Ingress Controller Fake Certificate
を使うため、ローカル環境では自己証明書の作成は不要 - nginx ingress controller の設定は ingressのannotationsを通して行う 参考: Annotations
- nginx ingress での認証はlogoutが存在しないため、どのように設定するかは確認中 (おそらくこれ)
- nginx ingress のannotations
nginx.ingress.kubernetes.io/configuration-snippet
にproxy_set_header
を設定して、認証されたことを設定できるが、認証情報などを設定したい場合はどうやるか確認中