/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Fluent Bit * ========== * Copyright (C) 2015-2022 The Fluent Bit Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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. */ #include #include #include #include #include #include #include #include #include #include #include #define AWS_IMDS_ROLE_PATH "/latest/meta-data/iam/security-credentials/" #define AWS_IMDS_ROLE_PATH_LEN 43 struct flb_aws_provider_ec2; static int get_creds_ec2(struct flb_aws_provider_ec2 *implementation); static int ec2_credentials_request(struct flb_aws_provider_ec2 *implementation, char *cred_path); /* EC2 IMDS Provider */ /* * A provider that obtains credentials from EC2 IMDS. */ struct flb_aws_provider_ec2 { struct flb_aws_credentials *creds; time_t next_refresh; /* upstream connection to IMDS */ struct flb_aws_client *client; /* IMDS interface */ struct flb_aws_imds *imds_interface; }; struct flb_aws_credentials *get_credentials_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_credentials *creds; int refresh = FLB_FALSE; struct flb_aws_provider_ec2 *implementation = provider->implementation; flb_debug("[aws_credentials] Requesting credentials from the " "EC2 provider.."); /* a negative next_refresh means that auto-refresh is disabled */ if (implementation->next_refresh > 0 && time(NULL) > implementation->next_refresh) { refresh = FLB_TRUE; } if (!implementation->creds || refresh == FLB_TRUE) { if (try_lock_provider(provider)) { get_creds_ec2(implementation); unlock_provider(provider); } } if (!implementation->creds) { /* * We failed to lock the provider and creds are unset. This means that * another co-routine is performing the refresh. */ flb_warn("[aws_credentials] No cached credentials are available and " "a credential refresh is already in progress. The current " "co-routine will retry."); return NULL; } creds = flb_calloc(1, sizeof(struct flb_aws_credentials)); if (!creds) { flb_errno(); return NULL; } creds->access_key_id = flb_sds_create(implementation->creds->access_key_id); if (!creds->access_key_id) { flb_errno(); flb_aws_credentials_destroy(creds); return NULL; } creds->secret_access_key = flb_sds_create(implementation->creds-> secret_access_key); if (!creds->secret_access_key) { flb_errno(); flb_aws_credentials_destroy(creds); return NULL; } if (implementation->creds->session_token) { creds->session_token = flb_sds_create(implementation->creds-> session_token); if (!creds->session_token) { flb_errno(); flb_aws_credentials_destroy(creds); return NULL; } } else { creds->session_token = NULL; } return creds; } int refresh_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_provider_ec2 *implementation = provider->implementation; int ret = -1; flb_debug("[aws_credentials] Refresh called on the EC2 IMDS provider"); if (try_lock_provider(provider)) { ret = get_creds_ec2(implementation); unlock_provider(provider); } return ret; } int init_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_provider_ec2 *implementation = provider->implementation; int ret = -1; implementation->client->debug_only = FLB_TRUE; flb_debug("[aws_credentials] Init called on the EC2 IMDS provider"); if (try_lock_provider(provider)) { ret = get_creds_ec2(implementation); unlock_provider(provider); } implementation->client->debug_only = FLB_FALSE; return ret; } void sync_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_provider_ec2 *implementation = provider->implementation; flb_debug("[aws_credentials] Sync called on the EC2 provider"); /* remove async flag */ flb_stream_disable_async_mode(&implementation->client->upstream->base); } void async_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_provider_ec2 *implementation = provider->implementation; flb_debug("[aws_credentials] Async called on the EC2 provider"); /* add async flag */ flb_stream_enable_async_mode(&implementation->client->upstream->base); } void upstream_set_fn_ec2(struct flb_aws_provider *provider, struct flb_output_instance *ins) { struct flb_aws_provider_ec2 *implementation = provider->implementation; flb_debug("[aws_credentials] upstream_set called on the EC2 provider"); /* Make sure TLS is set to false before setting upstream, then reset it */ ins->use_tls = FLB_FALSE; flb_output_upstream_set(implementation->client->upstream, ins); ins->use_tls = FLB_TRUE; } void destroy_fn_ec2(struct flb_aws_provider *provider) { struct flb_aws_provider_ec2 *implementation = provider->implementation; if (implementation) { if (implementation->creds) { flb_aws_credentials_destroy(implementation->creds); } if (implementation->imds_interface) { flb_aws_imds_destroy(implementation->imds_interface); } if (implementation->client) { flb_aws_client_destroy(implementation->client); } flb_free(implementation); provider->implementation = NULL; } return; } static struct flb_aws_provider_vtable ec2_provider_vtable = { .get_credentials = get_credentials_fn_ec2, .init = init_fn_ec2, .refresh = refresh_fn_ec2, .destroy = destroy_fn_ec2, .sync = sync_fn_ec2, .async = async_fn_ec2, .upstream_set = upstream_set_fn_ec2, }; struct flb_aws_provider *flb_ec2_provider_create(struct flb_config *config, struct flb_aws_client_generator *generator) { struct flb_aws_provider_ec2 *implementation; struct flb_aws_provider *provider; struct flb_upstream *upstream; provider = flb_calloc(1, sizeof(struct flb_aws_provider)); if (!provider) { flb_errno(); return NULL; } pthread_mutex_init(&provider->lock, NULL); implementation = flb_calloc(1, sizeof(struct flb_aws_provider_ec2)); if (!implementation) { flb_free(provider); flb_errno(); return NULL; } provider->provider_vtable = &ec2_provider_vtable; provider->implementation = implementation; upstream = flb_upstream_create(config, FLB_AWS_IMDS_HOST, FLB_AWS_IMDS_PORT, FLB_IO_TCP, NULL); if (!upstream) { flb_aws_provider_destroy(provider); flb_debug("[aws_credentials] unable to connect to EC2 IMDS."); return NULL; } /* IMDSv2 token request will timeout if hops = 1 and running within container */ upstream->base.net.connect_timeout = FLB_AWS_IMDS_TIMEOUT; upstream->base.net.io_timeout = FLB_AWS_IMDS_TIMEOUT; upstream->base.net.keepalive = FLB_FALSE; /* On timeout, the connection is broken */ implementation->client = generator->create(); if (!implementation->client) { flb_aws_provider_destroy(provider); flb_upstream_destroy(upstream); flb_error("[aws_credentials] EC2 IMDS: client creation error"); return NULL; } implementation->client->name = "ec2_imds_provider_client"; implementation->client->has_auth = FLB_FALSE; implementation->client->provider = NULL; implementation->client->region = NULL; implementation->client->service = NULL; implementation->client->port = 80; implementation->client->flags = 0; implementation->client->proxy = NULL; implementation->client->upstream = upstream; /* Use default imds configuration */ implementation->imds_interface = flb_aws_imds_create(&flb_aws_imds_config_default, implementation->client); if (!implementation->imds_interface) { flb_aws_provider_destroy(provider); flb_error("[aws_credentials] EC2 IMDS configuration error"); return NULL; } return provider; } /* Requests creds from IMDSv1 and sets them on the provider */ static int get_creds_ec2(struct flb_aws_provider_ec2 *implementation) { int ret; flb_sds_t instance_role; size_t instance_role_len; char *cred_path; size_t cred_path_size; flb_debug("[aws_credentials] requesting credentials from EC2 IMDS"); /* Get the name of the instance role */ ret = flb_aws_imds_request(implementation->imds_interface, AWS_IMDS_ROLE_PATH, &instance_role, &instance_role_len); if (ret < 0) { return -1; } flb_debug("[aws_credentials] Requesting credentials for instance role %s", instance_role); /* Construct path where we will find the credentials */ cred_path_size = sizeof(char) * (AWS_IMDS_ROLE_PATH_LEN + instance_role_len) + 1; cred_path = flb_malloc(cred_path_size); if (!cred_path) { flb_sds_destroy(instance_role); flb_errno(); return -1; } ret = snprintf(cred_path, cred_path_size, "%s%s", AWS_IMDS_ROLE_PATH, instance_role); if (ret < 0) { flb_sds_destroy(instance_role); flb_free(cred_path); flb_errno(); return -1; } /* request creds */ ret = ec2_credentials_request(implementation, cred_path); flb_sds_destroy(instance_role); flb_free(cred_path); return ret; } static int ec2_credentials_request(struct flb_aws_provider_ec2 *implementation, char *cred_path) { int ret; flb_sds_t credentials_response; size_t credentials_response_len; struct flb_aws_credentials *creds; time_t expiration; ret = flb_aws_imds_request(implementation->imds_interface, cred_path, &credentials_response, &credentials_response_len); if (ret < 0) { return -1; } creds = flb_parse_http_credentials(credentials_response, credentials_response_len, &expiration); if (creds == NULL) { flb_sds_destroy(credentials_response); return -1; } /* destroy existing credentials first */ flb_aws_credentials_destroy(implementation->creds); implementation->creds = NULL; /* set new creds */ implementation->creds = creds; implementation->next_refresh = expiration - FLB_AWS_REFRESH_WINDOW; flb_sds_destroy(credentials_response); return 0; }