Linux PAMをマスターする:SSHの2FA/MFA設定とサーバーログインポリシーの構築

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

背景とPAMが重要である理由

SSH経由でLinuxマシンにログインしたり、sudoを実行したり、suでユーザーを切り替えたりするたびに、PAM(Pluggable Authentication Modules)が認証の門番としての役割を果たしています。Linuxでは、各デーモンに個別の認証ルーチンを実装させるのではなく、その処理をPAMに委ねています。PAMは、ユーザー向けサービスと背後にあるセキュリティバックエンドを仲介するモジュール式のブローカーとして機能します。

PAMは、認証ワークフローを4つの異なる機能(ファシリティ)に整理しています。

  • auth: 主にパスワードやTOTP検証トークンの入力を促すことで、ユーザーの本人確認を行います。
  • account: アクセス資格を確認します。パスワードの有効期限、利用可能な時間帯、アカウントのロック状態などを検証します。
  • password: トークンのローテーションや複雑性の強制など、認証情報の更新を管理します。
  • session: ユーザーストレージのマウント、ulimitの適用、監査ログの更新など、シェルセッションの開始・終了に伴うタスクを処理します。

SSH鍵のみに依存した運用は一般的な手法です。しかし、パスフレーズが設定されていない鍵が入ったノートPCを紛失した場合、インフラが危険に晒されることになります。PAMのauthスタックにTOTP(Time-based One-Time Password)モジュールを追加することで、この問題を解決できます。攻撃者は秘密鍵ファイルだけでなく、モバイル端末に表示される一時的な6桁のコードの両方を手に入れなければ侵入できなくなります。

エッジワークロードを実行する2 vCPUのUbuntuノード群において、この構成ではローカルでのトークン検証が約8〜12ミリ秒で完了しました。これにより、ハンドシェイクごとにリモートのディレクトリサーバーに問い合わせていた際に発生していた250ミリ秒のレイテンシスパイクを解消できました。

インストール

ここでは、オープンソースのGoogle Authenticator PAMパッケージを使用します。このパッケージはRFC 6238に準拠しており、Aegis、1Password、Google Authenticator、Bitwardenなどの標準的な認証アプリと確実に連携できます。

DebianおよびUbuntu

リポジトリのインデックスを更新し、PAMモジュールをインストールします。

sudo apt update
sudo apt install -y libpam-google-authenticator

RHEL、Rocky Linux、AlmaLinux

まずEPELリポジトリを有効化してから、パッケージをインストールします。

sudo dnf install -y epel-release
sudo dnf install -y google-authenticator

設定手順

SSHのセキュリティを強化するには、ユーザーシークレットの生成、PAMサービス定義の更新、OpenSSHデーモンの再設定という3つの明確な段階が必要です。

ステップ1:TOTPシークレットキーの生成

SSHアクセスを必要とする一般ユーザー(非rootユーザー)としてログインします。ターミナルで直接生成ウィザードを実行してください。

google-authenticator -t -d -f -r 3 -R 30 -q

各フラグの役割は以下の通りです。

  • -t: カウンタベースのトークン(HOTP)ではなく、標準のタイムベーストークン(TOTP)を設定します。
  • -d: リプレイ攻撃を防ぐため、トークンの再利用をブロックします。
  • -f: ファイルパスの確認を求めず、~/.google_authenticatorに直接状態を書き込みます。
  • -r 3 -R 30: レート制限を適用します。30秒あたり最大3回までの認証試行に制限します。
  • -q: 静かに実行し、シークレット生成時に対話型の確認をスキップします。

ユーティリティによってBase32シークレット、ASCII形式のQRコード、および5つの緊急用バックアップコードが出力されます。このシークレットをスマートフォンの認証アプリに追加してください。8桁の緊急用コードはKeePassXCなどのオフラインパスワードマネージャーに保管しておきましょう。スマートフォンが故障・紛失した際、アクセスを取り戻す唯一の手段となります。

ステップ2:SSH用のPAM設定

テキストエディタでSSHのPAMプロファイルを開きます。

sudo nano /etc/pam.d/sshd

ほとんどのデフォルト設定には、標準的なパスワードプロンプトが含まれています。DebianやUbuntuでは@include common-authを、RHELではauth substack password-authを探してください。Linuxユーザーパスワードを要求せず、SSH鍵+TOTPのみを認証要件とする場合は、該当するインクルード行をコメントアウトします。

