agent

package
v1.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Overview

Package agent は自宅側のエージェント(仕様 7 節)。 認証情報ファイルの鍵と最後の全体状態でトンネルとリスナーを先に立て、その後 stream に繋いで全体状態を受け取る。

Index

Constants

View Source
const ControlPathLimit = 103

ControlPathLimit は、どの OS でも収まる制御ソケットのパスの長さ(バイト)。sockaddr_un の sun_path は Linux と Windows で 108 バイト、macOS で 104 バイトで、終端の NUL を含む(仕様 11a 節)。

公開しているのは、繋げなかった理由が長さにあるかどうかを外から判定する読み手がいるためである (設計文書 10.2c 節の agent.control)。写しを持たせると、片方だけを直したときに判定が食い違う。

View Source
const DoctorCommand = "doctor"

DoctorCommand は制御ソケットに送る 1 行の要求である。

View Source
const ReasonHandshakePending = "handshake not established"

heartbeat は処理済み世代、トンネルの状態、ルールごとの状態をまとめる(仕様 5.2 節)。 ReasonHandshakePending は、トンネルはあるが WireGuard のハンドシェイクがまだ済んでいないときの理由。 適用直後の追送り(needsHandshakeFollowUp)がこの値で判定するので、文言を変えるときは両方に効く。

公開しているのは、制御ソケットの doctor の応答から同じ場合を見分ける読み手がいるためである (設計文書 10.2c 節)。応答が載せるのは理由の文字列だけで、ハンドシェイク待ちを tunnel.Status の Err による誤りと分ける材料は他に無い。写しを持たせると、この文言を変えたときに読み手だけが 取り残される。

Variables

View Source
var ErrPinMismatch = errors.New("server certificate does not match the pinned hash")

ErrPinMismatch はサーバ証明書がピンと一致しない。teardown --purge のあとに立て直したサーバか、経路上の 第三者による TLS の終端で起きる。復帰は未使用の WGFT_JOIN による再登録(仕様 5.1 節)。

View Source
var ErrRegisterRejected = errors.New("registration rejected: join string already used or expired, or the name differs")

ErrRegisterRejected は登録が認証で拒否された(トークンが無効、名前違い)。

Functions

func ControlPath

func ControlPath(path string) string

ControlPath は認証情報ファイルに対応する制御ソケットの場所。

func PinnedClient

func PinnedClient(pin [32]byte) *http.Client

PinnedClient は証明書の SHA-256 がピンと一致するときだけ通す HTTP クライアント。 通常の検証(CA、ホスト名、期限)は使わない。IP 直打ちでも DNS 名でも同じ接続文字列が使える。

func PublicKey

func PublicKey(path string) (wgtypes.Key, error)

PublicKey は認証情報ファイルの鍵(なければ生成して保存)の公開鍵を返す。

func Register

func Register(ctx context.Context, j *Join, name string) (permanentToken, address, confirmedName string, err error)

Register は登録 API を呼び、恒久トークン、割り当てアドレス、確定した名前を返す。 name は任意(空なら送らない側に倣ってトークンに紐付いた名前で登録される)。

func RotateKey

func RotateKey(path string) (string, error)

RotateKey は CLI から呼ぶ。稼働中なら制御ソケット経由で、停止中なら認証情報ファイルの鍵と last_state を直接消す。

func Run

func Run(opts Options) error

Run は認証情報ファイルを読み、登録を確かめ、トンネルとリスナーを立て、stream に繋ぎ、シグナルまで動く。

Types

type DoctorAllowTargets added in v1.1.0

type DoctorAllowTargets struct {
	// Set は一覧を持っているかどうか。偽なら、server はこのエージェントが届くどの宛先も指せる
	Set bool `json:"set"`
	// List は正規化した一覧。Set が偽なら空
	List string `json:"list,omitempty"`
	// Env は一覧を渡す設定の名前
	Env string `json:"env"`
}

DoctorAllowTargets は宛先の許可一覧である。一覧の中身はエージェントのホストにしか無く、 server には届かない(設計文書 10.2c 節)。

type DoctorBudget added in v1.1.0

type DoctorBudget struct {
	Proto proto.Proto `json:"proto"`
	// Total は予算 T、InUse は今のフロー数 u である
	Total int `json:"total"`
	InUse int `json:"in_use"`
	// RuleCap はルールが 2 本以上あるときのルール 1 本の上限 C、Reserve はルール 1 本あたりの
	// 隔離予約 q、Rules は今受け付けているルールの数 N である
	RuleCap int `json:"rule_cap"`
	Reserve int `json:"reserve"`
	Rules   int `json:"rules"`
	// Refusals は拒否の累計である。起点は DoctorRuntimeState.RefusalsSince
	Refusals []DoctorRefusal `json:"refusals,omitempty"`
}

DoctorBudget は 1 つのプロトコルのフロー予算である。記号は設計文書 7a.10 節に合わせる。

