GitLab シークレット検出 ADR 004: シークレット検出スキャナーサービス
背景
シークレットプッシュ保護のフェーズ2において、目標は与えられた入力ブロブに対してシークレット検出スキャンを実行する責任を持つ専用サービスを持つことです。これは主にスケーラビリティの観点から行われます。シークレット検出スキャンの正規表現処理は高リソース消費するため、RailsやGitalyインスタンス内でスキャンを実行すると他の操作のリソース可用性に影響します。分離してスキャンを実行することで、リソース割り当てとサービスの独立したスケーリングに対する より高い制御が可能になります。
提案されたソリューション
シークレット検出スキャンの実行を担当するスタンドアロンのシークレット検出サービスを構築します。
シークレットプッシュ保護のワークフローにおける主な変更は、GitLab SaaS向けにスキャン責任をシークレット検出GemからRPCサービスに委譲することです。すなわち、Secrets Push Checkはシークレットをスキャンするブロブの配列でRPCサービスを呼び出します。なお、プロジェクトの適格性チェックは引き続きRails側で実行されます。
高レベルアーキテクチャ
サービスアーキテクチャはシークレット検出ロジックをスタンドアロンサービスに抽出し、そのサービスがRailsアプリケーションとGitalyの両方と直接通信します。これにより、シークレット検出ノードを独立してスケーリングし、Railsアプリケーションのリソース使用オーバーヘッドを削減できます。
スキャンは(潜在的に)ブロッキングなpre-receiveトランザクションとして同期的に実行されます。ブロブサイズは引き続き1MBに制限されます。
ノード数は純粋に例示的なものですが、スキャンサービスの独立したスケーリング要件を強調しています。
@startuml Phase2
skinparam linetype ortho
card "**External Load Balancer**" as elb #6a9be7
card "**Internal Load Balancer**" as ilb #9370DB
together {
collections "**GitLab Rails** x3" as gitlab #32CD32
collections "**Sidekiq** x3" as sidekiq #ff8dd1
}
together {
collections "**Consul** x3" as consul #e76a9b
}
card "SecretScanningService Cluster" as prsd_cluster {
collections "**SecretScanningService** x5" as prsd #FF8C00
}
card "Gitaly Cluster" as gitaly_cluster {
collections "**Gitaly** x3" as gitaly #FF8C00
}
card "Database" as database {
collections "**PGBouncer** x3" as pgbouncer #4EA7FF
}
elb -[#6a9be7]-> gitlab
gitlab -[#32CD32,norank]--> ilb
gitlab .[#32CD32]----> database
gitlab -[hidden]-> consul
sidekiq -[#ff8dd1,norank]--> ilb
sidekiq .[#ff8dd1]----> database
sidekiq -[hidden]-> consul
ilb -[#9370DB]--> prsd_cluster
ilb -[#9370DB]--> gitaly_cluster
ilb -[#9370DB]--> database
ilb -[hidden]u-> consul
consul .[#e76a9b]u-> gitlab
consul .[#e76a9b]u-> sidekiq
consul .[#e76a9b]-> database
consul .[#e76a9b]-> gitaly_cluster
consul .[#e76a9b]-> prsd_cluster
@enduml
サービスレベルインジケーター(SLI)
GitLabアプリケーションで採用されているSLI、すなわちApdexスコア、エラー率、およびサービス固有の2つの追加メトリクス - リクエストレイテンシとメモリ飽和率を採用します。
サービスレベル目標(SLO)
RPCサービスからベンチマークスコアを取得した後、閾値の制限を定義します。
サービスの実装
RPCを通信インターフェースとして、与えられた入力ブロブ内のシークレットを検出することを主な責任とするRPCサービスを構築します。このサービスは当初、Gitプッシュイベントの変更アクセスチェックを実行する際にRailsモノリスから呼び出され、最終的に他のユースケースにも拡張されます。
スキャンのビジネスロジックを再利用するため、RPCサービスとして機能を提供することに加えて、同じプロジェクトでRubyGemとして機能を配布するためのプロビジョニングも含めます。
言語/ツール/フレームワーク
- Ruby
3.2+ - RPCリクエスト処理のためのgRPCフレームワーク
- Protobufサービス定義ファイル
補足
RPCサービスはサービスの可用性を確保するためのヘルスチェックRPCエンドポイントも公開する必要があります。
Gemベースのアプローチとは異なり、RPCサーバーではサブプロセスのフォーキングのサポートが削除されているため、サブプロセス内でのスキャンアプローチを使用することはできません。ただし、RPCクライアント側からバッチリクエストを並行して処理するような最適化を検討できます。
