Skip to content

Commit 190fad2

Browse files
authored
add validation for MTU, update ANNOTATE_POD_IP README (#2798)
1 parent 80eadb9 commit 190fad2

File tree

6 files changed

+114
-50
lines changed

6 files changed

+114
-50
lines changed

README.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ Type: Integer as a String
226226

227227
Default: 9001
228228

229-
Used to configure the MTU size for attached ENIs. The valid range is from `576` to `9001`.
229+
Used to configure the MTU size for attached ENIs. The valid range for IPv4 is from `576` to `9001`, while the valid range for IPv6 is from `1280` to `9001`.
230230

231231
#### `AWS_VPC_K8S_CNI_EXTERNALSNAT`
232232

@@ -267,14 +267,14 @@ Default: empty
267267
Specify a comma-separated list of IPv4 CIDRs to exclude from SNAT. For every item in the list an `iptables` rule and off\-VPC
268268
IP rule will be applied. If an item is not a valid ipv4 range it will be skipped. This should be used when `AWS_VPC_K8S_CNI_EXTERNALSNAT=false`.
269269

270-
#### `POD_MTU` (v1.x.x+)
270+
#### `POD_MTU` (v1.16.4+)
271271

272272
Type: Integer as a String
273273

274-
*Note*: The default value is set to AWS_VPC_ENI_MTU, which defaults to 9001 if unset.
274+
*Note*: If unset, the default value is derived from `AWS_VPC_ENI_MTU`, which defaults to `9001`.
275275
Default: 9001
276276

277-
Used to configure the MTU size for pod virtual interfaces. The valid range is from `576` to `9001`.
277+
Used to configure the MTU size for pod virtual interfaces. The valid range for IPv4 is from `576` to `9001`, while the valid range for IPv6 is from `1280` to `9001`.
278278

279279
#### `WARM_ENI_TARGET`
280280

@@ -598,7 +598,7 @@ Setting `ANNOTATE_POD_IP` to `true` will allow IPAMD to add an annotation `vpc.a
598598

599599
There is a known [issue](https://github.com/kubernetes/kubernetes/issues/39113) with kubelet taking time to update `Pod.Status.PodIP` leading to calico being blocked on programming the policy. Setting `ANNOTATE_POD_IP` to `true` will enable AWS VPC CNI plugin to add Pod IP as an annotation to the pod spec to address this race condition.
600600

601-
To annotate the pod with pod IP, you will have to add "patch" permission for pods resource in aws-node clusterrole. You can use the below command -
601+
To annotate the pod with pod IP, you will have to add `patch` permission for pods resource in aws-node clusterrole. You can use the below command -
602602

603603
```
604604
cat << EOF > append.yaml
@@ -615,6 +615,8 @@ EOF
615615
kubectl apply -f <(cat <(kubectl get clusterrole aws-node -o yaml) append.yaml)
616616
```
617617

618+
NOTE: Adding `patch` permissions to the `aws-node` Daemonset increases the security scope for the plugin, so add this permission only after performing a proper security assessment of the tradeoffs.
619+
618620
#### `ENABLE_IPv4` (v1.10.0+)
619621

620622
Type: Boolean as a String

cmd/aws-vpc-cni/main.go

+33-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ const (
6666
defaultAWSconflistFile = "/app/10-aws.conflist"
6767
tmpAWSconflistFile = "/tmp/10-aws.conflist"
6868
defaultVethPrefix = "eni"
69-
defaultMTU = "9001"
69+
defaultMTU = 9001
70+
minMTUv4 = 576
71+
minMTUv6 = 1280
7072
defaultEnablePodEni = false
7173
defaultPodSGEnforcingMode = "strict"
7274
defaultPluginLogFile = "/var/log/aws-routed-eni/plugin.log"
@@ -279,8 +281,8 @@ func generateJSON(jsonFile string, outFile string, getPrimaryIP func(ipv4 bool)
279281
}
280282
}
281283
vethPrefix := utils.GetEnv(envVethPrefix, defaultVethPrefix)
282-
// Derive pod MTU from ENI MTU by default
283-
eniMTU := utils.GetEnv(envEniMTU, defaultMTU)
284+
// Derive pod MTU from ENI MTU by default (note that values have already been validated)
285+
eniMTU := utils.GetEnv(envEniMTU, strconv.Itoa(defaultMTU))
284286
// If pod MTU environment variable is set, overwrite ENI MTU.
285287
podMTU := utils.GetEnv(envPodMTU, eniMTU)
286288
podSGEnforcingMode := utils.GetEnv(envPodSGEnforcingMode, defaultPodSGEnforcingMode)
@@ -389,6 +391,11 @@ func validateEnvVars() bool {
389391
return false
390392
}
391393

394+
// Validate MTU value for ENIs and pods
395+
if !validateMTU(envEniMTU) || !validateMTU(envPodMTU) {
396+
return false
397+
}
398+
392399
prefixDelegationEn := utils.GetBoolAsStringEnvVar(envEnPrefixDelegation, defaultEnPrefixDelegation)
393400
warmIPTarget := utils.GetEnv(envWarmIPTarget, "0")
394401
warmPrefixTarget := utils.GetEnv(envWarmPrefixTarget, "0")
@@ -402,6 +409,29 @@ func validateEnvVars() bool {
402409
return true
403410
}
404411

412+
func validateMTU(envVar string) bool {
413+
// Validate MTU range based on IP address family
414+
enabledIPv6 := utils.GetBoolAsStringEnvVar(envEnIPv6, defaultEnableIPv6)
415+
416+
mtu, err, input := utils.GetIntFromStringEnvVar(envVar, defaultMTU)
417+
if err != nil {
418+
log.Errorf("%s MUST be a valid integer. %s is invalid", envVar, input)
419+
return false
420+
}
421+
if enabledIPv6 {
422+
if mtu < minMTUv6 || mtu > defaultMTU {
423+
log.Errorf("%s cannot be less than 1280 or greater than 9001 in IPv6. %s is invalid", envVar, input)
424+
return false
425+
}
426+
} else {
427+
if mtu < minMTUv4 || mtu > defaultMTU {
428+
log.Errorf("%s cannot be less than 576 or greater than 9001 in IPv4. %s is invalid", envVar, input)
429+
return false
430+
}
431+
}
432+
return true
433+
}
434+
405435
func main() {
406436
os.Exit(_main())
407437
}

cmd/aws-vpc-cni/main_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,40 @@ func TestGenerateJSONPlusBandwidthAndTuning(t *testing.T) {
4747
err := generateJSON(awsConflist, devNull, getPrimaryIPMock)
4848
assert.NoError(t, err)
4949
}
50+
51+
func TestMTUValidation(t *testing.T) {
52+
// By default, ENI MTU and pod MTU should be valid
53+
assert.True(t, validateMTU(envEniMTU))
54+
assert.True(t, validateMTU(envPodMTU))
55+
56+
// Non-integer values should fail
57+
_ = os.Setenv(envEniMTU, "true")
58+
_ = os.Setenv(envPodMTU, "abc")
59+
assert.False(t, validateMTU(envEniMTU))
60+
assert.False(t, validateMTU(envPodMTU))
61+
62+
// Integer values within IPv4 range should succeed
63+
_ = os.Setenv(envEniMTU, "5000")
64+
_ = os.Setenv(envPodMTU, "3000")
65+
assert.True(t, validateMTU(envEniMTU))
66+
assert.True(t, validateMTU(envPodMTU))
67+
68+
// Integer values outside IPv4 range should fail
69+
_ = os.Setenv(envEniMTU, "10000")
70+
_ = os.Setenv(envPodMTU, "500")
71+
assert.False(t, validateMTU(envEniMTU))
72+
assert.False(t, validateMTU(envPodMTU))
73+
74+
// Integer values within IPv6 range should succeed
75+
_ = os.Setenv(envEnIPv6, "true")
76+
_ = os.Setenv(envEniMTU, "5000")
77+
_ = os.Setenv(envPodMTU, "3000")
78+
assert.True(t, validateMTU(envEniMTU))
79+
assert.True(t, validateMTU(envPodMTU))
80+
81+
// Integer values outside IPv6 range should fail
82+
_ = os.Setenv(envEniMTU, "10000")
83+
_ = os.Setenv(envPodMTU, "1200")
84+
assert.False(t, validateMTU(envEniMTU))
85+
assert.False(t, validateMTU(envPodMTU))
86+
}

cmd/routed-eni-cni-plugin/cni.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ func add(args *skel.CmdArgs, cniTypes typeswrapper.CNITYPES, grpcClient grpcwrap
144144
return errors.Wrap(err, "add cmd: failed to load k8s config from arg")
145145
}
146146

147-
mtu := networkutils.GetEthernetMTU(conf.MTU)
147+
// Derive pod MTU. Note that the value has already been validated.
148+
mtu := networkutils.GetPodMTU(conf.MTU)
148149
log.Debugf("MTU value set is %d:", mtu)
149150

150151
// Set up a connection to the ipamD server.

pkg/networkutils/network.go

+23-31
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/coreos/go-iptables/iptables"
3030

3131
"github.com/aws/amazon-vpc-cni-k8s/pkg/sgpp"
32+
"github.com/aws/amazon-vpc-cni-k8s/utils"
3233

3334
"k8s.io/apimachinery/pkg/util/sets"
3435

@@ -115,7 +116,9 @@ const (
115116
defaultConnmark = 0x80
116117

117118
// envMTU gives a way to configure the MTU size for new ENIs attached. Range is from 576 to 9001.
118-
envMTU = "AWS_VPC_ENI_MTU"
119+
envMTU = "AWS_VPC_ENI_MTU"
120+
defaultMTU = 9001
121+
minMTUv4 = 576
119122

120123
// envVethPrefix is the environment variable to configure the prefix of the host side veth device names
121124
envVethPrefix = "AWS_VPC_K8S_CNI_VETHPREFIX"
@@ -126,10 +129,6 @@ const (
126129
// envEnIpv6Egress is the environment variable to enable IPv6 egress support on EKS v4 cluster
127130
envEnIpv6Egress = "ENABLE_V6_EGRESS"
128131

129-
// Range of MTU for each ENI and veth pair. Defaults to maximumMTU
130-
minimumMTU = 576
131-
maximumMTU = 9001
132-
133132
// number of retries to add a route
134133
maxRetryRouteAdd = 5
135134

@@ -198,7 +197,7 @@ func New() NetworkAPIs {
198197
typeOfSNAT: typeOfSNAT(),
199198
nodePortSupportEnabled: nodePortSupportEnabled(),
200199
mainENIMark: getConnmark(),
201-
mtu: GetEthernetMTU(""),
200+
mtu: GetEthernetMTU(),
202201
vethPrefix: getVethPrefixName(),
203202
podSGEnforcingMode: sgpp.LoadEnforcingModeFromEnv(),
204203

@@ -849,7 +848,7 @@ func GetConfigForDebug() map[string]interface{} {
849848
envExcludeSNATCIDRs: parseCIDRString(envExcludeSNATCIDRs),
850849
envExternalSNAT: useExternalSNAT(),
851850
envExternalServiceCIDRs: parseCIDRString(envExternalServiceCIDRs),
852-
envMTU: GetEthernetMTU(""),
851+
envMTU: GetEthernetMTU(),
853852
envVethPrefix: getVethPrefixName(),
854853
envNodePortSupport: nodePortSupportEnabled(),
855854
envRandomizeSNAT: typeOfSNAT(),
@@ -1293,32 +1292,25 @@ func (n *linuxNetwork) UpdateExternalServiceIpRules(ruleList []netlink.Rule, ext
12931292
return nil
12941293
}
12951294

1296-
// GetEthernetMTU gets the MTU setting from AWS_VPC_ENI_MTU if set, or takes the passed in string. Defaults to 9001 if not set.
1297-
func GetEthernetMTU(envMTUValue string) int {
1298-
inputStr, found := os.LookupEnv(envMTU)
1299-
if found {
1300-
envMTUValue = inputStr
1295+
// GetEthernetMTU returns the MTU value to program for ENIs. Note that the value was already validated during container initialization.
1296+
func GetEthernetMTU() int {
1297+
mtu, _, _ := utils.GetIntFromStringEnvVar(envMTU, defaultMTU)
1298+
return mtu
1299+
}
1300+
1301+
// GetPodMTU validates the pod MTU value. If an invalid value is passed, the default is used.
1302+
func GetPodMTU(podMTU string) int {
1303+
mtu, err := strconv.Atoi(podMTU)
1304+
if err != nil {
1305+
log.Errorf("Failed to parse pod MTU %s: %v", mtu, err)
1306+
return defaultMTU
13011307
}
1302-
if envMTUValue != "" {
1303-
mtu, err := strconv.Atoi(envMTUValue)
1304-
if err != nil {
1305-
log.Errorf("Failed to parse %s will use %d: %v", envMTU, maximumMTU, err.Error())
1306-
return maximumMTU
1307-
}
1308-
// Restrict range between jumbo frame and the maximum required size to assemble.
1309-
// Details in https://tools.ietf.org/html/rfc879 and
1310-
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/network_mtu.html
1311-
if mtu < minimumMTU {
1312-
log.Errorf("%s is too low: %d. Will use %d", envMTU, mtu, minimumMTU)
1313-
return minimumMTU
1314-
}
1315-
if mtu > maximumMTU {
1316-
log.Errorf("%s is too high: %d. Will use %d", envMTU, mtu, maximumMTU)
1317-
return maximumMTU
1318-
}
1319-
return mtu
1308+
1309+
// Only IPv4 bounds can be enforced, but note that the conflist value is already validated during container initialization.
1310+
if mtu < minMTUv4 || mtu > defaultMTU {
1311+
return defaultMTU
13201312
}
1321-
return maximumMTU
1313+
return mtu
13221314
}
13231315

13241316
// getVethPrefixName gets the name prefix of the veth devices based on the AWS_VPC_K8S_CNI_VETHPREFIX environment variable

pkg/networkutils/network_test.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -419,19 +419,21 @@ func TestSetupHostNetworkNodePortDisabledAndSNATEnabled(t *testing.T) {
419419
}, mockIptables.(*mock_iptables.MockIptables).DataplaneState)
420420
}
421421

422-
func TestLoadMTUFromEnvTooLow(t *testing.T) {
423-
os.Setenv(envMTU, "1")
424-
assert.Equal(t, GetEthernetMTU(""), minimumMTU)
425-
}
422+
func TestGetEthernetMTU(t *testing.T) {
423+
assert.Equal(t, GetEthernetMTU(), defaultMTU)
426424

427-
func TestLoadMTUFromEnv1500(t *testing.T) {
428-
os.Setenv(envMTU, "1500")
429-
assert.Equal(t, GetEthernetMTU(""), 1500)
425+
os.Setenv(envMTU, "5000")
426+
assert.Equal(t, GetEthernetMTU(), 5000)
430427
}
431428

432-
func TestLoadMTUFromEnvTooHigh(t *testing.T) {
433-
os.Setenv(envMTU, "65536")
434-
assert.Equal(t, GetEthernetMTU(""), maximumMTU)
429+
func TestGetPodMTU(t *testing.T) {
430+
// For any invalid value, return the default MTU
431+
assert.Equal(t, GetPodMTU("abc"), defaultMTU)
432+
assert.Equal(t, GetPodMTU("500"), defaultMTU)
433+
assert.Equal(t, GetPodMTU("10000"), defaultMTU)
434+
435+
// For any valid value, return the value
436+
assert.Equal(t, GetPodMTU("8000"), 8000)
435437
}
436438

437439
func TestLoadExcludeSNATCIDRsFromEnv(t *testing.T) {

0 commit comments

Comments
 (0)