x/crypto/ssh: add AuthCallback to ClientConfig
要約
概要
x/crypto/ssh パッケージの ClientConfig に AuthCallback フィールドを追加するプロポーザルです。これにより、SSH クライアント認証フローを動的に制御できるようになります。
ステータス変更
likely_accept → accepted
2026年3月11日に「likely accept」となり、1週間後の2026年3月18日にコンセンサスに変化がないとして正式に accepted となりました。aclements(Proposal Review Group)が承認を宣言し、現在はIssueが実装トラッキング用として機能しています。
技術的背景
現状の問題点
現行の x/crypto/ssh パッケージでは、クライアント認証方法は静的に定義されています。クライアントはまず none 認証を試み、その後 ClientConfig.Auth に定義されたメソッドを順番に試みます。この仕組みには以下の制限があります。
- 動的な認証選択が不可能: サーバーから返されるメタデータや部分的な成功(partial success)に基づいて認証方法を切り替えられない
- 認証プロセスの中断手段がない: 実行時のコンテキストやポリシーに基づいて認証を中断するクリーンな方法がない
- ネゴシエーションデータへのアクセスが制限:
ConnMetadataやNegotiatedAlgorithmsは認証完了後にしかアクセスできず、認証中には利用できない
提案された解決策
ClientConfig に AuthCallback ClientAuthCallback フィールドを追加します。また、コールバックに渡すための新しい型 ClientAuthContext と、コールバック関数の型 ClientAuthCallback も導入されます。
当初はシンプルな関数型として提案されていましたが、レビュー過程でパラメーターの拡張性を考慮してコンテキスト構造体(ClientAuthContext)に切り替えられました。最終的に承認された設計は以下の通りです。
// ClientAuthContext には認証プロセスの現在の状態情報が格納される
type ClientAuthContext struct {
Metadata ConnMetadata
Algorithms NegotiatedAlgorithms
AllowedMethods []string // サーバーが現在受け付けている認証方法
PartialSuccessMethods []string // 部分的に成功した認証方法のリスト
TriedMethods []string // 試みて失敗した認証方法のリスト
}
// ClientAuthCallback は認証試行の前に呼び出されるコールバック
type ClientAuthCallback func(ctx *ClientAuthContext) (AuthMethod, error)
type ClientConfig struct {
// 既存フィールド ...
AuthCallback ClientAuthCallback
}
コールバックの戻り値によって動作が決まります。
(AuthMethod, nil): 返されたAuthMethodを次に試みる(ClientConfig.Authに含まれていなくてもよい)(nil, nil): デフォルト動作(ClientConfig.Authの未試行メソッドを順番に試みる)に従う(nil, error): 認証プロセスを即座に中断し、エラーを返す
コールバックは初回のnone認証後、サーバーがサポートする認証方法が判明した時点から、各認証試行の前に繰り返し呼び出されます。
これによって何ができるようになるか
サーバーの応答やセッションの状態に基づいてリアルタイムに認証戦略を変更できるようになります。
コード例
// Before: 静的な認証リストのみ(公開鍵失敗後にパスワードを試みる手段がない)
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
ssh.Password("static-password"),
},
}
// After: AuthCallbackによる動的認証制御
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
AuthCallback: func(ctx *ssh.ClientAuthContext) (ssh.AuthMethod, error) {
// 公開鍵が失敗した場合のみパスワードを動的に要求
for _, tried := range ctx.TriedMethods {
if tried == "publickey" {
password := promptUserForPassword()
return ssh.Password(password), nil
}
}
// サーバーが password 認証を受け付けていない場合は中断
for _, allowed := range ctx.AllowedMethods {
if allowed == "password" {
return nil, nil // デフォルト動作に従う
}
}
return nil, fmt.Errorf("no supported auth methods available")
},
}
実践的なユースケースとしては以下が挙げられます。
- 段階的認証フローの制御: 多要素認証(MFA)や partial success を要求するサーバーへの対応
- 遅延パスワード取得: 公開鍵認証が失敗した場合にのみユーザーにパスワードを要求し、不要なプロンプトを避ける
- サーバーポリシーへの動的適応: Teleport のような高度なアクセス制御システムで、サーバーのネゴシエーション結果に基づいて認証方式を変更する
議論のハイライト
- スタイルの改善(aclements): 当初はフラット関数シグネチャだったが、将来のパラメーター追加に対応するためコンテキスト構造体への変更が指示された。また、関数型に独立した名前付き型を与えるよう求められた
ClientConfig.Authとの関係の明確化: コールバックが返すAuthMethodはClientConfig.Authに含まれている必要がないことが明確化され、完全に独立した動的認証メソッド指定が可能となったnil, nil返却時の動作の明確化: コールバックがnil, nilを返した場合でも、その後の認証試行前に再度コールバックが呼び出されることが確認され、ドキュメントに反映されたAlgorithmsフィールドの設計判断:ConnMetadataはAlgorithmsConnMetadataを実装しているため型アサートで取得可能だが、利便性のためClientAuthContextに直接含めることになった- 実運用での需要: Teleport(大規模SSH アクセス管理システム)が即時のセキュリティ改善のためにこの機能を必要としており、コミュニティからの圧力が承認を後押しした