Documentation
¶
Overview ¶
Package proto は vpsd と agent が共有する、ルールと全体状態の JSON スキーマを定める(仕様 5.2, 5.3 節)。
Index ¶
- Constants
- Variables
- func AddSources(list, add []netip.Prefix) []netip.Prefix
- func DeletedIDs(diff []RuleChange) []string
- func ParseSource(s string) (netip.Prefix, error)
- func ParseSourceLines(text string) ([]netip.Prefix, error)
- func ParseSources(args []string) ([]netip.Prefix, error)
- func RemoveSources(list, rm []netip.Prefix) []netip.Prefix
- func RulesDigest(rules []Rule) string
- func SelectProtocolVersion(local, remote ProtocolRange) (version int, ok bool)
- func UnchangedIDs(rules, before []Rule) map[string]bool
- func ValidateRules(rules []Rule, reserved Reserved) error
- func ValidateUpsert(rules, before []Rule, reserved Reserved) error
- type AgentRule
- type ChangeKind
- type FieldChange
- type Heartbeat
- type MergeBlocker
- type Message
- type Negotiated
- type PortRange
- type Proto
- type ProtocolRange
- type Rate
- type RateUnit
- type Reserved
- type Rule
- type RuleChange
- type RuleStatus
- type State
- type TunnelStatus
- type VPSMode
- type WGConfig
Constants ¶
const ( MsgPublicKey = "pubkey" // エージェント → vpsd。接続直後の最初のメッセージ MsgState = "state" // vpsd → エージェント。差分ではなく全体 MsgHeartbeat = "heartbeat" // エージェント → vpsd。30 秒ごとと、全体状態の適用直後 )
stream(仕様 5.2 節)で流れるメッセージ。双方向で、エージェントは公開鍵とハートビートを、 vpsd は全体状態を送る。1 つの型で表し、Type で中身を選ぶ。
const ( StatusOK = "ok" StatusError = "error" )
状態の値。
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 ¶
var SupportedCapabilities = []string{}
SupportedCapabilities は、この build が持つ capability の語彙。今のところ語彙が無いため空。
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
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
ParseSource は接続元制限(source_deny / source_allow)の 1 項目を解釈する。 CIDR か単独のアドレスを受け、単独のアドレスは /32(IPv6 なら /128)で補う。ホスト部は落とす。 IPv4 かどうかはここでは見ない(Rule.Validate が見る)。CLI と Web UI が共有する。
func ParseSourceLines ¶ added in v0.4.0
ParseSourceLines は複数行のテキストを解釈する(Web UI の入力用)。 1 行に 1 つの CIDR かアドレスを書く。前後の空白と空行は無視する。 解釈できない行が 1 つでもあれば、その行番号(1 始まり、空行も数える)と内容を含むエラーで止め、 それまでに解釈できた行があっても何も返さない(呼び出し側が入力をそのまま残せるようにするため)。
func ParseSources ¶ added in v0.4.0
ParseSources は ParseSource を列に適用する。最初の誤りで止める。
func RemoveSources ¶ added in v0.4.0
RemoveSources は list から rm に含まれる CIDR を除く。空になれば空のスライスを返す。
func RulesDigest ¶ added in v0.4.0
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
UnchangedIDs は rules のうち、before に同じ ID で同じ内容の行がある ID の集合を返す。 ValidateUpsert と、Web UI の読み込みの確認ページ(仕様 10.1 節)が、どの行に Rule.Validate() を掛け直すかを同じ規則で決めるために使う。空の接続元リストは nil と 空スライスを同じとみなす(経路によって表現が揺れるため。RulesDigest と同じ扱い)。
func ValidateRules ¶
ValidateRules はルール集合全体の制約を検査する。 listen_port の重複はプロトコルごとに、有効無効を問わず見る(無効なルールを有効に戻したときに衝突させないため)。 予約ポートはプロトコルを問わず拒否する。
func ValidateUpsert ¶ added in v0.4.0
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 ¶
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
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 ¶
ParsePortRange は "2456" または "2456-2457" を解釈する。
func ParseSplitPoint ¶ added in v0.4.0
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) MarshalText ¶
func (*PortRange) UnmarshalText ¶
type ProtocolRange ¶ added in v0.4.0
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 ¶
Rate はレート制限の上限(仕様 5.3 節の new_flow_rate など)。 JSON では nftables と同じ "100/second" の文字列で表す。
func (Rate) MarshalText ¶
func (*Rate) UnmarshalText ¶
type Reserved ¶
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
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) Split ¶ added in v0.4.0
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
TargetDisplay は表示用の実効宛先。listen_port が範囲なら、target の先頭ポートから連番で写した 範囲を host:lo-hi の形で返す(仕様 5.3 節)。単一ポートや解釈できない値は target をそのまま返す。
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 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 節)。