diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go index 3a55e0db..9bfbeca1 100644 --- a/pkg/iptables/iptables.go +++ b/pkg/iptables/iptables.go @@ -45,21 +45,23 @@ var ( // FirewallConfiguration specifies how to configure iptables. type FirewallConfiguration struct { - Mode string - PortsToRedirectInbound []int - InboundPortsToIgnore []string - OutboundPortsToIgnore []string - SubnetsToIgnore []string - ProxyInboundPort int - ProxyOutgoingPort int - ProxyUID int - ProxyGID int - SimulateOnly bool - NetNs string - UseWaitFlag bool - BinPath string - SaveBinPath string - ContinueOnError bool + Mode string + PortsToRedirectInbound []int + InboundPortsToIgnore []string + OutboundPortsToIgnore []string + SubnetsToIgnore []string + InboundSubnetsToIgnore []string + OutboundSubnetsToIgnore []string + ProxyInboundPort int + ProxyOutgoingPort int + ProxyUID int + ProxyGID int + SimulateOnly bool + NetNs string + UseWaitFlag bool + BinPath string + SaveBinPath string + ContinueOnError bool } // ConfigureFirewall configures iptables to redirect all desired traffic through the proxy, allowing for @@ -198,6 +200,7 @@ func (fc FirewallConfiguration) addOutgoingTrafficRules(existingRules []byte, co commands = append(commands, fc.makeIgnoreLoopback(outputChainName, "ignore-loopback")) // Ignore ports commands = fc.addRulesForIgnoredPorts(fc.OutboundPortsToIgnore, outputChainName, commands) + commands = fc.addRulesForIgnoredSubnets(outputChainName, fc.OutboundSubnetsToIgnore, false, commands) commands = append(commands, fc.makeRedirectChainToPort(outputChainName, fc.ProxyOutgoingPort, "redirect-all-outgoing-to-proxy-port")) @@ -222,7 +225,7 @@ func (fc FirewallConfiguration) addIncomingTrafficRules(existingRules []byte, co commands = append(commands, fc.makeFlushChain(redirectChainName)) } commands = fc.addRulesForIgnoredPorts(fc.InboundPortsToIgnore, redirectChainName, commands) - commands = fc.addRulesForIgnoredSubnets(redirectChainName, commands) + commands = fc.addRulesForIgnoredSubnets(redirectChainName, fc.inboundSubnetsToIgnore(), true, commands) commands = fc.addRulesForInboundPortRedirect(redirectChainName, commands) if preroutingRuleRegex.Find(existingRules) == nil { @@ -268,9 +271,16 @@ func (fc FirewallConfiguration) addRulesForIgnoredPorts(portsToIgnore []string, return commands } -func (fc FirewallConfiguration) addRulesForIgnoredSubnets(chainName string, commands []*exec.Cmd) []*exec.Cmd { - for _, subnet := range fc.SubnetsToIgnore { - commands = append(commands, fc.makeIgnoreSubnet(chainName, subnet, fmt.Sprintf("ignore-subnet-%s", subnet))) +func (fc FirewallConfiguration) inboundSubnetsToIgnore() []string { + if len(fc.InboundSubnetsToIgnore) > 0 { + return fc.InboundSubnetsToIgnore + } + return fc.SubnetsToIgnore +} + +func (fc FirewallConfiguration) addRulesForIgnoredSubnets(chainName string, subnets []string, source bool, commands []*exec.Cmd) []*exec.Cmd { + for _, subnet := range subnets { + commands = append(commands, fc.makeIgnoreSubnet(chainName, subnet, source, fmt.Sprintf("ignore-subnet-%s", subnet))) } return commands } @@ -393,13 +403,17 @@ func (fc FirewallConfiguration) makeIgnorePorts(chainName string, destinations [ "--comment", formatComment(comment)) } -func (fc FirewallConfiguration) makeIgnoreSubnet(chainName string, subnet string, comment string) *exec.Cmd { +func (fc FirewallConfiguration) makeIgnoreSubnet(chainName string, subnet string, source bool, comment string) *exec.Cmd { + addressFlag := "-d" + if source { + addressFlag = "-s" + } return exec.Command(fc.BinPath, "-t", "nat", "-A", chainName, "-p", "all", "-j", "RETURN", - "-s", subnet, + addressFlag, subnet, "-m", "comment", "--comment", formatComment(comment)) } diff --git a/pkg/iptables/iptables_test.go b/pkg/iptables/iptables_test.go index 8254c1e1..6abf749b 100644 --- a/pkg/iptables/iptables_test.go +++ b/pkg/iptables/iptables_test.go @@ -113,6 +113,23 @@ func TestAddOutgoingTrafficRules(t *testing.T) { } +func TestAddIgnoredSubnetsUseTrafficDirection(t *testing.T) { + incoming := (&FirewallConfiguration{ + BinPath: "", + InboundPortsToIgnore: []string{"1234"}, + InboundSubnetsToIgnore: []string{"10.0.0.0/8"}, + }).addIncomingTrafficRules(nil, nil) + assertEqual(t, incoming[1], exec.Command("", "-t", "nat", "-A", "PROXY_INIT_REDIRECT", "-p", "tcp", "--match", "multiport", "--dports", "1234", "-j", "RETURN", "-m", "comment", "--comment", "proxy-init/ignore-port-1234")) + assertEqual(t, incoming[2], exec.Command("", "-t", "nat", "-A", "PROXY_INIT_REDIRECT", "-p", "all", "-j", "RETURN", "-s", "10.0.0.0/8", "-m", "comment", "--comment", "proxy-init/ignore-subnet-10.0.0.0/8")) + + outgoing := (&FirewallConfiguration{ + BinPath: "", + ProxyOutgoingPort: 1234, + OutboundSubnetsToIgnore: []string{"10.0.0.0/8"}, + }).addOutgoingTrafficRules(nil, nil) + assertEqual(t, outgoing[2], exec.Command("", "-t", "nat", "-A", "PROXY_INIT_OUTPUT", "-p", "all", "-j", "RETURN", "-d", "10.0.0.0/8", "-m", "comment", "--comment", "proxy-init/ignore-subnet-10.0.0.0/8")) +} + func TestCleanupFirewallConfig(t *testing.T) { wantCommands := []*exec.Cmd{ exec.Command("", "-t", "nat", "-D", "PREROUTING", "-j", "PROXY_INIT_REDIRECT", "-m", "comment", "--comment", "proxy-init/install-proxy-init-prerouting"), diff --git a/proxy-init/cmd/root.go b/proxy-init/cmd/root.go index b31dbf8a..682ab200 100644 --- a/proxy-init/cmd/root.go +++ b/proxy-init/cmd/root.go @@ -38,46 +38,50 @@ const ( // RootOptions provides the information that will be used to build a firewall configuration. type RootOptions struct { - IncomingProxyPort int - OutgoingProxyPort int - ProxyUserID int - ProxyGroupID int - PortsToRedirect []int - InboundPortsToIgnore []string - OutboundPortsToIgnore []string - SubnetsToIgnore []string - SimulateOnly bool - NetNs string - UseWaitFlag bool - TimeoutCloseWaitSecs int - LogFormat string - LogLevel string - FirewallBinPath string - FirewallSaveBinPath string - IPTablesMode string - IPv6 bool + IncomingProxyPort int + OutgoingProxyPort int + ProxyUserID int + ProxyGroupID int + PortsToRedirect []int + InboundPortsToIgnore []string + OutboundPortsToIgnore []string + SubnetsToIgnore []string + InboundSubnetsToIgnore []string + OutboundSubnetsToIgnore []string + SimulateOnly bool + NetNs string + UseWaitFlag bool + TimeoutCloseWaitSecs int + LogFormat string + LogLevel string + FirewallBinPath string + FirewallSaveBinPath string + IPTablesMode string + IPv6 bool } func newRootOptions() *RootOptions { return &RootOptions{ - IncomingProxyPort: -1, - OutgoingProxyPort: -1, - ProxyUserID: -1, - ProxyGroupID: -1, - PortsToRedirect: make([]int, 0), - InboundPortsToIgnore: make([]string, 0), - OutboundPortsToIgnore: make([]string, 0), - SubnetsToIgnore: make([]string, 0), - SimulateOnly: false, - NetNs: "", - UseWaitFlag: false, - TimeoutCloseWaitSecs: 0, - LogFormat: "plain", - LogLevel: "info", - FirewallBinPath: "", - FirewallSaveBinPath: "", - IPTablesMode: "", - IPv6: true, + IncomingProxyPort: -1, + OutgoingProxyPort: -1, + ProxyUserID: -1, + ProxyGroupID: -1, + PortsToRedirect: make([]int, 0), + InboundPortsToIgnore: make([]string, 0), + OutboundPortsToIgnore: make([]string, 0), + SubnetsToIgnore: make([]string, 0), + InboundSubnetsToIgnore: make([]string, 0), + OutboundSubnetsToIgnore: make([]string, 0), + SimulateOnly: false, + NetNs: "", + UseWaitFlag: false, + TimeoutCloseWaitSecs: 0, + LogFormat: "plain", + LogLevel: "info", + FirewallBinPath: "", + FirewallSaveBinPath: "", + IPTablesMode: "", + IPv6: true, } } @@ -148,6 +152,8 @@ func NewRootCmd() *cobra.Command { cmd.PersistentFlags().StringSliceVar(&options.InboundPortsToIgnore, "inbound-ports-to-ignore", options.InboundPortsToIgnore, "Inbound ports and/or port ranges (inclusive) to ignore and not redirect to proxy. This has higher precedence than any other parameters.") cmd.PersistentFlags().StringSliceVar(&options.OutboundPortsToIgnore, "outbound-ports-to-ignore", options.OutboundPortsToIgnore, "Outbound ports and/or port ranges (inclusive) to ignore and not redirect to proxy. This has higher precedence than any other parameters.") cmd.PersistentFlags().StringSliceVar(&options.SubnetsToIgnore, "subnets-to-ignore", options.SubnetsToIgnore, "Subnets to ignore and not redirect to proxy. This has higher precedence than any other parameters.") + cmd.PersistentFlags().StringSliceVar(&options.InboundSubnetsToIgnore, "inbound-subnets-to-ignore", options.InboundSubnetsToIgnore, "Inbound subnets to ignore and not redirect to proxy.") + cmd.PersistentFlags().StringSliceVar(&options.OutboundSubnetsToIgnore, "outbound-subnets-to-ignore", options.OutboundSubnetsToIgnore, "Outbound subnets to ignore and not redirect to proxy.") cmd.PersistentFlags().BoolVar(&options.SimulateOnly, "simulate", options.SimulateOnly, "Don't execute any command, just print what would be executed") cmd.PersistentFlags().StringVar(&options.NetNs, "netns", options.NetNs, "Optional network namespace in which to run the iptables commands") cmd.PersistentFlags().BoolVarP(&options.UseWaitFlag, "use-wait-flag", "w", options.UseWaitFlag, "Appends the \"-w\" flag to the iptables commands") @@ -192,31 +198,46 @@ func BuildFirewallConfiguration(options *RootOptions) (*iptables.FirewallConfigu cmd, cmdSave := getCommands(options) - sanitizedSubnets := []string{} - for _, subnet := range options.SubnetsToIgnore { - subnet := strings.TrimSpace(subnet) - _, _, err := net.ParseCIDR(subnet) - if err != nil { - return nil, fmt.Errorf("%s is not a valid CIDR address", subnet) + sanitizeSubnets := func(subnets []string) ([]string, error) { + sanitized := []string{} + for _, subnet := range subnets { + subnet := strings.TrimSpace(subnet) + if _, _, err := net.ParseCIDR(subnet); err != nil { + return nil, fmt.Errorf("%s is not a valid CIDR address", subnet) + } + sanitized = append(sanitized, subnet) } - - sanitizedSubnets = append(sanitizedSubnets, subnet) + return sanitized, nil + } + sanitizedSubnets, err := sanitizeSubnets(options.SubnetsToIgnore) + if err != nil { + return nil, err + } + inboundSubnets, err := sanitizeSubnets(options.InboundSubnetsToIgnore) + if err != nil { + return nil, err + } + outboundSubnets, err := sanitizeSubnets(options.OutboundSubnetsToIgnore) + if err != nil { + return nil, err } firewallConfiguration := &iptables.FirewallConfiguration{ - ProxyInboundPort: options.IncomingProxyPort, - ProxyOutgoingPort: options.OutgoingProxyPort, - ProxyUID: options.ProxyUserID, - ProxyGID: options.ProxyGroupID, - PortsToRedirectInbound: options.PortsToRedirect, - InboundPortsToIgnore: options.InboundPortsToIgnore, - OutboundPortsToIgnore: options.OutboundPortsToIgnore, - SubnetsToIgnore: sanitizedSubnets, - SimulateOnly: options.SimulateOnly, - NetNs: options.NetNs, - UseWaitFlag: options.UseWaitFlag, - BinPath: cmd, - SaveBinPath: cmdSave, + ProxyInboundPort: options.IncomingProxyPort, + ProxyOutgoingPort: options.OutgoingProxyPort, + ProxyUID: options.ProxyUserID, + ProxyGID: options.ProxyGroupID, + PortsToRedirectInbound: options.PortsToRedirect, + InboundPortsToIgnore: options.InboundPortsToIgnore, + OutboundPortsToIgnore: options.OutboundPortsToIgnore, + SubnetsToIgnore: sanitizedSubnets, + InboundSubnetsToIgnore: inboundSubnets, + OutboundSubnetsToIgnore: outboundSubnets, + SimulateOnly: options.SimulateOnly, + NetNs: options.NetNs, + UseWaitFlag: options.UseWaitFlag, + BinPath: cmd, + SaveBinPath: cmdSave, } if len(options.PortsToRedirect) > 0 { diff --git a/proxy-init/cmd/root_test.go b/proxy-init/cmd/root_test.go index dfaf32f6..bed9228e 100644 --- a/proxy-init/cmd/root_test.go +++ b/proxy-init/cmd/root_test.go @@ -14,19 +14,21 @@ func TestBuildFirewallConfiguration(t *testing.T) { expectedProxyUserID := 33 expectedProxyGroupID := 33 expectedConfig := &iptables.FirewallConfiguration{ - Mode: iptables.RedirectAllMode, - PortsToRedirectInbound: make([]int, 0), - InboundPortsToIgnore: make([]string, 0), - OutboundPortsToIgnore: make([]string, 0), - SubnetsToIgnore: make([]string, 0), - ProxyInboundPort: expectedIncomingProxyPort, - ProxyOutgoingPort: expectedOutgoingProxyPort, - ProxyUID: expectedProxyUserID, - ProxyGID: expectedProxyGroupID, - SimulateOnly: false, - UseWaitFlag: false, - BinPath: "iptables-legacy", - SaveBinPath: "iptables-legacy-save", + Mode: iptables.RedirectAllMode, + PortsToRedirectInbound: make([]int, 0), + InboundPortsToIgnore: make([]string, 0), + OutboundPortsToIgnore: make([]string, 0), + SubnetsToIgnore: make([]string, 0), + InboundSubnetsToIgnore: make([]string, 0), + OutboundSubnetsToIgnore: make([]string, 0), + ProxyInboundPort: expectedIncomingProxyPort, + ProxyOutgoingPort: expectedOutgoingProxyPort, + ProxyUID: expectedProxyUserID, + ProxyGID: expectedProxyGroupID, + SimulateOnly: false, + UseWaitFlag: false, + BinPath: "iptables-legacy", + SaveBinPath: "iptables-legacy-save", } options := newRootOptions()