diff --git a/Dockerfile b/Dockerfile index b704f501..21220f7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -213,7 +213,7 @@ ENV VPN_SERVICE_PROVIDER=pia \ FIREWALL_VPN_INPUT_PORTS= \ FIREWALL_INPUT_PORTS= \ FIREWALL_OUTBOUND_SUBNETS= \ - FIREWALL_DEBUG=off \ + FIREWALL_IPTABLES_LOG_LEVEL=info \ # Logging LOG_LEVEL=info \ # Health diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index db5c7a83..89af4dab 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -222,11 +222,11 @@ func _main(ctx context.Context, buildInfo models.BuildInformation, return err } + iptablesLogLevel, _ := log.ParseLevel(allSettings.Firewall.Iptables.LogLevel) + iptablesLogger := logger.New(log.SetComponent("iptables"), log.SetLevel(iptablesLogLevel)) + firewallLogger := logger.New(log.SetComponent("firewall")) - if *allSettings.Firewall.IptablesDebug { // To remove in v4 - firewallLogger.Patch(log.SetLevel(log.LevelDebug)) - } - firewallConf, err := firewall.NewConfig(ctx, firewallLogger, cmder, + firewallConf, err := firewall.NewConfig(ctx, firewallLogger, iptablesLogger, cmder, defaultRoutes, localNetworks) if err != nil { return err diff --git a/internal/configuration/settings/firewall.go b/internal/configuration/settings/firewall.go index 19712d66..7f94d989 100644 --- a/internal/configuration/settings/firewall.go +++ b/internal/configuration/settings/firewall.go @@ -15,7 +15,7 @@ type Firewall struct { InputPorts []uint16 OutboundSubnets []netip.Prefix Enabled *bool - Debug *bool + Iptables Iptables } func (f Firewall) validate() (err error) { @@ -33,6 +33,11 @@ func (f Firewall) validate() (err error) { } } + err = f.Iptables.validate() + if err != nil { + return fmt.Errorf("iptables settings: %w", err) + } + return nil } @@ -51,7 +56,7 @@ func (f *Firewall) copy() (copied Firewall) { InputPorts: gosettings.CopySlice(f.InputPorts), OutboundSubnets: gosettings.CopySlice(f.OutboundSubnets), Enabled: gosettings.CopyPointer(f.Enabled), - Debug: gosettings.CopyPointer(f.Debug), + Iptables: f.Iptables.copy(), } } @@ -63,12 +68,12 @@ func (f *Firewall) overrideWith(other Firewall) { f.InputPorts = gosettings.OverrideWithSlice(f.InputPorts, other.InputPorts) f.OutboundSubnets = gosettings.OverrideWithSlice(f.OutboundSubnets, other.OutboundSubnets) f.Enabled = gosettings.OverrideWithPointer(f.Enabled, other.Enabled) - f.Debug = gosettings.OverrideWithPointer(f.Debug, other.Debug) + f.Iptables.overrideWith(other.Iptables) } -func (f *Firewall) setDefaults() { +func (f *Firewall) setDefaults(globalLogLevel string) { f.Enabled = gosettings.DefaultPointer(f.Enabled, true) - f.Debug = gosettings.DefaultPointer(f.Debug, false) + f.Iptables.setDefaults(globalLogLevel) } func (f Firewall) String() string { @@ -83,9 +88,7 @@ func (f Firewall) toLinesNode() (node *gotree.Node) { return node } - if *f.Debug { - node.Appendf("Debug mode: on") - } + node.AppendNode(f.Iptables.toLinesNode()) if len(f.VPNInputPorts) > 0 { vpnInputPortsNode := node.Appendf("VPN input ports:") @@ -133,9 +136,9 @@ func (f *Firewall) read(r *reader.Reader) (err error) { return err } - f.Debug, err = r.BoolPtr("FIREWALL_DEBUG") + err = f.Iptables.read(r) if err != nil { - return err + return fmt.Errorf("reading iptables settings: %w", err) } return nil diff --git a/internal/configuration/settings/firewall_test.go b/internal/configuration/settings/firewall_test.go index e79ca3f1..825db124 100644 --- a/internal/configuration/settings/firewall_test.go +++ b/internal/configuration/settings/firewall_test.go @@ -4,6 +4,7 @@ import ( "net/netip" "testing" + "github.com/qdm12/log" "github.com/stretchr/testify/assert" ) @@ -15,7 +16,10 @@ func Test_Firewall_validate(t *testing.T) { errWrapped error errMessage string }{ - "empty": {}, + "empty": { + errWrapped: log.ErrLevelNotRecognized, + errMessage: "iptables settings: log level: level is not recognized: ", + }, "zero_vpn_input_port": { firewall: Firewall{ VPNInputPorts: []uint16{0}, @@ -41,6 +45,7 @@ func Test_Firewall_validate(t *testing.T) { }, "public_outbound_subnet": { firewall: Firewall{ + Iptables: Iptables{LogLevel: log.LevelInfo.String()}, OutboundSubnets: []netip.Prefix{ netip.MustParsePrefix("1.2.3.4/32"), }, @@ -48,6 +53,7 @@ func Test_Firewall_validate(t *testing.T) { }, "valid_settings": { firewall: Firewall{ + Iptables: Iptables{LogLevel: log.LevelInfo.String()}, VPNInputPorts: []uint16{100, 101}, InputPorts: []uint16{200, 201}, OutboundSubnets: []netip.Prefix{ diff --git a/internal/configuration/settings/iptables.go b/internal/configuration/settings/iptables.go new file mode 100644 index 00000000..b6e283cc --- /dev/null +++ b/internal/configuration/settings/iptables.go @@ -0,0 +1,67 @@ +package settings + +import ( + "fmt" + + "github.com/qdm12/gosettings" + "github.com/qdm12/gosettings/reader" + "github.com/qdm12/gotree" + "github.com/qdm12/log" +) + +// Iptables contains settings to customize iptables. +type Iptables struct { + LogLevel string +} + +func (i Iptables) validate() (err error) { + _, err = log.ParseLevel(i.LogLevel) + if err != nil { + return fmt.Errorf("log level: %w", err) + } + + return nil +} + +func (i *Iptables) copy() (copied Iptables) { + return Iptables{ + LogLevel: i.LogLevel, + } +} + +func (i *Iptables) overrideWith(other Iptables) { + i.LogLevel = gosettings.OverrideWithComparable(i.LogLevel, other.LogLevel) +} + +func (i *Iptables) setDefaults(globalLogLevel string) { + defaultLevel := globalLogLevel + if defaultLevel == log.LevelDebug.String() { + // Given iptables debug logger is quite verbose, we only turn it to debug level + // if it is explicitly asked to be at debug level; even if the global logger is + // at the debug level, we keep iptables at info level by default. + defaultLevel = log.LevelInfo.String() + } + i.LogLevel = gosettings.DefaultComparable(i.LogLevel, defaultLevel) +} + +func (i Iptables) String() string { + return i.toLinesNode().String() +} + +func (i Iptables) toLinesNode() (node *gotree.Node) { + node = gotree.New("Iptables settings:") + node.Appendf("Log level: %s", i.LogLevel) + return node +} + +func (i *Iptables) read(r *reader.Reader) (err error) { + debugMode, err := r.BoolPtr("FIREWALL_DEBUG", reader.IsRetro("FIREWALL_IPTABLES_LOG_LEVEL")) + if err != nil { + return err + } + if debugMode != nil && *debugMode { + i.LogLevel = log.LevelDebug.String() + } + i.LogLevel = r.String("FIREWALL_IPTABLES_LOG_LEVEL") + return nil +} diff --git a/internal/configuration/settings/settings.go b/internal/configuration/settings/settings.go index 66944215..66dc7555 100644 --- a/internal/configuration/settings/settings.go +++ b/internal/configuration/settings/settings.go @@ -120,10 +120,10 @@ func (s *Settings) OverrideWith(other Settings, func (s *Settings) SetDefaults() { s.ControlServer.setDefaults() s.DNS.setDefaults() - s.Firewall.setDefaults() + s.Log.setDefaults() + s.Firewall.setDefaults(s.Log.Level) s.Health.SetDefaults() s.HTTPProxy.setDefaults() - s.Log.setDefaults() s.PublicIP.setDefaults() s.Shadowsocks.setDefaults() s.Storage.setDefaults() diff --git a/internal/configuration/settings/settings_test.go b/internal/configuration/settings/settings_test.go index b4e23cc4..01a1ac39 100644 --- a/internal/configuration/settings/settings_test.go +++ b/internal/configuration/settings/settings_test.go @@ -62,7 +62,9 @@ func Test_Settings_String(t *testing.T) { | ├── Block ads: no | └── Block surveillance: yes ├── Firewall settings: -| └── Enabled: yes +| ├── Enabled: yes +| └── Iptables settings: +| └── Log level: INFO ├── Log settings: | └── Log level: INFO ├── Health settings: diff --git a/internal/firewall/firewall.go b/internal/firewall/firewall.go index 3b9b902a..6b542a9e 100644 --- a/internal/firewall/firewall.go +++ b/internal/firewall/firewall.go @@ -34,11 +34,11 @@ type Config struct { // NewConfig creates a new Config instance and returns an error // if no iptables implementation is available. -func NewConfig(ctx context.Context, logger Logger, +func NewConfig(ctx context.Context, logger, iptablesLogger Logger, runner CmdRunner, defaultRoutes []routing.DefaultRoute, localNetworks []routing.LocalNetwork, ) (config *Config, err error) { - impl, err := iptables.New(ctx, runner, logger) + impl, err := iptables.New(ctx, runner, iptablesLogger) if err != nil { return nil, fmt.Errorf("creating iptables firewall: %w", err) } diff --git a/internal/firewall/iptables/interfaces.go b/internal/firewall/iptables/interfaces.go index ebfe8842..aee2d537 100644 --- a/internal/firewall/iptables/interfaces.go +++ b/internal/firewall/iptables/interfaces.go @@ -8,7 +8,5 @@ type CmdRunner interface { type Logger interface { Debug(s string) - Info(s string) Warn(s string) - Error(s string) } diff --git a/internal/firewall/iptables/mocks_test.go b/internal/firewall/iptables/mocks_test.go index c4c0b828..7cb5c607 100644 --- a/internal/firewall/iptables/mocks_test.go +++ b/internal/firewall/iptables/mocks_test.go @@ -84,30 +84,6 @@ func (mr *MockLoggerMockRecorder) Debug(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), arg0) } -// Error mocks base method. -func (m *MockLogger) Error(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Error", arg0) -} - -// Error indicates an expected call of Error. -func (mr *MockLoggerMockRecorder) Error(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0) -} - -// Info mocks base method. -func (m *MockLogger) Info(arg0 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Info", arg0) -} - -// Info indicates an expected call of Info. -func (mr *MockLoggerMockRecorder) Info(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockLogger)(nil).Info), arg0) -} - // Warn mocks base method. func (m *MockLogger) Warn(arg0 string) { m.ctrl.T.Helper() diff --git a/internal/pmtud/tcp/helpers_test.go b/internal/pmtud/tcp/helpers_test.go index e5a21e05..24ed3e35 100644 --- a/internal/pmtud/tcp/helpers_test.go +++ b/internal/pmtud/tcp/helpers_test.go @@ -35,7 +35,7 @@ func getFirewall(t *testing.T) *firewall.Config { noopLogger := &noopLogger{} cmder := command.New() var err error - testFirewall, err = firewall.NewConfig(t.Context(), noopLogger, cmder, nil, nil) + testFirewall, err = firewall.NewConfig(t.Context(), noopLogger, noopLogger, cmder, nil, nil) if errors.Is(err, iptables.ErrNotSupported) { t.Skip("iptables not installed, skipping TCP PMTUD tests") }