メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#76146accepted

x/crypto/ssh: add AuthCallback to ClientConfig

ステータス変更: likely_accept accepted

要約

AIによる要約であり、誤りを含む場合があります。

概要

x/crypto/ssh パッケージの ClientConfigAuthCallback フィールドを追加するプロポーザルです。これにより、SSH クライアント認証フローを動的に制御できるようになります。

ステータス変更

likely_acceptaccepted
2026年3月11日に「likely accept」となり、1週間後の2026年3月18日にコンセンサスに変化がないとして正式に accepted となりました。aclements(Proposal Review Group)が承認を宣言し、現在はIssueが実装トラッキング用として機能しています。

技術的背景

現状の問題点

現行の x/crypto/ssh パッケージでは、クライアント認証方法は静的に定義されています。クライアントはまず none 認証を試み、その後 ClientConfig.Auth に定義されたメソッドを順番に試みます。この仕組みには以下の制限があります。

  • 動的な認証選択が不可能: サーバーから返されるメタデータや部分的な成功(partial success)に基づいて認証方法を切り替えられない
  • 認証プロセスの中断手段がない: 実行時のコンテキストやポリシーに基づいて認証を中断するクリーンな方法がない
  • ネゴシエーションデータへのアクセスが制限: ConnMetadataNegotiatedAlgorithms は認証完了後にしかアクセスできず、認証中には利用できない

提案された解決策

ClientConfigAuthCallback 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")
    },
}

実践的なユースケースとしては以下が挙げられます。

  1. 段階的認証フローの制御: 多要素認証(MFA)や partial success を要求するサーバーへの対応
  2. 遅延パスワード取得: 公開鍵認証が失敗した場合にのみユーザーにパスワードを要求し、不要なプロンプトを避ける
  3. サーバーポリシーへの動的適応: Teleport のような高度なアクセス制御システムで、サーバーのネゴシエーション結果に基づいて認証方式を変更する

議論のハイライト

  • スタイルの改善(aclements): 当初はフラット関数シグネチャだったが、将来のパラメーター追加に対応するためコンテキスト構造体への変更が指示された。また、関数型に独立した名前付き型を与えるよう求められた
  • ClientConfig.Auth との関係の明確化: コールバックが返す AuthMethodClientConfig.Auth に含まれている必要がないことが明確化され、完全に独立した動的認証メソッド指定が可能となった
  • nil, nil 返却時の動作の明確化: コールバックが nil, nil を返した場合でも、その後の認証試行前に再度コールバックが呼び出されることが確認され、ドキュメントに反映された
  • Algorithms フィールドの設計判断: ConnMetadataAlgorithmsConnMetadata を実装しているため型アサートで取得可能だが、利便性のため ClientAuthContext に直接含めることになった
  • 実運用での需要: Teleport(大規模SSH アクセス管理システム)が即時のセキュリティ改善のためにこの機能を必要としており、コミュニティからの圧力が承認を後押しした

関連リンク