0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

KibanaにMFA(多要素認証)の経路を作る

Posted at

はじめに

自宅のkubernetes上にデプロイしているkibanaをインターネットに外部公開しようとしましたが、デフォルトの認証ではなく多要素認証にしてみようと考えました。
どうせならアプリ側で都度認証の実装をするのではなく、OpenShiftのようにkubernetes上で汎用的な認証サービスをリバースプロキシで提供しようと考えました。
実装検証も兼ねているので、keycloak+mariadbとリバースプロキシ用のhttpdのpodを作成します。

前提環境

  • kubernetes 1.28.1 (aarch64 : Raspberry Pi 4B)
  • Synology NAS (Synology CSI Driver for Kubernetes)
  • Elasticsearch / Kibana 8.11.1

keycloak + mariadb (pod) の作成

keycloakをSSL化する際の証明書を作成してconfigmapにしておきます。(※お作法としてはsecretの方が良いです)

# kubectl create ns keycloak
# openssl req -x509 -nodes -newkey rsa:4096 -keyout keycloak.key -out keycloak.crt -days 365
# kubectl create configmap keycloak-crt -n keycloak --from-file=keycloak.crt --from-file=keycloak.key

mariadb用のPVCを用意します。永続ストレージはご自宅の環境に合う内容にしてください。

iscsi-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: iscsi-keycloak-pvc
  namespace: keycloak
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: synology-sc

(※AGEは無視してね)

# kubectl apply -f iscsi-pvc.yaml
# kubectl get pvc -n keycloak
NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
iscsi-keycloak-pvc   Bound    pvc-5b2f2957-f26f-4d49-bf11-1f7ca292e7ee   10Gi       RWO            synology-sc    56d

mariadbとkeycloakのコンテナでpodを作成します。分割してマイクロサービスにした方が良いです。
(※自宅でDBはElasticsearchを使っていて、mariadbはkeycloak専用です)

keycloak-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: keycloak
  namespace: keycloak
  labels:
    app: keycloak
spec:
  containers:
  - name: mariadb
    image: docker.io/mariadb:10.5
    env:
    - name: MARIADB_DATABASE
      value: "kcdb"
    - name: MARIADB_ROOT_PASSWORD
      value: "rootpass1105"
    ports:
    - containerPort: 3306
    volumeMounts:
    - name: data
      mountPath: /var/lib/mysql
  - name: keycloak
    image: quay.io/keycloak/keycloak:22.0.5
    env:
    - name: KC_DB
      value: "mariadb"
    - name: KC_DB_URL
      value: "jdbc:mariadb://localhost:3306/kcdb?characterEncoding=UTF-8"
    - name: KC_DB_USERNAME
      value: "root"
    - name: KC_DB_PASSWORD
      value: "rootpass1105"
    - name: KEYCLOAK_ADMIN
      value: "kcadmin"
    - name: KEYCLOAK_ADMIN_PASSWORD
      value: "kcadmin0420!"
    - name: KC_PROXY
      value: "edge"
    - name: KC_HTTP_ENABLED
      value: "true"
    - name: KC_HTTPS_PORT
      value: "8443"
    - name: KC_HTTPS_CERTIFICATE_FILE
      value: "/opt/keycloak/tls/keycloak.crt"
    - name: KC_HTTPS_CERTIFICATE_KEY_FILE
      value: "/opt/keycloak/tls/keycloak.key"
    args: ["start-dev"]
    ports:
    - name: http
      containerPort: 8080
    - name: https
      containerPort: 8443
    volumeMounts:
    - name: crtvol
      mountPath: /opt/keycloak/tls
  volumes:
  - name: crtvol
    configMap:
      name: keycloak-crt
  - name: data
    persistentVolumeClaim:
      claimName: iscsi-keycloak-pvc

バージョンは個人的な理由があり、mariadbは10.5でkeycloakはquay.ioの22.0.5を使います。
mariadb側の設定はpod内部の事なので割愛しますが、keycloakの管理ユーザーは「kcadmin」パスワードは「kcadmin0420!」にして、httpとhttpsの両方でアクセスできるようにしています。
小さい設定ファイルのためにストレージ切ったりコンテナイメージをビルドするのは嫌なので、configmapを活用しています。

# kubectl apply -f keycloak-pod.yaml
# kubectl get pods -n keycloak
NAME       READY   STATUS    RESTARTS   AGE
keycloak   2/2     Running   0          56d

