Files
x/config/config.go
T
ginuerzh e583f5b538 feat(parser): support HTTP(S) URL config sources with -C flag
Add remote config fetching capability to the -C CLI flag, enabling
centralized fleet management of gost nodes.

- readConfig() now detects http:// and https:// URLs and fetches
  them via HTTP GET with a 30s timeout and shared client pool
- Format auto-detection from Content-Type header (using mime
  parsing) with fallback to URL path extension (.yaml/.json)
- 10 MiB response size limit to prevent memory exhaustion
- URL userinfo stripped from error messages for safety
- Fix ReadFile to force viper config type from file extension,
  preventing format detection ambiguity
- Fix viper config search path fallback: skip cfg.Load() when
  -C flags are explicitly provided, avoiding unwanted merges
- Tests: URL fetch (YAML/JSON), non-200, oversize, detectFormat,
  isHTTPURL, sanitizeURL, and combined file+URL Parse scenario
2026-06-20 20:11:43 +08:00

687 lines
28 KiB
Go

package config
import (
"encoding/json"
"io"
"path/filepath"
"strings"
"sync"
"time"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
var (
v = viper.GetViper()
)
func init() {
v.SetConfigName("gost")
v.AddConfigPath("/etc/gost/")
v.AddConfigPath("$HOME/.gost/")
v.AddConfigPath(".")
}
var (
global = &Config{}
globalMux sync.RWMutex
)
func Global() *Config {
globalMux.RLock()
defer globalMux.RUnlock()
cfg := &Config{}
*cfg = *global
return cfg
}
func Set(c *Config) {
globalMux.Lock()
defer globalMux.Unlock()
global = c
}
func OnUpdate(f func(c *Config) error) error {
globalMux.Lock()
defer globalMux.Unlock()
return f(global)
}
type LogConfig struct {
Output string `yaml:",omitempty" json:"output,omitempty"`
Level string `yaml:",omitempty" json:"level,omitempty"`
Format string `yaml:",omitempty" json:"format,omitempty"`
Rotation *LogRotationConfig `yaml:",omitempty" json:"rotation,omitempty"`
}
type LogRotationConfig struct {
// MaxSize is the maximum size in megabytes of the log file before it gets
// rotated. It defaults to 100 megabytes.
MaxSize int `yaml:"maxSize,omitempty" json:"maxSize,omitempty"`
// MaxAge is the maximum number of days to retain old log files based on the
// timestamp encoded in their filename. Note that a day is defined as 24
// hours and may not exactly correspond to calendar days due to daylight
// savings, leap seconds, etc. The default is not to remove old log files
// based on age.
MaxAge int `yaml:"maxAge,omitempty" json:"maxAge,omitempty"`
// MaxBackups is the maximum number of old log files to retain. The default
// is to retain all old log files (though MaxAge may still cause them to get
// deleted.)
MaxBackups int `yaml:"maxBackups,omitempty" json:"maxBackups,omitempty"`
// LocalTime determines if the time used for formatting the timestamps in
// backup files is the computer's local time. The default is to use UTC
// time.
LocalTime bool `yaml:"localTime,omitempty" json:"localTime,omitempty"`
// Compress determines if the rotated log files should be compressed
// using gzip. The default is not to perform compression.
Compress bool `yaml:"compress,omitempty" json:"compress,omitempty"`
}
type LoggerConfig struct {
Name string `json:"name"`
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
}
type ProfilingConfig struct {
Addr string `json:"addr"`
}
type APIConfig struct {
Addr string `json:"addr"`
PathPrefix string `yaml:"pathPrefix,omitempty" json:"pathPrefix,omitempty"`
AccessLog bool `yaml:"accesslog,omitempty" json:"accesslog,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
}
type MetricsConfig struct {
Addr string `json:"addr"`
Path string `yaml:",omitempty" json:"path,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
}
type TLSConfig struct {
CertFile string `yaml:"certFile,omitempty" json:"certFile,omitempty"`
KeyFile string `yaml:"keyFile,omitempty" json:"keyFile,omitempty"`
CAFile string `yaml:"caFile,omitempty" json:"caFile,omitempty"`
Secure bool `yaml:",omitempty" json:"secure,omitempty"`
ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"`
Options *TLSOptions `yaml:",omitempty" json:"options,omitempty"`
// for auto-generated default certificate.
Validity time.Duration `yaml:",omitempty" json:"validity,omitempty"`
CommonName string `yaml:"commonName,omitempty" json:"commonName,omitempty"`
Organization string `yaml:",omitempty" json:"organization,omitempty"`
RejectUnknownSNI bool `yaml:"rejectUnknownSNI,omitempty" json:"rejectUnknownSNI,omitempty"`
ServerNames []string `yaml:"serverNames,omitempty" json:"serverNames,omitempty"`
}
type TLSOptions struct {
MinVersion string `yaml:"minVersion,omitempty" json:"minVersion,omitempty"`
MaxVersion string `yaml:"maxVersion,omitempty" json:"maxVersion,omitempty"`
CipherSuites []string `yaml:"cipherSuites,omitempty" json:"cipherSuites,omitempty"`
ALPN []string `yaml:"alpn,omitempty" json:"alpn,omitempty"`
}
type PluginConfig struct {
Type string `json:"type"`
Addr string `json:"addr"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
Token string `yaml:",omitempty" json:"token,omitempty"`
}
type AutherConfig struct {
Name string `json:"name"`
Auths []*AuthConfig `yaml:",omitempty" json:"auths,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type AuthConfig struct {
Username string `yaml:",omitempty" json:"username,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
File string `yaml:",omitempty" json:"file,omitempty"`
}
type SelectorConfig struct {
Strategy string `json:"strategy"`
MaxFails int `yaml:"maxFails" json:"maxFails"`
FailTimeout time.Duration `yaml:"failTimeout" json:"failTimeout"`
}
type AdmissionConfig struct {
Name string `json:"name"`
// Deprecated: use whitelist instead
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type BypassConfig struct {
Name string `json:"name"`
// Deprecated: use whitelist instead
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type FileLoader struct {
Path string `json:"path"`
}
type RedisLoader struct {
Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"`
Username string `yaml:",omitempty" json:"username,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
Key string `yaml:",omitempty" json:"key,omitempty"`
Type string `yaml:",omitempty" json:"type,omitempty"`
}
type HTTPLoader struct {
URL string `yaml:"url" json:"url"`
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
}
type NameserverConfig struct {
Addr string `json:"addr"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
Prefer string `yaml:",omitempty" json:"prefer,omitempty"`
ClientIP string `yaml:"clientIP,omitempty" json:"clientIP,omitempty"`
Hostname string `yaml:",omitempty" json:"hostname,omitempty"`
TTL time.Duration `yaml:",omitempty" json:"ttl,omitempty"`
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
Async bool `yaml:",omitempty" json:"async,omitempty"`
Only string `yaml:",omitempty" json:"only,omitempty"`
}
type ResolverConfig struct {
Name string `json:"name"`
Nameservers []*NameserverConfig `yaml:",omitempty" json:"nameservers,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type HostMappingConfig struct {
IP string `json:"ip"`
Hostname string `json:"hostname"`
Aliases []string `yaml:",omitempty" json:"aliases,omitempty"`
}
type HostsConfig struct {
Name string `json:"name"`
Mappings []*HostMappingConfig `yaml:",omitempty" json:"mappings,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type IngressRuleConfig struct {
Hostname string `json:"hostname"`
Endpoint string `json:"endpoint"`
}
type IngressConfig struct {
Name string `json:"name"`
Rules []*IngressRuleConfig `yaml:",omitempty" json:"rules,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type SDConfig struct {
Name string `json:"name"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type RouterRouteConfig struct {
// Deprecated: use dst instead
Net string `yaml:",omitempty" json:"net,omitempty"`
Dst string `yaml:",omitempty" json:"dst,omitempty"`
Gateway string `json:"gateway"`
}
type RouterConfig struct {
Name string `json:"name"`
Routes []*RouterRouteConfig `yaml:",omitempty" json:"routes,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type RecorderConfig struct {
Name string `json:"name"`
File *FileRecorder `yaml:",omitempty" json:"file,omitempty"`
TCP *TCPRecorder `yaml:"tcp,omitempty" json:"tcp,omitempty"`
HTTP *HTTPRecorder `yaml:"http,omitempty" json:"http,omitempty"`
Redis *RedisRecorder `yaml:",omitempty" json:"redis,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type FileRecorder struct {
Path string `json:"path"`
Sep string `yaml:",omitempty" json:"sep,omitempty"`
Rotation *LogRotationConfig `yaml:",omitempty" json:"rotation,omitempty"`
}
type TCPRecorder struct {
Addr string `json:"addr"`
Timeout time.Duration `json:"timeout"`
}
type HTTPRecorder struct {
URL string `yaml:"url" json:"url"`
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
}
type RedisRecorder struct {
Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"`
Username string `yaml:",omitempty" json:"username,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
Key string `yaml:",omitempty" json:"key,omitempty"`
Type string `yaml:",omitempty" json:"type,omitempty"`
}
type RecorderObject struct {
Name string `json:"name"`
Record string `json:"record"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type LimiterConfig struct {
Name string `json:"name"`
Limits []string `yaml:",omitempty" json:"limits,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type ObserverConfig struct {
Name string `json:"name"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type ListenerConfig struct {
Type string `json:"type"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
ChainGroup *ChainGroupConfig `yaml:"chainGroup,omitempty" json:"chainGroup,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
Authers []string `yaml:",omitempty" json:"authers,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type HandlerConfig struct {
Type string `json:"type"`
Retries int `yaml:",omitempty" json:"retries,omitempty"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
ChainGroup *ChainGroupConfig `yaml:"chainGroup,omitempty" json:"chainGroup,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
Authers []string `yaml:",omitempty" json:"authers,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
Observer string `yaml:",omitempty" json:"observer,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ForwarderConfig struct {
// Deprecated: use hop instead
Name string `yaml:",omitempty" json:"name,omitempty"`
// the referenced hop name
Hop string `yaml:",omitempty" json:"hop,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Nodes []*ForwardNodeConfig `json:"nodes"`
}
type ForwardNodeConfig struct {
Name string `yaml:",omitempty" json:"name,omitempty"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
// Deprecated: use matcher instead
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
// Deprecated: use matcher instead
Host string `yaml:",omitempty" json:"host,omitempty"`
// Deprecated: use matcher instead
Path string `yaml:",omitempty" json:"path,omitempty"`
// Deprecated: use matcher instead
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
// Deprecated: use http.auth instead
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type HTTPURLRewriteConfig struct {
Match string
Replacement string
}
type HTTPBodyRewriteConfig struct {
// filter by MIME types
Type string
Match string
Replacement string
}
type NodeFilterConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
}
type NodeMatcherConfig struct {
Rule string `yaml:",omitempty" json:"rule,omitempty"`
Priority int `yaml:",omitempty" json:"priority,omitempty"`
}
type HTTPNodeConfig struct {
// rewrite host header
Host string `yaml:",omitempty" json:"host,omitempty"`
// Deprecated: use requestHeader instead
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
// additional request header
RequestHeader map[string]string `yaml:"requestHeader,omitempty" json:"requestHeader,omitempty"`
// additional response header
ResponseHeader map[string]string `yaml:"responseHeader,omitempty" json:"responseHeader,omitempty"`
// Deprecated: use rewriteURL instead
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
// rewrite URL
RewriteURL []HTTPURLRewriteConfig `yaml:"rewriteURL,omitempty" json:"rewriteURL,omitempty"`
// Deprecated: use rewriteResponseBody instead
RewriteBody []HTTPBodyRewriteConfig `yaml:"rewriteBody,omitempty" json:"rewriteBody,omitempty"`
// rewrite request body
RewriteRequestBody []HTTPBodyRewriteConfig `yaml:"rewriteRequestBody,omitempty" json:"rewriteRequestBody,omitempty"`
// rewrite response body
RewriteResponseBody []HTTPBodyRewriteConfig `yaml:"rewriteResponseBody,omitempty" json:"rewriteResponseBody,omitempty"`
// HTTP basic auth
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
}
type TLSNodeConfig struct {
ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"`
Secure bool `yaml:",omitempty" json:"secure,omitempty"`
Options *TLSOptions `yaml:",omitempty" json:"options,omitempty"`
}
type DialerConfig struct {
Type string `json:"type"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ConnectorConfig struct {
Type string `json:"type"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type SockOptsConfig struct {
Mark int `yaml:",omitempty" json:"mark,omitempty"`
}
type ServiceConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Admission string `yaml:",omitempty" json:"admission,omitempty"`
Admissions []string `yaml:",omitempty" json:"admissions,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
Quotas []string `yaml:",omitempty" json:"quotas,omitempty"`
CLimiter string `yaml:"climiter,omitempty" json:"climiter,omitempty"`
RLimiter string `yaml:"rlimiter,omitempty" json:"rlimiter,omitempty"`
Logger string `yaml:",omitempty" json:"logger,omitempty"`
Loggers []string `yaml:",omitempty" json:"loggers,omitempty"`
Observer string `yaml:",omitempty" json:"observer,omitempty"`
Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
// service status, read-only
Status *ServiceStatus `yaml:",omitempty" json:"status,omitempty"`
}
type ServiceStatus struct {
CreateTime int64 `yaml:"createTime" json:"createTime"`
State string `yaml:"state" json:"state"`
Events []ServiceEvent `yaml:",omitempty" json:"events,omitempty"`
Stats *ServiceStats `yaml:",omitempty" json:"stats,omitempty"`
StoppedByLimit bool `yaml:"stoppedByLimit,omitempty" json:"stoppedByLimit,omitempty"`
Quotas []ServiceQuota `yaml:",omitempty" json:"quotas,omitempty"`
}
type ServiceEvent struct {
Time int64 `yaml:"time" json:"time"`
Msg string `yaml:"msg" json:"msg"`
}
type ServiceStats struct {
TotalConns uint64 `yaml:"totalConns" json:"totalConns"`
CurrentConns uint64 `yaml:"currentConns" json:"currentConns"`
TotalErrs uint64 `yaml:"totalErrs" json:"totalErrs"`
InputBytes uint64 `yaml:"inputBytes" json:"inputBytes"`
OutputBytes uint64 `yaml:"outputBytes" json:"outputBytes"`
}
// QuotaConfig is a named cumulative traffic-volume limiter; the same name
// referenced by several services (ServiceConfig.Quotas) shares one counter.
type QuotaConfig struct {
Name string `json:"name"`
Limit string `yaml:",omitempty" json:"limit,omitempty"`
StartsAt string `yaml:"startsAt,omitempty" json:"startsAt,omitempty"`
ExpiresAt string `yaml:"expiresAt,omitempty" json:"expiresAt,omitempty"`
Direction string `yaml:",omitempty" json:"direction,omitempty"`
Flush string `yaml:",omitempty" json:"flush,omitempty"`
Store *QuotaStoreConfig `yaml:",omitempty" json:"store,omitempty"`
// read-only
Status *QuotaStatus `yaml:",omitempty" json:"status,omitempty"`
}
type QuotaStoreConfig struct {
Type string `json:"type"` // "file" (default) | "redis" (stub, not implemented)
File string `yaml:",omitempty" json:"file,omitempty"`
Redis *QuotaRedisConfig `yaml:",omitempty" json:"redis,omitempty"`
}
type QuotaRedisConfig struct {
Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"`
Username string `yaml:",omitempty" json:"username,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
Key string `yaml:",omitempty" json:"key,omitempty"`
}
type QuotaStatus struct {
Used uint64 `yaml:"used" json:"used"`
Limit uint64 `yaml:"limit" json:"limit"`
StartsAt int64 `yaml:"startsAt,omitempty" json:"startsAt,omitempty"`
ExpiresAt int64 `yaml:"expiresAt,omitempty" json:"expiresAt,omitempty"`
Active bool `yaml:"active" json:"active"`
Expired bool `yaml:"expired,omitempty" json:"expired,omitempty"`
Blocked bool `yaml:"blocked" json:"blocked"`
Direction string `yaml:"direction,omitempty" json:"direction,omitempty"`
}
type ServiceQuota struct {
Name string `yaml:"name" json:"name"`
Used uint64 `yaml:"used" json:"used"`
Limit uint64 `yaml:"limit" json:"limit"`
StartsAt int64 `yaml:"startsAt,omitempty" json:"startsAt,omitempty"`
ExpiresAt int64 `yaml:"expiresAt,omitempty" json:"expiresAt,omitempty"`
Active bool `yaml:"active" json:"active"`
Expired bool `yaml:"expired,omitempty" json:"expired,omitempty"`
Blocked bool `yaml:"blocked" json:"blocked"`
Direction string `yaml:"direction,omitempty" json:"direction,omitempty"`
}
type ChainConfig struct {
Name string `json:"name"`
Hops []*HopConfig `json:"hops"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ChainGroupConfig struct {
Chains []string `yaml:",omitempty" json:"chains,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
}
type HopConfig struct {
Name string `json:"name"`
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type NodeConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// Deprecated: use metadata.netns instead
Netns string `yaml:",omitempty" json:"netns,omitempty"`
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
// Deprecated: use matcher instead
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type Config struct {
Services []*ServiceConfig `json:"services"`
Chains []*ChainConfig `yaml:",omitempty" json:"chains,omitempty"`
Hops []*HopConfig `yaml:",omitempty" json:"hops,omitempty"`
Authers []*AutherConfig `yaml:",omitempty" json:"authers,omitempty"`
Admissions []*AdmissionConfig `yaml:",omitempty" json:"admissions,omitempty"`
Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"`
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
Ingresses []*IngressConfig `yaml:",omitempty" json:"ingresses,omitempty"`
Routers []*RouterConfig `yaml:",omitempty" json:"routers,omitempty"`
SDs []*SDConfig `yaml:"sds,omitempty" json:"sds,omitempty"`
Recorders []*RecorderConfig `yaml:",omitempty" json:"recorders,omitempty"`
Limiters []*LimiterConfig `yaml:",omitempty" json:"limiters,omitempty"`
Quotas []*QuotaConfig `yaml:",omitempty" json:"quotas,omitempty"`
CLimiters []*LimiterConfig `yaml:"climiters,omitempty" json:"climiters,omitempty"`
RLimiters []*LimiterConfig `yaml:"rlimiters,omitempty" json:"rlimiters,omitempty"`
Observers []*ObserverConfig `yaml:",omitempty" json:"observers,omitempty"`
Loggers []*LoggerConfig `yaml:",omitempty" json:"loggers,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
API *APIConfig `yaml:",omitempty" json:"api,omitempty"`
Metrics *MetricsConfig `yaml:",omitempty" json:"metrics,omitempty"`
}
func (c *Config) Load() error {
if err := v.ReadInConfig(); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) Read(r io.Reader, configType string) error {
v.SetConfigType(configType)
if err := v.ReadConfig(r); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) ReadFile(file string) error {
v.SetConfigFile(file)
v.SetConfigType(strings.TrimPrefix(filepath.Ext(file), ".")) // force format from extension
if err := v.ReadInConfig(); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) Write(w io.Writer, format string) error {
switch format {
case "json":
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(c)
return nil
case "yaml":
fallthrough
default:
enc := yaml.NewEncoder(w)
defer enc.Close()
enc.SetIndent(2)
return enc.Encode(c)
}
}