proto

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 26, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package proto は vpsd と agent が共有する、ルールと全体状態の JSON スキーマを定める(仕様 5.2, 5.3 節)。

Index

Constants

View Source
const (
	MsgPublicKey = "pubkey"    // エージェント → vpsd。接続直後の最初のメッセージ
	MsgState     = "state"     // vpsd → エージェント。差分ではなく全体
	MsgHeartbeat = "heartbeat" // エージェント → vpsd。30 秒ごとと、全体状態の適用直後
)

stream(仕様 5.2 節)で流れるメッセージ。双方向で、エージェントは公開鍵とハートビートを、 vpsd は全体状態を送る。1 つの型で表し、Type で中身を選ぶ。

View Source
const (
	StatusOK    = "ok"
	StatusError = "error"
)

状態の値。

View Source
const (
	CloseSuperseded       = 4000 // 同じエージェントの新しい接続に置き換わった
	CloseRevoked          = 4001 // 恒久トークンが無効化された
	CloseHeartbeatTimeout = 4002 // ハートビート(30 秒間隔)が 90 秒間届かなかった
	CloseProtocolMismatch = 4003 // 版の範囲(protocol_min/protocol_max)の共通部分が無い(仕様 7a.6 節)
	// CloseProtocolMalformed は、版の advertisement 自体が壊れている場合(protocol_min/
	// protocol_max の片方だけがある、または Min < 1 か Min > Max の無効な範囲)。
	// CloseProtocolMismatch(双方とも正当な範囲を宣言したが共通部分が無い)とは原因が異なる。
	// 前者は相手の実装の不具合、後者は版を上げれば直る正常な状態なので、コードでも区別できる
	// ようにした(仕様 7a.6 節)。
	CloseProtocolMalformed = 4004
)

WebSocket の切断理由コード(4000 番台はアプリケーション用)。

Variables

View Source
var SupportedCapabilities = []string{}

SupportedCapabilities は、この build が持つ capability の語彙。今のところ語彙が無いため空。

View Source
var SupportedProtocol = ProtocolRange{Min: 1, Max: 1}

SupportedProtocol は、この build の server と agent が共通に対応できる、番号の付いた版の範囲。 現在の版と直前の版を必ず支えるという約束により、v2 を追加する変更は Max を 2 に広げるだけで、 v1 を落とすときに初めて Min を 2 に上げる。SelectProtocolVersion 自体は変えなくてよい。

Functions

func AddSources added in v0.4.0

func AddSources(list, add []netip.Prefix) []netip.Prefix

AddSources は list の末尾に add を加える。list に既にある CIDR と、add の中の重複は加えない。

func DeletedIDs added in v0.4.0

func DeletedIDs(diff []RuleChange) []string

DeletedIDs は diff の Deleted 行の ID を集める。読み込みの適用(仕様 10.1 節)と CLI の `rule import` が、削除するルールの組み立てをここで共有する。

func ParseSource added in v0.4.0

func ParseSource(s string) (netip.Prefix, error)

ParseSource は接続元制限(source_deny / source_allow)の 1 項目を解釈する。 CIDR か単独のアドレスを受け、単独のアドレスは /32(IPv6 なら /128)で補う。ホスト部は落とす。 IPv4 かどうかはここでは見ない(Rule.Validate が見る)。CLI と Web UI が共有する。

func ParseSourceLines added in v0.4.0

func ParseSourceLines(text string) ([]netip.Prefix, error)

ParseSourceLines は複数行のテキストを解釈する(Web UI の入力用)。 1 行に 1 つの CIDR かアドレスを書く。前後の空白と空行は無視する。 解釈できない行が 1 つでもあれば、その行番号(1 始まり、空行も数える)と内容を含むエラーで止め、 それまでに解釈できた行があっても何も返さない(呼び出し側が入力をそのまま残せるようにするため)。

func ParseSources added in v0.4.0

func ParseSources(args []string) ([]netip.Prefix, error)

ParseSources は ParseSource を列に適用する。最初の誤りで止める。

func RemoveSources added in v0.4.0

func RemoveSources(list, rm []netip.Prefix) []netip.Prefix

