エラーメッセージ
ssh: this private key is passphrase protected
別の表記
- ssh: no key found
「ssh: no key found」(別名として掲載)は、同じパーサーが報告する別の状態で、バイト列に PEM の鍵ブロックが含まれていないことを示します。たとえば、公開鍵、PuTTY の `.ppk` ファイル、空のファイルです。
golang.org/x/crypto/ssh (Go) — *ssh.PassphraseMissingError from ssh.ParsePrivateKey
このエラーの意味
`ssh.ParsePrivateKey` は暗号化された秘密鍵を認識しましたが、復号に使うパスフレーズがないため処理を中止しました。返される `*ssh.PassphraseMissingError` には公開鍵まで含まれていますが、署名には復号済みの秘密鍵が必要です。`ssh.ParsePrivateKeyWithPassphrase` を呼び出すか、署名を SSH エージェントに任せてください。関連するエラーの「ssh: no key found」は、入力に認識できる鍵がまったく含まれていないことを意味します。
発生する理由
鍵がパスフレーズ付きで作成されている
よくある`ssh-keygen` はパスフレーズの入力を求め、それを使って鍵ファイルを暗号化します。対話型の OpenSSH はパスフレーズを尋ねるかエージェントを使いますが、Go のプログラムではパスフレーズを明示的に渡す必要があります。
ツールが、エージェントによる鍵のロック解除を前提にしていた
ときどきワークステーションでは `ssh-add` が復号済みの鍵を保持しており、`ssh` がファイルを読み込むことはありません。ファイルを直接読み込む Go のコードは、エージェントの恩恵を受けられません。
暗号化された個人の鍵を CI にコピーしている
ときどき自動化用のデプロイキーは、通常は暗号化されておらず、アクセス範囲も狭く限定されています。暗号化された個人の鍵を CI で使い回すと、ここで失敗します。
修正方法
- 1.
エラーを検出してパスフレーズを渡す
`errors.As` でパスフレーズがないことを判別できます。パスフレーズはシークレットストアから読み込んでください。ソースコードに含めてはいけません。
go signer, err := ssh.ParsePrivateKey(keyBytes) var missing *ssh.PassphraseMissingError if errors.As(err, &missing) { passphrase := []byte(os.Getenv("SSH_KEY_PASSPHRASE")) signer, err = ssh.ParsePrivateKeyWithPassphrase(keyBytes, passphrase) } if err != nil { return err } - 2.
SSH エージェント経由で署名する
`golang.org/x/crypto/ssh/agent` を使えば、秘密鍵がプロセスに読み込まれることはなく、パスフレーズもエージェントが処理します。
go import "golang.org/x/crypto/ssh/agent" conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) if err != nil { return err } agentClient := agent.NewClient(conn) auth := ssh.PublicKeysCallback(agentClient.Signers) // the agent holds the decrypted key