Background
Keycloak を LDAP 認証に繋げていましたが、 2FA したくなったのでやってみました。
Goal
Keycloakのログイン画面で今まで通りの LDAP or kerberos 認証を行います。
認証に成功すると、DuoにRedirectされます。
Duoがスマホにpush通知を送ります。
approveすると、ログイン成功となりサービスの画面が開きます。
Preparation: Keycloak を upgrade する
まず今回使う Keycloak Plugin はこちらです。
DuoUniversalKeycloakAuthenticator
現時点では v1.0.7 が最新です。Keycloakは 23 が出てますが、22.0.1に対応してます。
Keycloak 23.0.1 でも動いたんですが、設定画面が一部表示されなかったりしたので素直に Keycloak 22.0.1 で使いました。
Keycloakのupgradeが必要な人は前回の記事を見てください。WildFly版の Keycloak には(多分)非対応なので、素直に Quarkus な v22 まで上げましょう。v16 に未来はないです。
Keycloak 23 に対応する PR も出てるので、Java 得意な人は build してみてください。私は苦手なので...
Keycloak に DuoUniversalKeycloakAuthenticator を追加しよう
Dockerfile
docker imageに DuoUniversalKeycloakAuthenticator を追加します。
まず、入れたい DuoUniversalKeycloakAuthenticator を ↓ からダウンロードしておきます。.jar ファイルが登録されています。maven の build いらず。Java 苦手民に優しい。
ダウンロードした .jar を Keycloak の image に追加します。
official doc では build stageを分けるように言っているのでその通りに従ってみました。
FROM quay.io/keycloak/keycloak:22.0.1-2 as builder
WORKDIR /opt/keycloak
COPY DuoUniversalKeycloakAuthenticator-1.0.7_22.0.1wd.jar /opt/keycloak/providers
RUN /opt/keycloak/bin/kc.sh build
FROM quay.io/keycloak/keycloak:22.0.1-2
COPY --from=builder /opt/keycloak/ /opt/keycloak/
EXPOSE 8080
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start-dev"]
この部分でpluginを追加しています。特定のファイル名に rename とかは不要です。
COPY DuoUniversalKeycloakAuthenticator-1.0.7_22.0.1wd.jar /opt/keycloak/providers
Quarkus版ではプラグインのフォルダが /opt/keycloak/providers
に変わっています。ここに .jar
を置いておくと、 keycloakを起動(kc.sh start/start-dev
) した時に、まずこの plugin をinstallしてから動作を開始してくれます。便利。
Duo Universal MFA Config の設定
Keycloakを起動したら admin console を開きます。あとは README の通りです。
想定外だったのは、 Keycloak side bar の Identity providers
を使わないことでした。plugin は専用の auth step として追加されてます。へ〜〜。
- Side menu から Authentication を開く
-
browser
をduplicateして、Copy of Browser
を作ります。 -
Copy of Browser
を開きます -
Add step
を押します -
Duo Universal MFA
を探して追加します。Required
にします。 -
Username Password Form
の下に移動します。- これで通常のログイン画面が終わった後に実行されます。
- Gear iconをクリックして設定画面を開きます。
- Duo の
WebSDK
で発行したコードを入力します。- Duo の情報はこう mapping されます
- テスト中は
Fail Safe
を on にした方がいいです。Duo 認証に失敗してもログインできます。Admin consoleに 2FA を追加して admin が入れなくなると、めんどいので・・・ :( -
Client Overrides
は、Clientごとに Duo の key を変えたい時に使えそうです。(試してない)- (Clientごとに Auth Flow を作ったほうがわかりやすいような気もするけど、それも大変だからこの機能があるんだろうな・・)
- とある Keycloak clientにかける場合は、clientのAdvancedな設定から Authentication flowを
Copy of Browser
で override すれば試せます。 -
side bar
>Authentication
>Copy of browser
> "..." なコンテキストぽいメニュー >Bind flow
でbrowser
に当てれば admin console含めて web form全部に適用されますけど、ミスるとログインできずなんの設定も変えれなくなります・・ :(
Troubleshooting
うまく行かない時はまずそのサーバから Duo に push できるか試しましょう。これはpythonの例です。
import duo_client
import json
# Duo Securityの設定
ikey = 'Client ID'
skey = 'your-secret-key'
host = 'api-xxxxxxxx.duosecurity.com'
username = 'your duo user id`
# Duo Securityのクライアントを作成
client = duo_client.Auth(ikey, skey, host)
# Duo SecurityのAPIに接続
response = client.check()
# レスポンスを表示
print(response)
# Device状況を確認
res = client.preauth(username)
print(json.dumps(res, indent=2))
# Push送信
res = client.auth('push', username=username, device='auto')
print(json.dumps(res, indent=2))
Errors
Login request denied.
Duo WebSDK の IP ACL にかかると、Login request denied.
エラーが出ます。Duo側の設定を見直しましょう。そのサーバが使う Public IP address を許可する必要があります。
{
"result": "deny",
"status": "deny",
"status_msg": "Login request denied."
}
Read time out
preauth()
は成功するのに auth()
が成功しないという悪夢がありました。Keycloak log を見ると
Keycloak が duo exception で read timeout
を吐いてました。Plugin のエラーハンドリングは効いていません。これはなんと、その環境がたまたま MTU の問題を抱えていて特定のサイズを超えるとパケットの送受信ができないからでした。まともにインターネットができなかったとは・・。これはハマった。。。そんなのを引くのは私くらいでしょう。
終わり
書いてみると簡単ですけど、それはまあ関係ない問題もありハマりにハマりました。どうか皆さん、気楽な 2FA ライフを送ってください。人柱は自分だけで十分だ!