# 標準のUNIXパスワードへのフォールバックを無効化
# @include common-auth

次に、認証モジュールをファイルの末尾に追加します。

auth required pam_google_authenticator.so nullok

nullokパラメータを指定すると、ユーザーがTOTPトークンを生成する前でも通常通りログインできるようになります。これにより、導入移行時の意図しない締め出し(ロックアウト)を防げます。チーム全員がQRコードの登録を完了したら、nullokを削除して全ユーザーにMFAを強制してください。

ステップ3:OpenSSHで鍵認証+MFAを強制する

続いて、/etc/ssh/sshd_configのデーモン設定を編集します。

sudo nano /etc/ssh/sshd_config

OpenSSHとPAMを連携させるために、以下のディレクティブを設定します。

# PAMおよび対話型入力プロンプトを有効化
UsePAM yes
KbdInteractiveAuthentication yes
PasswordAuthentication no

# 最初に公開鍵認証を要求し、その後にPAM TOTPプロンプトを要求
AuthenticationMethods publickey,keyboard-interactive

注意:OpenSSHのバージョンが8.7より古い場合は、KbdInteractiveAuthentication yesの代わりにChallengeResponseAuthentication yesを使用してください。

ここで最も重要なのがAuthenticationMethodsの行です。OpenSSHは公開鍵を検証した後にのみ、認証コードを入力するための対話型プロンプト(keyboard-interactive)を表示するようになります。

変更を適用する前に構文チェックを行います。問題がなければサービスを再起動してください。

sudo sshd -t && sudo systemctl restart sshd

ステップ4:アカウントロックアウトによるブルートフォース攻撃の防止

MFAは個々の認証情報を保護しますが、頻繁なブルートフォース攻撃はCPUリソースを無駄に消費します。pam_faillockを使用すれば、攻撃を抑制できます。/etc/security/faillock.confを開きます。

# 15分間に4回試行が失敗した場合にユーザーをロックアウト
deny = 4
fail_interval = 900
unlock_time = 600
even_deny_root

この設定により、ログインに4回連続で失敗すると、アカウントが10分間(600秒間)凍結されます。クレデンシャルスタッフィングツールを動かすボットネットの侵入試行を即座に遮断できます。

動作確認とトラブルシューティング

現在のrootターミナルセッションは開いたままにしておいてください。設定をテストするために、作業端末で新しいターミナルタブを開きます。万が一問題が発生した場合でも、既存のシェルから修正できるため締め出しを防ぐことができます。

ステップ1:ログインシーケンスのテスト

詳細ログを出力するオプション(-v)を指定してSSH接続を開始します。

ssh -v [email protected]

クライアントが秘密鍵を送信し、部分的な成功シグナルを受け取った後、動的トークンの入力を求めるプロンプトが表示されます。

部分的に認証に成功しました。
認証コード:

6桁の認証コードを入力してEnterキーを押します。正常に完了すれば、すぐにシェルが起動します。

ステップ2:リアルタイムのPAMログを確認する

ログインの発生に合わせてPAMイベントを追跡します。UbuntuやDebianシステムでは、認証ログをリアルタイムで確認します。

sudo tail -f /var/log/auth.log | grep -E "sshd|pam"

RHEL、Fedora、Rocky Linuxの場合は、systemdジャーナルを確認します。

sudo journalctl -u sshd -f

コードの入力を誤ると、PAMに明確な失敗理由が記録されます。

pam_google_authenticator(sshd:auth): ユーザー deployer の認証コードが無効です
198.51.100.24 ポート 48122 ssh2 からの deployer の keyboard-interactive/pam 認証に失敗しました

ステップ3:ロックアウト状態の確認と解除

エンジニアがトークンを打ち間違えてロックアウトが発生した場合は、失敗ステータスを確認します。

sudo faillock --user deployer

失敗した各試行のタイムスタンプ付きログが表示されます。ロックを解除して即座にアクセスを復旧するには、次のように実行します。

sudo faillock --user deployer --reset

pam_faillockとTOTPの必須化を組み合わせることで、サードパーティのSaaS型IDブローカーに依存することなく、Linuxホスト上で強固な多層防御(Defense-in-depth)を実現できます。

Share: