posts
自動デプロイ用の秘密鍵をGitHubに上げてしまったので、鍵を作り直した話
(更新 )サイトのフォルダを整理していたら、GitHub Actionsで使っているSSHの秘密鍵が、サイトのフォルダの中に入っていました。しかも、いつもの git add . でいっしょにコミットされ、GitHubにも上がっていました。
先に結論です。
- 秘密鍵は、サーバーに入るための合鍵そのもの。上げてしまったら「ファイルを消す」では足りない
- やるべきことは、新しい鍵を作って、古い鍵をサーバー側で使えなくすること
- 順番を間違えると自動デプロイが止まるので、「新しい鍵で動くのを確かめてから、古い鍵を消す」
何が起きていたか
番外編2では、git push で自動公開するために、GitHub Actions専用の鍵を作りました。
- 公開鍵(
.pubが付いている方) … サーバーに登録する - 秘密鍵(
.pubが付いていない方) … GitHubの「Secrets」に登録する
秘密鍵は、本当はGitHubのSecretsに貼るだけで、ファイルとしてサイトのフォルダに置く必要はありません。ところが、フォルダを見直してみると、github_actions_key と github_actions_key.pub がサイトのフォルダの中にありました。.gitignore(GitHubに上げないものの一覧)にも書いていなかったので、git add . のたびに対象になり、そのままGitHubに上がっていたのです。
ssh-keygen の -f のあとに、github_actions_key のようにファイル名だけを書くと、鍵は今いるフォルダに作られます。サイトのフォルダでPowerShellを開いて作業していると、そこに鍵ができてしまいます。
なぜ危ないのか
この秘密鍵の公開鍵は、サーバーの ~/.ssh/authorized_keys(入ってよい鍵の一覧)に登録されています。つまり、この秘密鍵を持っている人は、誰でもサーバーに管理者(root)として入れる状態です。
しかも、GitHubからファイルを消しても、過去のコミット(履歴)には残ります。あとで分かったのですが、このときのリポジトリは公開(Public)になっていて、履歴は誰でも見られる状態でした(下の「後日談」に書きます)。
だから対策は「ファイルを消す」ではなく、鍵そのものを使えなくすることです。古い鍵をサーバーの一覧から外してしまえば、履歴に残っている鍵は、もうどこの扉も開けない、ただの文字列になります。
やったこと(順番が大事)
1. 新しい鍵を、サイトのフォルダの外に作る
手元のPowerShellで打ちます。パスフレーズは聞かれても何も入れずにEnterです(Actionsで自動で使うため)。
ssh-keygen -t ed25519 -f $env:USERPROFILE\.ssh\gha_deploy
$env:USERPROFILE\.ssh\ は、ユーザーのフォルダの中の .ssh フォルダです。サイトのフォルダの外なので、git add . の対象になりません。
2. 新しい公開鍵をサーバーに登録する
type $env:USERPROFILE\.ssh\gha_deploy.pub | ssh root@xxx.xxx.xxx.xxx "cat >> ~/.ssh/authorized_keys"
この時点では、古い鍵も新しい鍵も両方使える状態です。
3. GitHubのSecretsを新しい秘密鍵に差し替える
- 新しい秘密鍵の中身を表示する
Get-Content $env:USERPROFILE\.ssh\gha_deploy
-----BEGINから-----END ...-----までを全部コピーする- GitHubのリポジトリ →「Settings」→「Secrets and variables」→「Actions」
SSH_PRIVATE_KEYの編集を開き、入力欄に貼って保存する
表示した秘密鍵の中身は、Secretの入力欄以外には絶対に貼らないでください(チャットやメモにも)。
4. 新しい鍵でActionsが動くことを確かめる
何か小さな変更をして git push し、GitHubの「Actions」タブで緑のチェックが付くのを確認します。これで、新しい鍵でサーバーに入れることが確かめられます。
古い鍵を消すのは、ここで緑になってからです。先に古い鍵を消してしまうと、Secretsの差し替えに失敗していたとき、自動デプロイが止まります。
5. サーバーから古い鍵を消す
サーバーに入って、入ってよい鍵の一覧を開きます。
nano ~/.ssh/authorized_keys
1行が1つの鍵です。各行の最後には、鍵を作ったPCの名前などのコメントが付いています。手元の古い公開鍵(github_actions_key.pub)をメモ帳ではなくVSCodeで開き、中身が同じ行を探して、その行だけ消して保存します。
自分のPCから入るための鍵(第5回で作った id_ed25519 の行)は消さないように注意してください。消すと、自分もパスワードなしでは入れなくなります。
消したあとにもう一度Actionsを動かして、緑になることを確かめておくと安心です。
6. フォルダから鍵を消し、二度と上がらないようにする
サイトのフォルダにあった古い鍵のファイル2つを削除し、.gitignore に次の行を足しました。
github_actions_key*
gha_deploy*
* は「この後ろに何が付いていても」という意味で、.pub 付きも含めて対象になります。最後に、いつもどおり git add . → git commit → git push で反映しました。
履歴からも消すべき?
過去のコミットから鍵のファイルを消す方法(履歴の書き換え)もありますが、今回はやっていません。
- 古い鍵は、手順5でサーバーから外したので、もう何にも使えない
- 履歴の書き換えは手順が多く、失敗するとリポジトリを壊すおそれがある
鍵を無効にすることが本当の対策で、履歴の掃除はおまけ、という考え方です。逆に言うと、鍵を無効にしないまま履歴だけきれいにしても、安全にはなりません。
後日談:リポジトリが「公開」になっていた
鍵を作り直してから数日後、サイト全体のセキュリティを点検していて、もう1つ大事なことに気づきました。このブログのGitHubリポジトリが、Public(誰でも見られる)になっていたのです。
番外編2では「Private」を選ぶように書いていたのに、自分のリポジトリは公開のままでした。つまり、履歴に残っていた古い秘密鍵は、本当に誰でも取り出せる状態だったことになります。
ここで効いたのが、手順5で古い鍵をサーバーの一覧から外していたことです。念のため、本当に外れているかを「指紋(フィンガープリント)」で確かめました。指紋は、鍵の中身を短く要約した文字列で、鍵ごとに違います。
古い鍵の指紋は、GitHubの履歴に残っていた公開鍵(.pub)から出せます。サイトのフォルダでPowerShellを開いて打ちます。
git log --all --oneline -- github_actions_key.pub
git show (上で出たコミットの番号):github_actions_key.pub > old_key.pub
ssh-keygen -lf old_key.pub
次に、サーバーに登録されている鍵の指紋を出します(サーバーで打ちます)。
ssh-keygen -lf ~/.ssh/authorized_keys | awk '{print NR": "$2}'
古い鍵の指紋は、サーバーの一覧のどれとも一致しませんでした。漏れた鍵では、もうログインできません。確かめ終わったら、old_key.pub は消しておきます。
そのうえで、リポジトリを非公開に切り替えました。GitHubでリポジトリを開き、「Settings」→ 一番下の「Danger Zone」→「Change repository visibility」→「Make private」です。非公開にしても、GitHub Actionsでの自動公開はそのまま動きます。
この点検の続きで、止めたはずのパスワードログインが有効のままだったことや、使っていない鍵が残っていたことも見つかりました。その話は VPSのSSHを点検したら、パスワードログインが有効のままで、使っていない鍵も残っていた にまとめています。
自分のリポジトリをチェックする方法
GitHubに上がっているファイルの一覧から、鍵らしい名前を探せます。サイトのフォルダでPowerShellを開いて打ちます。
git ls-files | Select-String "key|pem|id_"
何も出てこなければ大丈夫です。何か出てきたら、それが秘密鍵ではないか確認してください。
学んだこと
| 起きたこと・気をつけたこと | 理由 | 今のルール |
|---|---|---|
| 秘密鍵をGitHubに上げていた | サイトのフォルダの中で鍵を作り、.gitignore にも入れていなかった |
鍵は必ず $env:USERPROFILE\.ssh\ に作る |
| ファイルを消せば大丈夫、ではなかった | 履歴には残るし、鍵自体はまだ使える | 漏れた鍵は作り直して、サーバー側で無効にする |
| 鍵を入れ替える順番 | 古い鍵を先に消すと、自動デプロイが止まる | 新しい鍵で動くのを確かめてから、古い鍵を消す |
「鍵を漏らした」と気づいたときは焦りましたが、やることを順番に並べると、思ったより落ち着いて対応できました。技術ブログを書いていると、スクショやコマンドの結果を人に見せる場面が多いので、見せてはいけない情報を一度整理しておくのがおすすめです。→ 第2回 機材編 の「記事やスクショに載せてはいけない情報」
あわせて読みたい
- VPSのSSHを点検したら、パスワードログインが有効のままで、使っていない鍵も残っていた — このあとSSH全体を点検した記録
- 【番外編2】GitHub連携編 — git pushだけで公開まで自動化する — 自動デプロイ用の鍵の作り方と、Secretsへの登録
- 【第5回】環境づくり編 — SSH・ufw・Nginx・Hugoを準備する — 鍵で入る仕組み(公開鍵認証)の設定
- git pushしたらデザインが消えた。CSSが404になった原因はフォルダの権限だった — 自動デプロイで起きたもう1つのトラブル