Skip to content
Merged
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
169 changes: 89 additions & 80 deletions iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ const (

// IptablesMultiportLimit specifies the maximum number of port references per single iptables command.
IptablesMultiportLimit = 15
outputChainName = "PROXY_INIT_OUTPUT"
redirectChainName = "PROXY_INIT_REDIRECT"
)

var (
// ExecutionTraceID provides a unique identifier for this script's execution.
ExecutionTraceID = strconv.Itoa(int(time.Now().Unix()))

sectionDelimiter = strings.Repeat("-", 60)
)

// FirewallConfiguration specifies how to configure a pod's iptables.
Expand All @@ -53,32 +57,40 @@ type FirewallConfiguration struct {
// https://github.com/istio/istio/blob/e83411e/pilot/docker/prepare_proxy.sh
func ConfigureFirewall(firewallConfiguration FirewallConfiguration) error {

log.Printf("Tracing this script execution as [%s]\n", ExecutionTraceID)
fmt.Printf("Tracing this script execution as [%s]\n", ExecutionTraceID)

log.Println("State of iptables rules before run:")
err := executeCommand(firewallConfiguration, makeShowAllRules())
if err != nil {
log.Println("Aborting firewall configuration")
startSection("current state")
if err := executeCommand(firewallConfiguration, makeShowAllRules()); err != nil {
fmt.Println("Aborting firewall configuration")
return err
}
endSection()

commands := make([]*exec.Cmd, 0)

startSection("configuration")

commands = addIncomingTrafficRules(commands, firewallConfiguration)

commands = addOutgoingTrafficRules(commands, firewallConfiguration)

commands = append(commands, makeShowAllRules())
endSection()

log.Println("Executing commands:")
startSection("adding rules")

for _, cmd := range commands {
err := executeCommand(firewallConfiguration, cmd)
if err != nil {
log.Println("Aborting firewall configuration")
if err := executeCommand(firewallConfiguration, cmd); err != nil {
fmt.Println("Aborting firewall configuration")
return err
}
}

endSection()

startSection("end state")
_ = executeCommand(firewallConfiguration, makeShowAllRules())
endSection()

return nil
}

Expand All @@ -88,89 +100,91 @@ func formatComment(text string) string {
return fmt.Sprintf("proxy-init/%s/%s", text, ExecutionTraceID)
}

func addOutgoingTrafficRules(commands []*exec.Cmd, firewallConfiguration FirewallConfiguration) []*exec.Cmd {
outputChainName := "PROXY_INIT_OUTPUT"
redirectChainName := "PROXY_INIT_REDIRECT"
err := executeCommand(firewallConfiguration, makeFlushChain(outputChainName))
if err != nil {
log.Printf("An error occurred while FLUSHING the chain in addOutgoingTrafficRules. Startup will continue, but there may be additional errors\n [error]: %v", err)
}
func startSection(text string) {
fmt.Printf("%s\n%s\n", text, sectionDelimiter)
}

err = executeCommand(firewallConfiguration, makeDeleteChain(outputChainName))
if err != nil {
log.Printf("An error occurred while DELETING the chain in addOutgoingTrafficRules. Startup will continue, but there may be additional errors\n [error]: %v", err)
}
func endSection() {
fmt.Printf("\n\n")
}

func addOutgoingTrafficRules(commands []*exec.Cmd, firewallConfiguration FirewallConfiguration) []*exec.Cmd {
commands = append(commands, makeCreateNewChain(outputChainName, "redirect-common-chain"))

// Ignore traffic from the proxy
if firewallConfiguration.ProxyUID > 0 {
log.Printf("Ignoring uid %d", firewallConfiguration.ProxyUID)
fmt.Printf("Ignoring uid %d\n", firewallConfiguration.ProxyUID)
// Redirect calls originating from the proxy destined for an app container e.g. app -> proxy(outbound) -> proxy(inbound) -> app
commands = append(commands, makeRedirectChainForOutgoingTraffic(outputChainName, redirectChainName, firewallConfiguration.ProxyUID, "redirect-non-loopback-local-traffic"))
commands = append(commands, makeIgnoreUserID(outputChainName, firewallConfiguration.ProxyUID, "ignore-proxy-user-id"))
} else {
log.Println("Not ignoring any uid")
fmt.Println("Not ignoring any uid")
}

// Ignore loopback
commands = append(commands, makeIgnoreLoopback(outputChainName, "ignore-loopback"))
// Ignore ports
commands = addRulesForIgnoredPorts(firewallConfiguration.OutboundPortsToIgnore, outputChainName, commands)

log.Printf("Redirecting all OUTPUT to %d", firewallConfiguration.ProxyOutgoingPort)
fmt.Printf("Redirecting all OUTPUT to %d\n", firewallConfiguration.ProxyOutgoingPort)
commands = append(commands, makeRedirectChainToPort(outputChainName, firewallConfiguration.ProxyOutgoingPort, "redirect-all-outgoing-to-proxy-port"))

//Redirect all remaining outbound traffic to the proxy.
commands = append(commands, makeJumpFromChainToAnotherForAllProtocols(IptablesOutputChainName, outputChainName, "install-proxy-init-output"))
commands = append(
commands,
makeJumpFromChainToAnotherForAllProtocols(
IptablesOutputChainName,
outputChainName,
"install-proxy-init-output",
false))

return commands
}

func addIncomingTrafficRules(commands []*exec.Cmd, firewallConfiguration FirewallConfiguration) []*exec.Cmd {
redirectChainName := "PROXY_INIT_REDIRECT"
err := executeCommand(firewallConfiguration, makeFlushChain(redirectChainName))
if err != nil {
log.Printf("An error occurred while FLUSHING the chain in addIncomingTrafficRules. Startup will continue, but there may be additional errors\n [error]: %v", err)
}

err = executeCommand(firewallConfiguration, makeDeleteChain(redirectChainName))
if err != nil {
log.Printf("An error occurred while DELETING the chain in addIncomingTrafficRules. Startup will continue, but there may be additional errors\n [error]: %v", err)
}

commands = append(commands, makeCreateNewChain(redirectChainName, "redirect-common-chain"))
commands = addRulesForIgnoredPorts(firewallConfiguration.InboundPortsToIgnore, redirectChainName, commands)
commands = addRulesForInboundPortRedirect(firewallConfiguration, redirectChainName, commands)

//Redirect all remaining inbound traffic to the proxy.
commands = append(commands, makeJumpFromChainToAnotherForAllProtocols(IptablesPreroutingChainName, redirectChainName, "install-proxy-init-prerouting"))
commands = append(
commands,
makeJumpFromChainToAnotherForAllProtocols(
IptablesPreroutingChainName,
redirectChainName,
"install-proxy-init-prerouting",
false))

return commands
}

func addRulesForInboundPortRedirect(firewallConfiguration FirewallConfiguration, chainName string, commands []*exec.Cmd) []*exec.Cmd {
if firewallConfiguration.Mode == RedirectAllMode {
log.Print("Will redirect all INPUT ports to proxy")
fmt.Println("Will redirect all INPUT ports to proxy")
//Create a new chain for redirecting inbound and outbound traffic to the proxy port.
commands = append(commands, makeRedirectChainToPort(chainName,
firewallConfiguration.ProxyInboundPort,
"redirect-all-incoming-to-proxy-port"))

} else if firewallConfiguration.Mode == RedirectListedMode {
log.Printf("Will redirect some INPUT ports to proxy: %v", firewallConfiguration.PortsToRedirectInbound)
fmt.Printf("Will redirect some INPUT ports to proxy: %v\n", firewallConfiguration.PortsToRedirectInbound)
for _, port := range firewallConfiguration.PortsToRedirectInbound {
commands = append(commands, makeRedirectChainToPortBasedOnDestinationPort(chainName,
port,
firewallConfiguration.ProxyInboundPort,
fmt.Sprintf("redirect-port-%d-to-proxy-port", port)))
commands = append(
commands,
makeRedirectChainToPortBasedOnDestinationPort(
chainName,
port,
firewallConfiguration.ProxyInboundPort,
fmt.Sprintf("redirect-port-%d-to-proxy-port", port)))
}
}
return commands
}

func addRulesForIgnoredPorts(portsToIgnore []string, chainName string, commands []*exec.Cmd) []*exec.Cmd {
for _, destinations := range makeMultiportDestinations(portsToIgnore) {
log.Printf("Will ignore port(s) %s on chain %s", destinations, chainName)
fmt.Printf("Will ignore port %s on chain %s\n", destinations, chainName)

commands = append(commands, makeIgnorePorts(chainName, destinations, fmt.Sprintf("ignore-port-%s", strings.Join(destinations, ","))))
}
return commands
Expand Down Expand Up @@ -207,34 +221,35 @@ func makeMultiportDestinations(portsToIgnore []string) [][]string {
}

func executeCommand(firewallConfiguration FirewallConfiguration, cmd *exec.Cmd) error {
originalCmd := strings.Trim(fmt.Sprintf("%v", cmd.Args), "[]")
log.Printf("> %s", originalCmd)

if firewallConfiguration.UseWaitFlag {
log.Print("Setting UseWaitFlag: iptables will wait for xtables to become available")
fmt.Println("Setting UseWaitFlag: iptables will wait for xtables to become available")
cmd.Args = append(cmd.Args, "-w")
}

if !firewallConfiguration.SimulateOnly {
// wrap up the cmd with nsenter if we were givin a netns
if len(firewallConfiguration.NetNs) > 0 {
netnsArg := fmt.Sprintf("--net=%s", firewallConfiguration.NetNs)
originalCmdAsArgs := strings.Split(originalCmd, " ")
nsenterArgs := []string{
netnsArg,
}
finalArgs := append(nsenterArgs, originalCmdAsArgs...)
if firewallConfiguration.SimulateOnly {
return nil
}

log.Printf(">> nsenter %v", finalArgs)
cmd = exec.Command("nsenter", finalArgs...)
}
// wrap up the cmd with nsenter if we were givin a netns
if len(firewallConfiguration.NetNs) > 0 {
cmd.Args = append([]string{
"nsenter",
"--net", firewallConfiguration.NetNs,
}, cmd.Args...)
}

out, err := cmd.CombinedOutput()
log.Printf("< %s\n", string(out))
if err != nil {
return err
}
fmt.Printf(":; %s\n", strings.Trim(fmt.Sprintf("%v", cmd.Args), "[]"))

out, err := cmd.CombinedOutput()

if len(out) > 0 {
fmt.Printf("%s\n", out)
}

if err != nil {
return err
}

return nil
}

Expand All @@ -257,18 +272,6 @@ func makeCreateNewChain(name string, comment string) *exec.Cmd {
"--comment", formatComment(comment))
}

func makeFlushChain(name string) *exec.Cmd {
return exec.Command("iptables",
"-t", "nat",
"-F", name)
}

func makeDeleteChain(name string) *exec.Cmd {
return exec.Command("iptables",
"-t", "nat",
"-X", name)
}

func makeRedirectChainToPort(chainName string, portToRedirect int, comment string) *exec.Cmd {
return exec.Command("iptables",
"-t", "nat",
Expand Down Expand Up @@ -314,10 +317,16 @@ func makeRedirectChainToPortBasedOnDestinationPort(chainName string, destination
"--comment", formatComment(comment))
}

func makeJumpFromChainToAnotherForAllProtocols(chainName string, targetChain string, comment string) *exec.Cmd {
func makeJumpFromChainToAnotherForAllProtocols(
chainName string, targetChain string, comment string, delete bool) *exec.Cmd {
action := "-A"
if delete {
action = "-D"
}

return exec.Command("iptables",
"-t", "nat",
"-A", chainName,
action, chainName,
"-j", targetChain,
"-m", "comment",
"--comment", formatComment(comment))
Expand All @@ -337,7 +346,7 @@ func makeRedirectChainForOutgoingTraffic(chainName string, redirectChainName str
}

func makeShowAllRules() *exec.Cmd {
return exec.Command("iptables", "-t", "nat", "-vnL")
return exec.Command("iptables-save")
}

// asDestination formats the provided `PortRange` for output in commands.
Expand Down