サーバーアクセス用のsshキーペアを管理する(翻訳)
最近、主にBridgetownのデプロイフローを改善するために、RubyとRailsアプリをVPSにデプロイする作業に手を付け始めました。Rails 8では「PaaS無用」が流行語となっていますが、私のサーバー管理スキルにはまだまだ大きなギャップがあるので、この作業に時間を投資する価値があることに気づきました。
サーバーへのssh接続で(キーペアではなく)パスワード認証を使うのは、一般に安全ではないと考えられています。ssh接続では、sshキーペアを使うことが推奨されています。そういうわけで、これらのキーを安全に管理するためのベストプラクティスを探しているうちに迷路に迷い込んでしまったのでした。
本記事では、この体験から学んだことについて解説します。
sshキーペアとは
sshキーペアは、公開鍵(public key)と秘密鍵(private key)のペアで構成されます。このうち秘密鍵は、それを生成したシステムの外に「決して持ち出してはいけません」。秘密鍵は非常に重要なので、安全に保管しておく必要があります。
公開鍵は、セキュア接続の対象となるサーバーにインストールします。サーバー上の~/.ssh/authorized_keys
ファイルには、ssh接続の作成を承認されたすべての公開鍵が含まれています。
sshハンドシェイクでは、「ローカルの秘密鍵」と「サーバー上の公開鍵」を用いてアクセスを認証します。以下の動画の説明はよくできています。
1個のキーは、1つのエンティティを識別するのに使われます。自分のコンピュータから複数のサーバーにデプロイを行う場合、それら全サーバーへのアクセスに自分個人のキーを使うことになります。キーはユーザーを一意に識別できるため、その気になれば同じ1つのキーをGitHubリポジトリへの書き込みに使うことも、gitコミットへの署名に使うことも可能です。
🔗 キーペアを生成する
キーペアはssh-keygen
というコマンドラインユーティリティで生成できます。ED25519は現在利用可能なキー種別の中で最も安全性が高いので、これを使うことにします。
キーペアを生成するときに-C
フラグでコメントを書くと、公開鍵に追加されます。こうすることで、サーバーにインストールする公開鍵ごとにコンテキスト情報を提供できます。
$ ssh-keygen -t ed25519 -C "your_email@example.com"
実行すると、キーペアを保存するためのファイル名を入力するよう求められます。デフォルトのファイル名で構わないのでEnterキーを押します。
Enter file in which to save the key (/Users/[USER]/.ssh/id_ed25519):
次に、自分のコンピュータのディスク上にある秘密鍵を暗号化するためのパスフレーズ(passphrase)を入力するよう求められます。こうすることで、たとえ誰かがディスクの内容にアクセスしても、パスフレーズを知らない限り秘密鍵を表示できなくなります。
セキュリティを保つため、パスフレーズなしにはせず、パスワードマネージャで安全なパスフレーズを生成して保存しておきましょう。
Enter passphrase (empty for no passphrase):
パスフレーズを確認した後、キーペアが生成されます。
Your identification has been saved in /Users/[USER]/.ssh/id_ed25519
Your public key has been saved in /Users/[USER]/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:GcsT23BKVXAugQoK0z+gWidXIqqzyg7O9KlrOQWRdNY your_email@example.com
The key's randomart image is:
+--[ED25519 256]--+
|.o.o. .+oo |
|oo* oE. .. + |
| =.= + .= o . |
|o.+ = .o @ . |
|o..+ . S . |
|+ . . |
|.+o |
|B+. . |
|=B+o |
+----[SHA256]-----+
これで、公開鍵を表示できるようになりました。
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHD9BSFIEWuUq0ZCnlwRG/68rSbV0LcEAzLTYjOUqf2L your_email@example.com
メモ
秘密鍵用のパスフレーズをパスワードマネージャに保存し、鍵をディスク上に保存するのは少々面倒です。私はssh鍵の生成と保存を1Passwordで行うのが好みです。この方法なら、秘密鍵が1Passwordの暗号化モデルによって保護されますし、1Passwordでログインできるすべてのデバイスで利用できるようになります。また、ノートPCを紛失したときに秘密鍵が流出するリスクも軽減できます。
🔗 自動化デプロイで秘密鍵を使う
本記事の前半で、秘密鍵はそれを生成したシステムの外に持ち出してはならないと書きました。しかしCI/CDパイプラインの一環として自動化デプロイをセットアップするには、このルールを破る必要があります。
CI サーバーはユーザーとは異なるエンティティであるため、ユーザー用とは別のキーペアが独自に必要です。また、このエンティティは信頼できるため、自分のコンピュータ上で生成した秘密鍵を安全にCIサーバーに転送できます。
注意
自分個人のsshキーをCIワークフローに流用するのは「絶対にやめましょう」。sshキーは使い回さず、必ず用途専用のキーペアをその都度生成すること。
CIサービスには、機密情報を保存するための安全なメカニズムがあります。GitHub Actionsでは、これらのsecrets(秘密情報)を呼び出してワークフローで参照できます(他のサービスにも同等のものがあります)。この手法を使って、CIサービスに秘密鍵を安全に保存します。
秘密鍵の取り扱いには細心の注意を払い、間違っても暗号化されていない状態で保管してはいけません。デプロイ用のキーペアのコピーを自分のコンピュータ上に保存する場合は、安全に保管されていることを確認しましょう。1Passwordなどのパスワードマネージャは有用です。
🔗 sshエージェント
前述のとおり、秘密鍵はディスク上で暗号化されています。サーバーに接続するたびに秘密鍵を復号するためにパスフレーズを入力するのは面倒ですが、こんなときはsshエージェントの出番です。
sshエージェントはキーと証明書を暗号化なしの状態でメモリ上に保持し、ssh
コマンドで使えるようにします。暗号化なしのデータはメモリ上にのみ保持され、ディスクに書き込まれることはないため安全です。
sshエージェントにキーを追加するには以下のコマンドを実行します。
$ ssh-add ~/.ssh/id_ed25519
Macの場合、パスフレーズを「キーチェイン」アプリ(Keychain Access)に保存しておけば、必要な場合に自動的に復号してくれます。
$ ssh-add --apple-use-keychain ~/.ssh/id_ed25519
1Passwordにも独自のsshエージェントがあるので、これを使って1Passwordの保管庫内に保存されているキーにアクセスできるようになります。
🔗 sshコンフィグ
sshを効率よく利用するためのsshコンフィグファイル(config
)があります。このファイルをセットアップしたことがない場合は存在しません。
$ touch ~/.ssh/config
$ cat ~/.ssh/config
このconfig
ファイル内には、さまざまなホスト向けのオプションを設定できます。以下の例では、ユーザー個人のキーをデフォルトとし、GitHubの場合にのみ1Passwordエージェントを使うようになっています。
Host *
IdentityFile ~/.ssh/id_ed25519
Host github.com
HostName github.com
IdentityAgent ~/.1password/agent.sock
IdentityFile none
config
ファイルについて詳しくは、以下を参照してください。
参考: SSH config file syntax and how-tos for configuring the OpenSSH client
🔗 まとめ
要点: sshの各キーペアは、単一のエンティティを一意に識別します。gitコミットに署名したり、サーバーにssh接続したりするために、自分用のキーペアを1つ用意する必要があります。自動デプロイには自分用のキーを流用せずに別のキーを使うこと。
sshキーは、1Passwordなどのパスワードマネージャで管理できます。あるいは、秘密鍵を安全なパスフレーズで確実に暗号化したうえで、そのパスフレーズを安全な方法で保管します。
最後に、sshのコンフィグファイルを使うとワークフローを効率化できます。
PS: sshに関する私のしょうもない質問に辛抱強く答えて、本記事をレビューしてくれたWill Jessopに心から感謝いたします。
関連資料
- sshでGitHubに接続する: SSH について - GitHub Docs
-
GitHubによるsshキー生成ガイド: 新しい SSH キーを生成して ssh-agent に追加する - GitHub Docs
-
sshエージェント転送(リモートサーバーから別のサーバーに接続するときにローカルの秘密鍵を使う手法。たとえばsshセッション中にgitリポジトリをチェックアウトする場合): SSHエージェント転送の利用 - GitHub Docs
-
sshがサーバー上のどの公開鍵を使うかを認識する方法: How does SSH know which public key to use from authorized_keys? - Information Security Stack Exchange
-
サーバーに公開鍵をインストールする: How to Add SSH Public Key to Server
概要
原著者の許諾を得て翻訳・公開いたします。
表記は小文字のsshに統一しました。
また、keyは「公開鍵」「秘密鍵」以外は「キー」としました。