Skip to content

Commit 607c6e3

Browse files
authored
Merge pull request #4384 from stefanmcshane/main
IMDSv2 on AWSManagedMachinePool
2 parents 81566e4 + 43a4cb9 commit 607c6e3

File tree

9 files changed

+262
-20
lines changed

9 files changed

+262
-20
lines changed

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinepools.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,59 @@ spec:
623623
description: ImageLookupOrg is the AWS Organization ID to use
624624
for image lookup if AMI is not set.
625625
type: string
626+
instanceMetadataOptions:
627+
description: InstanceMetadataOptions defines the behavior for
628+
applying metadata to instances.
629+
properties:
630+
httpEndpoint:
631+
default: enabled
632+
description: "Enables or disables the HTTP metadata endpoint
633+
on your instances. \n If you specify a value of disabled,
634+
you cannot access your instance metadata. \n Default: enabled"
635+
enum:
636+
- enabled
637+
- disabled
638+
type: string
639+
httpPutResponseHopLimit:
640+
default: 1
641+
description: "The desired HTTP PUT response hop limit for
642+
instance metadata requests. The larger the number, the further
643+
instance metadata requests can travel. \n Default: 1"
644+
format: int64
645+
maximum: 64
646+
minimum: 1
647+
type: integer
648+
httpTokens:
649+
default: optional
650+
description: "The state of token usage for your instance metadata
651+
requests. \n If the state is optional, you can choose to
652+
retrieve instance metadata with or without a session token
653+
on your request. If you retrieve the IAM role credentials
654+
without a token, the version 1.0 role credentials are returned.
655+
If you retrieve the IAM role credentials using a valid session
656+
token, the version 2.0 role credentials are returned. \n
657+
If the state is required, you must send a session token
658+
with any instance metadata retrieval requests. In this state,
659+
retrieving the IAM role credentials always returns the version
660+
2.0 credentials; the version 1.0 credentials are not available.
661+
\n Default: optional"
662+
enum:
663+
- optional
664+
- required
665+
type: string
666+
instanceMetadataTags:
667+
default: disabled
668+
description: "Set to enabled to allow access to instance tags
669+
from the instance metadata. Set to disabled to turn off
670+
access to instance tags from the instance metadata. For
671+
more information, see Work with instance tags using the
672+
instance metadata (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#work-with-tags-in-IMDS).
673+
\n Default: disabled"
674+
enum:
675+
- enabled
676+
- disabled
677+
type: string
678+
type: object
626679
instanceType:
627680
description: 'InstanceType is the type of instance to create.
628681
Example: m4.xlarge'

config/crd/bases/infrastructure.cluster.x-k8s.io_awsmanagedmachinepools.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,59 @@ spec:
611611
description: ImageLookupOrg is the AWS Organization ID to use
612612
for image lookup if AMI is not set.
613613
type: string
614+
instanceMetadataOptions:
615+
description: InstanceMetadataOptions defines the behavior for
616+
applying metadata to instances.
617+
properties:
618+
httpEndpoint:
619+
default: enabled
620+
description: "Enables or disables the HTTP metadata endpoint
621+
on your instances. \n If you specify a value of disabled,
622+
you cannot access your instance metadata. \n Default: enabled"
623+
enum:
624+
- enabled
625+
- disabled
626+
type: string
627+
httpPutResponseHopLimit:
628+
default: 1
629+
description: "The desired HTTP PUT response hop limit for
630+
instance metadata requests. The larger the number, the further
631+
instance metadata requests can travel. \n Default: 1"
632+
format: int64
633+
maximum: 64
634+
minimum: 1
635+
type: integer
636+
httpTokens:
637+
default: optional
638+
description: "The state of token usage for your instance metadata
639+
requests. \n If the state is optional, you can choose to
640+
retrieve instance metadata with or without a session token
641+
on your request. If you retrieve the IAM role credentials
642+
without a token, the version 1.0 role credentials are returned.
643+
If you retrieve the IAM role credentials using a valid session
644+
token, the version 2.0 role credentials are returned. \n
645+
If the state is required, you must send a session token
646+
with any instance metadata retrieval requests. In this state,
647+
retrieving the IAM role credentials always returns the version
648+
2.0 credentials; the version 1.0 credentials are not available.
649+
\n Default: optional"
650+
enum:
651+
- optional
652+
- required
653+
type: string
654+
instanceMetadataTags:
655+
default: disabled
656+
description: "Set to enabled to allow access to instance tags
657+
from the instance metadata. Set to disabled to turn off
658+
access to instance tags from the instance metadata. For
659+
more information, see Work with instance tags using the
660+
instance metadata (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#work-with-tags-in-IMDS).
661+
\n Default: disabled"
662+
enum:
663+
- enabled
664+
- disabled
665+
type: string
666+
type: object
614667
instanceType:
615668
description: 'InstanceType is the type of instance to create.
616669
Example: m4.xlarge'

docs/book/src/topics/instance-metadata.md

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
Instance metadata is data about your instance that you can use to configure or manage the running instance which you can access from a running instance using one of the following methods:
44

5-
* Instance Metadata Service Version 1 (IMDSv1) – a request/response method
6-
* Instance Metadata Service Version 2 (IMDSv2) – a session-oriented method
5+
- Instance Metadata Service Version 1 (IMDSv1) – a request/response method
6+
- Instance Metadata Service Version 2 (IMDSv2) – a session-oriented method
77

88
CAPA defaults to use IMDSv2 as optional property when creating instances.
99

@@ -12,6 +12,7 @@ CAPA expose options to configure IMDSv2 as required when creating instances, as
1212
It is possible to configure the instance metadata options using the field called `instanceMetadataOptions` in the `AWSMachineTemplate`.
1313

1414
Example:
15+
1516
```yaml
1617
---
1718
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
@@ -31,8 +32,66 @@ spec:
3132
To use IMDSv2, simply set `httpTokens` value to `required` (in other words, set the use of IMDSv2 to required).
3233
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).
3334

35+
Similarly, this can be done with `AWSManagedMachinePool` for use with EKS Managed Nodegroups. One slight difference here is that you [must use Launch Templates to configure IMDSv2 with Autoscaling Groups](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-transition-to-version-2.html). In order to configure the LaunchTemplate, you must use a custom AMI type according to the AWS API. This can be done by setting `AWSManagedMachinePool.spec.amiType` to `CUSTOM`. This change means that you must also specify a bootstrapping script to the worker node, which allows it to be joined to the EKS cluster. The default AWS Managed Node Group bootstrap script can be found [here on Github](https://github.com/awslabs/amazon-eks-ami/blob/master/files/bootstrap.sh).
36+
37+
The following example will use the default Amazon EKS Worker Node AMI which includes the default EKS Bootstrapping script. This must be installed on the management cluster as a Secret, under the key `value`. The secret's name must then be included in your `MachinePool` manifest at `MachinePool.spec.template.spec.bootstrap.dataSecretName`. Some assumptions are made for this example:
38+
39+
- Your cluster name is `capi-imds`, which CAPA renames to `default_capi-imds-control-plane` automatically
40+
- Your cluster is Kubernetes Version `v1.25.9`
41+
- Your `AWSManagedCluster` is deployed in the `default` namespace along with the bootstrap secret `eks-bootstrap`
42+
43+
```yaml
44+
kind: Secret
45+
apiVersion: v1
46+
type: Opaque
47+
data:
48+
value: IyEvYmluL2Jhc2ggLXhlCi9ldGMvZWtzL2Jvb3RzdHJhcC5zaCBkZWZhdWx0X2NhcGktaW1kcy1jb250cm9sLXBsYW5l
49+
metadata:
50+
name: eks-bootstrap
51+
---
52+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
53+
kind: AWSManagedMachinePool
54+
metadata:
55+
name: "capi-imds-pool-launchtemplate"
56+
spec:
57+
amiType: CUSTOM
58+
awsLaunchTemplate:
59+
name: my-aws-launch-template
60+
instanceType: t3.nano
61+
metadataOptions:
62+
httpTokens: required
63+
httpPutResponseHopLimit: 2
64+
---
65+
apiVersion: cluster.x-k8s.io/v1beta1
66+
kind: MachinePool
67+
metadata:
68+
name: "capi-imds-pool-1"
69+
spec:
70+
clusterName: "capi-imds"
71+
replicas: 1
72+
template:
73+
spec:
74+
version: v1.25.9
75+
clusterName: "capi-imds"
76+
bootstrap:
77+
dataSecretName: "eks-bootstrap"
78+
infrastructureRef:
79+
name: "capi-imds-pool-launchtemplate"
80+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
81+
kind: AWSManagedMachinePool
82+
```
83+
84+
`IyEvYmluL2Jhc2ggLXhlCi9ldGMvZWtzL2Jvb3RzdHJhcC5zaCBkZWZhdWx0X2NhcGktaW1kcy1jb250cm9sLXBsYW5l` in the above secret is a Base64 encoded version of the following script:
85+
86+
```bash
87+
#!/bin/bash -xe
88+
/etc/eks/bootstrap.sh default_capi-imds-control-plane
89+
```
90+
91+
If your cluster is not named `default_capi-imds-control-plane` in the AWS EKS console, you must update the name and store it as a Secret again.
92+
3493
See [the CLI command reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/modify-instance-metadata-options.html) for more information.
3594

36-
Before you decide to use IMDSv2 for the cluster instances, please make sure all your applications are compatible to IMDSv2.
95+
Before you decide to use IMDSv2 for the cluster instances, please make sure all your applications are compatible with IMDSv2.
3796

3897
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.

exp/api/v1beta1/conversion.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func (src *AWSMachinePool) ConvertTo(dstRaw conversion.Hub) error {
4444
if dst.Spec.RefreshPreferences != nil && restored.Spec.RefreshPreferences != nil {
4545
dst.Spec.RefreshPreferences.Disable = restored.Spec.RefreshPreferences.Disable
4646
}
47+
if restored.Spec.AWSLaunchTemplate.InstanceMetadataOptions != nil {
48+
dst.Spec.AWSLaunchTemplate.InstanceMetadataOptions = restored.Spec.AWSLaunchTemplate.InstanceMetadataOptions
49+
}
4750

4851
return nil
4952
}
@@ -78,6 +81,18 @@ func (src *AWSManagedMachinePool) ConvertTo(dstRaw conversion.Hub) error {
7881
if err := Convert_v1beta1_AWSManagedMachinePool_To_v1beta2_AWSManagedMachinePool(src, dst, nil); err != nil {
7982
return err
8083
}
84+
// Manually restore data.
85+
restored := &infrav1exp.AWSManagedMachinePool{}
86+
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
87+
return err
88+
}
89+
90+
if restored.Spec.AWSLaunchTemplate != nil {
91+
if dst.Spec.AWSLaunchTemplate == nil {
92+
dst.Spec.AWSLaunchTemplate = restored.Spec.AWSLaunchTemplate
93+
}
94+
dst.Spec.AWSLaunchTemplate.InstanceMetadataOptions = restored.Spec.AWSLaunchTemplate.InstanceMetadataOptions
95+
}
8196

8297
return nil
8398
}
@@ -90,7 +105,7 @@ func (r *AWSManagedMachinePool) ConvertFrom(srcRaw conversion.Hub) error {
90105
return err
91106
}
92107

93-
return nil
108+
return utilconversion.MarshalData(src, r)
94109
}
95110

96111
// Convert_v1beta2_AWSManagedMachinePoolSpec_To_v1beta1_AWSManagedMachinePoolSpec is a conversion function.

exp/api/v1beta1/zz_generated.conversion.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

exp/api/v1beta2/types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ type AWSLaunchTemplate struct {
122122

123123
// SpotMarketOptions are options for configuring AWSMachinePool instances to be run using AWS Spot instances.
124124
SpotMarketOptions *infrav1.SpotMarketOptions `json:"spotMarketOptions,omitempty"`
125+
126+
// InstanceMetadataOptions defines the behavior for applying metadata to instances.
127+
// +optional
128+
InstanceMetadataOptions *infrav1.InstanceMetadataOptions `json:"instanceMetadataOptions,omitempty"`
125129
}
126130

127131
// Overrides are used to override the instance type specified by the launch template with multiple
@@ -217,10 +221,8 @@ type AutoScalingGroup struct {
217221
// ASGStatus is a status string returned by the autoscaling API.
218222
type ASGStatus string
219223

220-
var (
221-
// ASGStatusDeleteInProgress is the string representing an ASG that is currently deleting.
222-
ASGStatusDeleteInProgress = ASGStatus("Delete in progress")
223-
)
224+
// ASGStatusDeleteInProgress is the string representing an ASG that is currently deleting.
225+
var ASGStatusDeleteInProgress = ASGStatus("Delete in progress")
224226

225227
// TaintEffect is the effect for a Kubernetes taint.
226228
type TaintEffect string

exp/api/v1beta2/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cloud/services/ec2/launchtemplate.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,20 @@ func (s *Service) createLaunchTemplateData(scope scope.LaunchTemplateScope, imag
456456
UserData: pointer.String(base64.StdEncoding.EncodeToString(userData)),
457457
}
458458

459+
if lt.InstanceMetadataOptions != nil {
460+
data.MetadataOptions = &ec2.LaunchTemplateInstanceMetadataOptionsRequest{
461+
HttpEndpoint: aws.String(string(lt.InstanceMetadataOptions.HTTPEndpoint)),
462+
InstanceMetadataTags: aws.String(string(lt.InstanceMetadataOptions.InstanceMetadataTags)),
463+
}
464+
465+
if lt.InstanceMetadataOptions.HTTPTokens != "" {
466+
data.MetadataOptions.HttpTokens = aws.String(string(lt.InstanceMetadataOptions.HTTPTokens))
467+
}
468+
if lt.InstanceMetadataOptions.HTTPPutResponseHopLimit != 0 {
469+
data.MetadataOptions.HttpPutResponseHopLimit = aws.Int64(lt.InstanceMetadataOptions.HTTPPutResponseHopLimit)
470+
}
471+
}
472+
459473
if len(lt.IamInstanceProfile) > 0 {
460474
data.IamInstanceProfile = &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{
461475
Name: aws.String(lt.IamInstanceProfile),
@@ -638,6 +652,21 @@ func (s *Service) SDKToLaunchTemplate(d *ec2.LaunchTemplateVersion) (*expinfrav1
638652
VersionNumber: d.VersionNumber,
639653
}
640654

655+
if v.MetadataOptions != nil {
656+
i.InstanceMetadataOptions = &infrav1.InstanceMetadataOptions{
657+
HTTPPutResponseHopLimit: aws.Int64Value(v.MetadataOptions.HttpPutResponseHopLimit),
658+
HTTPTokens: infrav1.HTTPTokensState(aws.StringValue(v.MetadataOptions.HttpTokens)),
659+
HTTPEndpoint: infrav1.InstanceMetadataEndpointStateEnabled,
660+
InstanceMetadataTags: infrav1.InstanceMetadataEndpointStateDisabled,
661+
}
662+
if v.MetadataOptions.HttpEndpoint != nil && aws.StringValue(v.MetadataOptions.HttpEndpoint) == "disabled" {
663+
i.InstanceMetadataOptions.HTTPEndpoint = infrav1.InstanceMetadataEndpointStateDisabled
664+
}
665+
if v.MetadataOptions.InstanceMetadataTags != nil && aws.StringValue(v.MetadataOptions.InstanceMetadataTags) == "enabled" {
666+
i.InstanceMetadataOptions.InstanceMetadataTags = infrav1.InstanceMetadataEndpointStateEnabled
667+
}
668+
}
669+
641670
if v.IamInstanceProfile != nil {
642671
i.IamInstanceProfile = aws.StringValue(v.IamInstanceProfile.Name)
643672
}
@@ -680,6 +709,9 @@ func (s *Service) LaunchTemplateNeedsUpdate(scope scope.LaunchTemplateScope, inc
680709
if incoming.InstanceType != existing.InstanceType {
681710
return true, nil
682711
}
712+
if !cmp.Equal(incoming.InstanceMetadataOptions, existing.InstanceMetadataOptions) {
713+
return true, nil
714+
}
683715

684716
incomingIDs, err := s.GetAdditionalSecurityGroupsIDs(incoming.AdditionalSecurityGroups)
685717
if err != nil {

0 commit comments

Comments
 (0)