Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Randomise outgoing port for connections in the SNAT iptables rule. #246

Merged
merged 16 commits into from
Jan 9, 2019
Merged
Show file tree
Hide file tree
Changes from 5 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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ Default: `false`
Specifies whether an external NAT gateway should be used to provide SNAT of secondary ENI IP addresses\. If set to `true`, the SNAT `iptables` rule and off\-VPC IP rule are not applied, and these rules are removed if they have already been applied\.
Disable SNAT if you need to allow inbound communication to your pods from external VPNs, direct connections, and external VPCs, and your pods do not need to access the Internet directly via an Internet Gateway\. However, your nodes must be running in a private subnet and connected to the internet through an AWS NAT Gateway or another external NAT device\.

`AWS_VPC_K8S_CNI_RANDOMIZESNAT`
Type: String
Default: `hashrandom`
Valid Values: `hashrandom`, `prng`, `none`
Specifies weather the SNAT `iptables` rule should randomize the outgoing ports for connections. When enabled (`hashrandom`) the `--random` flag will be added to the SNAT `iptables` rule. To use pseudo random number generation rather than hash based (i.e. `--random-fully`) use `prng` for the environment variable.
Disable (`none`) this functionality if you rely on sequential port allocation for outgoing connections.

`WARM_ENI_TARGET`
Type: Integer
Default: `1`
Expand Down
55 changes: 51 additions & 4 deletions pkg/networkutils/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ const (
// be installed and will be removed if they are already installed. Defaults to false.
envExternalSNAT = "AWS_VPC_K8S_CNI_EXTERNALSNAT"

// This environment is used to specify weather the SNAT rule added to iptables should randomize port
// allocation for outgoing connections. If set to "hashrandom" the SNAT iptables rule will have the "--random" flag
// added to it. Set it to "prng" if you want to use a pseudo random numbers, i.e. "--random-fully".
// Defaults to hashrandom.
envRandomizeSNAT = "AWS_VPC_K8S_CNI_RANDOMIZESNAT"

// envNodePortSupport is the name of environment variable that configures whether we implement support for
// NodePorts on the primary ENI. This requires that we add additional iptables rules and loosen the kernel's
// RPF check as described below. Defaults to true.
Expand Down Expand Up @@ -97,6 +103,7 @@ type NetworkAPIs interface {

type linuxNetwork struct {
useExternalSNAT bool
randomizeSNAT snatType
nodePortSupportEnabled bool
connmark uint32

Expand All @@ -114,10 +121,19 @@ type iptablesIface interface {
NewChain(table, chain string) error
}

type snatType uint32

const (
sequentialSNAT snatType = 0
randomHashSNAT snatType = 1
randomPRNGSNAT snatType = 2
taylorb-syd marked this conversation as resolved.
Show resolved Hide resolved
)

// New creates a linuxNetwork object
func New() NetworkAPIs {
return &linuxNetwork{
useExternalSNAT: useExternalSNAT(),
randomizeSNAT: randomizeSNAT(),
nodePortSupportEnabled: nodePortSupportEnabled(),
mainENIMark: getConnmark(),

Expand Down Expand Up @@ -295,15 +311,21 @@ func (n *linuxNetwork) SetupHostNetwork(vpcCIDR *net.IPNet, vpcCIDRs []*string,
}

curChain := fmt.Sprintf("AWS-SNAT-CHAIN-%d", len(vpcCIDRs))
snatRule := []string{"-m", "comment", "--comment", "AWS, SNAT",
"-m", "addrtype", "!", "--dst-type", "LOCAL",
"-j", "SNAT", "--to-source", primaryAddr.String()}
if n.randomizeSNAT == randomHashSNAT {
snatRule = append(snatRule, "--random")
}
if n.randomizeSNAT == randomPRNGSNAT {
snatRule = append(snatRule, "--random-fully")
taylorb-syd marked this conversation as resolved.
Show resolved Hide resolved
}
iptableRules = append(iptableRules, iptablesRule{
name: "last SNAT rule for non-VPC outbound traffic",
shouldExist: !n.useExternalSNAT,
table: "nat",
chain: curChain,
rule: []string{
"-m", "comment", "--comment", "AWS, SNAT",
"-m", "addrtype", "!", "--dst-type", "LOCAL",
"-j", "SNAT", "--to-source", primaryAddr.String()},
rule: snatRule,
})

log.Debugf("iptableRules: %v", iptableRules)
Expand Down Expand Up @@ -427,6 +449,31 @@ func useExternalSNAT() bool {
return getBoolEnvVar(envExternalSNAT, false)
}

func randomizeSNAT() snatType {
defaultValue := randomHashSNAT
defaultString := "hash based random"
strValue := os.Getenv(envRandomizeSNAT)
taylorb-syd marked this conversation as resolved.
Show resolved Hide resolved
if strValue == "" {
// empty means default
return defaultValue
}
if strValue == "prng" {
// prng means to use --random-fully
return randomPRNGSNAT
}
if strValue == "none" {
// none means to disable randomisation (no flag)
return sequentialSNAT
}
if strValue == "hashrandom" {
// hashrandom means to use --random
return randomHashSNAT
}
// if we get to this point, the environment variable has an invalid value
log.Error("Failed to parse " + envRandomizeSNAT + "; using default: " + defaultString + ". Provided string was " + strValue)
taylorb-syd marked this conversation as resolved.
Show resolved Hide resolved
return defaultValue
taylorb-syd marked this conversation as resolved.
Show resolved Hide resolved
}

func nodePortSupportEnabled() bool {
return getBoolEnvVar(envNodePortSupport, true)
}
Expand Down