RemoveSources は list から rm に含まれる CIDR を除く。空になれば空のスライスを返す。

func RulesDigest added in v0.4.0

func RulesDigest(rules []Rule) string

RulesDigest はルール集合の内容だけに依存するハッシュ(SHA-256 の16進表記)を返す。 ID でソートしてから JSON にすることで、並び順の違いを無視する。Web UI の読み込みの 確認ページ(仕様 10.1 節)が、確認を表示した時点の全体と適用しようとする時点の 全体を比べ、CLI からの変更などによる食い違いを検出するために使う。group、note、 接続元制限、レートの変更は世代を上げない(5.3 節)ため、世代だけの照合では見逃す。 空の接続元リストは nil と空スライスのどちらでも同じ値にする。JSON では null と [] に分かれるが意味は同じで、経路(ストアの複製、API の JSON の往復)によって表現が 揺れると、変更が無いのに食い違いと判定してしまうため。

func SelectProtocolVersion added in v0.4.0

func SelectProtocolVersion(local, remote ProtocolRange) (version int, ok bool)

SelectProtocolVersion は、local と remote の範囲の共通部分のうち最大の版を選ぶ(仕様 7a.6 節)。 共通部分が無ければ ok は false。local と remote のどちらかが Valid でなければ、選びようが 無いので常に ok は false を返す(呼び出し元が範囲の検査を怠っても、無効な範囲から版が 選ばれることはない。例えば {Min:0,Max:1} と {Min:1,Max:1} は、素朴な区間交差の計算では 1 を選んでしまうが、{Min:0,Max:1} は版 0 を含む無効な範囲なので拒む)。

func UnchangedIDs added in v0.4.0

func UnchangedIDs(rules, before []Rule) map[string]bool

UnchangedIDs は rules のうち、before に同じ ID で同じ内容の行がある ID の集合を返す。 ValidateUpsert と、Web UI の読み込みの確認ページ(仕様 10.1 節)が、どの行に Rule.Validate() を掛け直すかを同じ規則で決めるために使う。空の接続元リストは nil と 空スライスを同じとみなす(経路によって表現が揺れるため。RulesDigest と同じ扱い)。

func ValidateRules

func ValidateRules(rules []Rule, reserved Reserved) error

ValidateRules はルール集合全体の制約を検査する。 listen_port の重複はプロトコルごとに、有効無効を問わず見る(無効なルールを有効に戻したときに衝突させないため)。 予約ポートはプロトコルを問わず拒否する。

func ValidateUpsert added in v0.4.0

func ValidateUpsert(rules, before []Rule, reserved Reserved) error

ValidateUpsert は ValidateRules と同じだが、before と ID・内容がまったく同じ行には Rule.Validate() を掛け直さない(5.4 節)。store.ApplyBatch はバッチのたびに rules 全体を この関数で検査するため、Rule.Validate() に検査を後から増やすと、増やす前から保存されていた 触っていない行のせいで、以後の無関係なバッチまで失敗しかねない(例:proxy の範囲を拒否する 検査を追加した後、それ以前に作られた proxy の範囲ルールが 1 件あるだけで、他のルールの 有効無効を切り替えるだけのバッチも失敗する)。before に無い(新規)行や、値が変わった行は 通常どおり検査する。ID の重複・予約ポート・listen_port の重なりは before の有無に関わらず 全体に対して行う(これらは以前から常に全体を検査していたため、検査対象から外しても保存された データが既に満たしている)。

Types

type AgentRule

type AgentRule struct {
	ID         string    `json:"id"`
	Proto      Proto     `json:"proto"`
	ListenPort PortRange `json:"listen_port"`
	Target     string    `json:"target"`
	Enabled    bool      `json:"enabled"`
}

AgentRule はエージェントに配るルールの部分集合。vps_mode と接続元制限は配らない(仕様 5.3 節)。

func (AgentRule) EffectiveTarget

func (r AgentRule) EffectiveTarget(p uint16) (target string, ok bool)

