// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. //go:build freebsd || linux || netbsd || openbsd || darwin || windows // +build freebsd linux netbsd openbsd darwin windows package diagnostics import ( "context" "fmt" "net/http" "time" "github.com/aws/amazon-ssm-agent/agent/appconfig" "github.com/aws/amazon-ssm-agent/agent/cli/diagnosticsutil" "github.com/aws/amazon-ssm-agent/agent/log/logger" "github.com/aws/amazon-ssm-agent/agent/network" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" ) const ( maxTimeCallMetadataSeconds = 3 metadataCheckStrName = "EC2 IMDS" metadataCheckStrInstanceIdTimeout = "instance id request timeout" metadataCheckStrInstanceIdFailure = "failed to query instance id from IMDS: %v" metadataCheckStrRegionTimeout = "region request timeout" metadataCheckStrRegionFailure = "failed to query region from IMDS: %v" metadataCheckStrHasHybrid = "Instance has hybrid registration" metadataCheckStrIMDSTimeout = "IMDS request timeout" metadataCheckStrIMDSFailure = "Failed to query IMDS: %v" metadataCheckStrSuccess = "IMDS is accessible and has instance id %s in region %s" ) type stringStringErrorTuple struct { firstStr string secondStr string err error } type metadataCheckQuery struct{} func (q metadataCheckQuery) GetName() string { return metadataCheckStrName } func (metadataCheckQuery) GetPriority() int { return 1 } func (metadataCheckQuery) getRegionAndInstanceId(resChan chan stringStringErrorTuple) { log := logger.NewSilentLogger() config := appconfig.DefaultConfig() tr := network.GetDefaultTransport(log, config) awsConfig := &aws.Config{ HTTPClient: &http.Client{ Transport: tr, }, } awsConfig = awsConfig.WithMaxRetries(2) awsConfig = awsConfig.WithEC2MetadataDisableTimeoutOverride(false) sess, _ := session.NewSession(awsConfig) client := ec2metadata.New(sess) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() instanceId, err := client.GetMetadataWithContext(ctx, "instance-id") if err != nil { if aerr, ok := err.(awserr.Error); ok && aerr.Code() == request.CanceledErrorCode { resChan <- stringStringErrorTuple{ "", "", fmt.Errorf(metadataCheckStrInstanceIdTimeout), } return } resChan <- stringStringErrorTuple{ "", "", fmt.Errorf(metadataCheckStrInstanceIdFailure, err), } return } ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second) defer cancel2() region, err := client.RegionWithContext(ctx2) if err != nil { if aerr, ok := err.(awserr.Error); ok && aerr.Code() == request.CanceledErrorCode { resChan <- stringStringErrorTuple{ "", "", fmt.Errorf(metadataCheckStrRegionTimeout), } return } resChan <- stringStringErrorTuple{ "", "", fmt.Errorf(metadataCheckStrRegionFailure, err), } return } resChan <- stringStringErrorTuple{ instanceId, region, nil, } } func (q metadataCheckQuery) Execute() diagnosticsutil.DiagnosticOutput { if diagnosticsutil.IsOnPremRegistration() { return diagnosticsutil.DiagnosticOutput{ Check: q.GetName(), Status: diagnosticsutil.DiagnosticsStatusSkipped, Note: metadataCheckStrHasHybrid, } } resChan := make(chan stringStringErrorTuple, 1) go q.getRegionAndInstanceId(resChan) select { case <-time.After(maxTimeCallMetadataSeconds * time.Second): return diagnosticsutil.DiagnosticOutput{ Check: q.GetName(), Status: diagnosticsutil.DiagnosticsStatusFailed, Note: metadataCheckStrIMDSTimeout, } case respTouple := <-resChan: if respTouple.err != nil { return diagnosticsutil.DiagnosticOutput{ Check: q.GetName(), Status: diagnosticsutil.DiagnosticsStatusFailed, Note: fmt.Sprintf(metadataCheckStrIMDSFailure, respTouple.err), } } return diagnosticsutil.DiagnosticOutput{ Check: q.GetName(), Status: diagnosticsutil.DiagnosticsStatusSuccess, Note: fmt.Sprintf(metadataCheckStrSuccess, respTouple.firstStr, respTouple.secondStr), } } } func init() { diagnosticsutil.RegisterDiagnosticQuery(metadataCheckQuery{}) }