アーキテクチャ

アーキテクチャと設計

なぜRustなのか

SSH-FrontièreがRustで書かれている理由は3つあります:

  1. メモリ安全性:バッファオーバーフロー、ユーズアフターフリー、ヌルポインタなし。ログインシェルとして動作するセキュリティコンポーネントにとって、これは重要です。

  2. 静的バイナリx86_64-unknown-linux-muslターゲット(他のターゲットも可能ですが、機能は保証されません)でコンパイルすると、バイナリは約1 MBでシステム依存関係がありません。サーバーにコピーするだけで準備完了です。

  3. パフォーマンス:プログラムは起動、検証、実行、終了をミリ秒で行います。ランタイム、ガベージコレクター、JITはありません。

同期的かつ一時的

SSH-Frontièreは同期的でワンショットのプログラムです。デーモンなし、非同期なし、Tokioなし。

ライフサイクルはシンプルです:

  1. sshdが鍵でSSH接続を認証
  2. sshdがフォークしてssh-frontiereをログインシェルとして実行
  3. ssh-frontiereがコマンドを検証して実行
  4. プロセスが終了

各SSH接続は新しいプロセスを作成します。接続間の共有状態なし、並行性の問題なし。

コード構造

コードは明確な責務を持つモジュールで構成されています:

モジュール責務
main.rsエントリポイント、引数のフラット化、オーケストレーター呼び出し
orchestrator.rsメインフロー:バナー、ヘッダ、コマンド、レスポンス、セッションループ
config.rsTOML設定構造体、フェイルファスト検証
protocol.rsヘッダプロトコル:パーサー、バナー、認証、セッション、ボディ
crypto.rsSHA-256(FIPS 180-4実装)、base64、ノンス、チャレンジ・レスポンス
dispatch.rsコマンドパース(引用符、key=value)、解決、RBAC
chain_parser.rsコマンドチェーンパーサー(演算子;&|
chain_exec.rsチェーン実行:厳密な順序(;)、寛容(&)、フォールバック(|
discovery.rshelplistコマンド:ドメインとアクションの発見
logging.rs構造化JSONログ、機密引数のマスキング
output.rsJSONレスポンス、終了コード
lib.rsproofバイナリとfuzzヘルパー用にcryptoを公開

各モジュールには同じディレクトリにテストファイル(*_tests.rs)があります。

補助的なproofバイナリ(src/bin/proof.rs)は、E2Eテストとクライアント統合のための認証プルーフを計算します。

ヘッダプロトコル

SSH-Frontièreはstdin/stdout上のテキストプロトコルを使用します。プレフィックスは方向によって異なります:

クライアントからサーバー(stdin):

プレフィックス役割
+ 設定:ディレクティブ(authsessionbody
# コメント:サーバーに無視される
(プレーンテキスト)コマンドドメイン アクション [引数]
.(行に単独で記載)ブロック終了:コマンドブロックを終了

サーバーからクライアント(stdout):

プレフィックス役割
#> コメント:バナー、情報メッセージ
+> 設定:機能、チャレンジノンス
>>> レスポンス:最終JSONレスポンス
>> 標準出力:標準出力のストリーミング(ADR 0011)
>>! 標準エラー:エラー出力のストリーミング

接続フロー

クライアント                               サーバー
  |                                        |
  |  <-- バナー + 機能 ----------------  |   #> ssh-frontiere 0.1.0
  |                                        |   +> capabilities rbac, session, help, body
  |                                        |   +> challenge nonce=a1b2c3...
  |                                        |   #> type "help" for available commands
  |                                        |
  |  --- +auth(任意) ---------------->   |   + auth token=runner-ci proof=deadbeef...
  |  --- +session(任意) ------------->   |   + session keepalive
  |                                        |
  |  --- コマンド(プレーンテキスト) -->   |   forgejo backup-config
  |  --- ブロック終了 ----------------->   |   .
  |  <-- stdout ストリーミング --------   |   >> Backup completed
  |  <-- 最終JSONレスポンス ----------   |   >>> {"status_code":0,"status_message":"executed",...}
  |                                        |
  |  (session keepaliveの場合)           |
  |  --- コマンド2 -------------------->   |   infra healthcheck
  |  --- ブロック終了 ----------------->   |   .
  |  <-- JSONレスポンス2 -------------   |   >>> {"status_code":0,...}
  |  --- セッション終了(空ブロック) ->   |   .
  |  <-- セッション終了 ---------------   |   #> session closed

JSONレスポンス

各コマンドは>>>プレフィックス付きの1行の最終JSONレスポンスを生成します。標準出力とエラーは>>>>!でストリーミング送信されます:

>> Backup completed
>>> {"command":"forgejo backup-config","status_code":0,"status_message":"executed","stdout":null,"stderr":null}

ボディプロトコル

+bodyヘッダにより、stdin経由で子プロセスに複数行のコンテンツを送信できます。4つの区切りモード:

TOML設定

設定フォーマットは宣言的TOMLです。ADR 0001で文書化された選択理由:

設定は読み込み時に検証されます(フェイルファスト):TOML構文、フィールドの完全性、プレースホルダの整合性、少なくとも1つのドメイン、ドメインごとに少なくとも1つのアクション、空でないenum値。

依存関係ポリシー

SSH-Frontièreは不要な依存関係ゼロのポリシーを持っています。各外部クレートは実際のニーズによって正当化されなければなりません。

現在の依存関係

直接依存関係3つ、推移的依存関係約20:

クレート用途
serde + serde_jsonJSONシリアライゼーション(ログ、レスポンス)
tomlTOML設定の読み込み

評価マトリックス

依存関係を追加する前に、8つの加重基準(5点満点)で評価されます:ライセンス(不適格の場合は排除)、ガバナンス(×3)、コミュニティ(×2)、更新頻度(×2)、サイズ(×3)、推移的依存関係(×3)、機能(×2)、非ロックイン(×1)。最低スコア:3.5/5。

監査

プロジェクトの設計経緯

SSH-Frontièreは連続するフェーズ(1〜9、中間フェーズ2.5と5.5を含む)で開発され、体系的なTDD手法によりClaude Codeエージェントが主導しました:

フェーズ内容
1機能的なディスパッチャー、TOML設定、3段階RBAC
2本番設定、運用スクリプト
2.5SHA-256 FIPS 180-4、BTreeMap、グレースフルタイムアウト
3統合ヘッダプロトコル、チャレンジ・レスポンス認証、セッション
4E2E SSH Dockerテスト、コードクリーンアップ、forge統合
5可視性タグ、水平的トークンフィルタリング
5.5オプションノンス、名前付き引数、proofバイナリ(フェーズ6を含む、統合)
7設定ガイド、ドライラン--check-config、プレフィックスなしhelp
8構造化エラー型、ペダンティックclipy、cargo-fuzz、proptest
9ボディプロトコル、自由引数、max_body_size、終了コード133

プロジェクトの設計者:

人間と機械が共に、より良く、より速く、より高いセキュリティで協働する場所。