diff --git a/api/types/installers/installer.sh.tmpl b/api/types/installers/installer.sh.tmpl index 38f4627f50786..76f1c77a22131 100644 --- a/api/types/installers/installer.sh.tmpl +++ b/api/types/installers/installer.sh.tmpl @@ -3,19 +3,19 @@ set -eu on_ec2() { - EC2_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS "http://169.254.169.254/latest/meta-data") - # EC2 metadata sometimes requires token access, so a successful hit may - # return unauthorized/forbidden. - [ "$EC2_STATUS" = "200" ] || [ "$EC2_STATUS" = "401" ] || [ "$EC2_STATUS" = "403" ] + IMDS_TOKEN=$(curl -m5 -sS -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 300") + [ -z "$IMDS_TOKEN" ] && return 1 + EC2_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS -H "X-aws-ec2-metadata-token: ${IMDS_TOKEN}" "http://169.254.169.254/latest/meta-data") + [ "$EC2_STATUS" = "200" ] } on_azure() { - AZURE_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/versions") + AZURE_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01") [ "$AZURE_STATUS" = "200" ] } on_gcp() { - GCP_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS "metadata.google.internal") + GCP_STATUS=$(curl -o /dev/null -w "%{http_code}" -m5 -sS -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1") [ "$GCP_STATUS" = "200" ] } @@ -61,8 +61,8 @@ on_gcp() { fi if on_azure; then - API_VERSION=$(curl -m5 -sS -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/versions" | jq -r ".apiVersions[-1]") - INSTANCE_INFO=$(curl -m5 -sS -H "Metadata:true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=$API_VERSION&format=json") + API_VERSION=$(curl -m5 -sS -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/versions" | jq -r ".apiVersions[-1]") + INSTANCE_INFO=$(curl -m5 -sS -H "Metadata: true" --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=$API_VERSION&format=json") REGION="$(echo "$INSTANCE_INFO" | jq -r .compute.location)" RESOURCE_GROUP="$(echo "$INSTANCE_INFO" | jq -r .compute.resourceGroupName)" @@ -73,7 +73,7 @@ on_gcp() { LABELS="teleport.internal/vm-id=${VM_ID},teleport.internal/subscription-id=${SUBSCRIPTION_ID},teleport.internal/region=${REGION},teleport.internal/resource-group=${RESOURCE_GROUP}" elif on_ec2; then IMDS_TOKEN=$(curl -m5 -sS -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 300") - INSTANCE_INFO=$(curl -m5 -sS -H "X-aws-ec2-metadata-token: ${IMDS_TOKEN}" http://169.254.169.254/latest/dynamic/instance-identity/document) + INSTANCE_INFO=$(curl -m5 -sS -H "X-aws-ec2-metadata-token: ${IMDS_TOKEN}" "http://169.254.169.254/latest/dynamic/instance-identity/document") ACCOUNT_ID="$(echo "$INSTANCE_INFO" | jq -r .accountId)" INSTANCE_ID="$(echo "$INSTANCE_INFO" | jq -r .instanceId)" diff --git a/lib/inventory/metadata/metadata.go b/lib/inventory/metadata/metadata.go index d593cbc9c123e..09050eea4bc76 100644 --- a/lib/inventory/metadata/metadata.go +++ b/lib/inventory/metadata/metadata.go @@ -313,12 +313,37 @@ func (c *fetchConfig) fetchCloudEnvironment(ctx context.Context) string { // the instance is running on AWS. // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html func (c *fetchConfig) awsHTTPGetSuccess(ctx context.Context) bool { - url := "http://169.254.169.254/latest/meta-data/" - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + url := "http://169.254.169.254/latest/api/token" + req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, nil) + if err != nil { + return false + } + + req.Header.Add("X-aws-ec2-metadata-token-ttl-seconds", "300") + + const insecureSkipVerify = false + resp, err := c.httpDo(req, insecureSkipVerify) + if err != nil { + return false + } + defer resp.Body.Close() + + imdsToken, err := io.ReadAll(resp.Body) + if err != nil { + return false + } + + if resp.StatusCode != http.StatusOK || imdsToken == nil { + return false + } + + url = "http://169.254.169.254/latest/meta-data/" + req, err = http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return false } + req.Header.Add("X-aws-ec2-metadata-token", string(imdsToken)) return c.httpReqSuccess(req) } diff --git a/lib/inventory/metadata/metadata_test.go b/lib/inventory/metadata/metadata_test.go index 82d0f695dba3e..cc06582cacd79 100644 --- a/lib/inventory/metadata/metadata_test.go +++ b/lib/inventory/metadata/metadata_test.go @@ -299,18 +299,45 @@ func TestFetchCloudEnvironment(t *testing.T) { if insecureSkipVerify { return nil, trace.BadParameter("insecureSkipVerify should be false") } - if req.URL.String() != "http://169.254.169.254/latest/meta-data/" { - return nil, trace.NotFound("not found") + + if req.URL.String() == "http://169.254.169.254/latest/api/token" { + if req.Method != http.MethodPut { + return nil, trace.NotFound("not found") + } + if len(req.Header) != 1 { + return nil, trace.NotFound("not found") + } + if len(req.Header["X-Aws-Ec2-Metadata-Token-Ttl-Seconds"]) != 1 { + return nil, trace.NotFound("not found") + } + if req.Header["X-Aws-Ec2-Metadata-Token-Ttl-Seconds"][0] != "300" { + return nil, trace.NotFound("not found") + } + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader("thisIsAFakeTestToken")), + }, nil } - if len(req.Header) != 0 { - return nil, trace.NotFound("not found") + + if req.URL.String() == "http://169.254.169.254/latest/meta-data/" { + if len(req.Header) != 1 { + return nil, trace.NotFound("not found") + } + if len(req.Header["X-Aws-Ec2-Metadata-Token"]) != 1 { + return nil, trace.NotFound("not found") + } + if req.Header["X-Aws-Ec2-Metadata-Token"][0] != "thisIsAFakeTestToken" { + return nil, trace.NotFound("not found") + } + return success, nil } - return success, nil + + return nil, trace.NotFound("not found") }, expected: "aws", }, { - desc: "gcp if on gcp ", + desc: "gcp if on gcp", httpDo: func(req *http.Request, insecureSkipVerify bool) (*http.Response, error) { if insecureSkipVerify { return nil, trace.BadParameter("insecureSkipVerify should be false")