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
285
#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
         bool warm);
63

            
64
  const SecretData& secretData() const;
65

            
66
protected:
67
  // Ordered for hash stability.
68
  using FileContentMap = std::map<std::string, std::string>;
69

            
70
  // Creates new secrets.
71
  virtual void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret&) PURE;
72
  // Refresh secrets, e.g. re-resolve symlinks in secret paths.
73
115
  virtual void resolveSecret(const FileContentMap& /*files*/) {};
74
  virtual void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret&) PURE;
75
  Common::CallbackManager<absl::Status> update_callback_manager_;
76
  Common::CallbackManager<absl::Status> remove_callback_manager_;
77

            
78
  // Config::SubscriptionCallbacks
79
  absl::Status onConfigUpdate(const std::vector<Config::DecodedResourceRef>& resources,
80
                              const std::string& version_info) override;
81
  absl::Status onConfigUpdate(const std::vector<Config::DecodedResourceRef>& added_resources,
82
                              const Protobuf::RepeatedPtrField<std::string>& removed_resources,
83
                              const std::string& system_version_info) override;
84
  void onConfigUpdateFailed(Envoy::Config::ConfigUpdateFailureReason reason,
85
                            const EnvoyException* e) override;
86
  virtual std::vector<std::string> getDataSourceFilenames() PURE;
87
  virtual Config::WatchedDirectory* getWatchedDirectory() PURE;
88

            
89
  void resolveDataSource(const FileContentMap& files,
90
                         envoy::config::core::v3::DataSource& data_source);
91

            
92
  Init::SharedTargetImpl init_target_;
93
  Event::Dispatcher& dispatcher_;
94
  Api::Api& api_;
95

            
96
  // Invoked for filesystem watches on update. Protected so subclasses can set up the callback.
97
  void onWatchUpdate();
98

            
99
  // Initializes the SDS API.
100
  void initialize(bool warm);
101

            
102
private:
103
  absl::Status validateUpdateSize(uint32_t added_resources_num,
104
                                  uint32_t removed_resources_num) const;
105
  FileContentMap loadFiles();
106
  uint64_t getHashForFiles(const FileContentMap& files);
107
  SdsApiStats generateStats(Stats::Scope& scope);
108

            
109
  Stats::ScopeSharedPtr scope_;
110
  SdsApiStats sds_api_stats_;
111

            
112
  const envoy::config::core::v3::ConfigSource sds_config_;
113
  Config::SubscriptionPtr subscription_;
114
  const std::string sds_config_name_;
115

            
116
  uint64_t secret_hash_{0};
117
  uint64_t files_hash_{0};
118
  Cleanup clean_up_;
119
  Config::SubscriptionFactory& subscription_factory_;
120
  TimeSource& time_source_;
121
  SecretData secret_data_;
122
  std::unique_ptr<Filesystem::Watcher> watcher_;
123
};
124

            
125
class TlsCertificateSdsApi;
126
class CertificateValidationContextSdsApi;
127
class TlsSessionTicketKeysSdsApi;
128
class GenericSecretSdsApi;
129
using TlsCertificateSdsApiSharedPtr = std::shared_ptr<TlsCertificateSdsApi>;
130
using CertificateValidationContextSdsApiSharedPtr =
131
    std::shared_ptr<CertificateValidationContextSdsApi>;
132
using TlsSessionTicketKeysSdsApiSharedPtr = std::shared_ptr<TlsSessionTicketKeysSdsApi>;
133
using GenericSecretSdsApiSharedPtr = std::shared_ptr<GenericSecretSdsApi>;
134

            
135
/**
136
 * Shared implementation of the subscription callbacks from SecretProvider.
137
 */
138
template <typename SecretType>
139
class DynamicSecretProvider : public SdsApi, public SecretProvider<SecretType> {
140
public:
141
  DynamicSecretProvider(const envoy::config::core::v3::ConfigSource& sds_config,
142
                        const std::string& sds_config_name,
143
                        Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
144
                        ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
145
                        std::function<void()> destructor_cb, Event::Dispatcher& dispatcher,
146
                        Api::Api& api, bool warm)
147
284
      : SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
148
284
               stats, std::move(destructor_cb), dispatcher, api, warm) {}
149

            
150
  virtual const SecretType* secret() const override PURE;
151

            
152
  ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
153
18
  addValidationCallback(std::function<absl::Status(const SecretType&)> callback) override {
154
18
    return validation_callback_manager_.add(callback);
155
18
  }
156

            
157
  ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
158
184
  addUpdateCallback(std::function<absl::Status()> callback) override {
159
184
    if (secret()) {
160
8
      THROW_IF_NOT_OK(callback());
161
8
    }
162
184
    return update_callback_manager_.add(callback);
163
184
  }