type DoctorRefusal added in v1.1.0

type DoctorRefusal struct {
	RuleID string `json:"rule_id"`
	// Reason は budget、rule_cap、reserve のいずれか(設計文書 7a.10 節)
	Reason resource.Reason `json:"reason"`
	Count  uint64          `json:"count"`
}

DoctorRefusal はルール 1 本の 1 つの理由の拒否の累計である。

type DoctorResponse added in v1.1.0

type DoctorResponse struct {
	// Error は応答を組む処理が失敗したことと、その理由である。運用者に何が起きたかを伝えるために
	// 載せる。値があるとき、残りの項目は無い
	Error string `json:"error,omitempty"`

	// AllowTargets は宛先の許可一覧である。起動時に決まって以後変わらず、実行時の排他も要らないので、
	// 排他を取れなかった応答にも載る
	AllowTargets *DoctorAllowTargets `json:"allow_targets,omitempty"`

	// Stream は制御ストリームの観測である。streamMu だけで読めるので、実行時の排他を取れなかった
	// 応答にも載る
	Stream *DoctorStream `json:"stream,omitempty"`

	// RuntimeState は実行時の排他の下でしか読めない状態である。排他を期限内に取れなければ無い
	RuntimeState *DoctorRuntimeState `json:"runtime_state,omitempty"`
	// RuntimeStateTimeout は、実行時の排他を取れなかったときに待った期限である。単位はナノ秒。
	// 取れた場合は 0 になる
	RuntimeStateTimeout time.Duration `json:"runtime_state_timeout,omitempty"`
}

DoctorResponse は doctor の応答である。1 行の JSON として送る。

Error があるときは、他の 3 つの項目が無い。応答を組む処理が panic したことを表すためである。 RuntimeState が無く RuntimeStateTimeout があるときは、実行時の状態を守る排他を期限内に 取れなかったことを表す。エージェントは生きているが内部の処理で詰まっている。

type DoctorRule added in v1.1.0

type DoctorRule struct {
	ID string `json:"id"`
	// State と Reason はハートビートが組み立てる proto.RuleStatus の値そのものである
	State  string `json:"state"`
	Reason string `json:"reason,omitempty"`
	// Proto は tcp か udp
	Proto proto.Proto `json:"proto,omitempty"`
	// Listeners はこのルールに属するリスナーの数、Listening はそのうち待ち受けを開けている数である
	Listeners int `json:"listeners"`
	Listening int `json:"listening"`
	// BindErrors は待ち受けを開けなかったリスナーの数、BindError はその 1 つの理由である。
	// 開けない原因はポートの衝突を指す
	BindErrors int    `json:"bind_errors"`
	BindError  string `json:"bind_error,omitempty"`
	// TargetErrors は待ち受けは開いていて宛先に届かないリスナーの数、TargetError はその 1 つの
	// 理由である。届かない原因は宛先の機器を指すので、運用者の次の行動が bind の失敗とは違う
	TargetErrors int    `json:"target_errors"`
	TargetError  string `json:"target_error,omitempty"`
	// Sessions は中継が持っている接続の数である。TCP は公開側と宛先側の両方を数えるので、
	// フロー予算の上限の対象とは一致しない。上限の対象の数は Flows である
	Sessions int `json:"sessions"`
	Flows    int `json:"flows"`
}

DoctorRule はルール 1 本の状態である。リスナー 1 つずつは並べない。ポート範囲の幅に上限が無く、 listen_port=1-65535 のルール 1 本で 65535 個のリスナーができるので、そのまま並べると 1 行が 数 MB になる(設計文書 10.2c 節)。

type DoctorRuntimeState added in v1.1.0

type DoctorRuntimeState struct {
	// Generation は最後に受け取って処理した全体状態の世代
	Generation uint64       `json:"generation"`
	Tunnel     DoctorTunnel `json:"tunnel"`
	// Rules はルールごとの状態である。リスナー 1 つずつは並べない。中継が無ければ項目ごと出ない
	Rules []DoctorRule `json:"rules,omitempty"`
	// Budgets はプロトコルごとのフロー予算である。中継が無ければ項目ごと出ない
	Budgets []DoctorBudget `json:"budgets,omitempty"`
	// RefusalsSince は Budgets の拒否の累計の起点、つまり今のトンネルを立てた時刻である。
	// フロー予算はトンネルを立て直すたびに中継ごと作り直され、累計はそのたびに 0 に戻る
	// (設計文書 10.2c 節)。中継が無ければゼロ値の時刻になる。項目そのものは必ず出るので、
	// 読み手は IsZero で判定する
	RefusalsSince time.Time `json:"refusals_since"`
}

DoctorRuntimeState は実行時の排他の下で 1 度に読んだ状態である。トンネル、ルール、フロー予算の 値はどれも同じ時点のものである。

type DoctorStream added in v1.1.0

type DoctorStream struct {
	Connected bool `json:"connected"`
	// DisconnectedAt と RetryAt、LastPingAt、LastPongAt は、値が無ければゼロ値の時刻になる。
	// encoding/json の omitempty は struct に効かないので、項目そのものは必ず出る。読み手は
	// IsZero で判定する
	DisconnectedAt   time.Time `json:"disconnected_at"`
	DisconnectReason string    `json:"disconnect_reason,omitempty"`
	// Backoff は直近に待った再接続の間隔。単位はナノ秒
	Backoff      time.Duration `json:"backoff,omitempty"`
	RetryAt      time.Time     `json:"retry_at"`
	LastPingAt   time.Time     `json:"last_ping_at"`
	LastPongAt   time.Time     `json:"last_pong_at"`
	AwaitingPong bool          `json:"awaiting_pong"`
}

DoctorStream は制御ストリームの観測の写しである。項目の意味は streamObservation にある。

type DoctorTunnel added in v1.1.0

type DoctorTunnel struct {
	// Present はトンネルがあるかどうか。偽なら Reason がその理由を言う
	Present bool `json:"present"`
	// State は ok か error
	State  string `json:"state"`
	Reason string `json:"reason,omitempty"`
	// Endpoint は解決済みのエンドポイント。初回の名前解決に失敗したトンネルは持たない
	Endpoint string `json:"endpoint,omitempty"`
	// LastHandshake は今の device から読んだ最終ハンドシェイクである。watchdog が別に持つ値は
	// トンネルを閉じても消えず、立て直した直後は前のトンネルの値が残るので、そちらは載せない。
	// 成立していなければゼロ値の時刻になる。項目そのものは必ず出るので、読み手は IsZero で判定する
	LastHandshake time.Time `json:"last_handshake"`
	RxBytes       int64     `json:"rx_bytes"`
	TxBytes       int64     `json:"tx_bytes"`
	// StartedAt は今のトンネルを立てた時刻。トンネルが無ければゼロ値の時刻になる
	StartedAt time.Time      `json:"started_at"`
	Watchdog  DoctorWatchdog `json:"watchdog"`
}

DoctorTunnel はトンネルの状態である。State と Reason はハートビートが組み立てる値そのもので、 doctor のための 2 つ目の判定は持たない(設計文書 10.2c 節)。

type DoctorWatchdog added in v1.1.0

type DoctorWatchdog struct {
	// RebuildInterval は判定に使う作り直しの間隔の実効値である。単位はナノ秒
	RebuildInterval time.Duration `json:"rebuild_interval"`
	// RetryAt は、作成に失敗して試し直しを待っている場合の予定の時刻。待っていなければゼロ値の
	// 時刻になる。項目そのものは必ず出るので、読み手は IsZero で判定する
	RetryAt time.Time `json:"retry_at"`
}

DoctorWatchdog はトンネルを作り直す判定の状態である。次の作り直しまでの残り時間は載せない。 残りは保持されておらず、示すには起点を求める規則を診断の側に写すことになるためである (設計文書 10.2c 節)。

type Join

type Join struct {
	Endpoint string // host:port(エージェント用 API)
	Token    string // 1 回限りの登録トークン
	Pin      [32]byte
	// contains filtered or unexported fields
}

Join は接続文字列 wgft://host:port/token#sha256:<hex> の中身(仕様 5.1 節)。

func ParseJoin

func ParseJoin(s string) (*Join, error)

ParseJoin は接続文字列を解釈する。scheme、ポート、sha256 のピンをすべて要求する。

func (*Join) TokenHash

func (j *Join) TokenHash() string

TokenHash は使用済みトークンの記録用(仕様 5.1 節の復帰経路で比較する)。

type Options

type Options struct {
	// AllowTargets は接続してよい宛先の許可一覧(仕様 7 節、WGFT_AGENT_ALLOW_TARGETS)。
	// nil なら制限せず、vpsd が配るどの宛先へも接続する
	AllowTargets    *allowtargets.List
	CredentialsPath string          // 認証情報ファイル
	Join            string          // 接続文字列(WGFT_JOIN か --join)。初回登録に使う
	Limits          resource.Limits // 同時フロー数のプロセス全体の予算(仕様 7 節)。ゼロ値は既定値
	Name            string          // エージェント名(WGFT_NAME か --name)。任意。接続文字列の発行時の名前に紐付いているので、与えなければトークンに紐付いた名前で登録される
	Version         string          // 起動ログに出す wgft の版(cmd 側の effectiveVersion())。空なら "dev" として出す
}

Options は agent の起動オプション。

Directories

Path Synopsis
Package allowtargets は、エージェントが接続してよい宛先の一覧(設計文書 7 節)。
Package allowtargets は、エージェントが接続してよい宛先の一覧(設計文書 7 節)。
Package credentials はエージェントの認証情報ファイル(agent.json)を扱う(仕様 9 節)。
Package credentials はエージェントの認証情報ファイル(agent.json)を扱う(仕様 9 節)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL