Vaultwardenをセルフホストして、パスワードレス化&既存パスワード管理のベンダ依存から脱却
Table of Contents
自宅k8sにBitwarden互換なOSS Vaultwardenをセルフホストして、ベンダーに非依存なパスワードレスログインを実現する
きっかけ
サービスへのログインを色々なベンダに依存しているのを、予予気持ちが悪いと思っていた。
最近Yubikey 5 NFCを買ったことをきっかけに、重い腰を上げて、普段使っているサービスをベンダ依存しないようにしつつ、パスワードレスでログイン出来るように設定してみた。
現状どのようなサービスをベンダ依存しているか整理してみると、以下の通り。
- パスワード
- Safari/Chromeのパスワードマネージャ
- TOTP(二段階認証コード)
- MS/GoogleのAuthenticator
- Passkey
- iOS パスキー/MS Authenticator
ここから、
- パスワード
- 各パスワードマネージャから、Vaultwardenに一本化
- Vaultwardenはマスターパスワード+Yubikey(iOSではFaceID)で保護
- TOTP
- VaultwardenのビルトインTOTP機能
- 万が一Vaultwardenが使えない際のバックアップとして、Yubikeyの組み込みTOTP機能を利用(Yubikey 5の指紋認証モデルでは使えないので注意)
- Passkey
- マルチデバイス対応のため原則Vaultwardenにパスキーを保存
- サービス側がセキュリティキーに対応している場合、2本目のパスキーとしてYubikeyを登録
を目指す。
表にするとこんな感じ。Vaultwardenが死んでいる時のバックアップとして、Yubikeyを持っておく感じ。
| サービスの種類 | メイン(Vaultwarden) | バックアップ(非Vaultwarden) |
|---|---|---|
| パスキー対応サイト | Vaultwardenに保存 | Yubikeyを直接登録 (物理セキュリティキー非対応のサイトの場合、iOSのデフォルトパスキーアプリで登録) |
| TOTPのみ対応サイト | Vaultwardenで管理 | **Yubikey本体(Yubico Auth)**に保存 |
| Vaultwarden自体 | マスターパスワード | YubikeyをFIDO2として登録 + リカバリコード |
そもそもパスキーとは
さすがにこのご時世にパスキーを聞いたことがない人はいないと思うが、パスキーが他の認証手段に比べてなぜセキュアなのか。それはパスキーやYubikey等のFIDO2規格は「共有された秘密」を持たない仕組みだから。 と言える。
従来型の認証はユーザとサーバが同じ秘密を保持し、それが一致するかを確認する仕組みだが、この方式ではサーバ側から漏洩する、通信経路で盗まれるという構造的な欠陥が存在する。
対してパスキー(FIDO2)では「公開鍵暗号方式」を利用するので、デバイス内に保管された秘密鍵は外に出ることがない。 デバイスを盗まれたら終わりと思うかもしれないが、FIDO2準拠デバイスにはソフトウェア的な暗号化だけではなく、物理的な防御1(耐タンパ性などと言ったりする)が備わっているため、中の秘密鍵を取り出すことは不可能に近い。
FIDO2の認証フローはざっくりと以下の通りである。
加えて、FIDO2が他の認証方式に比べてセキュアだと言える根拠として、フィッシング耐性がある。
パスキー等で認証する際は、「アクセスしているドメイン(URL)」を厳格にチェックする。フィッシングサイトが署名を求めてきても、デバイス側が「URLが違う」と判断して拒否するため、騙される余地がない。
以上の要素から、パスキーを標準の認証方法として設定できるようにしているサイトが増えている背景がある。
Vaultwardenのインストール
非公式だが、それなりにメンテされているhelmチャートがあるのでそれ経由で
helm repo add vaultwarden https://guerzon.github.io/vaultwarden
helm repo update
kubectl create namespace vaultwarden
# argon2でトークン生成(/adminページにアクセスする際のパスワード)
echo -n "your-admin-password" | argon2 "$(openssl rand -base64 32)" -id -t 3 -m 16 -p 4 -l 64 -e
# シークレット作成
kubectl create secret generic vaultwarden-secret \
--namespace vaultwarden \
--from-literal=ADMIN_TOKEN='$argon2id$...' # 上記出力を貼る
helm install vaultwarden vaultwarden/vaultwarden \
--namespace vaultwarden \
--values values.yamlstorageやingressは適宜読み替えて
domain: "https://vaultwarden.4nm1tsu.com"
adminToken:
# 事前にargon2でシークレット作成
existingSecret: "vaultwarden-secret"
existingSecretKey: "ADMIN_TOKEN"
# 初期設定後falseに
signupsAllowed: true
storage:
existingVolumeClaim:
claimName: "pvc-vaultwarden-data"
dataPath: "/data"
attachmentsPath: "/data/attachments"
service:
type: ClusterIP
ingress:
enabled: true
class: "nginx"
hostname: "vaultwarden.4nm1tsu.com"
path: "/"
pathType: "Prefix"
tls: true
tlsSecret: "letsencrypt-cert-vaultwarden"
nginxIngressAnnotations: true
additionalAnnotations:
cert-manager.io/cluster-issuer: letsencrypt-issuer確認
$ kubectl get pods -n vaultwarden
NAME READY STATUS RESTARTS AGE
vaultwarden-0 1/1 Running 0 68sVaultwardenでの設定
アカウント新規作成
初回ログインすると以下のような画面が出てくる。