EffectiveTarget は listen_port 内のポート p に対応する実効宛先(仕様 7 節)。 target のポートに、範囲内での位置を足したもの。p が範囲外なら ok は false。 Rule.Validate は「target のポートに範囲の幅を足した実効宛先が 65535 を超える場合」を拒否する (仕様 5.3 節)が、AgentRule は Validate を経ずに vpsd から届いた State 経由でも組み立てられる (internal/dataplane/linuxkernel/nft.planAgentRule も同じ算出を自前で検査しているのはこのため)。 ここでも同じ上限を検査し、越える p には ok=false を返す。

type ChangeKind added in v0.4.0

type ChangeKind string

ChangeKind は読み込み確認(仕様 10.1 節)の 1 行の種別。

const (
	ChangeAdded     ChangeKind = "added"
	ChangeChanged   ChangeKind = "changed"
	ChangeDeleted   ChangeKind = "deleted"
	ChangeUnchanged ChangeKind = "unchanged"
)

type FieldChange added in v0.4.0

type FieldChange struct {
	Field    string
	Old, New string
}

FieldChange は Changed な行の中で変わったフィールド 1 つ。Field は英語の固定キー (agent、target、source_deny など)で、表示用の訳は呼び出し側(Web UI)が持つ。 Old/New はそのまま表示できる値。接続元制限(source_allow/source_deny)は件数の文字列、 レートは Rate.String() の形、他は Rule のフィールドの文字列表現である。

type Heartbeat

type Heartbeat struct {
	Generation uint64       `json:"generation"` // 最後に受け取って処理した世代(部分失敗でも進める)
	Tunnel     TunnelStatus `json:"tunnel"`
	Rules      []RuleStatus `json:"rules"`
}

Heartbeat はエージェントの状態(仕様 5.2 節)。

type MergeBlocker added in v0.4.0

type MergeBlocker string

MergeBlocker はルール 2 つが統合できない理由の分類(仕様 10.1、10.2 節)。 CLI の `rule merge` が失敗するときの判定と、Web UI の統合区画が候補に出さない 隣接ルールの理由を説明するのとで、同じ集合を共有する。

const (
	// BlockNone は理由が無い、つまり統合できることを表す。
	BlockNone        MergeBlocker = ""
	BlockAgent       MergeBlocker = "agent"
	BlockProto       MergeBlocker = "proto"
	BlockMode        MergeBlocker = "mode"
	BlockNotAdjacent MergeBlocker = "not_adjacent"
	BlockTargetGap   MergeBlocker = "target_gap"
	// BlockProxyRange は vps_mode=proxy の 2 つの統合を表す。統合は listen_port を必ず
	// 2 ポート以上の範囲に広げるため、proxy は単一ポート運用(Rule.Validate)の下では
	// 組み合わせを問わず常に統合できない(仕様 5.4、6.2 節)
	BlockProxyRange MergeBlocker = "proxy_range"
	// proxy_protocol は vps_mode=proxy でしか立てられない(Rule.Validate)ので、
	// 違いは BlockProxyRange で先に止まり、専用の理由は持たない
	BlockDenyList  MergeBlocker = "deny_list"
	BlockAllowList MergeBlocker = "allow_list"
	BlockRates     MergeBlocker = "rates"
	BlockEnabled   MergeBlocker = "enabled"
)

func FindMergeBlocker added in v0.4.0

func FindMergeBlocker(a, b Rule) MergeBlocker

FindMergeBlocker は a と b(順不同)が統合できない最初の理由を返す。すべて揃えば BlockNone(統合できる)を返す。listen_port が小さい方を lo、大きい方を hi として、 エージェント、プロトコル、方式、隣接、実効宛先の連続、proxy かどうか、拒否/許可 リスト、3 つのレート、enabled の順に見る。統合すると self の値だけが残るので、 拒否/許可リストから enabled までが揃わない組を統合すると、もう一方の設定が黙って 失われる。Merge も Web UI の候補の絞り込みも、この関数で同じ判定をする。

type Message