164
  ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
165
29
  addRemoveCallback(std::function<absl::Status()> callback) override {
166
29
    return remove_callback_manager_.add(callback);
167
29
  }
168

            
169
272
  const Init::Target* initTarget() override { return &init_target_; }
170
25
  void start() override { initialize(false); }
171

            
172
protected:
173
  Common::CallbackManager<absl::Status, const SecretType&> validation_callback_manager_;
174
};
175

            
176
/**
177
 * TlsCertificateSdsApi implementation maintains and updates dynamic TLS certificate secrets.
178
 */
179
class TlsCertificateSdsApi
180
    : public DynamicSecretProvider<envoy::extensions::transport_sockets::tls::v3::TlsCertificate> {
181
public:
182
  static TlsCertificateSdsApiSharedPtr
183
  create(Server::Configuration::ServerFactoryContext& server_context,
184
         const envoy::config::core::v3::ConfigSource& sds_config,
185
         const std::string& sds_config_name, std::function<void()> destructor_cb, bool warm);
186

            
187
  TlsCertificateSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
188
                       const std::string& sds_config_name,
189
                       Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
190
                       ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
191
                       std::function<void()> destructor_cb, Event::Dispatcher& dispatcher,
192
                       Api::Api& api, bool warm)
193
139
      : DynamicSecretProvider(sds_config, sds_config_name, subscription_factory, time_source,
194
139
                              validation_visitor, stats, std::move(destructor_cb), dispatcher, api,
195
139
                              warm) {}
196

            
197
  // SecretProvider
198
404
  const envoy::extensions::transport_sockets::tls::v3::TlsCertificate* secret() const override {
199
404
    return resolved_tls_certificate_secrets_.get();
200
404
  }
201
  ABSL_MUST_USE_RESULT Common::CallbackHandlePtr addValidationCallback(
202
      std::function<absl::Status(
203
          const envoy::extensions::transport_sockets::tls::v3::TlsCertificate&)>) override {
204
    // This is unnecessary but there is no callers to this function.
205
    return nullptr;
206
  }
207

            
208
protected:
209
  void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override;
210
  void resolveSecret(const FileContentMap& files) override;
211
127
  void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret&) override {}
212
  std::vector<std::string> getDataSourceFilenames() override;
213
127
  Config::WatchedDirectory* getWatchedDirectory() override { return watched_directory_.get(); }
214

            
215
private:
216
  // Path to watch for rotation.
217
  Config::WatchedDirectoryPtr watched_directory_;
218
  // TlsCertificate according to SDS source.
219
  TlsCertificatePtr sds_tls_certificate_secrets_;
220
  // TlsCertificate after reloading. Path based certificates are inlined for
221
  // future read consistency.
222
  TlsCertificatePtr resolved_tls_certificate_secrets_;
223
};
224

            
225
/**
226
 * CertificateValidationContextSdsApi implementation maintains and updates dynamic certificate
227
 * validation context secrets.
228
 */
229
class CertificateValidationContextSdsApi
230
    : public DynamicSecretProvider<
231
          envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext> {
232
public:
233
  static CertificateValidationContextSdsApiSharedPtr
234
  create(Server::Configuration::ServerFactoryContext& server_context,
235
         const envoy::config::core::v3::ConfigSource& sds_config,
236
         const std::string& sds_config_name, std::function<void()> destructor_cb, bool warm);
237
  CertificateValidationContextSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
238
                                     const std::string& sds_config_name,
239
                                     Config::SubscriptionFactory& subscription_factory,
240
                                     TimeSource& time_source,
241
                                     ProtobufMessage::ValidationVisitor& validation_visitor,
242
                                     Stats::Store& stats, std::function<void()> destructor_cb,
243
                                     Event::Dispatcher& dispatcher, Api::Api& api, bool warm)
244
43
      : DynamicSecretProvider(sds_config, sds_config_name, subscription_factory, time_source,
245
43
                              validation_visitor, stats, std::move(destructor_cb), dispatcher, api,
246
43
                              warm) {}
247

            
248
  // SecretProvider
249
  const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext*
250
177
  secret() const override {
251
177
    return resolved_certificate_validation_context_secrets_.get();
252
177
  }
253

            
254
protected:
255
  void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override;
256
  void resolveSecret(const FileContentMap& files) override;
257

            
258
  void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override;
259
  std::vector<std::string> getDataSourceFilenames() override;
260
43
  Config::WatchedDirectory* getWatchedDirectory() override { return watched_directory_.get(); }
261

            
262
private:
263
  // Directory to watch for rotation.
264
  Config::WatchedDirectoryPtr watched_directory_;
265
  // CertificateValidationContext according to SDS source;
