SSHに鍵認証を設定したから安心、と思っていませんか。実は秘密鍵ファイルがマルウェアや誤操作で漏洩したとき、鍵認証だけでは不正ログインを防げません。二要素認証(2FA)を追加すれば、鍵が流出しても「もう一つの確認」なしでは入れない状態を作れます。
この記事では、PAM(Pluggable Authentication Modules)とGoogle Authenticatorを組み合わせ、LinuxサーバーへのSSHログインに時刻ベースのワンタイムパスワード(TOTP)を追加する手順を解説します。Ubuntu/Debian/RHEL系に対応した実践手順です。
SSH二要素認証とは?鍵認証との組み合わせが重要な理由
SSH鍵認証はパスワード認証と比べて大幅に安全です。しかし「秘密鍵さえ守れば大丈夫」という一点突破の構造には根本的な限界があります。
認証の3要素は「知っているもの(パスワード)」「所持しているもの(鍵・トークン)」「本人であること(生体認証)」です。SSH鍵認証は「所持しているもの」の1要素だけを使っています。二要素認証は、これに「知っているもの」や「別の所持物から生成されるもの」を加えることで、一つの要素が漏洩しても攻撃者を弾ける構造を作ります。
SSHに置き換えると:
・所持しているもの(第1要素): SSH秘密鍵ファイル
・所持物から生成されるもの(第2要素): スマートフォン上のTOTP(時刻連動ワンタイムパスワード)
TOTPはRFC 6238に準拠した業界標準規格で、サーバーとアプリが共有する秘密鍵と現在時刻から独立してコードを計算します。30秒ごとに変わる6桁の数字は、Google Authenticator・Authy・Microsoft Authenticatorなど主要なスマートフォンアプリで生成できます。
攻撃者が鍵認証を突破するシナリオ
「鍵認証があるから安全」という認識が崩れる場面を、守るために整理しておきましょう。
・マルウェアによる鍵ファイル盗取: PCに感染したマルウェアが ~/.ssh/ 配下を丸ごと外部へ送信する。このケースは実際のAPT攻撃でも確認されている。
・Gitリポジトリへの誤コミット: Terraform・AnsibleなどIaCファイルに秘密鍵をベタ書きし、パブリックリポジトリに公開してしまう。GitGuardianの調査では毎年数百万件規模で発生している。
・クラウドストレージ経由の流出: iCloud Drive・OneDriveの同期対象フォルダに ~/.ssh/ が含まれ、別端末から秘密鍵が取得される。
・パスフレーズの解読: 短いパスフレーズを設定した秘密鍵は、鍵ファイルを入手されるとオフラインでブルートフォース解読できる場合がある。
・CI/CDパイプライン経由の流出: ビルドサーバーのシークレット管理が甘く、ビルドログに秘密鍵が混入して流出する。
TOTPを追加しておけば、これらすべての状況で「鍵が盗まれてもログインできない」状態を作れます。
事前確認と準備
1. 動作確認済み環境
・Ubuntu 22.04 / 24.04 LTS: apt でインストール可能
・Debian 11 / 12: apt でインストール可能
・Rocky Linux / AlmaLinux 8・9: dnf + EPELリポジトリで対応
・RHEL 8 / 9: サブスクリプション環境で dnf 対応
【重要】作業前の必須確認事項
設定ミスで締め出されると、リモートからリカバリできなくなります。以下を必ず守ってください。
・作業中は別の緊急ログインセッションを開いたまま進める: 新しいSSH設定のテストは、既存セッションを切断せずに別ウィンドウから行う
・VPS・クラウドの場合は管理コンソールのシリアルアクセスを事前確認: 万が一締め出されてもコンソールから復旧できる状態を作っておく
・SSH鍵認証が動作済みであること: 鍵認証を土台として2FAを追加します。まずSSH鍵認証を完成させてから実施してください(SSH接続のセキュリティ対策参照)
・スマートフォンにGoogle Authenticatorをインストール済みであること: iOS / Android どちらも無料で利用可能
具体的な設定手順(Ubuntu/Debian系)
1. libpam-google-authenticatorのインストール
# パッケージリストを更新してインストール sudo apt update sudo apt install libpam-google-authenticator -y
2. 各ユーザーごとのTOTPシークレット生成
サーバーにSSHログインするユーザー(例: ubuntu、deploy等)それぞれが自分のアカウントで実行します。rootで代行しないでください。
# このコマンドはSSHログインするユーザー自身で実行する google-authenticator
コマンドを実行すると対話形式で質問が始まります。以下が推奨の回答です:
・Do you want authentication tokens to be time-based? → y(TOTPを使用)
・QRコードの表示: 表示されたQRコードをGoogle Authenticatorアプリでスキャンしてアカウントを追加
・緊急コード(Emergency scratch codes): 表示される5個のコードを印刷して安全な場所に保管する。スマートフォンを紛失した際の緊急ログインに使用する
・Do you want me to update your “~/.google_authenticator” file? → y
・Do you want to disallow multiple uses of the same authentication token? → y(リプレイ攻撃防止)
・Do you want to increase the original generation time limit? → n(時刻同期が取れていれば不要)
・Do you want to enable rate-limiting? → y(ブルートフォース防止)
設定完了後、`~/.google_authenticator` ファイルが生成されます。このファイルにはシークレットキーが含まれるため、アクセス権を確認します:
# シークレットファイルのパーミッション確認(600=自分のみ読み書き) ls -la ~/.google_authenticator # -rw------- 1 username username ... が正しい状態
3. PAMの設定変更
PAM(Pluggable Authentication Modules)はLinuxの認証基盤です。SSHの認証処理にGoogle Authenticatorモジュールを組み込みます。
sudo nano /etc/pam.d/sshd
ファイルの先頭(`@include common-auth` より前)に以下の1行を追加します:
# Google Authenticator (TOTP) を追加 — common-authより前に記述する auth required pam_google_authenticator.so nullok
nullokオプションについて: `~/.google_authenticator` を作成していないユーザーはTOTPなしでログインできます。段階的に展開する場合に有効です。全ユーザーの設定が完了したら `nullok` を削除して全員に強制適用します。
4. SSHDの設定変更
sudo nano /etc/ssh/sshd_config
以下のパラメータを確認・変更します:
# 鍵認証を有効に保つ(デフォルトyesだが明示的に記述) PubkeyAuthentication yes # チャレンジレスポンス認証を有効化(PAMのTOTPプロンプトに必要) KbdInteractiveAuthentication yes # PAMを有効化 UsePAM yes # 認証方式の組み合わせを指定 # カンマ区切り = AND条件(両方クリアで初めてログイン許可) AuthenticationMethods publickey,keyboard-interactive
AuthenticationMethods publickey,keyboard-interactiveが最重要の設定です。カンマ区切りはAND条件を意味し、「SSH鍵認証が通ったかつTOTPが通った」場合のみログインを許可します。
設定変更後、SSHデーモンを再起動します:
# 設定ファイルの構文チェック(再起動前に必ず実施) sudo sshd -t # エラーがなければ再起動 sudo systemctl restart sshd
5. 動作確認
既存のSSHセッションを切断しないまま、別のターミナルから接続テストを行います。
# 別のターミナルウィンドウから接続テスト ssh username@server-address
正常に設定されていると、以下の順番で認証が行われます:
・Step 1: SSH鍵の確認(自動・プロンプトなし)
・Step 2: `Verification code:` というプロンプトが表示される
・Step 3: Google AuthenticatorアプリのコードをTOTPが有効な30秒以内に入力
・Step 4: ログイン成功
`Verification code:` が表示されれば2FAが機能しています。コードを誤入力すると `Permission denied` となり、ログインできません。
RHEL/Rocky Linux/AlmaLinux 8・9での設定手順
Red Hat系ではEPELリポジトリを先に有効化する必要があります。
# EPELリポジトリを追加 sudo dnf install epel-release -y # google-authenticatorをインストール sudo dnf install google-authenticator qrencode -y
PAMの設定ファイル(`/etc/pam.d/sshd`)の編集手順はUbuntu/Debianと同じです。sshdの設定は、古いバージョンのOpenSSHでは `ChallengeResponseAuthentication` というパラメータ名が使われています:
# OpenSSH 8.7以前(RHEL 8等)では下記の旧パラメータ名を使う ChallengeResponseAuthentication yes # OpenSSH 8.7以降(RHEL 9等)では新パラメータ名を使う KbdInteractiveAuthentication yes
どちらか片方のみ記述してください。両方記述すると競合します。現在のバージョンは `ssh -V` で確認できます。
設定時のよくある失敗と対処法
| 症状 | 原因 | 対処法 |
|---|---|---|
| Verification codeが表示されない | KbdInteractiveAuthentication が no のまま | sshd_configを確認しyesに変更→systemctl restart sshd |
| コードを入力しても「Permission denied」 | AuthenticationMethodsの記述ミス、またはPAM設定ミス | sudo sshd -t で構文確認、/var/log/auth.log(Debian系)または/var/log/secure(RHEL系)を確認 |
| 「Invalid verification code」が続く | サーバーとスマートフォンの時刻がずれている | sudo chronyc tracking で時刻同期を確認。NTPサービスを再起動する |
| google-authenticatorコマンドが見つからない | インストール失敗またはパスが通っていない | which google-authenticator で確認。見つからなければ再インストール |
| Ansibleなどの自動化が止まった | AuthenticationMethodsが全ユーザーに適用されている | 下記のMatch Userブロックで自動化アカウントを除外する |
Ansibleなど自動化ツール用のサービスアカウントがある場合は、`sshd_config` 末尾に以下を追加して2FAを除外します:
# /etc/ssh/sshd_config の末尾に追加 # 自動化用サービスアカウントは鍵認証のみに限定(TOTPなし) Match User ansible AuthenticationMethods publickey
中小企業でも今日からできること
大規模なシステム変更をしなくても、以下のアプローチで段階的に導入できます。
・まず検証サーバー1台で試す: 本番環境への適用前に、検証サーバーで完全に動作確認してから展開する
・nullokで段階的に展開: 最初は `pam_google_authenticator.so nullok` で運用し、google-authenticatorを設定済みのユーザーにのみ2FAが適用される状態にする。全員の設定が完了後にnullokを削除して全員強制適用
・緊急コードを組織で管理: 各ユーザーの緊急コードを印刷して会社の金庫に保管するか、IT担当者が一括管理する体制を作る
・管理者以外から先に展開: 情シス担当者が最後に適用することで、ロックアウト時の復旧手段を確保する
コストはゼロです。Google Authenticatorアプリも無料です。情シスが1人でも、週末の数時間で全社員のSSHアクセスに2FAを展開できます。
よくある誤解と注意点
誤解1: TOTPはSMSで届くコードと同じもの
TOTPはRFC 6238に基づき、サーバーとアプリが共有するシークレットキーと現在時刻から独立して計算します。SMSで届くコードとは異なり、通信会社へのSIMスワッピング攻撃の影響を受けません。オフラインでも生成できる点でSMS認証より安全です。
誤解2: 2FAを設定すれば鍵認証は不要
逆です。2FAはあくまで「鍵認証に重ねる追加要素」として設定します。パスワード認証+TOTPの組み合わせよりも、鍵認証+TOTPの方が大幅に安全です。TOTPだけに変更するのはセキュリティの後退になります。
誤解3: スマートフォンを紛失したらサーバーに入れなくなる
google-authenticatorコマンド実行時に生成された緊急コード(Emergency scratch codes)を安全な場所に保管しておけば、スマートフォンなしでもログインできます。緊急コードは使い捨てで、1回使ったら次のコードを使います。また管理者がサーバーコンソールから `~/.google_authenticator` を削除することでリセットできます。
誤解4: 100%安全になる
2FAは攻撃者のハードルを大幅に上げますが、完全な防御はありません。フィッシングによるリアルタイムのOTP窃取や、デバイス自体のマルウェア感染には別途対策が必要です。2FAはあくまでも多層防御の一つとして位置づけてください。
本記事のまとめ
| ステップ | 作業内容 | 難易度 |
|---|---|---|
| 1. インストール | libpam-google-authenticatorをaptまたはdnf(+EPEL)で導入 | 低 |
| 2. シークレット生成 | 各ユーザーがgoogle-authenticatorコマンドを実行してQRをスキャン | 低 |
| 3. PAM設定 | /etc/pam.d/sshdにpam_google_authenticator.soを追加 | 中 |
| 4. SSHD設定 | AuthenticationMethods publickey,keyboard-interactiveを追記 | 中 |
| 5. 動作確認 | 別セッションからログインしてVerification codeが要求されることを確認 | 低 |
| 6. 全員展開 | nullokを削除して全ユーザーに強制適用 | 低 |
SSH二要素認証は、Linuxサーバーへの不正アクセスを防ぐ最も費用対効果の高い施策の一つです。秘密鍵が盗まれるリスクをゼロにはできなくても、「鍵が流出しても入れない状態」を作ることで実質的な被害を防げます。
「鍵認証で十分」と思っていた方も、今日紹介した手順で一段階上の防御を追加してみてください。コストゼロで導入できる2FAは、情シス1人体制の中小企業にこそ有効な対策です。
姉妹サイトLinuxMaster.JPでは、Linuxのファイル権限管理やシステム運用の基礎を詳しく解説しています。SSHやPAMの理解を深めたい方はあわせてご覧ください。
PR
PAM・SELinux・SSH設定からログ監視まで、Linuxサーバーセキュリティの実践的な手法を体系的に学べる一冊。本記事で触れたPAMの設定やSSH堅牢化をさらに深掘りしたい方に最適です。