type Message struct {
	Type      string     `json:"type"`
	PublicKey string     `json:"public_key,omitempty"` // MsgPublicKey:wg 公開鍵(base64)
	State     *State     `json:"state,omitempty"`      // MsgState
	Heartbeat *Heartbeat `json:"heartbeat,omitempty"`  // MsgHeartbeat

	// ProtocolMin/ProtocolMax/Capabilities は MsgPublicKey に載る版と機能の交渉(仕様 7a.6 節)。
	// ポインタと *[]string にしてあるのは、フィールドが無いこと(legacy v0 の agent)と、
	// 空配列(版はあるが追加の機能は無い)を JSON の上で区別するため。encoding/json の
	// omitempty は空スライスも「空」として省いてしまうので、[]string のままでは区別できない
	ProtocolMin  *int      `json:"protocol_min,omitempty"`
	ProtocolMax  *int      `json:"protocol_max,omitempty"`
	Capabilities *[]string `json:"capabilities,omitempty"`
}

Message は stream の 1 メッセージ。

type Negotiated added in v0.4.0

type Negotiated struct {
	Legacy       bool
	Version      int      // 選んだ版(Legacy なら 0)
	AgentMin     int      // agent が宣言した protocol_min(Legacy なら 0)
	AgentMax     int      // agent が宣言した protocol_max(Legacy なら 0)
	Capabilities []string // agent が宣言した capabilities(Legacy か、宣言が無ければ nil)
}

Negotiated は、1 本の stream 接続について選んだ版と、agent が宣言した機能(仕様 7a.6 節)。 Legacy が true なら agent は版のフィールドを持たない legacy v0 で、Version 以下のフィールドは 意味を持たない(全体状態には版のフィールドを載せない)。

type PortRange

type PortRange struct {
	Lo, Hi uint16
}

PortRange は VPS で待ち受けるポートの範囲(仕様 5.3 節の listen_port)。 単一ポートは Lo == Hi。JSON では "2456" または "2456-2457" の文字列で表す。

func ParsePortRange

func ParsePortRange(s string) (PortRange, error)

ParsePortRange は "2456" または "2456-2457" を解釈する。

func ParseSplitPoint added in v0.4.0

func ParseSplitPoint(s string) (PortRange, error)

ParseSplitPoint は分割位置の入力(CLI の引数、Web UI のフォーム値)を解釈する (仕様 10.1、10.2 節。CLI の `rule split` と Web UI の分割区画が共有する)。 解釈できないか、範囲でなく単一ポートでない入力は、どちらも同じ "split point must be a single port" で誤りとする(Rule.Split 自身の範囲外検査とは別に、 呼び出し側が Split を呼ぶ前に共通の形で弾む)。

func (PortRange) Contains

func (r PortRange) Contains(p uint16) bool

func (PortRange) IsRange added in v0.4.0

func (r PortRange) IsRange() bool

IsRange は単一ポートでなく範囲かどうかを返す。Rule.Split の前提(仕様 10.1 節)。

func (PortRange) Len

func (r PortRange) Len() int

Len は範囲に含まれるポート数。

func (PortRange) MarshalText

func (r PortRange) MarshalText() ([]byte, error)

func (PortRange) Overlaps

func (r PortRange) Overlaps(o PortRange) bool

func (PortRange) String

func (r PortRange) String() string

func (*PortRange) UnmarshalText

func (r *PortRange) UnmarshalText(b []byte) error

type Proto

type Proto string

Proto は転送する L4 プロトコル。

const (
	TCP Proto = "tcp"
	UDP Proto = "udp"
)

type ProtocolRange added in v0.4.0

type ProtocolRange struct {
	Min int
	Max int
}

ProtocolRange は対応する版の範囲(両端を含む)。

func (ProtocolRange) Valid added in v0.4.0

func (r ProtocolRange) Valid() bool

Valid は、r が番号の付いた版の範囲として意味を持つかを返す。番号の付いた版は 1 から始まるので、 Min が 1 未満、または Min が Max を超える範囲は無効(仕様 7a.6 節。malformed な advertisement の 判定にも使う)。

type Rate

type Rate struct {
	Count uint64
	Unit  RateUnit
}

Rate はレート制限の上限(仕様 5.3 節の new_flow_rate など)。 JSON では nftables と同じ "100/second" の文字列で表す。

func ParseRate

func ParseRate(s string) (Rate, error)

ParseRate は "100/second" を解釈する。

func (Rate) MarshalText

func (r Rate) MarshalText() ([]byte, error)

func (Rate) String

