diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c548e0283..3565afe96 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -31,7 +31,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/). * FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth/): allow overriding `Host` header with a target host before sending to a downstream. See this [issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6453) * FEATURE: [dashboards](https://grafana.com/orgs/victoriametrics): add [Grafana dashboard](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/dashboards/vmauth.json) and [alerting rules](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-vmauth.yml) for [vmauth](https://docs.victoriametrics.com/vmauth/) dashboard. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4313) for details. - +* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent/): added `yandexcloud_sd` AWS API IMDSv2 support. * BUGFIX: [docker-compose](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#docker-compose-environment-for-victoriametrics): fix incorrect link to vmui from [VictoriaMetrics plugin in Grafana](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#grafana). * BUGFIX: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/) and `vmstorage` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): Fix the dateMetricIDCache consistency issue that leads to duplicate per-day index entries when new time series are inserted concurrently. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6534) for details. diff --git a/lib/promscrape/discovery/yandexcloud/api.go b/lib/promscrape/discovery/yandexcloud/api.go index 149aaa40c..dbe91400b 100644 --- a/lib/promscrape/discovery/yandexcloud/api.go +++ b/lib/promscrape/discovery/yandexcloud/api.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "net/url" + "strconv" "sync" "time" @@ -36,6 +37,10 @@ type apiConfig struct { // credsLock protects the refresh of creds credsLock sync.Mutex creds *apiCredentials + + // metadataCredsLock protects the refresh of metadataCreds + metadataCredsLock sync.Mutex + metadataCreds *apiCredentials } func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { @@ -119,12 +124,53 @@ func getCreds(cfg *apiConfig) (*apiCredentials, error) { }, nil } +// getMetadataCreds gets Yandex Cloud IAM metadata token +func getMetadataCreds(cfg *apiConfig) (*apiCredentials, error) { + cfg.metadataCredsLock.Lock() + defer cfg.metadataCredsLock.Unlock() + + if cfg.metadataCreds != nil && time.Until(cfg.metadataCreds.Expiration) > 10*time.Second { + // Credentials aren't expired yet. + return cfg.metadataCreds, nil + } + + endpoint := "http://169.254.169.254/latest/api/token" + req, err := http.NewRequest(http.MethodPut, endpoint, nil) + if err != nil { + return nil, fmt.Errorf("cannot create metadata token request: %w", err) + } + ttl := 1800 + expiration := time.Now().Add(time.Duration(ttl) * time.Second) + req.Header.Add("X-aws-ec2-metadata-token-ttl-seconds", strconv.Itoa(ttl)) + resp, err := cfg.client.Do(req) + if err != nil { + return nil, fmt.Errorf("cannot perform metadata token request: %w", err) + } + data, err := readResponseBody(resp, endpoint) + if err != nil { + return nil, fmt.Errorf("cannot read metadata creds from %s: %w", endpoint, err) + } + return &apiCredentials{ + Token: string(data), + Expiration: expiration, + }, nil +} + // getInstanceCreds gets Yandex Cloud IAM token using instance Service Account // // See https://cloud.yandex.com/en-ru/docs/compute/operations/vm-connect/auth-inside-vm func getInstanceCreds(cfg *apiConfig) (*apiCredentials, error) { + metadataCreds, err := getMetadataCreds(cfg) + if err != nil { + return nil, err + } endpoint := "http://169.254.169.254/latest/meta-data/iam/security-credentials/default" - resp, err := cfg.client.Get(endpoint) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, fmt.Errorf("cannot create instance creds request: %w", err) + } + req.Header.Add("X-aws-ec2-metadata-token", metadataCreds.Token) + resp, err := cfg.client.Do(req) if err != nil { return nil, fmt.Errorf("cannot read instance creds from %s: %w", endpoint, err) } @@ -132,6 +178,7 @@ func getInstanceCreds(cfg *apiConfig) (*apiCredentials, error) { if err != nil { return nil, err } + var ac apiCredentials if err := json.Unmarshal(data, &ac); err != nil { return nil, fmt.Errorf("cannot parse auth credentials response from %s: %w", endpoint, err)