Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/secret/sds_api.h
Line
Count
Source (jump to first uncovered line)
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