v0.20.0 mtls hardening, fail-closed config, startup warnings, sink fixes
This commit is contained in:
1 parent
815ef345ea
commit
f192c26fa2
37 files changed
+1042
-152
No files matched your search
@@ -9,8 +9,6 @@ import (
|
||||
"time"
|
||||
|
||||
"logwisp/internal/testutil"
|
||||
|
||||
lconfig "github.com/lixenwraith/config"
|
||||
)
|
||||
|
||||
func TestResolveConfigArguments(t *testing.T) {
|
||||
@@ -185,7 +183,7 @@ allow = ["one", "two"]
|
||||
t.Fatal(err)
|
||||
}
|
||||
opts := TCPSinkOptions{Host: "127.0.0.1", BufferSize: 1000}
|
||||
if err := lconfig.ScanMap(cfg.Pipelines[0].PluginSinks[0].Config, &opts); err != nil {
|
||||
if err := Scan(cfg.Pipelines[0].PluginSinks[0].Config, &opts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if opts.Host != "127.0.0.1" || opts.BufferSize != 1000 || opts.Port != 9000 || opts.WriteTimeoutMS != 9007199254740993 ||
|
||||
@@ -193,3 +191,40 @@ allow = ["one", "two"]
|
||||
t.Fatalf("plugin defaults, nested tables or integer precision lost: %+v", opts)
|
||||
}
|
||||
}
|
||||
|
||||
// A misspelled key, even inside a nested tls or auth table, fails plugin
|
||||
// construction instead of leaving the protection it named switched off.
|
||||
func TestPluginConfigRejectsUnknownKeys(t *testing.T) {
|
||||
isolateConfig(t)
|
||||
for key, table := range map[string]string{
|
||||
"bogus": "bogus = true",
|
||||
"tls.enabeld": "[pipelines.plugin_sinks.config.tls]\nenabeld = true",
|
||||
"auth.tpye": "[pipelines.plugin_sinks.config.auth]\ntpye = \"mtls\"",
|
||||
} {
|
||||
testutil.WriteFile(t, "plugins.toml", `
|
||||
[[pipelines]]
|
||||
name = "typo"
|
||||
[[pipelines.plugin_sources]]
|
||||
id = "in"
|
||||
type = "null"
|
||||
[[pipelines.plugin_sinks]]
|
||||
id = "out"
|
||||
type = "tcp"
|
||||
[pipelines.plugin_sinks.config]
|
||||
port = 9000
|
||||
`+table+"\n")
|
||||
m, err := Load([]string{"-c", "plugins.toml"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg, err := m.Snapshot()
|
||||
m.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = Scan(cfg.Pipelines[0].PluginSinks[0].Config, &TCPSinkOptions{})
|
||||
if err == nil || !strings.Contains(err.Error(), `"`+key+`"`) {
|
||||
t.Errorf("unknown key %s: err = %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
lconfig "github.com/lixenwraith/config"
|
||||
)
|
||||
@@ -81,3 +85,55 @@ func validateLogConfig(cfg *LogConfig) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan decodes a plugin's config map into target, rejecting keys target does
|
||||
// not declare: a misspelled tls or auth key must fail, not silently disable
|
||||
// the protection it was meant to configure.
|
||||
func Scan(configMap map[string]any, target any) error {
|
||||
if err := unknownKeys(configMap, reflect.TypeOf(target), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return lconfig.ScanMap(configMap, target)
|
||||
}
|
||||
|
||||
// unknownKeys walks nested tables against the toml tags of t. Map-typed fields
|
||||
// hold free-form keys and are not descended into.
|
||||
func unknownKeys(m map[string]any, t reflect.Type, prefix string) error {
|
||||
for t.Kind() == reflect.Pointer || t.Kind() == reflect.Slice {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
fields := make(map[string]reflect.Type, t.NumField())
|
||||
for f := range t.Fields() {
|
||||
if name, _, _ := strings.Cut(f.Tag.Get("toml"), ","); name != "" && name != "-" {
|
||||
fields[name] = f.Type
|
||||
}
|
||||
}
|
||||
for _, key := range slices.Sorted(maps.Keys(m)) {
|
||||
ft, ok := fields[key]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown key %q", prefix+key)
|
||||
}
|
||||
var tables []map[string]any
|
||||
switch v := m[key].(type) {
|
||||
case map[string]any:
|
||||
tables = []map[string]any{v}
|
||||
case []map[string]any:
|
||||
tables = v
|
||||
case []any:
|
||||
for _, e := range v {
|
||||
if table, ok := e.(map[string]any); ok {
|
||||
tables = append(tables, table)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, table := range tables {
|
||||
if err := unknownKeys(table, ft, prefix+key+"."); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user