@@ -34,21 +34,24 @@ constexpr std::chrono::milliseconds REFRESH_AWS_CREDS =
34
34
struct ThreadLocalCredentials : public Envoy ::ThreadLocal::ThreadLocalObject {
35
35
ThreadLocalCredentials (CredentialsConstSharedPtr credentials)
36
36
: credentials_(credentials) {}
37
+ ThreadLocalCredentials (StsCredentialsProviderPtr credentials)
38
+ : sts_credentials_(std::move(credentials)) {}
37
39
CredentialsConstSharedPtr credentials_;
40
+ StsCredentialsProviderPtr sts_credentials_;
38
41
};
39
42
40
43
} // namespace
41
44
42
45
AWSLambdaConfigImpl::AWSLambdaConfigImpl (
43
46
std::unique_ptr<Extensions::Common::Aws::CredentialsProvider> &&provider,
44
- Upstream::ClusterManager &cluster_manager ,
45
- StsCredentialsProviderFactory &sts_factory, Event::Dispatcher &dispatcher ,
47
+ std::unique_ptr<StsCredentialsProviderFactory> &&sts_factory ,
48
+ Event::Dispatcher &dispatcher, Api::Api &api ,
46
49
Envoy::ThreadLocal::SlotAllocator &tls, const std::string &stats_prefix,
47
- Stats::Scope &scope, Api::Api &api,
50
+ Stats::Scope &scope,
48
51
const envoy::config::filter::http::aws_lambda::v2::AWSLambdaConfig
49
52
&protoconfig)
50
- : context_factory_(cluster_manager, api),
51
- stats_ (generateStats(stats_prefix, scope )) {
53
+ : stats_(generateStats(stats_prefix, scope)), api_( api),
54
+ file_watcher_ (dispatcher.createFilesystemWatcher( )) {
52
55
53
56
// Initialize Credential fetcher, if none exists do nothing. Filter will
54
57
// implicitly use protocol options data
@@ -73,9 +76,23 @@ AWSLambdaConfigImpl::AWSLambdaConfigImpl(
73
76
case envoy::config::filter::http::aws_lambda::v2::AWSLambdaConfig::
74
77
CredentialsFetcherCase::kServiceAccountCredentials : {
75
78
ENVOY_LOG (debug, " {}: Using STS credentials source" , __func__);
79
+
80
+ // Load all of the env data for STS credentials
81
+ loadSTSData ();
76
82
// use service account credentials provider
83
+ tls_slot_ = tls.allocateSlot ();
84
+ // transfer ptr ownership to sts_factor isn't cleaned up before we get into
85
+ // tls set
86
+ sts_factory_ = std::move (sts_factory);
77
87
auto service_account_creds = protoconfig.service_account_credentials ();
78
- sts_credentials_provider_ = sts_factory.create (service_account_creds);
88
+ tls_slot_->set ([this , web_token = web_token_, role_arn = role_arn_,
89
+ service_account_creds](Event::Dispatcher &dispatcher) {
90
+ StsCredentialsProviderPtr sts_cred_provider = sts_factory_->build (
91
+ service_account_creds, dispatcher, web_token, role_arn);
92
+ return std::make_shared<ThreadLocalCredentials>(
93
+ std::move (sts_cred_provider));
94
+ });
95
+ sts_enabled_ = true ;
79
96
break ;
80
97
}
81
98
case envoy::config::filter::http::aws_lambda::v2::AWSLambdaConfig::
@@ -85,15 +102,75 @@ AWSLambdaConfigImpl::AWSLambdaConfigImpl(
85
102
}
86
103
}
87
104
105
+ void AWSLambdaConfigImpl::loadSTSData () {
106
+ // AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN must be set for STS
107
+ // credentials to be enabled
108
+ token_file_ =
109
+ absl::NullSafeStringView (std::getenv (AWS_WEB_IDENTITY_TOKEN_FILE));
110
+ if (token_file_ == " " ) {
111
+ throw EnvoyException (fmt::format (" Env var {} must be present, and set" ,
112
+ AWS_WEB_IDENTITY_TOKEN_FILE));
113
+ }
114
+ role_arn_ = absl::NullSafeStringView (std::getenv (AWS_ROLE_ARN));
115
+ if (role_arn_ == " " ) {
116
+ throw EnvoyException (
117
+ fmt::format (" Env var {} must be present, and set" , AWS_ROLE_ARN));
118
+ }
119
+ // File must exist on system
120
+ if (!api_.fileSystem ().fileExists (token_file_)) {
121
+ throw EnvoyException (
122
+ fmt::format (" Web token file {} does not exist" , token_file_));
123
+ }
124
+
125
+ web_token_ = api_.fileSystem ().fileReadToEnd (token_file_);
126
+ // File should not be empty
127
+ if (web_token_ == " " ) {
128
+ throw EnvoyException (
129
+ fmt::format (" Web token file {} exists but is empty" , token_file_));
130
+ }
131
+ }
132
+
133
+ void AWSLambdaConfigImpl::init () {
134
+ if (sts_enabled_) {
135
+ // Add file watcher for token file
136
+ auto shared_this = shared_from_this ();
137
+ file_watcher_->addWatch (
138
+ token_file_, Filesystem::Watcher::Events::Modified,
139
+ [shared_this](uint32_t ) {
140
+ try {
141
+ const auto web_token = shared_this->api_ .fileSystem ().fileReadToEnd (
142
+ shared_this->token_file_ );
143
+ // Set the web token on all sts credentials providers
144
+ shared_this->tls_slot_ ->runOnAllThreads (
145
+ [web_token](ThreadLocal::ThreadLocalObjectSharedPtr previous)
146
+ -> ThreadLocal::ThreadLocalObjectSharedPtr {
147
+ auto prev_config =
148
+ std::dynamic_pointer_cast<ThreadLocalCredentials>(
149
+ previous);
150
+ prev_config->sts_credentials_ ->setWebToken (web_token);
151
+ return previous;
152
+ });
153
+ // TODO: check if web_token is valid
154
+ // TODO: stats here
155
+ } catch (const EnvoyException &e) {
156
+ ENVOY_LOG_TO_LOGGER (
157
+ Envoy::Logger::Registry::getLog (Logger::Id::aws), warn,
158
+ " {}: Exception while reading file during watch ({}): {}" ,
159
+ __func__, shared_this->token_file_ , e.what ());
160
+ }
161
+ });
162
+ }
163
+ }
164
+
88
165
/*
89
166
* Three options, in order of precedence
90
167
* 1. Protocol Options
91
168
* 2. Default Provider
92
169
* 3. STS
93
170
*/
94
- ContextSharedPtr AWSLambdaConfigImpl::getCredentials (
171
+ StsConnectionPool::Context * AWSLambdaConfigImpl::getCredentials (
95
172
SharedAWSLambdaProtocolExtensionConfig ext_cfg,
96
- StsCredentialsProvider ::Callbacks *callbacks) const {
173
+ StsConnectionPool::Context ::Callbacks *callbacks) const {
97
174
// Always check extension config first, as it overrides
98
175
if (ext_cfg->accessKey ().has_value () && ext_cfg->secretKey ().has_value ()) {
99
176
ENVOY_LOG (trace, " {}: Credentials found from protocol options" , __func__);
@@ -111,22 +188,20 @@ ContextSharedPtr AWSLambdaConfigImpl::getCredentials(
111
188
return nullptr ;
112
189
}
113
190
191
+ auto &thread_local_credentials =
192
+ tls_slot_->getTyped <ThreadLocalCredentials>();
114
193
if (provider_) {
115
194
ENVOY_LOG (trace, " {}: Credentials found from default source" , __func__);
116
- callbacks->onSuccess (
117
- tls_slot_->getTyped <ThreadLocalCredentials>().credentials_ );
195
+ callbacks->onSuccess (thread_local_credentials.credentials_ );
118
196
// no context necessary as these credentials are available immediately
119
197
return nullptr ;
120
198
}
121
199
122
- if (sts_credentials_provider_ ) {
200
+ if (sts_enabled_ ) {
123
201
ENVOY_LOG (trace, " {}: Credentials being retrieved from STS provider" ,
124
202
__func__);
125
- // return the context directly to the filter, as no direct credentials can
126
- // be sent
127
- auto context = context_factory_.create (callbacks);
128
- sts_credentials_provider_->find (ext_cfg->roleArn (), context);
129
- return context;
203
+ return thread_local_credentials.sts_credentials_ ->find (ext_cfg->roleArn (),
204
+ callbacks);
130
205
}
131
206
132
207
ENVOY_LOG (debug, " {}: No valid credentials source found" , __func__);
@@ -164,6 +239,24 @@ void AWSLambdaConfigImpl::timerCallback() {
164
239
}
165
240
}
166
241
242
+ std::shared_ptr<AWSLambdaConfigImpl> AWSLambdaConfigImpl::create (
243
+ std::unique_ptr<Envoy::Extensions::Common::Aws::CredentialsProvider>
244
+ &&provider,
245
+ std::unique_ptr<StsCredentialsProviderFactory> &&sts_factory,
246
+ Event::Dispatcher &dispatcher, Api::Api &api,
247
+ Envoy::ThreadLocal::SlotAllocator &tls, const std::string &stats_prefix,
248
+ Stats::Scope &scope,
249
+ const envoy::config::filter::http::aws_lambda::v2::AWSLambdaConfig
250
+ &protoconfig) {
251
+ // We can't use make_shared here because the constructor of this class is
252
+ // private.
253
+ std::shared_ptr<AWSLambdaConfigImpl> ptr (new AWSLambdaConfigImpl (
254
+ std::move (provider), std::move (sts_factory), dispatcher, api, tls,
255
+ stats_prefix, scope, protoconfig));
256
+ ptr->init ();
257
+ return ptr;
258
+ }
259
+
167
260
AwsLambdaFilterStats
168
261
AWSLambdaConfigImpl::generateStats (const std::string &prefix,
169
262
Stats::Scope &scope) {
0 commit comments