From 2bca14c2b739c8ebacb1ffe878cd92c981682c52 Mon Sep 17 00:00:00 2001 From: Yike Wang Date: Thu, 18 May 2023 10:21:58 +0800 Subject: [PATCH] Default IMDSv2 to optional --- api/v1beta2/awsmachinetemplate_webhook_test.go | 4 ++-- api/v1beta2/types.go | 10 ++++++---- ...cluster.x-k8s.io_awsmanagedcontrolplanes.yaml | 8 ++++---- ...rastructure.cluster.x-k8s.io_awsclusters.yaml | 4 ++-- ...rastructure.cluster.x-k8s.io_awsmachines.yaml | 4 ++-- ...ure.cluster.x-k8s.io_awsmachinetemplates.yaml | 4 ++-- controllers/awsmachine_controller_unit_test.go | 4 ++-- docs/book/src/topics/instance-metadata.md | 16 +++++++++++----- pkg/cloud/services/ec2/instances.go | 2 ++ test/e2e/suites/unmanaged/helpers_test.go | 2 +- .../unmanaged/unmanaged_functional_test.go | 3 ++- 11 files changed, 36 insertions(+), 25 deletions(-) diff --git a/api/v1beta2/awsmachinetemplate_webhook_test.go b/api/v1beta2/awsmachinetemplate_webhook_test.go index 399a5b0bf5..92c8e7ea2f 100644 --- a/api/v1beta2/awsmachinetemplate_webhook_test.go +++ b/api/v1beta2/awsmachinetemplate_webhook_test.go @@ -127,8 +127,8 @@ func TestAWSMachineTemplateValidateUpdate(t *testing.T) { InstanceType: "test", InstanceMetadataOptions: &InstanceMetadataOptions{ HTTPEndpoint: InstanceMetadataEndpointStateEnabled, - HTTPPutResponseHopLimit: 2, - HTTPTokens: HTTPTokensStateRequired, + HTTPPutResponseHopLimit: 1, + HTTPTokens: HTTPTokensStateOptional, InstanceMetadataTags: InstanceMetadataEndpointStateDisabled, }, }, diff --git a/api/v1beta2/types.go b/api/v1beta2/types.go index 839bfadabd..5b95b4acd3 100644 --- a/api/v1beta2/types.go +++ b/api/v1beta2/types.go @@ -264,9 +264,10 @@ type InstanceMetadataOptions struct { // always returns the version 2.0 credentials; the version 1.0 credentials are // not available. // - // Default: required + // Default: optional + // // +kubebuilder:validation:Enum:=optional;required - // +kubebuilder:default=required + // +kubebuilder:default=optional HTTPTokens HTTPTokensState `json:"httpTokens,omitempty"` // Set to enabled to allow access to instance tags from the instance metadata. @@ -275,6 +276,7 @@ type InstanceMetadataOptions struct { // (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#work-with-tags-in-IMDS). // // Default: disabled + // // +kubebuilder:validation:Enum:=enabled;disabled // +kubebuilder:default=disabled InstanceMetadataTags InstanceMetadataState `json:"instanceMetadataTags,omitempty"` @@ -285,10 +287,10 @@ func (obj *InstanceMetadataOptions) SetDefaults() { obj.HTTPEndpoint = InstanceMetadataEndpointStateEnabled } if obj.HTTPPutResponseHopLimit == 0 { - obj.HTTPPutResponseHopLimit = 2 // Defaults to 2 in container environment + obj.HTTPPutResponseHopLimit = 1 } if obj.HTTPTokens == "" { - obj.HTTPTokens = HTTPTokensStateRequired // Defaults to IMDSv2 + obj.HTTPTokens = HTTPTokensStateOptional // Defaults to IMDSv1 } if obj.InstanceMetadataTags == "" { obj.InstanceMetadataTags = InstanceMetadataEndpointStateDisabled diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml index f92029cfef..a5659d9605 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_awsmanagedcontrolplanes.yaml @@ -866,7 +866,7 @@ spec: minimum: 1 type: integer httpTokens: - default: required + default: optional description: "The state of token usage for your instance metadata requests. \n If the state is optional, you can choose to retrieve instance metadata with or without a session token @@ -878,7 +878,7 @@ spec: with any instance metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available. - \n Default: required" + \n Default: optional" enum: - optional - required @@ -2288,7 +2288,7 @@ spec: minimum: 1 type: integer httpTokens: - default: required + default: optional description: "The state of token usage for your instance metadata requests. \n If the state is optional, you can choose to retrieve instance metadata with or without a session token @@ -2300,7 +2300,7 @@ spec: with any instance metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available. - \n Default: required" + \n Default: optional" enum: - optional - required diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index 1bbb0fadeb..cfdbf35e54 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -1358,7 +1358,7 @@ spec: minimum: 1 type: integer httpTokens: - default: required + default: optional description: "The state of token usage for your instance metadata requests. \n If the state is optional, you can choose to retrieve instance metadata with or without a session token @@ -1370,7 +1370,7 @@ spec: with any instance metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available. - \n Default: required" + \n Default: optional" enum: - optional - required diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml index 7ce292bd38..46e9f16278 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml @@ -688,7 +688,7 @@ spec: minimum: 1 type: integer httpTokens: - default: required + default: optional description: "The state of token usage for your instance metadata requests. \n If the state is optional, you can choose to retrieve instance metadata with or without a session token on your request. @@ -699,7 +699,7 @@ spec: you must send a session token with any instance metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 - credentials are not available. \n Default: required" + credentials are not available. \n Default: optional" enum: - optional - required diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml index 7b7aef383d..53e7a64b61 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml @@ -637,7 +637,7 @@ spec: minimum: 1 type: integer httpTokens: - default: required + default: optional description: "The state of token usage for your instance metadata requests. \n If the state is optional, you can choose to retrieve instance metadata with or without @@ -650,7 +650,7 @@ spec: metadata retrieval requests. In this state, retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not - available. \n Default: required" + available. \n Default: optional" enum: - optional - required diff --git a/controllers/awsmachine_controller_unit_test.go b/controllers/awsmachine_controller_unit_test.go index 864c201722..c549c49bf9 100644 --- a/controllers/awsmachine_controller_unit_test.go +++ b/controllers/awsmachine_controller_unit_test.go @@ -2551,8 +2551,8 @@ func TestAWSMachineReconcilerReconcileDefaultsToLoadBalancerTypeClassic(t *testi }, MetadataOptions: &ec2.InstanceMetadataOptionsResponse{ HttpEndpoint: aws.String(string(infrav1.InstanceMetadataEndpointStateEnabled)), - HttpPutResponseHopLimit: aws.Int64(2), - HttpTokens: aws.String(string(infrav1.HTTPTokensStateRequired)), + HttpPutResponseHopLimit: aws.Int64(1), + HttpTokens: aws.String(string(infrav1.HTTPTokensStateOptional)), InstanceMetadataTags: aws.String(string(infrav1.InstanceMetadataEndpointStateDisabled)), }, }, diff --git a/docs/book/src/topics/instance-metadata.md b/docs/book/src/topics/instance-metadata.md index cf086e4125..bd0dd98628 100644 --- a/docs/book/src/topics/instance-metadata.md +++ b/docs/book/src/topics/instance-metadata.md @@ -5,8 +5,9 @@ Instance metadata is data about your instance that you can use to configure or m * Instance Metadata Service Version 1 (IMDSv1) – a request/response method * Instance Metadata Service Version 2 (IMDSv2) – a session-oriented method -CAPA defaults to IMDSv2 when creating instances, as it provides a [better level of security](https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/). -CAPA defaults to 2 hot limit when creating instances with IMDSv2, as it is recommended in container environment according to [AWS document](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#imds-considerations). +CAPA defaults to use IMDSv2 as optional property when creating instances. + +CAPA expose options to configure IMDSv2 as required when creating instances, as it provides a [better level of security](https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/). It is possible to configure the instance metadata options using the field called `instanceMetadataOptions` in the `AWSMachineTemplate`. @@ -22,11 +23,16 @@ spec: spec: instanceMetadataOptions: httpEndpoint: enabled - httpPutResponseHopLimit: 2 - httpTokens: required + httpPutResponseHopLimit: 1 + httpTokens: optional instanceMetadataTags: disabled ``` -To use IMDSv1, simply set `httpTokens` value to `optional` (in other words, set the use of IMDSv2 to optional). +To use IMDSv2, simply set `httpTokens` value to `required` (in other words, set the use of IMDSv2 to required). +To use IMDSv2, please also set `httpPutResponseHopLimit` value to `2`, as it is recommended in container environment according to [AWS document](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#imds-considerations). See [the CLI command reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/modify-instance-metadata-options.html) for more information. + +Before you decide to use IMDSv2 for the cluster instances, please make sure all your applications are compatible to IMDSv2. + +See the [transition guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-transition-to-version-2.html#recommended-path-for-requiring-imdsv2) for more information. diff --git a/pkg/cloud/services/ec2/instances.go b/pkg/cloud/services/ec2/instances.go index dbbabf4121..91f31c7546 100644 --- a/pkg/cloud/services/ec2/instances.go +++ b/pkg/cloud/services/ec2/instances.go @@ -236,6 +236,7 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, use input.Tenancy = scope.AWSMachine.Spec.Tenancy s.scope.Debug("Running instance", "machine-role", scope.Role()) + s.scope.Debug("Running instance with instance metadata options", "metadata options", input.InstanceMetadataOptions) out, err := s.runInstance(scope.Role(), input) if err != nil { // Only record the failure event if the error is not related to failed dependencies. @@ -981,6 +982,7 @@ func (s *Service) ModifyInstanceMetadataOptions(instanceID string, options *infr InstanceId: aws.String(instanceID), } + s.scope.Info("Updating instance metadata options", "instance id", instanceID, "options", input) if _, err := s.EC2Client.ModifyInstanceMetadataOptions(input); err != nil { return err } diff --git a/test/e2e/suites/unmanaged/helpers_test.go b/test/e2e/suites/unmanaged/helpers_test.go index 34373d6e8f..b4f838b31f 100644 --- a/test/e2e/suites/unmanaged/helpers_test.go +++ b/test/e2e/suites/unmanaged/helpers_test.go @@ -594,7 +594,7 @@ func assertInstanceMetadataOptions(instanceID string, expected infrav1.InstanceM metadataOptions := result.Reservations[0].Instances[0].MetadataOptions Expect(metadataOptions).ToNot(BeNil()) - Expect(metadataOptions.HttpTokens).To(HaveValue(Equal(string(expected.HTTPTokens)))) // IMDSv2 enabled + Expect(metadataOptions.HttpTokens).To(HaveValue(Equal(string(expected.HTTPTokens)))) Expect(metadataOptions.HttpEndpoint).To(HaveValue(Equal(string(expected.HTTPEndpoint)))) Expect(metadataOptions.InstanceMetadataTags).To(HaveValue(Equal(string(expected.InstanceMetadataTags)))) Expect(metadataOptions.HttpPutResponseHopLimit).To(HaveValue(Equal(expected.HTTPPutResponseHopLimit))) diff --git a/test/e2e/suites/unmanaged/unmanaged_functional_test.go b/test/e2e/suites/unmanaged/unmanaged_functional_test.go index 71f714e0cc..23e9811c73 100644 --- a/test/e2e/suites/unmanaged/unmanaged_functional_test.go +++ b/test/e2e/suites/unmanaged/unmanaged_functional_test.go @@ -196,9 +196,10 @@ var _ = ginkgo.Context("[unmanaged] [functional]", func() { mdName := clusterName + "-md01" machineTempalte := makeAWSMachineTemplate(namespace.Name, mdName, e2eCtx.E2EConfig.GetVariable(shared.AwsNodeMachineType), nil) + // A test to set IMDSv2 explicitly machineTempalte.Spec.Template.Spec.InstanceMetadataOptions = &infrav1.InstanceMetadataOptions{ HTTPEndpoint: infrav1.InstanceMetadataEndpointStateEnabled, - HTTPPutResponseHopLimit: 1, + HTTPPutResponseHopLimit: 2, HTTPTokens: infrav1.HTTPTokensStateRequired, // IMDSv2 InstanceMetadataTags: infrav1.InstanceMetadataEndpointStateDisabled, }