セキュアトランスポート: エンドツーエンド構成
このページは、次の3つのトポロジでクライアントをデーモンに安全に接続するための完全な設定リファレンスです:
- クライアントからデーモンへ直接接続 - 相互TLS対応WSS、リレーなし。
- デーモンからリレーへ - デーモンは、指定されたリレーへのアウトバウンドブリッジを維持することで、NAT/CGNAT の背後からも到達可能になります。
- クライアントからリレー経由でデーモンへ - クライアントはそのリレー を介して デーモンに接続しますが、実際のクライアント<->デーモン間の mTLS は引き続きデーモンで終端します。
60秒で始めるクイックスタートについては、リモートセットアップ(WSS)を参照してください。このページでは、各設定項目について詳しく説明します。
メンタルモデル: 2つのエンベロープ、1つの信頼境界
TLS レイヤーは 2 つあり、そのうちセキュリティ境界となるのは 1 つだけです:
- 内部 mTLS(実際の境界)。 TLS 1.3 のみで、相互認証です。クライアントはデーモン発行の証明書を提示し、デーモンはサーバーリーフ証明書を提示します。これは RPC プレーンです。サーバーのみ/未認証のパスは存在しません - クライアント証明書が常に必要です。
- 外側の TLS(メタデータの境界)。 リレーが経路上にある場合、リレーは外側の TLS + WebSocket セッションを終端し、不透明な暗号文を転送します。内部 RPC を読み取れる鍵を保持することは決してありません。直接トポロジーでは外側のレイヤーはありません。
デフォルトポート(すべて設定可能):
| 平面 | デフォルト | 設定 |
|---|---|---|
| デーモン WSS(内部 mTLS RPC) | 9781 | [wss].port |
| デーモン登録エンドポイント | 9782 | [enroll].port |
| Relay(外側 TLS + WS) | 8443 | リレー --bind / [bind] |
文書全体を通して、<data_dir> はデーモンのデータディレクトリ(通常は ~/.zeroclaw)を指し、<config-dir> はクライアントの zerocode 設定ディレクトリ(--config-dir、通常は ~/.zeroclaw)を指します。設定ファイルでは ~ は展開されません。絶対パスを使用してください。
トポロジー 1: クライアントからデーモンへの直接接続
zerocode ===== mutual-TLS WSS (TLS 1.3) =====> daemon [wss] :9781
1a. デーモン側
WSS リスナーを有効にします。安全なデフォルト設定では、初回起動時にデーモンが 独自の CA とサーバー証明書を自動生成するため、TLS 資材を手動で管理する必要はありません。
[wss]
enabled = true
# bind = "0.0.0.0" # デフォルト
# port = 9781 # デフォルト
# cert_path/key_path を空のままにすると、初回起動時に
# <data_dir>/tls/ 配下にサーバー証明書が自動生成されます。独自のサーバー証明書を使用する場合のみ設定してください。
[wss].enabled = true での初回起動時に、デーモンはディレクトリモード 0700 の <data_dir>/tls/ 配下に次のファイルを書き込みます:
| ファイル | 目的 | モード |
|---|---|---|
ca.crt | デーモンごとの CA 証明書(公開) | デフォルトの umask |
ca.key | CA 秘密鍵(クライアント証明書に署名) | 0600 |
server.crt | WSS サーバーのリーフ証明書(SAN localhost、127.0.0.1) | デフォルトの umask |
server.key | WSS サーバー秘密鍵 | 0600 |
秘密鍵は 0600 で書き込まれ、公開証明書にはプロセスの umask が使用されます。tls/ ディレクトリ自体は 0700 です。
CA は通知なしにローテーションされることは決してありません: ca.crt と ca.key が存在する場合は再利用されます。自動生成される CA の有効期間は 10 年、サーバーリーフ証明書の有効期間は約 27 か月、発行されるクライアント証明書の有効期間は 30 日です。
ポート(sudo ufw allow 9781/tcp)を開き、デーモンを起動します。WSS リスナーが 0.0.0.0:9781 で起動したことを示すログ行が表示されるはずです。
クライアントに証明書が必要になりました。取得する方法は 2 つあります。
1b. クライアント側 - オプション A: 登録(推奨)
登録により、証明書を持たないクライアントは、ペアリングでアクセスが制限されたサーバー認証済みエンドポイントを介して、証明書を手動で扱うことなく最初の証明書を取得できます。有効にするには:
[enroll]
enabled = true
# bind = "0.0.0.0" # デフォルト
# port = 9782 # デフォルト
# [wss] の enabled とデーモン CA キー(上記で自動生成されるもの、または BYO+key)が必要です。
# CA キーがない場合、エンドポイントはフェイルクローズし、証明書は
# 帯域外で用意する必要があります。
デーモンは起動時に、ペアリングコードとshort-auth-string (SAS)をコンソールまたはログに一度だけ出力します。このコードは1回しか使用できず、発行されてから10分後に有効期限が切れます。証明書発行に使用できる唯一のベアラー認証情報であり、コンソールやログに出力されるため、コピーされたコードがオペレーターによる使用後すぐに機能しなくなる必要があります。期限切れのコードは拒否されて消去されます。新しいコードが必要になったら、ゲートウェイのペアリング API から交換用コードを発行してください。その後、ワークステーションで次の操作を行います。
# インタラクティブ: 証明書なしのクライアントは初回接続時に自動登録されます。
zerocode --connect wss://<remote-host>:9781
# または明示的に / 非対話的に:
zerocode --enroll --connect wss://<remote-host>:9781
zerocode はペアリングコードの入力を求め、P-256 鍵と CSR をローカルで生成し(秘密鍵がデバイスから外に出ることはありません)、SAS を表示します。SAS がデーモンによって出力されたものと一致することを確認してください(これにより中間者 CA を検出できます)。また、<config-dir>/tls/ 配下に次をキャッシュします:
| ファイル | 目的 | モード |
|---|---|---|
client.crt | 発行済みクライアント証明書 | デフォルトの umask |
client.key | クライアント秘密鍵(ローカルで生成) | 0600 |
ca.crt | RPC プレーン用にピン留めされたデーモンの CA チェーン | デフォルトの umask |
profile.json | キャッシュされた device_id、not_after、リレープロファイル | デフォルトの umask |
以降の実行はすべて設定不要です(zerocode --connect wss://<remote-host>:9781、または設定に uri がある場合は単に zerocode)。証明書は有効期間の約50%(約15日)が経過した時点で、稼働中の mTLS セッションを介して自動更新されます。失効した証明書は自動更新できません。
登録エンドポイントのデフォルト: --enroll-host は --connect のホストをデフォルトとし、--enroll-port のデフォルトは 9782 です。
初回リリースでは、意図的にすべての登録でペアリングコードを必須とします。予約済みの allow_unpaired_enrollment 設定は、クライアントがピン留めされたデーモン CA フィンガープリントなど、コード不要の明示的な信頼アンカーを持つようになるまで、デーモンの起動時に拒否されます。これにより、暫定的な登録 TLS パスが盲目的な trust-on-first-use に変わるのを防ぎます。
1c. クライアント側 - オプション B: オペレーター発行の証明書
デーモン上で証明書を発行して取り出したい場合は:
# デーモンホスト上で。--out-dir は、ドロップイン用の ca.crt/client.crt/client.key も書き込みます。
zeroclaw security issue-client-cert --name my-laptop --out-dir /tmp/my-laptop-tls
# この名前の既存の証明書を上書きするには --force を追加します
クライアントの <config-dir>/tls/ に 3 つのファイルを ca.crt、client.crt、client.key としてコピーします(その後 zerocode --connect wss://host:9781 を実行すると自動的に見つかります)。または、それらを明示的に指定します:
zerocode --connect wss://<remote-host>:9781 \
--tls-ca-cert /path/ca.crt \
--tls-client-cert /path/client.crt \
--tls-client-key /path/client.key
同等の設定(単独の zerocode で動作します):
[connection.wss]
uri = "wss://<remote-host>:9781"
[connection.wss.tls]
ca_cert_path = "/abs/path/ca.crt"
client_cert_path = "/abs/path/client.crt"
client_key_path = "/abs/path/client.key"
証明書を持たないクライアントが登録せずに WSS プレーンへ接続した場合、対処可能な「先に enroll してください」というメッセージが表示され(デーモンは拒否された未移行クライアントをログに記録します)、サイレントハングが発生することはありません。
--tls-skip-verifyは、自己署名の開発用デーモンに対する サーバー 検証を緩和するだけであり、クライアント証明書は引き続き必須です。
トポロジー 2: デーモンからリレーへ
daemon ====== outbound: register + bridge ======> relay :8443
[relay] (blind forwarder)
デーモンはリレーに接続し、固定された Ed25519 ID を証明して node-id を登録します。クライアントは後でその node-id に接続します(Topology 3)。リレーが転送するのは暗号文だけです。
2a. リレー (zerorelay) を実行する
relay.toml(apps/zerorelay/relay.example.tomlを参照)で設定します。すべてのCLIフラグは、対応するファイルの値を上書きします。[admission]セクションはSIGHUPでホットリロードされます。明示的なオプトインなしにパブリックリレーをオープンでトークン不要の受け入れに変更するリロードは拒否され、以前のポリシーが引き続き有効になります。
# relay.toml
bind = "0.0.0.0:8443"
[tls]
# 初回実行時に cert/key を省略すると、外側の TLS 証明書を dir に自己プロビジョニングします(
# openssl は不要です)。sans には relay のパブリックなホスト名/IP を設定します。
dir = "/data/tls"
sans = ["relay.example.com"]
# または、独自の証明書を用意します(例: パブリック CA の証明書):
# cert = "/etc/zerorelay/fullchain.pem"
# key = "/etc/zerorelay/privkey.pem"
[admission]
# "open" は署名済みの任意のデーモンを許可します(deny リストに従います)。"allowlist" は
# 列挙されたデーモンの公開鍵フィンガープリントのみを許可します。deny が常に優先されます。
mode = "open"
allow = []
deny = []
# パブリック(ループバック以外)の relay では、登録を必ず制御してください。ここに共有シークレットを設定します
# (各デーモンは [relay] relay_token を介して提示します)または mode = "allowlist" を使用します。
# そうしないと、パブリック bind 上の OPEN でトークンなしの relay は起動を拒否します。これは、
# インターネット上の任意のデーモンが登録して未要求の node-ids を占有できてしまうためです。(
# ローカル開発用のループバック bind は対象外です。意図的に open なパブリック relay では、
# allow_public_open = true で上書きできます。)
relay_token = "change-me-to-a-long-random-secret"
[limits]
max_conns_per_node = 256
idle_timeout_secs = 300
lease_ttl_secs = 300
accept_burst_per_ip = 30
accept_rate_per_ip = 10.0
connect_burst_per_node = 60
connect_rate_per_node = 20.0
実行:
zerorelay --config /etc/zerorelay/relay.toml
# 同様に、フラグのみ(パブリックバインドにはトークンまたは許可リストが必要で、そうでなければその
# relay refuses to start):
zerorelay --bind 0.0.0.0:8443 --tls-san relay.example.com \
--relay-token change-me-to-a-long-random-secret
--tls-cert/--tls-key を省略すると、リレーは TLS ディレクトリ配下に CA + サーバー証明書を自動プロビジョニングします(解決順序: $ZERORELAY_DATA_DIR/tls、次に $HOME/.zerorelay/tls、最後に ./zerorelay/tls)。SAN には常に localhost と 127.0.0.1 が含まれます。自動プロビジョニングされた ca.crt は、デーモン/クライアントがリレーの外側の TLS を信頼するために使用されます。
アクセス許可。 open モードにオプションの relay_token を加えるのが、最も単純なアクセス制御です。allowlist モードでは、デーモンの登録用公開鍵のフィンガープリント(デーモンの <data_dir>/relay/registration.key にある Ed25519 鍵の SHA-256 16 進数)を基準にします。フィンガープリントを allow に追加し、kill -HUP <pid> で再読み込みします。node-id は最初の登録者の公開鍵にバインドされるため、別の鍵で稼働中の node-id を乗っ取ることはできません(node_taken になります)。
Docker. apps/zerorelay/Dockerfile は distroless で実行され、CMD ["--config", "/etc/zerorelay/relay.toml"] とシェルを使用しない zerorelay healthcheck --addr HEALTHCHECK を使用します。compose.yaml は /data にボリュームをマウントするため、自動プロビジョニングされた TLS が保持されます。8443 を公開します。
2b. デーモンをリレーに向ける
[wss]
enabled = true # 必須: リレーはローカル WSS リスナーに転送します
[relay]
enabled = true
url = "relay.example.com:8443"
# node_id: 空のままにしておく(推奨)と、ランダムな128ビットの
# ケイパビリティを <data_dir>/relay/node_id に自動発行して永続化します。特定のIDに固定する場合のみ設定してください。
# token = "change-me" # 設定されている場合は、リレーの [admission].relay_token と一致する必要があります
# リレーの外側の証明書の信頼設定 - 1つを選択:
relay_ca_path = "/path/to/relay/ca.crt" # リレーの(自己署名)CAを信頼する
# tofu = true # または、初回使用時にリレーのリーフ証明書をピン留めする
# relay_insecure = true # または、外側の検証をスキップする(開発時のみ)
# (パブリックCAを使用するリレーの場合は、組み込みのパブリックルートを使用するため、3つすべて未設定のままにする)
[relay] には [wss] の有効化が必要です(リレーは 127.0.0.1:<wss.port> にブリッジします)。url が空の場合はフェイルクローズします。デーモンは起動時に、ヒント “クライアントにはこれを –relay-node として渡してください” とともに node-id をログに出力します。<data_dir>/relay/node_id から読み取ることもできます。デーモンの固定された登録キーは <data_dir>/relay/registration.key (0600) に作成されます。
外側証明書の信頼優先順位(高い順):relay_insecure > relay_ca_path > <data_dir>/relay/relay_pin に保存されたピン(明示的または TOFU) > tofu > パブリックルート。したがって、CA を設定すると、古いピンが削除されずに置き換えられます。tofu = true の場合、観測されたリレーのリーフ証明書のフィンガープリントが <data_dir>/relay/relay_pin にピン留めされ、登録時に同じピンがクライアントに渡されるため、クライアントは同一のリーフ証明書をピン留めします。
2c.(オプション)node-id のローテーションと外部 mTLS
[relay]
node_id_rotation_days = 30 # auto-rotate the auto-minted id every N days (0 = never)
ローテーションでは新しい ID を発行し、進行中のクライアントが切断されないよう 10 分間の猶予期間中は古い ID と併用してから、古い ID を廃止します。新しい ID は、クライアントの次回の証明書更新時にインバンドでクライアントに届きます。zeroclaw security relay-rotate-node-id を使用して今すぐ 1 つ強制発行できます(auto-mint モードのみ。固定された node_id はローテーションされません)。
外側のレイヤーでもデーモンを認証するリレーの場合は、リレー側で[admission].outer_client_auth = "required" + outer_client_caを設定し、デーモン側で[relay].outer_client_cert / outer_client_keyを設定します。これは外側の TLS に追加されるもので、内側の mTLS には一切影響しません。
トポロジー 3: クライアントからリレーを経由してデーモンへ
zerocode ==outer TLS+WS==> relay ==forwards ciphertext==> daemon
\________________ inner mutual-TLS (TLS 1.3) terminates here _______________/
これはトポロジー 1 と 2 を組み合わせたものです。クライアントには、内側のクライアント証明書(1b と同様の enroll)およびリレーの接続情報(アドレス、node-id、リレーの外側の証明書に対する信頼)が必要です。
3a. 簡単な方法: 登録にリレープロファイルを含める
デーモンに [relay] が設定されている場合、その登録応答には リレープロファイル(relay_url、node_id、およびリレーのリーフ relay_cert_pin)が含まれます。つまり、1 回の登録ですべてがプロビジョニングされます。
zerocode --enroll --connect wss://<daemon-host>:9781
zerocode は内部証明書 と リレープロファイルを <config-dir>/tls/profile.json にキャッシュします。その後、通常の zerocode はフラグなしでリレー経由でデーモンに接続します。リレーのアドレス、node-id、ピンはすでに把握しています。
3b. 手動パス
クライアントにリレーの接続先情報を明示的に渡します。内部証明書は引き続き登録または --tls-* から取得されます(トポロジー 1):
zerocode \
--relay relay.example.com:8443 \
--relay-node <node-id-from-daemon-log> \
--relay-ca /path/to/relay/ca.crt
# inner mTLS material: from <config-dir>/tls (after enrolling), or pass --tls-* flags
リレーの外部証明書について、デーモンと同様に、信頼モードを必ず1つだけ選択します。
| フラグ | 意味 |
|---|---|
--relay-ca <pem> | リレーの(自己署名)CAを信頼する |
--relay-pin <sha256> | リレーの外側のリーフをピン留めします(通常は登録時に配布されます) |
--relay-tofu | 初回使用時に信頼し、PIN を <config-dir>/relay/relay_pin に保存します。 |
--relay-insecure | 外部検証をスキップ(開発環境/自己署名のみ) |
| (none) | 組み込みのパブリックルートを使用する(public-CA リレー) |
--relay-host <name> | 想定される外部証明書の SAN を上書きします(デフォルトは --relay ホスト) |
設定に相当するもの(zerocode だけで動作します):
[connection.wss]
relay_url = "relay.example.com:8443"
relay_node = "<node-id>"
リレーの外側の信頼設定(
--relay-ca/--relay-pin/--relay-tofu/--relay-insecure)は、[connection.wss]のキーではなく、フラグまたはキャッシュされた登録ピンによって指定されます。
3c. 直接接続を優先し、失敗時はリレーにフォールバック
クライアントに直接アドレスとリレーの 両方 を指定すると、直接経路を優先し、リレーにフォールバックした後、再プローブして直接経路に戻ります:
zerocode --connect wss://<daemon-host>:9781 \
--relay relay.example.com:8443 --relay-node <node-id>
チューニング([connection.wss] 内):
| キー | デフォルト | 意味 |
|---|---|---|
direct_attempts | 2 | リレーにフォールバックする前に直接接続を試行する |
direct_timeout_secs | 3 | 試行ごとの直接接続タイムアウト |
reprobe_secs | 30 | 直接接続に戻すための再プローブ間隔(0で無効) |
リレー専用モード(--connect/uri なし)では、内部 WSS URL のデフォルトは wss://127.0.0.1:9781 です。これは、内部 mTLS がデーモンのループバックリスナーで終端されるためです。リレーアドレスは TCP のダイヤル先にすぎません。
設定リファレンス
デーモン [wss]
| キー | デフォルト | 説明 |
|---|---|---|
enabled | false | 相互 TLS WSS リスナーを有効にする |
bind | 0.0.0.0 | バインドアドレス |
port | 9781 | リッスンポート |
cert_path | (空) | サーバー証明書を持ち込む。空の場合は <data_dir>/tls/ 配下に自動生成されます |
key_path | (空) | サーバーキーを持ち込む場合は指定。空欄の場合は自動生成 |
デーモン [wss.client_auth](省略可能。いずれにしても mTLS は常に有効です)
| キー | デフォルト | 説明 |
|---|---|---|
enabled | false | BYO CA を使用します。false の場合、デーモンは自動生成された CA を使用します |
ca_cert_path | (空) | クライアント証明書の検証に使用する PEM CA(BYO モード) |
pinned_certs | [] | 空でない場合は、これらの SHA-256 フィンガープリントに一致するクライアント証明書のみが受け入れられます |
crl_path | (空) | 失効フィンガープリントファイル。空の場合は、台帳からマテリアライズされた <data_dir>/tls/revoked を使用します |
デーモン [enroll]
| キー | デフォルト | 説明 |
|---|---|---|
enabled | false | 登録エンドポイントを有効にする([wss] + CA キーが必要) |
bind | 0.0.0.0 | バインドアドレス |
port | 9782 | リッスンポート |
allow_unpaired_enrollment | (空) | 予約済みです。no-code クライアントのトラストアンカーが存在するまで、空でない値は拒否されます。 |
デーモン [relay]
| キー | デフォルト | 説明 |
|---|---|---|
enabled | false | リレーブリッジを有効にする([wss] が必要) |
url | (空) | リレーアドレス host:port; 有効時に必須 |
node_id | (空) | 空の場合、128 ビット ID を自動生成して永続化します。1 つを固定するには設定します。 |
token | (空、シークレット) | 登録時に提示された共有シークレット |
relay_ca_path | (空) | リレーの外側の証明書用の PEM CA。空の場合は公開ルートを使用します |
relay_host | (空) | 外側の証明書の SAN を想定。空の場合は url から導出されます |
relay_insecure | false | 外側の証明書の検証をスキップ(開発環境のみ) |
tofu | false | 初回使用時にリレーリーフを <data_dir>/relay/relay_pin にピン留めします |
outer_client_cert | (空) | リレー受け入れ用の Daemon の外部 mTLS クライアント証明書 |
outer_client_key | (空) | outer_client_cert のキー |
node_id_rotation_days | 0 | 自動発行された node-id を N 日ごとに自動ローテーション(0 = 実行しない) |
Relay relay.toml
| Section.key | デフォルト | 説明 |
|---|---|---|
bind | 0.0.0.0:8443 | リッスンアドレス(デーモン + クライアント) |
[tls].cert / .key | (セルフプロビジョニング) | 外側 TLS ID; 両方を省略すると自己プロビジョニング |
[tls].dir | データディレクトリ /tls | 自己プロビジョニングされた証明書の書き込み先 |
[tls].sans | [] | 追加の SAN(localhost、127.0.0.1 は常に含まれます) |
[admission].mode | open | open または allowlist |
[admission].allow / .deny | [] | デーモンの公開鍵フィンガープリント(拒否が優先) |
[admission].relay_token | (none) | 共有シークレットによるオプションのゲート |
[admission].outer_client_auth | off | off / optional / required(外側の mTLS) |
[admission].outer_client_ca | (none) | 外部クライアント証明書用の PEM CA |
[admission].route_by_client_cert | false | 外側の証明書の CN の node-id でルーティング |
[limits].max_conns_per_node | 256 | node-id あたりの同時クライアント接続数 |
[limits].idle_timeout_secs | 300 | アイドル状態のクライアント接続を N 秒後に切断する |
[limits].lease_ttl_secs | 300 | 登録時に通知されるリース TTL(v1 では参考値: 実際のクリーンアップ規則は WebSocket の生存性) |
[limits].accept_burst_per_ip / accept_rate_per_ip | 30 / 10.0 | IP ごとのハンドシェイク用トークンバケット |
[limits].connect_burst_per_node / connect_rate_per_node | 60 / 20.0 | ノードごとの接続トークンバケット |
[limits].max_pending_handshakes | 256 | accept 後で、まだ分類されていないソケット |
[limits].handshake_timeout_secs | 10 | TLS、WS アップグレード、署名付き登録に共通の単一の期限 |
[limits].max_registered_nodes | 1024 | 同時に登録されたデーモン(N+1 は registry_full を取得) |
zerorelay CLI(relay.toml を上書き)
--config --bind --tls-cert --tls-key --tls-dir --tls-san (複数回指定可能) --registration-mode --allow (複数回指定可能) --deny (複数回指定可能) --relay-token --max-conns-per-node --idle-timeout-secs --lease-ttl-secs --status-file。サブコマンド: healthcheck [--addr 127.0.0.1:8443]、status --file <path>。
zerocode [connection.wss] と CLI
[connection.wss] キー | デフォルト | CLI オーバーライド |
|---|---|---|
uri | (none) | --connect |
relay_url | (none) | --relay(--relay-node が必要) |
relay_node | (none) | --relay-node(--relayが必要) |
direct_attempts | 2 | - |
direct_timeout_secs | 3 | - |
reprobe_secs | 30 | - |
[connection.wss.tls] キー | デフォルト | CLI オーバーライド |
|---|---|---|
ca_cert_path | <config-dir>/tls/ca.crt | --tls-ca-cert |
client_cert_path | <config-dir>/tls/client.crt | --tls-client-cert(キーが必要) |
client_key_path | <config-dir>/tls/client.key | --tls-client-key (証明書が必要) |
skip_verify | false | --tls-skip-verify |
Relay の外部信頼と登録は CLI/キャッシュ専用です: --relay-ca --relay-host --relay-insecure --relay-pin --relay-tofu --relay-client-cert --relay-client-key --enroll --enroll-host --enroll-port。
ファイルレイアウト
デーモン <data_dir>/:
tls/ca.crt tls/ca.key per-daemon CA (key 0600)
tls/server.crt tls/server.key WSS server leaf (key 0600)
tls/ledger.db issued-cert ledger (SQLite)
tls/revoked revoked fingerprints (handshake-checked)
relay/registration.key Ed25519 relay identity (0600)
relay/node_id auto-minted node-id
relay/relay_pin pinned relay outer-leaf fingerprint (TOFU)
クライアント <config-dir>/:
tls/client.crt tls/client.key client identity (key 0600)
tls/ca.crt pinned daemon CA
tls/profile.json device_id, not_after, cached relay profile
relay/relay_pin relay outer-leaf pin (--relay-tofu)
Relay <tls-dir>/: ca.crt, server.crt, server.key(自己プロビジョニング済み)。
既存の発行済み証明書台帳のアップグレード
tls/ledger.db にはスキーマバージョンが記録されています。以前のリビジョンで書き込まれた台帳は、新しいデーモンが初めて開く際にその場で再構築され、発行済みおよび失効済みのすべての証明書と、それぞれのデバイス ID、有効期間、監査主体が保持されます。再構築は単一のトランザクションで実行されます。完了できない場合、デーモンは起動を拒否し、移行途中の状態にするのではなく元の台帳を変更せずに残します。また、エラーにはそのファイル名が示されます。オペレーターによる操作は不要で、登録済みのデバイスを再登録する必要もありません。
未配信の証明書
デーモンは、登録クライアント、更新クライアント、またはオペレーターの issue-client-cert ファイルに証明書を引き渡す 前に、発行を台帳に記録します。その順序は意図的です。別の順序にすると、台帳に行がないまま、CA 署名済みの有効な証明書が誰かの手元に残る可能性があり、台帳が存在を把握していない証明書は一覧表示も失効もできません。
その代償として、途中で障害が発生すると(応答の途中で切断するクライアントや、rename の失敗など)、誰も受け取っていない証明書の active 行が残ります。このような行は undelivered として別途追跡され、1時間を超えた時点で失効します。次のうち最初に発生した時点で:
- 証明書の新規発行または更新、
- 任意の登録接続、
- デーモンの再起動やその他の台帳のオープン。
これはバックグラウンドタイマーではありません。証明書処理をまったく行っていないデーモンでは、次にその処理を行うまでスイープが延期されます。それまでは証明書は tls/revoked の外に残り、更新はできないものの、WSS ハンドシェイクには引き続き通過します。認証に失敗する接続を含め、登録または更新のトラフィックがあれば、それを整合させるのに十分です。
照合済みの証明書は取り消され、削除されることはありません。台帳の行は残り、フィンガープリントはオペレーターによる取り消しの場合とまったく同様に tls/revoked に登録されます。監査アクターには reconcile:undelivered が設定されるため、監査証跡で両者を区別できます。security list-client-certs は ACTIVE の証明書のみを表示するため、照合済みの証明書はその一覧から表示されなくなります - インシデント対応中は、取り消し履歴を確認するために tls/revoked または監査ログを参照してください。デバイスが、_実際に_受け取った証明書が機能しなくなったと報告した場合は、そのアクターを探してください - これは配布記録が失われたことを意味するため、再登録または再発行が必要です。
検証とトラブルシューティング
- デーモン起動済み:
0.0.0.0:9781の WSS リスナーログを確認し、リレーを使用している場合はnode_idのログ行も確認します。 - 証明書なしの接続が拒否されました: mTLS プレーンでは想定される動作です - 先に登録してください(トポロジー 1b)。このメッセージは対処可能であり、ハングではありません。
- Relay に到達可能:
zerorelay healthcheck --addr <host>:8443は終了コード 0 で終了します。 - Relay メトリクス:
--status-file <path>を指定して実行し、その後zerorelay status --file <path>を実行します(カウントのみで、ペイロードは一切含まれません)。 - 紛失したデバイスを失効させる: デーモンの台帳で失効処理を行うと
<data_dir>/tls/revokedが生成されます。証明書は次回のハンドシェイクで拒否され、自動更新できなくなります。 - 登録時のSAS不一致: クライアントは証明書の永続化を拒否して中止します。不一致は、受け取ったCAがデーモンのものではないことを意味します。再試行する前に、中間者攻撃の可能性を調査してください。
scripts/dev/mtls-relay-testbed.sh に、デーモン + リレーを起動し、証明書を発行し、ネットワーク経由で登録を行い、3 つすべてのトポロジを検証する自己完結型のエンドツーエンド・ハーネスがあります。実例として参照してください。