fix(config): stop mutating shared config structs during parsing
Prevent data loss and nil panics across 5 parse functions: - chain: nil-guard hop entries to avoid deref on empty YAML lists - hop: build merged metadata map instead of writing inherited values into the original NodeConfig; swap+restore around ParseNode call - node: use local connCfg/dialCfg instead of overwriting cfg.Connector and cfg.Dialer fields; only fill in Type when empty - parser: preserve existing config file fields when env vars override Level/Addr (was replacing the entire struct) - router: only update ipNet from dst CIDR if parse succeeds, keeping the route.Net fallback instead of overwriting with nil
This commit is contained in:
@@ -33,6 +33,10 @@ func ParseChain(cfg *config.ChainConfig, log logger.Logger) (chain.Chainer, erro
|
|||||||
)
|
)
|
||||||
|
|
||||||
for _, ch := range cfg.Hops {
|
for _, ch := range cfg.Hops {
|
||||||
|
if ch == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var hop hop.Hop
|
var hop hop.Hop
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|||||||
+17
-19
@@ -78,12 +78,13 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
m := v.Metadata
|
// Build a merged metadata map for inheritance without mutating
|
||||||
if m == nil {
|
// the original node config.
|
||||||
m = map[string]any{}
|
merged := make(map[string]any)
|
||||||
v.Metadata = m
|
for k, val := range v.Metadata {
|
||||||
|
merged[k] = val
|
||||||
}
|
}
|
||||||
md := metadata.NewMetadata(m)
|
md := metadata.NewMetadata(merged)
|
||||||
|
|
||||||
if v.Resolver == "" {
|
if v.Resolver == "" {
|
||||||
v.Resolver = cfg.Resolver
|
v.Resolver = cfg.Resolver
|
||||||
@@ -93,37 +94,30 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !md.IsExists(parsing.MDKeyInterface) {
|
if !md.IsExists(parsing.MDKeyInterface) {
|
||||||
// inherit from hop
|
|
||||||
if ifce != "" {
|
if ifce != "" {
|
||||||
m[parsing.MDKeyInterface] = ifce
|
merged[parsing.MDKeyInterface] = ifce
|
||||||
}
|
}
|
||||||
// node level
|
|
||||||
if v.Interface != "" {
|
if v.Interface != "" {
|
||||||
m[parsing.MDKeyInterface] = v.Interface
|
merged[parsing.MDKeyInterface] = v.Interface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !md.IsExists(parsing.MDKeySoMark) {
|
if !md.IsExists(parsing.MDKeySoMark) {
|
||||||
// inherit from hop
|
|
||||||
if soMark != 0 {
|
if soMark != 0 {
|
||||||
m[parsing.MDKeySoMark] = soMark
|
merged[parsing.MDKeySoMark] = soMark
|
||||||
}
|
}
|
||||||
// node level
|
|
||||||
if v.SockOpts != nil && v.SockOpts.Mark != 0 {
|
if v.SockOpts != nil && v.SockOpts.Mark != 0 {
|
||||||
m[parsing.MDKeySoMark] = v.SockOpts.Mark
|
merged[parsing.MDKeySoMark] = v.SockOpts.Mark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !md.IsExists(parsing.MDKeyProxyProtocol) && ppv > 0 {
|
if !md.IsExists(parsing.MDKeyProxyProtocol) && ppv > 0 {
|
||||||
// inherit from hop
|
merged[parsing.MDKeyProxyProtocol] = ppv
|
||||||
m[parsing.MDKeyProxyProtocol] = ppv
|
|
||||||
}
|
}
|
||||||
if !md.IsExists(parsing.MDKeyNetns) {
|
if !md.IsExists(parsing.MDKeyNetns) {
|
||||||
// inherit from hop
|
|
||||||
if netns != "" {
|
if netns != "" {
|
||||||
m[parsing.MDKeyNetns] = netns
|
merged[parsing.MDKeyNetns] = netns
|
||||||
}
|
}
|
||||||
// node level
|
|
||||||
if v.Netns != "" {
|
if v.Netns != "" {
|
||||||
m[parsing.MDKeyNetns] = v.Netns
|
merged[parsing.MDKeyNetns] = v.Netns
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +135,11 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
v.Dialer.Type = "tcp"
|
v.Dialer.Type = "tcp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporarily swap merged metadata so ParseNode sees inherited values.
|
||||||
|
origMeta := v.Metadata
|
||||||
|
v.Metadata = merged
|
||||||
node, err := node_parser.ParseNode(cfg.Name, v, log)
|
node, err := node_parser.ParseNode(cfg.Name, v, log)
|
||||||
|
v.Metadata = origMeta
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,29 +29,33 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Connector == nil {
|
connCfg := cfg.Connector
|
||||||
cfg.Connector = &config.ConnectorConfig{
|
if connCfg == nil {
|
||||||
Type: "http",
|
connCfg = &config.ConnectorConfig{}
|
||||||
}
|
}
|
||||||
|
if connCfg.Type == "" {
|
||||||
|
connCfg.Type = "http"
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Dialer == nil {
|
dialCfg := cfg.Dialer
|
||||||
cfg.Dialer = &config.DialerConfig{
|
if dialCfg == nil {
|
||||||
Type: "tcp",
|
dialCfg = &config.DialerConfig{}
|
||||||
}
|
}
|
||||||
|
if dialCfg.Type == "" {
|
||||||
|
dialCfg.Type = "tcp"
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeLogger := log.WithFields(map[string]any{
|
nodeLogger := log.WithFields(map[string]any{
|
||||||
"hop": hop,
|
"hop": hop,
|
||||||
"kind": "node",
|
"kind": "node",
|
||||||
"node": cfg.Name,
|
"node": cfg.Name,
|
||||||
"connector": cfg.Connector.Type,
|
"connector": connCfg.Type,
|
||||||
"dialer": cfg.Dialer.Type,
|
"dialer": dialCfg.Type,
|
||||||
})
|
})
|
||||||
|
|
||||||
serverName, _, _ := net.SplitHostPort(cfg.Addr)
|
serverName, _, _ := net.SplitHostPort(cfg.Addr)
|
||||||
|
|
||||||
tlsCfg := cfg.Connector.TLS
|
tlsCfg := connCfg.TLS
|
||||||
if tlsCfg == nil {
|
if tlsCfg == nil {
|
||||||
tlsCfg = &config.TLSConfig{}
|
tlsCfg = &config.TLSConfig{}
|
||||||
}
|
}
|
||||||
@@ -68,25 +72,22 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
"kind": "connector",
|
"kind": "connector",
|
||||||
})
|
})
|
||||||
var cr connector.Connector
|
var cr connector.Connector
|
||||||
if rf := registry.ConnectorRegistry().Get(cfg.Connector.Type); rf != nil {
|
if rf := registry.ConnectorRegistry().Get(connCfg.Type); rf != nil {
|
||||||
cr = rf(
|
cr = rf(
|
||||||
connector.AuthOption(auth_parser.Info(cfg.Connector.Auth)),
|
connector.AuthOption(auth_parser.Info(connCfg.Auth)),
|
||||||
connector.TLSConfigOption(tlsConfig),
|
connector.TLSConfigOption(tlsConfig),
|
||||||
connector.LoggerOption(connectorLogger),
|
connector.LoggerOption(connectorLogger),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unregistered connector: %s", cfg.Connector.Type)
|
return nil, fmt.Errorf("unregistered connector: %s", connCfg.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Connector.Metadata == nil {
|
if err := cr.Init(mdx.NewMetadata(connCfg.Metadata)); err != nil {
|
||||||
cfg.Connector.Metadata = make(map[string]any)
|
|
||||||
}
|
|
||||||
if err := cr.Init(mdx.NewMetadata(cfg.Connector.Metadata)); err != nil {
|
|
||||||
connectorLogger.Error("init: ", err)
|
connectorLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsCfg = cfg.Dialer.TLS
|
tlsCfg = dialCfg.TLS
|
||||||
if tlsCfg == nil {
|
if tlsCfg == nil {
|
||||||
tlsCfg = &config.TLSConfig{}
|
tlsCfg = &config.TLSConfig{}
|
||||||
}
|
}
|
||||||
@@ -106,21 +107,18 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
})
|
})
|
||||||
|
|
||||||
var d dialer.Dialer
|
var d dialer.Dialer
|
||||||
if rf := registry.DialerRegistry().Get(cfg.Dialer.Type); rf != nil {
|
if rf := registry.DialerRegistry().Get(dialCfg.Type); rf != nil {
|
||||||
d = rf(
|
d = rf(
|
||||||
dialer.AuthOption(auth_parser.Info(cfg.Dialer.Auth)),
|
dialer.AuthOption(auth_parser.Info(dialCfg.Auth)),
|
||||||
dialer.TLSConfigOption(tlsConfig),
|
dialer.TLSConfigOption(tlsConfig),
|
||||||
dialer.LoggerOption(dialerLogger),
|
dialer.LoggerOption(dialerLogger),
|
||||||
dialer.ProxyProtocolOption(mdutil.GetInt(md, parsing.MDKeyProxyProtocol)),
|
dialer.ProxyProtocolOption(mdutil.GetInt(md, parsing.MDKeyProxyProtocol)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unregistered dialer: %s", cfg.Dialer.Type)
|
return nil, fmt.Errorf("unregistered dialer: %s", dialCfg.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Dialer.Metadata == nil {
|
if err := d.Init(mdx.NewMetadata(dialCfg.Metadata)); err != nil {
|
||||||
cfg.Dialer.Metadata = make(map[string]any)
|
|
||||||
}
|
|
||||||
if err := d.Init(mdx.NewMetadata(cfg.Dialer.Metadata)); err != nil {
|
|
||||||
dialerLogger.Error("init: ", err)
|
dialerLogger.Error("init: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,24 +84,28 @@ func (p *parser) Parse() (*config.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
||||||
cfg.Log = &config.LogConfig{
|
if cfg.Log == nil {
|
||||||
Level: v,
|
cfg.Log = &config.LogConfig{}
|
||||||
}
|
}
|
||||||
|
cfg.Log.Level = v
|
||||||
}
|
}
|
||||||
if v := os.Getenv("GOST_API"); v != "" {
|
if v := os.Getenv("GOST_API"); v != "" {
|
||||||
cfg.API = &config.APIConfig{
|
if cfg.API == nil {
|
||||||
Addr: v,
|
cfg.API = &config.APIConfig{}
|
||||||
}
|
}
|
||||||
|
cfg.API.Addr = v
|
||||||
}
|
}
|
||||||
if v := os.Getenv("GOST_METRICS"); v != "" {
|
if v := os.Getenv("GOST_METRICS"); v != "" {
|
||||||
cfg.Metrics = &config.MetricsConfig{
|
if cfg.Metrics == nil {
|
||||||
Addr: v,
|
cfg.Metrics = &config.MetricsConfig{}
|
||||||
}
|
}
|
||||||
|
cfg.Metrics.Addr = v
|
||||||
}
|
}
|
||||||
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
||||||
cfg.Profiling = &config.ProfilingConfig{
|
if cfg.Profiling == nil {
|
||||||
Addr: v,
|
cfg.Profiling = &config.ProfilingConfig{}
|
||||||
}
|
}
|
||||||
|
cfg.Profiling.Addr = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.args.Debug || p.args.Trace {
|
if p.args.Debug || p.args.Trace {
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
|
|||||||
_, ipNet, _ := net.ParseCIDR(route.Net)
|
_, ipNet, _ := net.ParseCIDR(route.Net)
|
||||||
dst := route.Dst
|
dst := route.Dst
|
||||||
if dst != "" {
|
if dst != "" {
|
||||||
_, ipNet, _ = net.ParseCIDR(dst)
|
if _, parsed, _ := net.ParseCIDR(dst); parsed != nil {
|
||||||
|
ipNet = parsed
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ipNet != nil {
|
if ipNet != nil {
|
||||||
dst = ipNet.String()
|
dst = ipNet.String()
|
||||||
|
|||||||
Reference in New Issue
Block a user