アカウントの作成をクリックして、マスターパスワードを設定したら、速やかにvalues.yamlのsignupsAllowed:をfalseに設定する。
拡張機能インストール
このタイミングでchromeにBitwardenの拡張機能を入れておく。 自鯖に接続するにはポップアップ下部のbitwarden.comをクリックして、「自己ホスト型」を選択後、URLとマスターパスワードを入力する必要がある。

Chromeからcsvエクスポート
chrome://password-manager/settingsにアクセス。「パスワードをエクスポート」を選択して、ダウンロードしたファイルをVaultwardenの「ツール」>「Import」からインポートすると、Bitwarden拡張機能から、Chromeのパスワードマネージャから補完させていたパスワードはすべて補完されるようになる。※クレカの補完は含まれていない。

Yubikeyでの認証を必須化
「設定」>「セキュリティ」からマスターパスワード+FIDO2 WebAuthnでのログインを必須にする。

画面の指示に従ってYubikeyの設定を完了する。Yubikeyを使うのが初めての場合、この画面でPINの設定も行う必要がある。
Chromeでの補完を無効に
ChromeとBitwarden拡張機能で、ともにパスワード補完されてうざいので、Chrome側のパスワードを全削除する。

chrome://password-manager/settingsから「パスワードとパスキーを保存するか確認する」/「自動ログイン」をそれぞれOFFにしてもChromeのパスワード補完は消えなかった。環境イシューかも
iOSでの設定
スマホにもBitwarden authenticator/password managerアプリを入れる。

「設定アプリ」>「一般」>「自動入力パスワードとパスキー」からBitwardenをチェックし、ChromeはOFFに。
「確認コードを設定するアプリ」をBitwardenにすると、TOTPのデフォルトアプリがBitwardenになる。

また、そのままだと毎回マスターパスワードの入力を求められるので、password managerの設定から「Face IDでロック解除」をONに
Authenticatorとの同期を許可するをONにすると、TOTPコードの補完をしてくれるようになるが、2FAの意味がなくなるので、OFFにしておく。
各種サービスでPasskey/Yubikey登録
主要なサービスでPasskey/Yubikeyを登録していく
AWS
AWSコンソールログイン後、右上のユーザ名をクリックして、「セキュリティ認証情報」>「多要素認証(MFA)」の「MFAデバイスの割り当て」

MFAデバイスとして、「パスキーまたはセキュリティキー」を選択

ブラウザの指示に従って、パスキー・セキュリティキーをそれぞれ追加する。

ちなみに、AWSのrootユーザのログインをパスワードレスにはできなかった。
https://www.google.com/intl/ja/account/about/passkeys/にアクセス後、「パスキーを作成」をクリック
通常の方法でログイン後、「パスキーとセキュリティキー」というパスキー管理の画面になるので、「パスキーを作成」をクリックしてそれぞれの認証器を追加
GibHub
https://github.com/settings/securityにアクセスし、Passkeys > Add passkeyからそれぞれの認証器を追加
Authentik
Adminユーザでログイン後、右上の設定アイコンから「MFAデバイス」を選択し、「登録」>「WebAuthn Device」から各種認証器を追加。
この時点でパスワード+WebAuthnの2FAになっているが、完全なパスワードレスを実現したい場合はdefault-authentication-flowを編集する必要がある。時間があるときにパスワードレス化してみようと思う。
NAS
QNAPのページにパスキー認証の設定方法が記載されていたが、手持ちのNASは対応するOSにアップグレードできなそうだったので泣く泣く諦めた。
おわりに
これまで各ベンダの作法にバラバラに預けていた認証情報をVaultwardenという「自分専用のセキュアな拠点」に集約したことで、ようやく管理の主導権を取り戻せた実感が持てた。
単に一箇所にまとめただけではなく、そこにYubikey 5 NFCという物理的な「マスターキー」を組み合わせたのが今回の構成の肝だ。Vaultwarden自体のロック解除には生体認証やPINを使い、万が一のデバイス紛失やシステムトラブルに備えてYubikeyにバックアップを逃がしておく。
結果として、PCでもiPhoneでも、どのブラウザを開いても生体認証のみで安全にログインできる環境が整った。パスワードの使い回しや複雑な文字列の記憶といった不毛な運用から解放され、セキュリティレベルを底上げしつつ、「楽」なログイン体験を手に入れることができた。
デバイスに搭載されるTPM(Trusted Platform Module)や、セキュアエレメント等 ↩︎