Line data Source code
1 : #pragma once
2 :
3 : #include <functional>
4 :
5 : #include "envoy/api/api.h"
6 : #include "envoy/config/core/v3/config_source.pb.h"
7 : #include "envoy/config/subscription.h"
8 : #include "envoy/config/subscription_factory.h"
9 : #include "envoy/event/dispatcher.h"
10 : #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h"
11 : #include "envoy/extensions/transport_sockets/tls/v3/secret.pb.validate.h"
12 : #include "envoy/init/manager.h"
13 : #include "envoy/local_info/local_info.h"
14 : #include "envoy/runtime/runtime.h"
15 : #include "envoy/secret/secret_callbacks.h"
16 : #include "envoy/secret/secret_provider.h"
17 : #include "envoy/server/transport_socket_config.h"
18 : #include "envoy/service/discovery/v3/discovery.pb.h"
19 : #include "envoy/stats/stats.h"
20 : #include "envoy/upstream/cluster_manager.h"
21 :
22 : #include "source/common/common/callback_impl.h"
23 : #include "source/common/common/cleanup.h"
24 : #include "source/common/config/subscription_base.h"
25 : #include "source/common/config/utility.h"
26 : #include "source/common/config/watched_directory.h"
27 : #include "source/common/init/target_impl.h"
28 : #include "source/common/ssl/certificate_validation_context_config_impl.h"
29 : #include "source/common/ssl/tls_certificate_config_impl.h"
30 :
31 : namespace Envoy {
32 : namespace Secret {
33 :
34 : /**
35 : * All SDS API. @see stats_macros.h
36 : */
37 0 : #define ALL_SDS_API_STATS(COUNTER) COUNTER(key_rotation_failed)
38 :
39 : /**
40 : * Struct definition for all SDS API stats. @see stats_macros.h
41 : */
42 : struct SdsApiStats {
43 : ALL_SDS_API_STATS(GENERATE_COUNTER_STRUCT)
44 : };
45 :
46 : /**
47 : * SDS API implementation that fetches secrets from SDS server via Subscription.
48 : */
49 : class SdsApi : public Envoy::Config::SubscriptionBase<
50 : envoy::extensions::transport_sockets::tls::v3::Secret> {
51 : public:
52 : struct SecretData {
53 : const std::string resource_name_;
54 : std::string version_info_;
55 : SystemTime last_updated_;
56 : };
57 :
58 : SdsApi(envoy::config::core::v3::ConfigSource sds_config, absl::string_view sds_config_name,
59 : Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
60 : ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
61 : std::function<void()> destructor_cb, Event::Dispatcher& dispatcher, Api::Api& api);
62 :
63 : SecretData secretData();
64 :
65 : protected:
66 : // Ordered for hash stability.
67 : using FileContentMap = std::map<std::string, std::string>;
68 :
69 : // Creates new secrets.
70 : virtual void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret&) PURE;
71 : // Refresh secrets, e.g. re-resolve symlinks in secret paths.
72 0 : virtual void resolveSecret(const FileContentMap& /*files*/){};
73 : virtual void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret&) PURE;
74 : Common::CallbackManager<> update_callback_manager_;
75 :
76 : // Config::SubscriptionCallbacks
77 : absl::Status onConfigUpdate(const std::vector<Config::DecodedResourceRef>& resources,
78 : const std::string& version_info) override;
79 : absl::Status onConfigUpdate(const std::vector<Config::DecodedResourceRef>& added_resources,
80 : const Protobuf::RepeatedPtrField<std::string>& removed_resources,
81 : const std::string& system_version_info) override;
82 : void onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason reason,
83 : const EnvoyException* e) override;
84 : virtual std::vector<std::string> getDataSourceFilenames() PURE;
85 : virtual Config::WatchedDirectory* getWatchedDirectory() PURE;
86 :
87 : void resolveDataSource(const FileContentMap& files,
88 : envoy::config::core::v3::DataSource& data_source);
89 :
90 : Init::SharedTargetImpl init_target_;
91 : Event::Dispatcher& dispatcher_;
92 : Api::Api& api_;
93 :
94 : private:
95 : absl::Status validateUpdateSize(int num_resources);
96 : void initialize();
97 : FileContentMap loadFiles();
98 : uint64_t getHashForFiles(const FileContentMap& files);
99 : // Invoked for filesystem watches on update.
100 : void onWatchUpdate();
101 : SdsApiStats generateStats(Stats::Scope& scope);
102 :
103 : Stats::ScopeSharedPtr scope_;
104 : SdsApiStats sds_api_stats_;
105 :
106 : const envoy::config::core::v3::ConfigSource sds_config_;
107 : Config::SubscriptionPtr subscription_;
108 : const std::string sds_config_name_;
109 :
110 : uint64_t secret_hash_{0};
111 : uint64_t files_hash_;
112 : Cleanup clean_up_;
113 : Config::SubscriptionFactory& subscription_factory_;
114 : TimeSource& time_source_;
115 : SecretData secret_data_;
116 : std::unique_ptr<Filesystem::Watcher> watcher_;
117 : };
118 :
119 : class TlsCertificateSdsApi;
120 : class CertificateValidationContextSdsApi;
121 : class TlsSessionTicketKeysSdsApi;
122 : class GenericSecretSdsApi;
123 : using TlsCertificateSdsApiSharedPtr = std::shared_ptr<TlsCertificateSdsApi>;
124 : using CertificateValidationContextSdsApiSharedPtr =
125 : std::shared_ptr<CertificateValidationContextSdsApi>;
126 : using TlsSessionTicketKeysSdsApiSharedPtr = std::shared_ptr<TlsSessionTicketKeysSdsApi>;
127 : using GenericSecretSdsApiSharedPtr = std::shared_ptr<GenericSecretSdsApi>;
128 :
129 : /**
130 : * TlsCertificateSdsApi implementation maintains and updates dynamic TLS certificate secrets.
131 : */
132 : class TlsCertificateSdsApi : public SdsApi, public TlsCertificateConfigProvider {
133 : public:
134 : static TlsCertificateSdsApiSharedPtr
135 : create(Server::Configuration::TransportSocketFactoryContext& secret_provider_context,
136 : const envoy::config::core::v3::ConfigSource& sds_config,
137 0 : const std::string& sds_config_name, std::function<void()> destructor_cb) {
138 0 : // We need to do this early as we invoke the subscription factory during initialization, which
139 0 : // is too late to throw.
140 0 : auto& server_context = secret_provider_context.serverFactoryContext();
141 0 : Config::Utility::checkLocalInfo("TlsCertificateSdsApi", server_context.localInfo());
142 0 : return std::make_shared<TlsCertificateSdsApi>(
143 0 : sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(),
144 0 : server_context.mainThreadDispatcher().timeSource(),
145 0 : secret_provider_context.messageValidationVisitor(), server_context.serverScope().store(),
146 0 : destructor_cb, server_context.mainThreadDispatcher(), server_context.api());
147 0 : }
148 :
149 : TlsCertificateSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
150 : const std::string& sds_config_name,
151 : Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
152 : ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
153 : std::function<void()> destructor_cb, Event::Dispatcher& dispatcher,
154 : Api::Api& api)
155 : : SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
156 0 : stats, std::move(destructor_cb), dispatcher, api) {}
157 :
158 : // SecretProvider
159 0 : const envoy::extensions::transport_sockets::tls::v3::TlsCertificate* secret() const override {
160 0 : return resolved_tls_certificate_secrets_.get();
161 0 : }
162 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addValidationCallback(
163 : std::function<void(const envoy::extensions::transport_sockets::tls::v3::TlsCertificate&)>)
164 0 : override {
165 0 : return nullptr;
166 0 : }
167 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
168 0 : addUpdateCallback(std::function<void()> callback) override {
169 0 : if (secret()) {
170 0 : callback();
171 0 : }
172 0 : return update_callback_manager_.add(callback);
173 0 : }
174 0 : const Init::Target* initTarget() override { return &init_target_; }
175 :
176 : protected:
177 0 : void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
178 0 : sds_tls_certificate_secrets_ =
179 0 : std::make_unique<envoy::extensions::transport_sockets::tls::v3::TlsCertificate>(
180 0 : secret.tls_certificate());
181 0 : resolved_tls_certificate_secrets_ = nullptr;
182 0 : if (secret.tls_certificate().has_watched_directory()) {
183 0 : watched_directory_ = std::make_unique<Config::WatchedDirectory>(
184 0 : secret.tls_certificate().watched_directory(), dispatcher_);
185 0 : } else {
186 0 : watched_directory_.reset();
187 0 : }
188 0 : }
189 0 : void resolveSecret(const FileContentMap& files) override {
190 0 : resolved_tls_certificate_secrets_ =
191 0 : std::make_unique<envoy::extensions::transport_sockets::tls::v3::TlsCertificate>(
192 0 : *sds_tls_certificate_secrets_);
193 : // We replace path based secrets with inlined secrets on update.
194 0 : resolveDataSource(files, *resolved_tls_certificate_secrets_->mutable_certificate_chain());
195 0 : if (sds_tls_certificate_secrets_->has_private_key()) {
196 0 : resolveDataSource(files, *resolved_tls_certificate_secrets_->mutable_private_key());
197 0 : }
198 0 : }
199 0 : void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret&) override {}
200 : std::vector<std::string> getDataSourceFilenames() override;
201 0 : Config::WatchedDirectory* getWatchedDirectory() override { return watched_directory_.get(); }
202 :
203 : private:
204 : // Path to watch for rotation.
205 : Config::WatchedDirectoryPtr watched_directory_;
206 : // TlsCertificate according to SDS source.
207 : TlsCertificatePtr sds_tls_certificate_secrets_;
208 : // TlsCertificate after reloading. Path based certificates are inlined for
209 : // future read consistency.
210 : TlsCertificatePtr resolved_tls_certificate_secrets_;
211 : };
212 :
213 : /**
214 : * CertificateValidationContextSdsApi implementation maintains and updates dynamic certificate
215 : * validation context secrets.
216 : */
217 : class CertificateValidationContextSdsApi : public SdsApi,
218 : public CertificateValidationContextConfigProvider {
219 : public:
220 : static CertificateValidationContextSdsApiSharedPtr
221 : create(Server::Configuration::TransportSocketFactoryContext& secret_provider_context,
222 : const envoy::config::core::v3::ConfigSource& sds_config,
223 0 : const std::string& sds_config_name, std::function<void()> destructor_cb) {
224 0 : // We need to do this early as we invoke the subscription factory during initialization, which
225 0 : // is too late to throw.
226 0 : auto& server_context = secret_provider_context.serverFactoryContext();
227 0 : Config::Utility::checkLocalInfo("CertificateValidationContextSdsApi",
228 0 : server_context.localInfo());
229 0 : return std::make_shared<CertificateValidationContextSdsApi>(
230 0 : sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(),
231 0 : server_context.mainThreadDispatcher().timeSource(),
232 0 : secret_provider_context.messageValidationVisitor(), server_context.serverScope().store(),
233 0 : destructor_cb, server_context.mainThreadDispatcher(), server_context.api());
234 0 : }
235 : CertificateValidationContextSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
236 : const std::string& sds_config_name,
237 : Config::SubscriptionFactory& subscription_factory,
238 : TimeSource& time_source,
239 : ProtobufMessage::ValidationVisitor& validation_visitor,
240 : Stats::Store& stats, std::function<void()> destructor_cb,
241 : Event::Dispatcher& dispatcher, Api::Api& api)
242 : : SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
243 0 : stats, std::move(destructor_cb), dispatcher, api) {}
244 :
245 : // SecretProvider
246 : const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext*
247 0 : secret() const override {
248 0 : return resolved_certificate_validation_context_secrets_.get();
249 0 : }
250 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
251 0 : addUpdateCallback(std::function<void()> callback) override {
252 0 : if (secret()) {
253 0 : callback();
254 0 : }
255 0 : return update_callback_manager_.add(callback);
256 0 : }
257 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addValidationCallback(
258 : std::function<
259 : void(const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext&)>
260 0 : callback) override {
261 0 : return validation_callback_manager_.add(callback);
262 0 : }
263 0 : const Init::Target* initTarget() override { return &init_target_; }
264 :
265 : protected:
266 0 : void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
267 0 : sds_certificate_validation_context_secrets_ = std::make_unique<
268 0 : envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext>(
269 0 : secret.validation_context());
270 0 : resolved_certificate_validation_context_secrets_ = nullptr;
271 0 : if (secret.validation_context().has_watched_directory()) {
272 0 : watched_directory_ = std::make_unique<Config::WatchedDirectory>(
273 0 : secret.validation_context().watched_directory(), dispatcher_);
274 0 : } else {
275 0 : watched_directory_.reset();
276 0 : }
277 0 : }
278 :
279 0 : void resolveSecret(const FileContentMap& files) override {
280 : // Copy existing CertificateValidationContext.
281 0 : resolved_certificate_validation_context_secrets_ = std::make_unique<
282 0 : envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext>(
283 0 : *sds_certificate_validation_context_secrets_);
284 : // We replace path based secrets with inlined secrets on update.
285 0 : resolveDataSource(files,
286 0 : *resolved_certificate_validation_context_secrets_->mutable_trusted_ca());
287 0 : if (sds_certificate_validation_context_secrets_->has_crl()) {
288 0 : resolveDataSource(files, *resolved_certificate_validation_context_secrets_->mutable_crl());
289 0 : }
290 0 : }
291 :
292 : void
293 0 : validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
294 0 : validation_callback_manager_.runCallbacks(secret.validation_context());
295 0 : }
296 : std::vector<std::string> getDataSourceFilenames() override;
297 0 : Config::WatchedDirectory* getWatchedDirectory() override { return watched_directory_.get(); }
298 :
299 : private:
300 : // Directory to watch for rotation.
301 : Config::WatchedDirectoryPtr watched_directory_;
302 : // CertificateValidationContext according to SDS source;
303 : CertificateValidationContextPtr sds_certificate_validation_context_secrets_;
304 : // CertificateValidationContext after resolving paths via watched_directory_.
305 : CertificateValidationContextPtr resolved_certificate_validation_context_secrets_;
306 : // Path based certificates are inlined for future read consistency.
307 : Common::CallbackManager<
308 : const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext&>
309 : validation_callback_manager_;
310 : };
311 :
312 : /**
313 : * TlsSessionTicketKeysSdsApi implementation maintains and updates dynamic tls session ticket keys
314 : * secrets.
315 : */
316 : class TlsSessionTicketKeysSdsApi : public SdsApi, public TlsSessionTicketKeysConfigProvider {
317 : public:
318 : static TlsSessionTicketKeysSdsApiSharedPtr
319 : create(Server::Configuration::TransportSocketFactoryContext& secret_provider_context,
320 : const envoy::config::core::v3::ConfigSource& sds_config,
321 0 : const std::string& sds_config_name, std::function<void()> destructor_cb) {
322 0 : // We need to do this early as we invoke the subscription factory during initialization, which
323 0 : // is too late to throw.
324 0 : auto& server_context = secret_provider_context.serverFactoryContext();
325 0 : Config::Utility::checkLocalInfo("TlsSessionTicketKeysSdsApi", server_context.localInfo());
326 0 : return std::make_shared<TlsSessionTicketKeysSdsApi>(
327 0 : sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(),
328 0 : server_context.mainThreadDispatcher().timeSource(),
329 0 : secret_provider_context.messageValidationVisitor(), server_context.serverScope().store(),
330 0 : destructor_cb, server_context.mainThreadDispatcher(), server_context.api());
331 0 : }
332 :
333 : TlsSessionTicketKeysSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
334 : const std::string& sds_config_name,
335 : Config::SubscriptionFactory& subscription_factory,
336 : TimeSource& time_source,
337 : ProtobufMessage::ValidationVisitor& validation_visitor,
338 : Stats::Store& stats, std::function<void()> destructor_cb,
339 : Event::Dispatcher& dispatcher, Api::Api& api)
340 : : SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
341 0 : stats, std::move(destructor_cb), dispatcher, api) {}
342 :
343 : // SecretProvider
344 : const envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys*
345 0 : secret() const override {
346 0 : return tls_session_ticket_keys_.get();
347 0 : }
348 :
349 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
350 0 : addUpdateCallback(std::function<void()> callback) override {
351 0 : if (secret()) {
352 0 : callback();
353 0 : }
354 0 : return update_callback_manager_.add(callback);
355 0 : }
356 :
357 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addValidationCallback(
358 : std::function<
359 : void(const envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys&)>
360 0 : callback) override {
361 0 : return validation_callback_manager_.add(callback);
362 0 : }
363 0 : const Init::Target* initTarget() override { return &init_target_; }
364 :
365 : protected:
366 0 : void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
367 0 : tls_session_ticket_keys_ =
368 0 : std::make_unique<envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys>(
369 0 : secret.session_ticket_keys());
370 0 : }
371 :
372 : void
373 0 : validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
374 0 : validation_callback_manager_.runCallbacks(secret.session_ticket_keys());
375 0 : }
376 : std::vector<std::string> getDataSourceFilenames() override;
377 0 : Config::WatchedDirectory* getWatchedDirectory() override { return nullptr; }
378 :
379 : private:
380 : Secret::TlsSessionTicketKeysPtr tls_session_ticket_keys_;
381 : Common::CallbackManager<
382 : const envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys&>
383 : validation_callback_manager_;
384 : };
385 :
386 : /**
387 : * GenericSecretSdsApi implementation maintains and updates dynamic generic secret.
388 : */
389 : class GenericSecretSdsApi : public SdsApi, public GenericSecretConfigProvider {
390 : public:
391 : static GenericSecretSdsApiSharedPtr
392 : create(Server::Configuration::TransportSocketFactoryContext& secret_provider_context,
393 : const envoy::config::core::v3::ConfigSource& sds_config,
394 0 : const std::string& sds_config_name, std::function<void()> destructor_cb) {
395 0 : // We need to do this early as we invoke the subscription factory during initialization, which
396 0 : // is too late to throw.
397 0 : auto& server_context = secret_provider_context.serverFactoryContext();
398 0 : Config::Utility::checkLocalInfo("GenericSecretSdsApi", server_context.localInfo());
399 0 : return std::make_shared<GenericSecretSdsApi>(
400 0 : sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(),
401 0 : server_context.mainThreadDispatcher().timeSource(),
402 0 : secret_provider_context.messageValidationVisitor(), server_context.serverScope().store(),
403 0 : destructor_cb, server_context.mainThreadDispatcher(), server_context.api());
404 0 : }
405 :
406 : GenericSecretSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
407 : const std::string& sds_config_name,
408 : Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
409 : ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
410 : std::function<void()> destructor_cb, Event::Dispatcher& dispatcher,
411 : Api::Api& api)
412 : : SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
413 0 : stats, std::move(destructor_cb), dispatcher, api) {}
414 :
415 : // SecretProvider
416 0 : const envoy::extensions::transport_sockets::tls::v3::GenericSecret* secret() const override {
417 0 : return generic_secret_.get();
418 0 : }
419 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
420 0 : addUpdateCallback(std::function<void()> callback) override {
421 0 : return update_callback_manager_.add(callback);
422 0 : }
423 : ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addValidationCallback(
424 : std::function<void(const envoy::extensions::transport_sockets::tls::v3::GenericSecret&)>
425 0 : callback) override {
426 0 : return validation_callback_manager_.add(callback);
427 0 : }
428 0 : const Init::Target* initTarget() override { return &init_target_; }
429 :
430 : protected:
431 0 : void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
432 0 : generic_secret_ =
433 0 : std::make_unique<envoy::extensions::transport_sockets::tls::v3::GenericSecret>(
434 0 : secret.generic_secret());
435 0 : }
436 : void
437 0 : validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
438 0 : validation_callback_manager_.runCallbacks(secret.generic_secret());
439 0 : }
440 : std::vector<std::string> getDataSourceFilenames() override;
441 0 : Config::WatchedDirectory* getWatchedDirectory() override { return nullptr; }
442 :
443 : private:
444 : GenericSecretPtr generic_secret_;
445 : Common::CallbackManager<const envoy::extensions::transport_sockets::tls::v3::GenericSecret&>
446 : validation_callback_manager_;
447 : };
448 :
449 : } // namespace Secret
450 : } // namespace Envoy
|