Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pba-manager

strongSwan (IKEv2/IPsec) の CGNAT 環境向け、ユーザー単位ポートブロック割当 (PBA: Port Block Allocation) マネージャ。

VPN ユーザーごとに一意な public_ip:port_start-port_end の範囲を割り当てることで、Abuse(不正利用)対応時に「ある時刻に特定の public_ip:port を使っていたのは誰か」を特定できるようにする。

アーキテクチャ

Neutron 型のコントロールプレーン / データプレーン分離構成。pba-manager バイナリは 1 本で、サブコマンドによって役割が変わる。

VPN クライアント
  ↓ IKEv2 / EAP-MSCHAPv2
strongSwan (charon)
  ↓ VICI イベント (child-updown)
pba-agent  ─────────────────HTTP(bearer)────────────────▶  pba-controller
  │ nftables 再生成                                          │ 割当ロジック
  │ (ip pba_nat テーブル, host netns)                         ├─ Redis      — lease 状態・pool
  ↓                                                           └─ PostgreSQL — pba_sessions (Abuse ログ)
ホストの nftables
  • pba-controller: 割当ロジック本体。HTTP API(/v1/allocate /v1/release /v1/reconcile /v1/leases)と /metrics を公開。特権不要で、単一 replica で動作する(複製・排他制御は今後の拡張)。
  • pba-agent: strongSwan の VICI イベントを購読し、controller の API を呼び出してリース状態を同期。ホストの nftables(ip pba_nat テーブルの pba チェーン)を再生成する。network_mode: host + NET_ADMIN が必要。
  • 互換性のため、単一プロセスで両方の役割を担う daemon サブコマンドも残っている(非コンテナ環境向け)。

ポート割当・生成される nftables ルールの詳細は internal/allocator/internal/nft/ を参照。設計判断の背景は docs/containerization-migration.md にまとめている。

ポートブロック割当方式

公開 IP ごとにポート空間を 512 ポート単位のスロットに分割する。新規接続のたびに、現在の空き率に応じて連続する N 個のスロットを割り当てる。

空き率 ブロックサイズ
75% 以上 4096 ポート (8 スロット)
50% 以上 2048 ポート (4 スロット)
25% 以上 1024 ポート (2 スロット)
0% 以上 512 ポート (1 スロット)

空き率が下がるほど割当ブロックを小さくし、枯渇を遅らせる適応的な方式。

デプロイ方法

A. Docker Compose(推奨)

strongSwan・freeRADIUS・Redis・PostgreSQL・nginx・certbot・Prometheus/Grafana を含むスタック全体をコンテナで構成する。Let's Encrypt による証明書自動更新にも対応。

cd deploy/docker
cp .env.example .env            # ACME_DOMAIN 等を編集
make secrets-init               # 各種パスワード・トークンをランダム生成
make config-init                # config.yaml.example → config.yaml(public_ips 等を編集)
make init-cert                  # 初回のみ: HTTP-only nginx → Let's Encrypt staging 発行
make up

詳細な構成・ネットワーク設計・証明書更新の仕組みは docs/containerization-migration.md を参照。

B. systemd(単一ホスト直接運用)

# 1. ビルド
go build -o pba-manager ./cmd/pba-manager

# 2. インストール
install -m 755 pba-manager /usr/local/bin/

# 3. 設定
mkdir -p /etc/pba-manager
cp deploy/config.yaml.example /etc/pba-manager/config.yaml
# postgres の DSN と public_ips を編集(DSN は直書きせず PBA_PG_DSN_FILE 等の
# 環境変数/secret 経由での注入を推奨。docs/containerization-migration.md §8)

# 4. PostgreSQL スキーマ
psql -U pba -d pba -f schema/pba_sessions.sql

# 5. nftables(ip pba_nat という PBA 専用テーブルを使う。Docker の "ip nat" と衝突しないため)
cp deploy/nftables.conf.example /etc/nftables.conf
nft -f /etc/nftables.conf

# 6. Redis pool 初期化
pba-manager init-redis --config /etc/pba-manager/config.yaml

# 7. systemd(デーモン単体プロセス版。controller/agent 分離版を使う場合は
#    deploy/docker のコンテナ構成、または pba-manager controller / agent を
#    別々の unit として登録する)
cp deploy/pba-manager.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now pba-manager

CLI リファレンス

pba-manager [--config PATH] <command>
コマンド 説明
init-redis 設定から Redis のポートプールを初期化(既存 lease がある場合は --force なしでは拒否)
allocate --username U --vpn-ip IP ポートブロックを手動割当
release --vpn-ip IP ポートブロックを手動解放
list 現在の active lease とプール使用状況を表示
reconcile Redis ↔ VICI の状態を同期し nftables を再構築
daemon [--reconcile-interval N] イベント駆動デーモン(controller/agent 一体型。既定: 60 秒ごとに reconcile)
controller 割当ロジック + HTTP API サーバー(コンテナ構成向け)
agent [--reconcile-interval N] VICI 購読 + nftables 再生成(コンテナ構成向け、controller の API を呼び出す)

Redis キー設計

pba:pool:<public_ip>       Sorted Set  — 空き 512 ポートスロットの開始位置(score = port_start)
pba:lease:vpn:<vpn_ip>     Hash        — active lease の詳細(generation 含む)
pba:gen:<vpn_ip>           String      — VPN IP ごとの単調増加 generation カウンタ
pba:user:<username>        String      — ユーザー名に対応する vpn_ip

PostgreSQL Abuse 検索クエリ例

SELECT *
FROM pba_sessions
WHERE public_ip = '203.0.113.32'
  AND 44321 BETWEEN port_start AND port_end
  AND connected_at    <= '2026-07-07 12:34:56+00'
  AND (disconnected_at IS NULL
       OR disconnected_at >= '2026-07-07 12:34:56+00');

エラー時の挙動

コンポーネント 失敗時の挙動
Redis からのスロット取得 エラーを返し、スロットは消費しない
PostgreSQL への INSERT スロットをプールに返却してエラー
Redis への lease 保存 PG セッションをクローズ、スロットを返却してエラー
nftables 再生成 警告ログのみ。次の reconcile で修復される
VICI 切断(agent/daemon) reconnect_delay_sec 後に再接続。再接続直後は必ず full reconcile を先に実行
Redis 利用不可(起動時) 致命的エラーで終了。Redis 復旧後に再起動
PostgreSQL セッションクローズ失敗 警告ログのみ。Abuse ログの disconnected_at が欠落する可能性

release の冪等性・排他制御(generation ベースの conditional release、request_id によるリトライ安全性)については internal/controller/docs/containerization-migration.md §5 を参照。

VICI フィールド名

internal/strongswan/vici.go の実装状況:

  • uniqueid(IKE_SA 識別子)・remote-eap-id(EAP ユーザー名)・remote-host(クライアント実 IP)・remote-vips(割当仮想 IP の配列)は、swanctl --list-sas --raw の実出力で確認済み。
  • 一方、child-updown イベント購読時のイベント名・フィールド値(特に up キーの有無で up/down を判定するロジック)は、実機の swanctl --listen での検証がまだ TODO として残っている。本番投入前にテスト接続で確認すること。
  • agent はイベント判定に不確実性が残ることを見越し、child-down 受信時は ConfirmSAGonelist-sas での生存確認)を経てから解放する設計になっている(rekey / re-authentication による誤解放を防止)。

About

情技研が提供するインターネット接続サービス (VPN)

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages