対策・学習18分で読める

SSH鍵の最小権限:侵害されるおそれのある一時環境に本番のroot鍵を置かない

GPUクラウドのpodやCIランナーなど侵害され得る一時環境に、本番サーバーのroot鍵を置いていませんか。鍵を最小権限にする考え方と、非rootユーザー+command制限鍵で操作を1つに絞る作り方を解説します。

本文を読む
公開日
更新日
目次

対象:本番サーバーに、GPUクラウドのpodやCIランナー、使い捨てVMのような一時的な環境からSSHで接続している人。ここでは攻撃手順は扱わず、鍵を最小権限にして、漏れたときの被害の範囲を小さくすることだけを扱います。鍵の分離の基本は 最低限チェックリスト も参照。

当サイトの視点:一時環境は『信頼できない前提』で扱う

使い捨ての計算環境は、堅牢化が後回しになりがちで、侵害され得る場所です。そこに本番へのroot鍵を置くと、その環境の侵害がそのまま本番の侵害になります。当サイトはホストごとに別の鍵を使い、用途を絞り、不要になった鍵は即外す運用にしています(→ 鍵を分けて被害の範囲を最小化)。鍵は「便利なもの」ではなく「漏れたらすべてのサーバーに入られる、最も重要な資産」として扱うのが、結局いちばん安全です。

✗ 一時環境に root 鍵

一時環境が侵害 → その鍵で本番にroot → ファイル・秘密情報・他のサービスまで全部操作される。

○ command制限の非root鍵

同じ鍵が漏れても、できるのは指定した1コマンドだけ。シェルも転送も不可なので、被害は1操作に限られる。

鍵で操作できる範囲を最初から小さく設計する。漏れたときの被害が変わる。

なぜ一時環境のroot鍵が危ないのか

一時環境は「すぐ消すから」と堅牢化を省きがちで、攻撃者から見れば狙いやすく、本番への攻撃の中継に使われやすい場所です。そこに本番のroot鍵があると、被害は一度に最大になります。

root→全部
鍵が漏れると本番の全権限を奪われる
使い捨て
一時環境は堅牢化が後回し
使い回し
1本漏れると全ホストに入られる
最小権限
操作を1つに絞れば被害も1つ

たとえば、計算用に一時的に立てたpodから本番へ作業するために、rootのauthorized_keysに鍵を登録したとします。便利ですが、そのpodが侵害されれば、攻撃者はその鍵で本番にroot権限で入れてしまう。侵入のきっかけがRCEでもキー流出でも、最終的に「盗まれた鍵で本番に入られる」点は同じです(関連:RCEから鍵が盗まれ不正課金された話)。

鍵を最小権限にする3ステップ

「便利だから全権を渡す」をやめ、「できることを最小に絞る」へ切り替えます。

1

一時環境のroot鍵を外す

使わなくなった鍵は、本番の ~/.ssh/authorized_keys から削除する。アイドルなら消しても無影響。まず、使っていない鍵を残さない。
2

再び必要でもrootに戻さない

本番作業のために再登録するときは、rootではなく専用の非rootユーザーを作り、そのユーザーに鍵を登録する。万一漏れても、届く範囲がそのユーザーの権限までに限られる。
3

『できる操作を1つに絞った鍵』にする

authorized_keysの鍵行に command="..." と restrict を付ける。その鍵でログインできても、指定した1コマンドしか実行できず、ポート転送やシェルも禁止できる。流出しても、被害はその1操作に限られる。
# 非rootユーザーの authorized_keys に「できる操作を1つに絞った鍵」を登録する例
# この鍵でログインしても、指定コマンド以外は実行できず、転送・PTYも無効
command="/usr/local/bin/deploy-pull",restrict ssh-ed25519 AAAA...rest-of-public-key deploy@ci

restrict は転送・エージェント転送・PTY・X11等をまとめて無効化し、command="..." は実行できる処理を1つに固定します。デプロイの取り込みだけ、特定スクリプトだけ、のように目的1つに絞った鍵を用途ごとに作るのが基本です。

