diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 605b2847d3a..0eb2a911813 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -18,6 +18,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Make error message about locked data path actionable. {pull}18667[18667] - Fix panic with inline SSL when the certificate or key were small than 256 bytes. {pull}23820[23820] - Skip add_kubernetes_metadata processor when kubernetes metadata are already present {pull}27689[27689] +- Enable IMDSv2 support for `add_cloud_metadata` processor on AWS. {issue}22101[22101] {pull}28285[28285] *Auditbeat* diff --git a/libbeat/processors/add_cloud_metadata/provider_aws_ec2.go b/libbeat/processors/add_cloud_metadata/provider_aws_ec2.go index 350ec63b8c6..8978d51ae32 100644 --- a/libbeat/processors/add_cloud_metadata/provider_aws_ec2.go +++ b/libbeat/processors/add_cloud_metadata/provider_aws_ec2.go @@ -18,12 +18,88 @@ package add_cloud_metadata import ( + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/common" s "github.com/elastic/beats/v7/libbeat/common/schema" c "github.com/elastic/beats/v7/libbeat/common/schema/mapstriface" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) -const ec2InstanceIdentityURI = "/2014-02-25/dynamic/instance-identity/document" +const ( + ec2InstanceIdentityURI = "/2014-02-25/dynamic/instance-identity/document" + ec2InstanceIMDSv2TokenValueHeader = "X-aws-ec2-metadata-token" + ec2InstanceIMDSv2TokenTTLHeader = "X-aws-ec2-metadata-token-ttl-seconds" + ec2InstanceIMDSv2TokenTTLValue = "21600" + ec2InstanceIMDSv2TokenURI = "/latest/api/token" +) + +// fetches IMDSv2 token, returns empty one on errors +func getIMDSv2Token(c *common.Config) string { + logger := logp.NewLogger("add_cloud_metadata") + + config := defaultConfig() + if err := c.Unpack(&config); err != nil { + logger.Warnf("error when load config for getting IMDSv2 token: %s. No token in the metadata request will be used.", err) + return "" + } + + tlsConfig, err := tlscommon.LoadTLSConfig(config.TLS) + if err != nil { + logger.Warnf("error when load TLS config for getting IMDSv2 token: %s. No token in the metadata request will be used.", err) + return "" + } + + client := http.Client{ + Timeout: config.Timeout, + Transport: &http.Transport{ + DisableKeepAlives: true, + DialContext: (&net.Dialer{ + Timeout: config.Timeout, + KeepAlive: 0, + }).DialContext, + TLSClientConfig: tlsConfig.ToConfig(), + }, + } + + tokenReq, err := http.NewRequest("PUT", fmt.Sprintf("http://%s%s", metadataHost, ec2InstanceIMDSv2TokenURI), nil) + if err != nil { + logger.Warnf("error when make token request for getting IMDSv2 token: %s. No token in the metadata request will be used.", err) + return "" + } + + tokenReq.Header.Add(ec2InstanceIMDSv2TokenTTLHeader, ec2InstanceIMDSv2TokenTTLValue) + rsp, err := client.Do(tokenReq) + defer func(body io.ReadCloser) { + if body != nil { + body.Close() + } + }(rsp.Body) + + if err != nil { + logger.Warnf("error when read token request for getting IMDSv2 token: %s. No token in the metadata request will be used.", err) + return "" + } + + if rsp.StatusCode != http.StatusOK { + logger.Warnf("error when check request status for getting IMDSv2 token: http request status %d. No token in the metadata request will be used.", rsp.StatusCode) + return "" + } + + all, err := ioutil.ReadAll(rsp.Body) + if err != nil { + logger.Warnf("error when reading token request for getting IMDSv2 token: %s. No token in the metadata request will be used.", err) + return "" + } + + return string(all) +} // AWS EC2 Metadata Service var ec2MetadataFetcher = provider{ @@ -48,7 +124,13 @@ var ec2MetadataFetcher = provider{ return common.MapStr{"cloud": out} } - fetcher, err := newMetadataFetcher(config, "aws", nil, metadataHost, ec2Schema, ec2InstanceIdentityURI) + headers := make(map[string]string, 1) + token := getIMDSv2Token(config) + if len(token) > 0 { + headers[ec2InstanceIMDSv2TokenValueHeader] = token + } + + fetcher, err := newMetadataFetcher(config, "aws", headers, metadataHost, ec2Schema, ec2InstanceIdentityURI) return fetcher, err }, }