単一のラボサーバー、三つの信頼ロール、一つの障害ドメイン
VPN の入口、プライベートサービス、CI ジョブは、単一のサーバー上でも別々の権限を必要とする。ロールを分け、ネットワークを分離し、コンソールからの復旧を確保する。
ビルドランナーが VPN ゲストと同じホストソケットを必要とした瞬間にすべては破綻し、そのとき、なぜジョブがゲートウェイの鍵を読めるのかを誰も説明できない。すべては収まるからという理由で一台のマシンに入り、動かすにはそれが最短だからという理由で各ゲストに広いアクセスが与えられた。結果は、信頼境界が電源ケーブルであるホストだ。ロールを分けるのは聞こえるほど大変ではなく、まず名前を付けることから始まる。
現在すべてを共有している三つのロール
VPN の入口、プライベートサービスの集合、ビルドランナーはそれぞれ別のものを求める。一つ目は公開ポートとプライベート側の狭いネットワークを必要とする。二つ目は永続ストレージとアプリケーションの秘密を必要とする。三つ目は誰かが書いたコードを実行する。
三つすべてに同じアクセスを与えることが、どれでも他に影響を与えられるために症状を紛らわしくする。ハイパーバイザーのソケットに届くビルドジョブは、VPN を止められるジョブである。管理ネットワークへの経路を持つサービスゲストはスイッチ設定を変えられる。問題は権限であり、プロセス一覧はそれが現れる場所にすぎない。先にロールを名付ければ、各権限に住み場所ができる。
下のスケッチはその分割の出発点であり、高可用性の約束ではない。検証すべき仮説として扱い、完成した設計として扱わないこと。
三つのプロセスではなく、三つのロールを分ける
一台のホスト上の三つのゲストは、三つの権限セットと三つのネットワーク位置を与えるが、三つの障害ドメインは与えない。設計はそれをはっきり書くべきだ。
分離が効くのは、一つのゲストの侵害が他へ自動的に権限を与えない場合だけであり、それはハイパーバイザーの設定であって命名規則ではない。VM をゲートウェイと呼んでも、別のゲストがホストに届くのを止められない。ホスト名ではなく仮想スイッチの設定を読み、各ゲストが想定どおりの場所にいることを確認する。
host
├─ vpn-gw 10.60.10.2/24 public 51820/udp only
├─ services 10.60.0.10/24 no route to the management network
└─ ci-runner 10.60.20.2/24 outbound only, no host socket
トポロジーを書き出し、それを持っているツールで検証する。誤ったブリッジに現れるゲストは詳細ではなく発見である。サービス設定を変える前にネットワークを直す。
VPN ゲストには自分が持つ扉だけを開かせる
ルーターでトンネルのポートだけを公開する。ゲストの管理ポートは管理経路に属し、インターネットには属さず、ハイパーバイザー自身のインターフェースも同様だ。
プライベート側のネットワークは、リモート利用者が必要とするサービスに届き、そこで止まるべきだ。トンネルは輸送であってログインではないので、アプリケーション認証は入れたままにする。VPN ゲストのルーティングテーブルは一文で説明できる程度に小さく保つ。そう単純に説明できないなら、境界はすでにぼやけている。
virsh net-list --all
virsh net-dumpxml lab-private
sudo nft list ruleset | grep -n -E '51820|10.60'
ホストで待ち受けていて管理に不要なものは、そもそも待ち受けるべきではない。これにはハイパーバイザーの API も含まれ、公開インターフェースに晒したままにしやすい。
ビルドジョブには別の種類の境界が必要だ
CI ジョブはリポジトリが制御するコマンドを実行するので、その信頼は誰がコードを送れるかに依存する。シェル実行器はそのコマンドをホスト上のランナーの利用者として走らせ、これが利用できる最も広い境界だ。
コンテナ実行器はより狭く、特権コンテナは狭い側ではない。ランナーは独自のネットワーク範囲を持つ専用ゲストに置き、ジョブが必要とする成果物と配備へのアクセスだけを与える。使い捨てのワーカーは保持する状態を減らすが、到達性や秘密の露出は減らさない。どちらにも明示的な制限が要る。
サービスゲストがデータを持っているので、そのバックアップ計画が機能しなければならない。リポジトリ、アップロード、データベースを同じスケジュールに置く。ジョブの状態を読むのではなく、復元を試す。
三つが共有する障害に備える
一台のホストは一つの障害ドメインだ。再起動はトンネルとすべてのプライベートサービスを同時に消し、まさにそのときに、入る経路が最も重要になる。
VPN ゲストに依存しないコンソールアクセスを、物理コンソールでも帯域外管理インターフェースでも事業者のコンソールでもよいので保つ。同じディスクに保存したスナップショットは独立したコピーではなく、守るゲストの内側で走るバックアップもそうではない。次にコールドスタートは、ホスト、仮想ネットワーク、トンネルの経路、そして通常のアカウントでの名前付きアプリケーション一つという順序として、チェックリストではなく試す。
virsh list --all
systemctl is-active libvirtd
curl -sS -m 5 -o /dev/null -w '%{http_code}\n' https://10.60.0.10/
ss -ltnp | grep -E ':51820|:443'
プロセスの起動順は経路と DNS について何も証明しないので、ユニットの状態ではなく経路を試すこと。経路こそがリモート利用者に本当に必要なものだ。