やりがちな構成(危険)

  • 一時pod/CIから本番のrootに鍵を登録
  • 使い終わった鍵を消さずに放置
  • 1本の鍵を複数ホストで使い回し
  • 全権シェルが取れる鍵をばらまく

最小権限の構成

  • 本番へは非rootユーザーでのみ接続
  • 鍵はcommand="..." restrictで1操作に限定
  • ホスト/用途ごとに鍵を分離
  • 不要になった鍵は即authorized_keysから削除

使い回している鍵を、最も重要な資産として扱う

鍵を『便利な道具』だと思うと、つい使い回し、消し忘れます。考え方を変えて、1つ漏れたらすべてのサーバーに入られる、最も重要な資産として扱う。ホストごと・用途ごとに分け、一時環境には本番の鍵を置かず、置いたら使用後に必ず消す。これだけで、1本の漏えいが全体に広がる事態を防げます。

鍵の行に付けられる主な制限

OpenSSH では、authorized_keys の鍵の行の先頭にオプションを書くと、その鍵でできることを狭められます。よく使うものを表にまとめます。

書き方制限する内容使いどころ
command="..."鍵でログインすると、指定したコマンドだけが実行される。接続側が送ったコマンドは無視されるデプロイの取り込み、バックアップ取得など、決まった処理だけをさせる鍵
restrictポート転送・エージェント転送・X11転送・端末(PTY)の割り当て・~/.ssh/rc の実行をまとめて無効にする(OpenSSH 7.2以降)ほぼすべての自動処理用の鍵。必要な機能だけ pty などで個別に戻せる
from="..."鍵に加えて、接続元のホスト名またはIPアドレスが一致しないと受け付けない接続元のIPが固定できるCIや作業用サーバー
expiry-time="..."指定した日時を過ぎると鍵を受け付けない(OpenSSH 7.7以降)一時的に作業する期間だけ有効にしたい鍵
PermitRootLogin(sshd_config 側)root への直接ログインを許すかどうか。forced-commands-only なら command= 付きの鍵だけを root で許可root に直接入る必要がなければ no

restrict は、OpenSSH の変更履歴で「今あるものと将来追加されるものを含む、すべての鍵の制限」と説明されています。no-pty や no-port-forwarding を1つずつ並べるより、restrict を付けて必要なものだけ戻すほうが、新しい機能が増えたときに漏れがありません。

# 接続元と有効期限も絞った例(IPは説明用のアドレス)
from="192.0.2.10",expiry-time="20261231",command="/usr/local/bin/deploy-pull",restrict ssh-ed25519 AAAA...rest-of-public-key deploy@ci-2026-10

期限付きのアクセスを多くの鍵で扱うなら、OpenSSH の証明書(ssh-keygen -s で署名し、-V で有効期間、force-command や source-address で制限を付ける)という方法もあります。小さな構成なら、まずは鍵の行のオプションで十分です。

自分の環境で確認する方法

ここで挙げるのは、自分が管理するサーバーの設定と記録を読むだけの確認です。設定を変えるときは、今のSSH接続を1つ開いたまま別の端末から接続を試し、締め出されないことを確かめてから閉じます。

1. 登録されている鍵を一覧する

# root と各ユーザーの authorized_keys に登録された鍵の指紋とコメントを表示する
sudo ssh-keygen -lf /root/.ssh/authorized_keys
sudo ssh-keygen -lf /home/deploy/.ssh/authorized_keys

鍵ごとに 256 SHA256:… deploy@ci (ED25519) のような1行が出ます。コメント(末尾の deploy@ci の部分)から出どころが分からない鍵、使い終わった一時環境の鍵、root に登録された鍵を洗い出します。authorized_keys2 という古いファイル名も既定で読まれるので、あれば同じように確認します。

2. sshd が実際に使っている設定を確認する

# 構文の確認と、実際に効いている設定の表示
sudo sshd -t
sudo sshd -T | grep -iE 'permitrootlogin|authorizedkeysfile|pubkeyauthentication|passwordauthentication'

sshd -T は、設定ファイルを読んだ結果として実際に効いている値を表示します。permitrootlogin が yes や prohibit-password なら、鍵があれば root に直接入れる状態です。authorizedkeysfile が既定以外の場所を指していれば、そこも手順1の対象に加えます。