func (r Rate) String() string

func (*Rate) UnmarshalText

func (r *Rate) UnmarshalText(b []byte) error

type RateUnit

type RateUnit string

RateUnit は Rate の時間単位。nftables の limit と同じ語を使う。

const (
	PerSecond RateUnit = "second"
	PerMinute RateUnit = "minute"
	PerHour   RateUnit = "hour"
	PerDay    RateUnit = "day"
	PerWeek   RateUnit = "week"
)

type Reserved

type Reserved map[uint16]string

Reserved は listen_port に使えないポートと、その用途(エラーメッセージ用)。 WireGuard、エージェント用 API、管理用 API のポートを vpsd が入れる(仕様 5.3 節)。

type Rule

type Rule struct {
	ID            string         `json:"id"`
	Agent         string         `json:"agent"`
	Group         string         `json:"group"` // 任意。ルールを束ねるフラットなラベル 1 つ(仕様 5.3)
	Note          string         `json:"note"`  // 任意。何のためのルールかの自由記述
	Proto         Proto          `json:"proto"`
	ListenPort    PortRange      `json:"listen_port"`
	Target        string         `json:"target"` // host:port。範囲のときは先頭ポートに対応する
	VPSMode       VPSMode        `json:"vps_mode"`
	ProxyProtocol bool           `json:"proxy_protocol"`
	SourceAllow   []netip.Prefix `json:"source_allow"`
	SourceDeny    []netip.Prefix `json:"source_deny"`
	NewFlowRate   *Rate          `json:"new_flow_rate"`
	PacketRate    *Rate          `json:"packet_rate"`
	PerSourceRate *Rate          `json:"per_source_rate"`
	Enabled       bool           `json:"enabled"`
}

Rule は「VPS の <proto>/<listen_port> を、エージェント <agent> 経由で <target> へ届ける」宣言(仕様 5.3 節)。

func Merge added in v0.4.0

func Merge(self, other Rule) (merged Rule, err error)

Merge は self と other を 1 つに統合する(仕様 10.1、10.2 節。CLI の `rule merge <id1> <id2>` と Web UI の統合区画が共有する)。self の ID・group・note・拒否/許可 リスト・レート・enabled をそのまま残し、listen_port と実効宛先だけを other の分を 含むように広げる。other は呼び出し側がバッチで削除する。FindMergeBlocker が BlockNone を返す組み合わせでなければ誤りを返す。

func (*Rule) ForAgent

func (r *Rule) ForAgent() AgentRule

ForAgent はエージェントに配る部分だけを取り出す。

func (Rule) Split added in v0.4.0

func (r Rule) Split(at PortRange, tailID string) (head, tail Rule, err error)

Split は範囲の listen_port を持つルールを、ポート at の直前で 2 つに割る (仕様 10.1、10.2 節。CLI の `rule split` と Web UI の分割区画が共有する)。 head は元の ID を保ち [Lo, at-1] を、tail は tailID を新たな ID として [at, Hi] を持つ。 どちらの実効宛先も元のままなので(仕様 5.4、7 節)、通信中のセッションは切れない。 at は単一ポートで、範囲の先頭を除く内側でなければならない。 vps_mode=proxy のルールは単一ポート運用(Rule.Validate)なので、head・tail の両方が 単一ポートに収まる分割だけを受け付ける(2 ポートの範囲を境界で割る場合だけ両方が単一 ポートになる)。3 ポート以上の proxy の範囲は、この関数を後から検査を増やす前に作られた 既存データとしてしか存在しえず、単発の分割では単一ポートまで割り切れないので拒否する。 直す手段は削除して単一ポートずつ作り直すことになる(改訂の記録参照)。

func (Rule) TargetDisplay added in v0.1.1

func (r Rule) TargetDisplay() string

TargetDisplay は表示用の実効宛先。listen_port が範囲なら、target の先頭ポートから連番で写した 範囲を host:lo-hi の形で返す(仕様 5.3 節)。単一ポートや解釈できない値は target をそのまま返す。

func (*Rule) Validate

func (r *Rule) Validate() error

Validate はルール単体で判定できる制約を検査する。ルール間の制約は ValidateRules が見る。