keycloakはテキトーにNodePortで公開しておきます。

keycloak-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: keycloak
  namespace: keycloak
  labels:
    app: keycloak
spec:
  type: NodePort
  ports:
    - name: keycloak
      port: 8080
      targetPort: 8080
      nodePort: 30808
    - name: keycloaks
      port: 8443
      targetPort: 8443
      nodePort: 30843
  selector:
    app: keycloak
# kubectl apply -f keycloak-svc.yaml
# kubectl get svc -n keycloak
NAME       TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                         AGE
keycloak   NodePort   10.96.55.87   <none>        8080:30808/TCP,8443:30843/TCP   56d

keycloak側での設定

keycloakは元々設定項目が多いのですが、バージョンによってメニューや設定項目名が変わっているので、追従するのは大変です。(元々そんなに理解もしていないのに...)

keycloakのサービスにアクセスして「Administration Console」を開きます。

keycloak0.png

podで設定した管理ユーザー「kcadmin」でログインします。

keycloak1.png

Realmの作成

左上の「master」(レルム)がプルダウンメニューになっています。プルダウンから「Create Realm」ボタンを押します。

keycloak2.png

以下の項目を設定して「Create」ボタンを押します。
Realm name : "MyLabo"

keycloak3.png

Clientsの作成

左のメニューから「Clients」を選択して、「Create client」ボタンを押します。

keycloak4.png

以下の項目を設定して「Next」ボタンを押します。
Clinet ID : "kibana"

keycloak5.png

以下の項目を設定して「Next」ボタンを押します。
Client authentication : "On"

keycloak6.png

以下の項目を設定(リバースプロキシ予定のKibanaのURL)して「Save」ボタンを押します。
Valid redirect URIs : "http://192.168.1.9:31561/*"
(※とりあえず自宅内からアクセスできるURLにしてます。リバースプロキシを外部公開した場合は変更する個所です。)

keycloak7.png

「Credentials」タブを開いて、Client secretをメモ帳かどこかにコピーしておきます。

keycloak8.png

もう一つ、左メニュー「Realm settings」の「Endpoints」から「OpenID Endpoint Configuration」のURLをコピーしておきます。

keycloak9.png

ユーザーの作成

左のメニューから「Users」を選択して、「Add user」ボタンを押します。

keycloak10.png

ユーザー名は任意で、初回ログイン時にOTPの設定とパスワード再設定するように設定して「Create」ボタンを押します。
Required user actions : "Configure OTP" , "Update Password"
Username : "haomei"

keycloak11.png

Credentialsタブを開いて、「Set password」ボタンを押します。

keycloak12.png

ユーザーの初期パスワードを入力して「Save」ボタンを押します。

keycloak13.png

keycloak14.png

2FAの設定

左メニューの「Authentication」を選択して「Flows」タブの「browser」をクリックします。

keycloak15.png

「Browser - Conditional OTP」を「Required」にします。

keycloak16.png

リバースプロキシ(pod)の作成

httpdのコンテナイメージからカスタムイメージを作成します。

Dockerfile
FROM httpd:latest

RUN apt-get update && apt-get install -y \
    libapache2-mod-auth-openidc \
    apache2-utils \
    && apt-get clean

RUN sed -i \
    -e 's/^#\(Include .*httpd-proxy.conf\)/\1/' \
    -e 's/^#\(LoadModule .*mod_proxy.so\)/\1/' \
    -e 's/^#\(LoadModule .*mod_proxy_http.so\)/\1/' \
    conf/httpd.conf

RUN echo "Include conf/extra/proxy.conf" >> conf/httpd.conf
RUN echo "Include conf/extra/openidc.conf" >> conf/httpd.conf

dockerコマンドかpodmanコマンドでbuildして自宅のNodePortで公開しているコンテナレジストリにpushします。
自宅にコンテナレジストリが無い場合はkubernetesの各ノードにコピーしてください。

# podman build -t 192.168.1.9:30500/rproxy:v1.0 .
# podman push --tls-verify=false 192.168.1.9:30500/rproxy:v1.0

httpdの拡張設定(OIDC連携用)のファイルを作成します。
ほぼテンプレですが、以下の項目は今まで設定した内容や確認した内容で埋める必要があります。