3. どの鍵が実際に使われているかを確認する

sshd は、鍵でのログインに成功すると、通常はログに Accepted publickey for deploy from 192.0.2.10 port 50022 ssh2: ED25519 SHA256:… のような行を残します。Debian・Ubuntu では /var/log/auth.log、Red Hat 系では /var/log/secure、systemd の環境では journalctl -u ssh(Red Hat 系は -u sshd)で読めます。

この指紋を手順1の一覧と照らし合わせると、使われていない鍵が分かります。NIST の SSH 鍵管理の報告書(NISTIR 7966)は、まれにしか使われない鍵を消し過ぎないよう、6〜12か月ほど記録を見てから使われていない鍵を外すことを勧めています。一時環境の鍵のように使い終わったと分かっているものは、待たずに外して構いません。

4. 一時環境の側を確認する

  • CIのシークレットやGPU podの ~/.ssh に、本番へ入れる秘密鍵が置かれていないか
  • 本番へ入るとき、一時環境に ssh -A(エージェント転送)で接続していないか
  • 1本の鍵を、複数のCI・複数のサーバーで共用していないか

よくある間違いと直し方

  • 一時環境の秘密鍵を消して終わりにする:秘密鍵がすでにコピーされていれば、サーバー側に公開鍵が残っている限り使えます。必ず本番の authorized_keys から該当する行を消します。
  • command= で呼ぶスクリプトの中で、接続側が送ったコマンド(環境変数 SSH_ORIGINAL_COMMAND)をそのまま実行する:制限がないのと同じになります。受け取る値は決められた候補と完全に一致するときだけ使います。
  • 非rootユーザーにしたが、sudo を無条件で許可している:パスワードなしで何でも sudo できるなら、実質的に root と同じです。sudo が要るなら、許可するコマンドを sudoers で1つに絞ります。
  • 一時環境へ ssh -A で入る:その環境で権限を持つ人は、転送された接続を通じて手元のエージェントに読み込まれた鍵で認証できます(OpenSSH のマニュアルも注意を促しています)。経由する必要があるなら ssh -J(踏み台経由の接続)を使います。
  • 鍵にコメントを付けない:棚卸しのときに出どころが分かりません。作るときに ssh-keygen -C "deploy@ci-2026-10" のように、用途と作った時期を入れます。
  • 1本の鍵を複数の人や環境で共有する:NISTIR 7966 も秘密鍵の共有や別のシステムへのコピーを禁じるよう求めています。誰がどこで使ったかが追えず、1か所の漏れで全部を入れ替えることになります。

実際の例:CIで使うツールの侵害で、SSH鍵が持ち出しの対象になった

2026年3月、オープンソースの脆弱性スキャナ Trivy の不正な版と、GitHub Actions 用のアクションの書き換えが起きました。開発元の Aqua Security によると、不正なコードはCIの実行環境から認証情報を集めて外部に送るもので、対象には ~/.ssh の秘密鍵、authorized_keys、known_hosts、SSH のホスト鍵が含まれていました。Aqua は、該当する版が動いた可能性があれば、SSH 鍵を含むそのパイプラインの秘密情報をすべて漏れたものとして扱い、直ちに入れ替えるよう求めています(詳しくは Trivyのサプライチェーン侵害(2026年3月))。

2021年の Codecov の事例でも、CIで実行されるスクリプトが配布元で書き換えられ、約2か月にわたり利用企業のCI環境の秘密情報が外部へ送られました(→ Codecov サプライチェーン攻撃)。

どちらも、利用者側のコードには問題がなくても、CIの中にある鍵は持ち出され得ることを示しています。持ち出された鍵が本番の root に登録されていれば、その時点で本番を明け渡すことになります。非rootの専用ユーザーで command= と restrict を付け、接続元を from= で絞っていれば、同じ鍵が漏れても、できるのは決めた1つの処理だけで、別の場所からの接続は受け付けられません。

当サイトはどうしているか

