-
Notifications
You must be signed in to change notification settings - Fork 5.5k
api: add 'redacted' option for protobuf messages, and redact SSL certs #9315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 25 commits
e95cafb
794aa85
4948c4b
927d875
848b869
cf6ebc2
2f261f6
186a8b3
bd56dba
782d092
0f493fa
8013dbe
3552086
b4c9bf9
af5a7df
32673ff
09d70fc
11f9d43
2bf220f
f6e49c6
d9e836c
5dc3c06
29a6172
403fdd2
208e9b4
77f295b
2488863
769e817
891da34
e996e29
b690640
e77cb8f
7a6b20c
f8167a0
5f1211e
358bac9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,12 +8,15 @@ | |
|
|
||
| #include "common/common/assert.h" | ||
| #include "common/common/fmt.h" | ||
| #include "common/common/macros.h" | ||
| #include "common/config/api_type_oracle.h" | ||
| #include "common/config/version_converter.h" | ||
| #include "common/protobuf/message_validator_impl.h" | ||
| #include "common/protobuf/protobuf.h" | ||
|
|
||
| #include "absl/strings/match.h" | ||
| #include "udpa/annotations/sensitive.pb.h" | ||
| #include "udpa/type/v1/typed_struct.pb.h" | ||
| #include "yaml-cpp/yaml.h" | ||
|
|
||
| namespace Envoy { | ||
|
|
@@ -596,6 +599,118 @@ std::string MessageUtil::CodeEnumToString(ProtobufUtil::error::Code code) { | |
| } | ||
| } | ||
|
|
||
| namespace { | ||
|
|
||
| std::unique_ptr<Protobuf::Message> typeUrlToNewMessage(Protobuf::MessageFactory& message_factory, | ||
| absl::string_view type_url) { | ||
| const absl::string_view type_name = TypeUtil::typeUrlToDescriptorFullName(type_url); | ||
| const Protobuf::Descriptor* descriptor = | ||
| Protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName( | ||
| static_cast<std::string>(type_name)); | ||
| return std::unique_ptr<Protobuf::Message>( | ||
| descriptor == nullptr ? nullptr : message_factory.GetPrototype(descriptor)->New()); | ||
| } | ||
|
|
||
| // Forward declaration for mutually-recursive helper functions. | ||
| void redact(Protobuf::Message* message, bool ancestor_is_sensitive); | ||
|
|
||
| // To redact an `Any`, we have to unpack it to its original type to redact it. | ||
| bool redactAny(Protobuf::Message* message, bool ancestor_is_sensitive) { | ||
| auto* any = dynamic_cast<ProtobufWkt::Any*>(message); | ||
| if (any != nullptr) { | ||
|
mergeconflict marked this conversation as resolved.
Outdated
|
||
| Protobuf::DynamicMessageFactory message_factory; | ||
| auto typed_message = typeUrlToNewMessage(message_factory, any->type_url()); | ||
| if (typed_message == nullptr) { | ||
| // If the type URL doesn't correspond to a known proto, give up redacting and treat this | ||
| // message the same as a `ProtobufWkt::Struct`. See the documented limitation on | ||
| // `MessageUtil::redact()` for more context. | ||
| ENVOY_LOG_MISC(warn, "Could not redact ProtobufWkt::Any with unknown type URL {}", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there tests that check for this condition (ideally doing an
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are tests for this condition, although not using |
||
| any->type_url()); | ||
| return false; | ||
| } | ||
|
|
||
| any->UnpackTo(typed_message.get()); | ||
| redact(typed_message.get(), ancestor_is_sensitive); | ||
| any->PackFrom(*typed_message); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // To redact a `TypedStruct`, we have to reify it based on its `type_url` to redact it. | ||
| bool redactTypedStruct(Protobuf::Message* message, bool ancestor_is_sensitive) { | ||
| auto* typed_struct = dynamic_cast<udpa::type::v1::TypedStruct*>(message); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how safe this is in general. You can have a
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, they are of type
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, this was a really important catch. PTAL, thanks! |
||
| if (typed_struct != nullptr) { | ||
| Protobuf::DynamicMessageFactory message_factory; | ||
| auto typed_message = typeUrlToNewMessage(message_factory, typed_struct->type_url()); | ||
| if (typed_message == nullptr) { | ||
| // If the type URL doesn't correspond to a known proto, give up redacting and treat this | ||
| // message the same as a `ProtobufWkt::Struct`. See the documented limitation on | ||
| // `MessageUtil::redact()` for more context. | ||
| ENVOY_LOG_MISC(warn, "Could not redact udpa::type::v1::TypedStruct with unknown type URL {}", | ||
|
mergeconflict marked this conversation as resolved.
Outdated
|
||
| typed_struct->type_url()); | ||
| return false; | ||
| } | ||
|
|
||
| MessageUtil::jsonConvert(typed_struct->value(), ProtobufMessage::getNullValidationVisitor(), | ||
| *typed_message); | ||
| redact(typed_message.get(), ancestor_is_sensitive); | ||
| MessageUtil::jsonConvert(*typed_message, *(typed_struct->mutable_value())); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // Recursive helper method for MessageUtil::redact() below. | ||
| void redact(Protobuf::Message* message, bool ancestor_is_sensitive) { | ||
| if (redactAny(message, ancestor_is_sensitive) || | ||
| redactTypedStruct(message, ancestor_is_sensitive)) { | ||
| return; | ||
| } | ||
|
|
||
| const auto* reflection = message->GetReflection(); | ||
| std::vector<const Protobuf::FieldDescriptor*> field_descriptors; | ||
| reflection->ListFields(*message, &field_descriptors); | ||
|
mergeconflict marked this conversation as resolved.
Outdated
|
||
| for (const auto* field_descriptor : field_descriptors) { | ||
| // Redact if this field or any of its ancestors have the `sensitive` option set. | ||
| const bool sensitive = ancestor_is_sensitive || | ||
| field_descriptor->options().GetExtension(udpa::annotations::sensitive); | ||
|
|
||
| if (field_descriptor->type() == Protobuf::FieldDescriptor::TYPE_MESSAGE) { | ||
| // Recursive case: traverse message fields. | ||
| if (field_descriptor->is_repeated()) { | ||
| const int field_size = reflection->FieldSize(*message, field_descriptor); | ||
| for (int i = 0; i < field_size; ++i) { | ||
| redact(reflection->MutableRepeatedMessage(message, field_descriptor, i), sensitive); | ||
| } | ||
| } else { | ||
| redact(reflection->MutableMessage(message, field_descriptor), sensitive); | ||
| } | ||
| } else if (sensitive) { | ||
| // Base case: replace strings and bytes with "[redacted]" and clear all others. | ||
| if (field_descriptor->type() == Protobuf::FieldDescriptor::TYPE_STRING || | ||
| field_descriptor->type() == Protobuf::FieldDescriptor::TYPE_BYTES) { | ||
| if (field_descriptor->is_repeated()) { | ||
| const int field_size = reflection->FieldSize(*message, field_descriptor); | ||
| for (int i = 0; i < field_size; ++i) { | ||
| reflection->SetRepeatedString(message, field_descriptor, i, "[redacted]"); | ||
| } | ||
| } else { | ||
| reflection->SetString(message, field_descriptor, "[redacted]"); | ||
| } | ||
| } else { | ||
| reflection->ClearField(message, field_descriptor); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| void MessageUtil::redact(Protobuf::Message& message) { | ||
| ::Envoy::redact(&message, /* ancestor_is_sensitive = */ false); | ||
|
htuch marked this conversation as resolved.
|
||
| } | ||
|
|
||
| ProtobufWkt::Value ValueUtil::loadFromYaml(const std::string& yaml) { | ||
| try { | ||
| return parseYamlNode(YAML::Load(yaml)); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.