OIDCProviderMetadataURL : keycloakの「OpenID Endpoint Configuration」のURL
OIDCClientID : keycloakの「Clinet ID」
OIDCClientSecret : keycloakの「Client secret」
OIDCRedirectURI : keycloakの認証が成功した後に遷移させたいURL
OIDCCryptoPassphrase : 何でも良かったはず...

openidc.conf
LoadModule auth_openidc_module /usr/lib/apache2/modules/mod_auth_openidc.so

OIDCSSLValidateServer Off
OIDCProviderMetadataURL https://192.168.1.9:30843/realms/MyLabo/.well-known/openid-configuration
OIDCClientID kibana
OIDCClientSecret disSUTwote75wC7Ipho2c6JRik9YvB6a
OIDCRedirectURI http://192.168.1.9:31561/login
OIDCCryptoPassphrase hogehoge

<Location />
    AuthType openid-connect
    Require valid-user

    Define ES_USERNAME elastic
    Define ES_PASSWORD "password"
    Define CREDENTIALS ${ES_USERNAME}:${ES_PASSWORD}
    RequestHeader set Authorization "expr=Basic %{base64:${CREDENTIALS}}"
</Location>

次にリバースプロキシの設定ファイルを作成します。

proxy.conf
ServerName localhost

ProxyRequests Off
ProxyPass / http://kibana.elastic.svc.cluster.local:5601/
ProxyPassReverse / http://kibana.elastic.svc.cluster.local:5601/

<Location />
    ProxyPreserveHost On
    Require all granted
</Location>

設定ファイルをconfigmapにしておきます。

# kubectl create configmap extra-httpd-conf -n default --from-file=openidc.conf --from-file=proxy.conf

リバースプロキシのpodを作成します。imageはhttpdのコンテナイメージから作成したカスタムイメージを指定します。
設定ファイルはconfigmapをpod内のディレクトリにmountしているので、deploymentにすれば動的な設定変更の運用ができると思います。

rproxy-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: rproxy
  labels:
    app: rproxy
spec:
  containers:
  - name: httpd
    image: localhost:30500/rproxy:v1.0
    ports:
    - containerPort: 80
    volumeMounts:
    - name: exconf
      mountPath: /usr/local/apache2/conf/extra
  volumes:
  - name: exconf
    configMap:
      name: extra-httpd-conf
# kubectl apply -f rproxy-pod.yaml
# kubectl get pods/rproxy
NAME     READY   STATUS    RESTARTS   AGE
rproxy   1/1     Running   0          3h44m

nodePortのポート番号は、openidc.conf の OIDCRedirectURI で指定しているポート番号に合わせてください。
また、MFA対応のkibanaもこのポート番号でのログインをする事になります。

rproxy-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: rproxy
  labels:
    app: rproxy
spec:
  type: NodePort
  ports:
    - name: rproxy
      port: 80
      targetPort: 80
      nodePort: 31561
  selector:
    app: rproxy
# kubectl apply -f rproxy-svc.yaml
# kubectl get svc/rproxy
NAME     TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
rproxy   NodePort   10.96.6.229   <none>        80:31561/TCP   63d

リバースプロキシはURLベースにした方が便利だと思うのですが、kibana側の対応が必要になるので今回は見送ります。

kibanaへのログイン

リバースプロキシのサービス http://192.168.1.9:31561/ へアクセスすると、keycloakの認証画面に遷移します。keycloakで追加したユーザー名と初期パスワードでログインします。

login0.png

OTPの初回認証画面になります。スマホにTOTPのアプリをインストールしてカメラでQRコードを読み取るか、ブラウザにTOTPの拡張機能をインストールして同様にQRコードをキャプチャして設定します。
(ここからはもたもたしているとkeycloak側がタイムアウトしてしまいますのでスムーズに!)

login1.png

EdgeやChromeの場合は、Authenticator: 2FA Clientをインストールします。

login3.png

拡張機能からQRコードをスキャン(枠で囲む)して設定します。

login5.png

OTPのコードが表示されるので、クリックしてコピーします。

login7.png

「One-time code」はコピーしたOTPコードを貼り付けて「Device Name」はテキトーに入力して「Submit」ボタンを押します。

login8.png

次にパスワードの再設定も促されるので、設定して「Submit」ボタンを押します。

login9.png

うまく2要素認証でkibanaにログインできました。

login10.png

おわりに

長い手順でしたが、自宅サービスに2要素認証を取り入れる事ができそうです。
ユーザー追加の部分はkeycloakのAdmin CLIを使えばもっと簡素化できそうですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?