type RuleChange added in v0.4.0

type RuleChange struct {
	Kind         ChangeKind
	Rule         Rule
	Previous     Rule
	FieldChanges []FieldChange
}

RuleChange は DiffRules の 1 行。Added/Unchanged では Rule が読み込んだ側の値、 Deleted では現在の側の値を持つ。Changed では両方を持ち、FieldChanges に変わった フィールドを列挙する。

func DiffRules added in v0.4.0

func DiffRules(current, desired []Rule) []RuleChange

DiffRules は現在のルール集合 current と、読み込んだルール集合 desired を ID で 突き合わせ、行ごとの差分を返す(仕様 10.1 節)。desired に無い current の ID は Deleted として末尾に付く。順序は desired の順を保ち、その後に Deleted を current の 順で並べる。desired の各ルールは ID が確定していること(空 ID への割り当ては呼び出し側 が DiffRules の前に行うこと)を前提にする。CLI の `rule import` と Web UI の読み込みの 確認・適用がこの関数を共有する。

type RuleStatus

type RuleStatus struct {
	ID     string `json:"id"`
	State  string `json:"state"` // ok | error
	Reason string `json:"reason,omitempty"`
}

RuleStatus はルールごとの状態。error はリスナーの開放失敗か、TCP の target への接続確認の失敗。

type State

type State struct {
	Generation uint64      `json:"generation"`
	WG         WGConfig    `json:"wg"`
	Rules      []AgentRule `json:"rules"`

	// ServerProtocolVersion/ServerCapabilities は、その stream 接続で選んだ版と server の機能
	// (仕様 7a.6 節)。agent が legacy v0 なら vpsd はこのフィールドを載せない(nil のまま)。
	// *[]string にしてあるのは Message.Capabilities と同じ理由(空配列と不在の区別)
	ServerProtocolVersion *int      `json:"server_protocol_version,omitempty"`
	ServerCapabilities    *[]string `json:"server_capabilities,omitempty"`

	// AgentDisabled は、server がこのエージェントを無効にしていることを示す(仕様 5.1 節)。無効の間、
	// Rules はすべて enabled:false の写しで届く。エージェントが止まるのはその enabled:false による
	// ものであり、このフィールドはエージェント自身の診断のためだけにある。守りには使わない。
	// 加算のフィールドで、有効なら省く。旧い版のエージェントは読み飛ばす(Go の encoding/json は
	// 構造体に無いフィールドを無視する。仕様 7a.6 節)ので、版と機能の交渉は要らない
	AgentDisabled bool `json:"agent_disabled,omitempty"`
}

State は vpsd がエージェントに配る全体状態(仕様 5.2 節)。差分ではなく常に全体を送る。

type TunnelStatus

type TunnelStatus struct {
	State         string    `json:"state"` // ok | error
	Reason        string    `json:"reason,omitempty"`
	Endpoint      string    `json:"endpoint,omitempty"` // 解決したエンドポイント(ip:port)
	LastHandshake time.Time `json:"last_handshake,omitempty"`
}

TunnelStatus はトンネルの状態。

type VPSMode

type VPSMode string

VPSMode は VPS 側の転送方式(仕様 6 節)。

const (
	// ModeKernel は nftables の DNAT でカーネルが転送する。
	ModeKernel VPSMode = "kernel"
	// ModeProxy は vpsd 自身が TCP を受けて中継する。TCP のみ。
	ModeProxy VPSMode = "proxy"
)

type WGConfig

type WGConfig struct {
	ServerPubkey     string `json:"server_pubkey"`
	Endpoint         string `json:"endpoint"` // host:port
	Address          string `json:"address"`  // 10.200.0.2/24 の形
	MTU              int    `json:"mtu"`
	Keepalive        int    `json:"keepalive"`          // 秒
	UDPTimeout       int    `json:"udp_timeout"`        // VPS の nf_conntrack_udp_timeout(秒)
	UDPTimeoutStream int    `json:"udp_timeout_stream"` // VPS の nf_conntrack_udp_timeout_stream(秒)
}

WGConfig は全体状態でエージェントに渡す wg の設定(仕様 5.2 節)。

Jump to

Keyboard shortcuts

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