Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions pkg/iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"))

Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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))
}
Expand Down
17 changes: 17 additions & 0 deletions pkg/iptables/iptables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@ func TestAddOutgoingTrafficRules(t *testing.T) {

}

func TestAddIgnoredSubnetsUseTrafficDirection(t *testing.T) {
incoming := (&FirewallConfiguration{
BinPath: "<iptables>",
InboundPortsToIgnore: []string{"1234"},
InboundSubnetsToIgnore: []string{"10.0.0.0/8"},
}).addIncomingTrafficRules(nil, nil)
assertEqual(t, incoming[1], exec.Command("<iptables>", "-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("<iptables>", "-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: "<iptables>",
ProxyOutgoingPort: 1234,
OutboundSubnetsToIgnore: []string{"10.0.0.0/8"},
}).addOutgoingTrafficRules(nil, nil)
assertEqual(t, outgoing[2], exec.Command("<iptables>", "-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("<iptables>", "-t", "nat", "-D", "PREROUTING", "-j", "PROXY_INIT_REDIRECT", "-m", "comment", "--comment", "proxy-init/install-proxy-init-prerouting"),
Expand Down
135 changes: 78 additions & 57 deletions proxy-init/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
28 changes: 15 additions & 13 deletions proxy-init/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down