当サイトは、サーバーごとに専用の鍵を使い、鍵を使い回しません。デプロイは受け取り側を非rootの専用ユーザーにし、本番は「外へ取りに行かず、受け取るだけ」という一方向の構成にしています(→ 本番は受け取るだけ)。一時的な計算環境から本番へ常時の鍵を残すことはせず、必要時だけ最小権限で繋ぎます。理由はこの記事のとおりで、1か所の侵害が全体に波及しないように、鍵の届く範囲をあらかじめ小さく設計しておくためです。

次に読む

出典

  • OpenBSD manual「sshd(8)」AUTHORIZED_KEYS FILE FORMAT(command・restrict・from・expiry-time、SSH_ORIGINAL_COMMAND): man.openbsd.org
  • OpenBSD manual「sshd_config(5)」PermitRootLogin・AuthorizedKeysFile: man.openbsd.org
  • OpenBSD manual「ssh(1)」-A(エージェント転送の注意)・-J: man.openbsd.org
  • OpenBSD manual「ssh-keygen(1)」-l・-C・証明書のオプション: man.openbsd.org
  • OpenSSH 7.2 リリースノート(restrict の追加): openssh.org
  • OpenSSH 7.7 リリースノート(expiry-time の追加): openssh.org
  • NIST「NISTIR 7966: Security of Interactive and Automated Access Management Using Secure Shell (SSH)」(2015年10月): csrc.nist.gov
  • Wikibooks「OpenSSH/Logging and Troubleshooting」(ログイン成功の記録に残る鍵の指紋): en.wikibooks.org
  • Aqua Security「Trivy Supply Chain Attack: What You Need to Know」(2026年): aquasec.com
  • Codecov「Bash Uploader Security Update」(2021年): about.codecov.io

よくある質問

Qなぜ一時環境にroot鍵を置くと危険なのですか?
A

GPUクラウドのpodやCIランナーのような一時環境は、使い捨てゆえに堅牢化が後回しになりがちで、侵害され得ます。そこに本番サーバーへのroot鍵を置くと、その環境が侵害された瞬間に、鍵ごと本番のroot権限を奪われます。一時環境は『信頼できない前提』で扱い、本番へのroot鍵は置かないのが原則です。

Qそれでも一時環境から本番へ接続したい場合は?
A

rootに戻さず、専用の非rootユーザーを作り、そのユーザーに『できる操作を1つに限定した鍵』を登録します。authorized_keysで command="..." と restrict を付けると、その鍵でログインできても指定した1コマンドしか実行できません。流出しても、被害はその1操作に限られます。

Q鍵管理でいちばん大事な原則は何ですか?
A

使い回している鍵を最も重要な資産として扱い、1つ漏れたら全サーバーに入られる構成にしないことです。ホストごと・用途ごとに鍵を分け、不要になった鍵はauthorized_keysから消す。これで1本の漏えいが全体に波及するのを防げます。

Qサーバーに登録されているSSH鍵を確認するにはどうすればいいですか?
A

各ユーザーの ~/.ssh/authorized_keys を ssh-keygen -lf で読むと、登録されている鍵ごとに指紋(SHA256:…)とコメントが一覧できます。root を含む全ユーザーの分を見て、出どころが分からない鍵や、使い終わった一時環境の鍵を洗い出します。どの鍵が実際に使われているかは、sshd のログに残るログイン成功の記録の指紋と照らし合わせて確認できます。

QPermitRootLogin は何に設定すればいいですか?
A

OpenSSH の既定値は prohibit-password で、パスワードでは入れませんが、鍵があれば root に直接ログインできます。root に直接入る必要がなければ no にし、作業は非rootユーザーから行います。バックアップなどでどうしても root の鍵が必要なら forced-commands-only にすると、command="..." を付けた鍵だけが root で使えます。

QCIやGitHub ActionsからSSHでデプロイするときは、どうすれば安全ですか?
A

CIのランナーは一時環境の典型で、実際にCIで使うツールが侵害され、SSH鍵を含む秘密情報が持ち出された事例があります。デプロイ用の鍵は非rootの専用ユーザーに登録し、command="..." と restrict で取り込み処理1つに限定します。接続元のIPが固定できるなら from="..." で絞り、鍵はCIごと・環境ごとに分けて、侵害が公表されたら古い鍵を消してから新しい鍵を登録します。