初版功能完成
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultSiteTCPFlowLimit = 2048
|
||||
DefaultSiteUDPFlowLimit = 4096
|
||||
DefaultSiteUDPIdleSeconds = 60
|
||||
)
|
||||
|
||||
// EngineerConfig contains portable Engineer settings. JoinToken is an optional
|
||||
// plaintext first-registration convenience; durable Node secrets stay in DPAPI.
|
||||
type EngineerConfig struct {
|
||||
Server string `yaml:"server"`
|
||||
NodeName string `yaml:"node_name"`
|
||||
JoinToken string `yaml:"join_token,omitempty"`
|
||||
}
|
||||
|
||||
// SiteConfig contains portable Site settings. JoinToken has the same
|
||||
// first-registration-only semantics as EngineerConfig.JoinToken.
|
||||
type SiteConfig struct {
|
||||
Server string `yaml:"server"`
|
||||
NodeName string `yaml:"node_name"`
|
||||
JoinToken string `yaml:"join_token,omitempty"`
|
||||
Netstack SiteNetstackConfig `yaml:"netstack"`
|
||||
}
|
||||
|
||||
type SiteNetstackConfig struct {
|
||||
TCPFlowLimit int `yaml:"tcp_flow_limit"`
|
||||
UDPFlowLimit int `yaml:"udp_flow_limit"`
|
||||
UDPIdleSeconds int `yaml:"udp_idle_seconds"`
|
||||
}
|
||||
|
||||
// LoadEngineer loads and validates a strict Engineer YAML file.
|
||||
func LoadEngineer(path string) (EngineerConfig, error) {
|
||||
var config EngineerConfig
|
||||
if err := decodeStrict(path, &config); err != nil {
|
||||
return EngineerConfig{}, err
|
||||
}
|
||||
if err := validateClient(config.Server, config.NodeName); err != nil {
|
||||
return EngineerConfig{}, fmt.Errorf("validate engineer config %q: %w", path, err)
|
||||
}
|
||||
config.JoinToken = strings.TrimSpace(config.JoinToken)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// LoadSite loads and validates a strict Site YAML file.
|
||||
func LoadSite(path string) (SiteConfig, error) {
|
||||
config := SiteConfig{Netstack: SiteNetstackConfig{
|
||||
TCPFlowLimit: DefaultSiteTCPFlowLimit, UDPFlowLimit: DefaultSiteUDPFlowLimit,
|
||||
UDPIdleSeconds: DefaultSiteUDPIdleSeconds,
|
||||
}}
|
||||
if err := decodeStrict(path, &config); err != nil {
|
||||
return SiteConfig{}, err
|
||||
}
|
||||
if err := validateClient(config.Server, config.NodeName); err != nil {
|
||||
return SiteConfig{}, fmt.Errorf("validate site config %q: %w", path, err)
|
||||
}
|
||||
config.JoinToken = strings.TrimSpace(config.JoinToken)
|
||||
if config.Netstack.TCPFlowLimit < 1 || config.Netstack.UDPFlowLimit < 1 || config.Netstack.UDPIdleSeconds < 1 || config.Netstack.UDPIdleSeconds > 86400 {
|
||||
return SiteConfig{}, fmt.Errorf("validate site config %q: netstack limits must be positive and udp_idle_seconds must be within 1..86400", path)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// ResolveJoinToken applies the documented precedence. override is populated by
|
||||
// the -join-token flag, whose default is REMLINK_JOIN_TOKEN.
|
||||
func ResolveJoinToken(override, configured string) string {
|
||||
if value := strings.TrimSpace(override); value != "" {
|
||||
return value
|
||||
}
|
||||
return strings.TrimSpace(configured)
|
||||
}
|
||||
|
||||
func validateClient(server, nodeName string) error {
|
||||
if strings.TrimSpace(nodeName) == "" {
|
||||
return fmt.Errorf("node_name must not be empty")
|
||||
}
|
||||
return ValidateServerURL(server)
|
||||
}
|
||||
|
||||
// ValidateServerURL applies the single public Server URL trust boundary used
|
||||
// by YAML loading, persisted Node identities, and the Bootstrap client.
|
||||
func ValidateServerURL(server string) error {
|
||||
parsed, err := url.Parse(server)
|
||||
if err != nil || parsed.Host == "" || parsed.Opaque != "" {
|
||||
return fmt.Errorf("server must be an absolute HTTP URL")
|
||||
}
|
||||
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
||||
return fmt.Errorf("server URL scheme must be http or https")
|
||||
}
|
||||
if parsed.User != nil {
|
||||
return fmt.Errorf("server URL must not contain credentials")
|
||||
}
|
||||
if parsed.RawQuery != "" || parsed.ForceQuery || parsed.Fragment != "" {
|
||||
return fmt.Errorf("server URL must not contain a query or fragment")
|
||||
}
|
||||
if parsed.Path != "" && parsed.Path != "/" {
|
||||
return fmt.Errorf("server URL must not contain a path")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user