266
  CertificateValidationContextPtr sds_certificate_validation_context_secrets_;
267
  // CertificateValidationContext after resolving paths via watched_directory_.
268
  CertificateValidationContextPtr resolved_certificate_validation_context_secrets_;
269
  // Path based certificates are inlined for future read consistency.
270
};
271

            
272
/**
273
 * TlsSessionTicketKeysSdsApi implementation maintains and updates dynamic tls session ticket keys
274
 * secrets.
275
 */
276
class TlsSessionTicketKeysSdsApi
277
    : public DynamicSecretProvider<
278
          envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys> {
279
public:
280
  static TlsSessionTicketKeysSdsApiSharedPtr
281
  create(Server::Configuration::ServerFactoryContext& server_context,
282
         const envoy::config::core::v3::ConfigSource& sds_config,
283
         const std::string& sds_config_name, std::function<void()> destructor_cb, bool warm);
284

            
285
  TlsSessionTicketKeysSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
286
                             const std::string& sds_config_name,
287
                             Config::SubscriptionFactory& subscription_factory,
288
                             TimeSource& time_source,
289
                             ProtobufMessage::ValidationVisitor& validation_visitor,
290
                             Stats::Store& stats, std::function<void()> destructor_cb,
291
                             Event::Dispatcher& dispatcher, Api::Api& api, bool warm)
292
3
      : DynamicSecretProvider(sds_config, sds_config_name, subscription_factory, time_source,
293
3
                              validation_visitor, stats, std::move(destructor_cb), dispatcher, api,
294
3
                              warm) {}
295

            
296
  // SecretProvider
297
  const envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys*
298
12
  secret() const override {
299
12
    return tls_session_ticket_keys_.get();
300
12
  }
301

            
302
protected:
303
1
  void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
304
1
    tls_session_ticket_keys_ =
305
1
        std::make_unique<envoy::extensions::transport_sockets::tls::v3::TlsSessionTicketKeys>(
306
1
            secret.session_ticket_keys());
307
1
  }
308

            
309
  void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override;
310
  std::vector<std::string> getDataSourceFilenames() override;
311
1
  Config::WatchedDirectory* getWatchedDirectory() override { return nullptr; }
312

            
313
private:
314
  Secret::TlsSessionTicketKeysPtr tls_session_ticket_keys_;
315
};
316

            
317
/**
318
 * GenericSecretSdsApi implementation maintains and updates dynamic generic secret.
319
 */
320
class GenericSecretSdsApi
321
    : public DynamicSecretProvider<envoy::extensions::transport_sockets::tls::v3::GenericSecret> {
322
public:
323
  static GenericSecretSdsApiSharedPtr
324
  create(Server::Configuration::ServerFactoryContext& server_context,
325
         const envoy::config::core::v3::ConfigSource& sds_config,
326
         const std::string& sds_config_name, std::function<void()> destructor_cb, bool warm);
327

            
328
  GenericSecretSdsApi(const envoy::config::core::v3::ConfigSource& sds_config,
329
                      const std::string& sds_config_name,
330
                      Config::SubscriptionFactory& subscription_factory, TimeSource& time_source,
331
                      ProtobufMessage::ValidationVisitor& validation_visitor, Stats::Store& stats,
332
                      std::function<void()> destructor_cb, Event::Dispatcher& dispatcher,
333
                      Api::Api& api, bool warm)
334
99
      : DynamicSecretProvider(sds_config, sds_config_name, subscription_factory, time_source,
335
99
                              validation_visitor, stats, std::move(destructor_cb), dispatcher, api,
336
99
                              warm) {}
337

            
338
  // SecretProvider
339
221
  const envoy::extensions::transport_sockets::tls::v3::GenericSecret* secret() const override {
340
221
    return generic_secret_.get();
341
221
  }
342
  ABSL_MUST_USE_RESULT Common::CallbackHandlePtr
343
90
  addUpdateCallback(std::function<absl::Status()> callback) override {
344
    // This is unlike the other implementations - no immediate callback.
345
90
    return update_callback_manager_.add(callback);
346
90
  }
347

            
348
protected:
349
114
  void setSecret(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override {
350
114
    generic_secret_ =
351
114
        std::make_unique<envoy::extensions::transport_sockets::tls::v3::GenericSecret>(
352
114
            secret.generic_secret());
353
114
  }
354
  void validateConfig(const envoy::extensions::transport_sockets::tls::v3::Secret& secret) override;
355
  std::vector<std::string> getDataSourceFilenames() override;
356
114
  Config::WatchedDirectory* getWatchedDirectory() override { return nullptr; }
357

            
358
private:
359
  GenericSecretPtr generic_secret_;
360
};
361

            
362
} // namespace Secret
363
} // namespace Envoy