ProtectHome と TOTP シード: VPN ログインが失敗する理由
正しいコードでも、シードを開けない PAM モジュールは助けられません。drop-in と書き込み可能な状態パスのどちらかを選び、その後ユニットを確認してください。
認証アプリは新しい六桁のコードを表示するのに VPN はそれを拒否しますが、同じコードを自分のシェルから試すと問題なく検証されます。この差がすべてです。登録シークレットを開けない PAM モジュールは間違ったコードとまったく同じに見える失敗を返し、ホームディレクトリを隠すサービスの環境がそのよくある原因です。権限を緩める前に、自分が三つの状況のどれにいるのかを決めてください。
三つの失敗のうち、どれでしょう?
一つ目は、モジュールがまったく読めないシークレットです。二つ目は、読めるが更新できないシークレットです。三つ目は本当に間違ったコードで、通常は時計のずれや再生された値が原因です。
失敗した試行を一度分だけ、サービスのジャーナルとシステムの認証ログを読んで切り分け、プラグインがどの PAM サービスを読み込むのか、そのサービスがどのモジュール行を使うのかを確認します。一般的な認証失敗メッセージでは区別できず、手動テストが成功しても、あなたのシェルはサービスの環境ではないため同じように区別できません。
サービスだけが失敗する場合、ユニットのファイルシステムビューが通常の犯人です。
systemctl show -p ProtectHome -p ProtectSystem -p ReadWritePaths [email protected]
ls -l /var/lib/vpn-otp/*/.google_authenticator 2>/dev/null | head -n 20選択肢 A: シークレットをホームの外へ移す
サービスが /home を隠していて、その保護を残したい場合は、サービスから見える場所にシークレットを保管します。モジュールは secret= パスと、ファイル操作を行う user= アカウントを受け付けるので、中央の保管場所が多数の ID に対応できます。
これがすっきりした選択肢で最も注意を要するものであり、共有の保管場所では一つのディレクトリがすべてのアカウントのセキュリティ境界になるからです。
auth required pam_google_authenticator.so secret=/var/lib/vpn-otp/${USER}/.google_authenticator user=vpn-otp信頼する前に、アカウントが存在すること、モジュールのワーカーがそのアカウントに切り替えられること、ファイルが制限的なモードでそのアカウントに所有されていることの三つを確認します。~ やホーム変数のパスを user= と併用せず、パスを通すためにモジュールの所有権チェックを無効にしないでください。パスワード段階と残りのスタックはそのまま保ちます。
選択肢 B: ファイルを残してパスを開く
シークレットをユーザーのホームに残さなければならない場合、ユニットには、ホスト上のすべてのホームを露出せずにその一つのディレクトリだけを含むビューが必要です。drop-in はサンドボックス化されたホームの下に、範囲を狭く限定したバインドマウントを追加できます。
これによりユーザーごとの所有権をモデルとして維持できますが、サンドボックスが変わるたびに見直す必要があるユニット変更のコストがかかります。
見落としやすい限界が二つあります。サンドボックスが隠しているディレクトリの下に読み取り専用のパスを追加しても、親が存在しないため露出しません。また、モジュールが状態を更新する必要があるならマウントは書き込み可能でなければならず、それが次の選択肢につながります。drop-in は /usr/lib の下のパッケージ済みユニットを編集するのではなく別ファイルとして書き、パッケージ更新で黙って元に戻らないようにしてください。
選択肢 C: サンドボックスを保ち、状態の書き込みを直す
シークレットの読み取りは認証の半分にすぎません。モジュールは時刻のずれ情報、リプレイ防止、カウンターを書き換えることがあり、読み取りチェックを通ったファイルでも書き込みに失敗することがあります。
その失敗は今回ではなく次回の試行で間違ったコードのように見えるため、時計のずれと誤解されます。
ホーム全体を書き込み可能にするのではなく、専用の書き込み可能なディレクトリを与え、シークレットのパスをそこへ向けます。ディレクトリの所有者、ファイルシステムの空き容量、ユニットが他の場所で書き込みを制限していないかを確認します。その後、二回目が書き込み経路を証明し、一回目は読み取りだけを証明するため、連続した二回のログインを試してください。
自分自身ではなく、サービスとして検証する
重要なチェックはすべて、ユニットの ID でユニットのサンドボックス内で実行する必要があります。
root で成功するコマンドは、ファイルが存在することしか教えてくれません。実際のサービス経由のログインは、モジュールがシークレットを読み、使い、必要なものを書き戻せるかどうかを教えてくれます。
最後に、要件はそのままにしてください。null パスワードの選択肢を追加したり、読み取り専用の失敗を受け入れたり、サンドボックスを外したりすれば、ポリシーを変えて症状を消すことになります。コードの提示を求められるユーザーは、シークレットに到達できないときは依然として拒否されるべきです。