1
#include "source/extensions/formatter/generic_secret/config.h"
2

            
3
#include "envoy/extensions/formatter/generic_secret/v3/generic_secret.pb.h"
4
#include "envoy/extensions/formatter/generic_secret/v3/generic_secret.pb.validate.h"
5
#include "envoy/registry/registry.h"
6
#include "envoy/secret/secret_manager.h"
7
#include "envoy/secret/secret_provider.h"
8

            
9
#include "source/common/common/logger.h"
10
#include "source/common/formatter/substitution_format_utility.h"
11
#include "source/common/secret/secret_provider_impl.h"
12

            
13
#include "absl/container/flat_hash_map.h"
14
#include "absl/strings/string_view.h"
15

            
16
namespace Envoy {
17
namespace Extensions {
18
namespace Formatter {
19

            
20
namespace {
21

            
22
constexpr absl::string_view SecretCommand = "SECRET";
23

            
24
/**
25
 * FormatterProvider that returns the current value of a named generic secret.
26
 */
27
class GenericSecretFormatterProvider : public Envoy::Formatter::FormatterProvider {
28
public:
29
  GenericSecretFormatterProvider(
30
      std::shared_ptr<Secret::ThreadLocalGenericSecretProvider> secret_provider,
31
      absl::optional<size_t> max_length)
32
7
      : secret_provider_(std::move(secret_provider)), max_length_(max_length) {}
33

            
34
  absl::optional<std::string> format(const Envoy::Formatter::Context&,
35
7
                                     const StreamInfo::StreamInfo&) const override {
36
7
    const std::string& value = secret_provider_->secret();
37
7
    if (value.empty()) {
38
      return absl::nullopt;
39
    }
40
7
    std::string result = value;
41
7
    Envoy::Formatter::SubstitutionFormatUtils::truncate(result, max_length_);
42
7
    return result;
43
7
  }
44

            
45
  Protobuf::Value formatValue(const Envoy::Formatter::Context& context,
46
1
                              const StreamInfo::StreamInfo& stream_info) const override {
47
1
    Protobuf::Value val;
48
1
    const auto opt = format(context, stream_info);
49
1
    if (opt.has_value()) {
50
1
      val.set_string_value(*opt);
51
1
    }
52
1
    return val;
53
1
  }
54

            
55
private:
56
  std::shared_ptr<Secret::ThreadLocalGenericSecretProvider> secret_provider_;
57
  const absl::optional<size_t> max_length_;
58
};
59

            
60
/**
61
 * CommandParser that handles the %SECRET(name)% command.
62
 * Looks up the named secret from the map built at construction time.
63
 */
64
class GenericSecretCommandParser : public Envoy::Formatter::CommandParser {
65
public:
66
  using ProviderMap =
67
      absl::flat_hash_map<std::string, std::shared_ptr<Secret::ThreadLocalGenericSecretProvider>>;
68

            
69
7
  explicit GenericSecretCommandParser(ProviderMap providers) : providers_(std::move(providers)) {}
70

            
71
  Envoy::Formatter::FormatterProviderPtr parse(absl::string_view command,
72
                                               absl::string_view subcommand,
73
9
                                               absl::optional<size_t> max_length) const override {
74
9
    if (command != SecretCommand) {
75
1
      return nullptr;
76
1
    }
77
8
    const auto it = providers_.find(subcommand);
78
8
    if (it == providers_.end()) {
79
1
      throw EnvoyException(fmt::format(
80
1
          "envoy.formatter.generic_secret: secret '{}' is not configured in secret_configs",
81
1
          subcommand));
82
1
    }
83
7
    return std::make_unique<GenericSecretFormatterProvider>(it->second, max_length);
84
8
  }
85

            
86
private:
87
  ProviderMap providers_;
88
};
89

            
90
} // namespace
91

            
92
Envoy::Formatter::CommandParserPtr GenericSecretFormatterFactory::createCommandParserFromProto(
93
8
    const Protobuf::Message& config, Server::Configuration::GenericFactoryContext& context) {
94
8
  const auto& typed_config = MessageUtil::downcastAndValidate<
95
8
      const envoy::extensions::formatter::generic_secret::v3::GenericSecret&>(
96
8
      config, context.messageValidationVisitor());
97

            
98
  // This formatter creates thread locals which can only happen on the main thread.
99
8
  ASSERT_IS_MAIN_OR_TEST_THREAD();
100

            
101
8
  auto& server_context = context.serverFactoryContext();
102

            
103
8
  GenericSecretCommandParser::ProviderMap providers;
104
9
  for (const auto& entry : typed_config.secret_configs()) {
105
9
    const std::string& name = entry.first;
106
9
    const auto& secret_config = entry.second;
107
9
    Secret::GenericSecretConfigProviderSharedPtr provider;
108
9
    if (secret_config.has_sds_config()) {
109
1
      provider = server_context.secretManager().findOrCreateGenericSecretProvider(
110
1
          secret_config.sds_config(), secret_config.name(), server_context, context.initManager());
111
8
    } else {
112
8
      provider =
113
8
          server_context.secretManager().findStaticGenericSecretProvider(secret_config.name());
114
8
      if (provider == nullptr) {
115
1
        throw EnvoyException(
116
1
            fmt::format("envoy.formatter.generic_secret: secret '{}' not found in static "
117
1
                        "bootstrap resources",
118
1
                        secret_config.name()));
119
1
      }
120
8
    }
121

            
122
8
    auto tls_provider = THROW_OR_RETURN_VALUE(
123
8
        Secret::ThreadLocalGenericSecretProvider::create(
124
8
            std::move(provider), server_context.threadLocal(), server_context.api()),
125
8
        std::unique_ptr<Secret::ThreadLocalGenericSecretProvider>);
126

            
127
8
    providers.emplace(
128
8
        name, std::shared_ptr<Secret::ThreadLocalGenericSecretProvider>(std::move(tls_provider)));
129
8
  }
130

            
131
7
  return std::make_unique<GenericSecretCommandParser>(std::move(providers));
132
8
}
133

            
134
9
ProtobufTypes::MessagePtr GenericSecretFormatterFactory::createEmptyConfigProto() {
135
9
  return std::make_unique<envoy::extensions::formatter::generic_secret::v3::GenericSecret>();
136
9
}
137

            
138
8
std::string GenericSecretFormatterFactory::name() const { return "envoy.formatter.generic_secret"; }
139

            
140
REGISTER_FACTORY(GenericSecretFormatterFactory, Envoy::Formatter::CommandParserFactory);
141

            
142
} // namespace Formatter
143
} // namespace Extensions
144
} // namespace Envoy