mirror of https://github.com/grpc/grpc.git
[Audit Logging] Xds Audit Logger Registry (#32828)
Third-party loggers will be added in subsequent PRs once the logger factory APIs are available to validate the configs here. This registry is used in `xds_http_rbac_filter.cc` to generate service config json.pull/32939/head
parent
2eaa9d10eb
commit
2917804b9a
26 changed files with 792 additions and 5 deletions
@ -0,0 +1,106 @@ |
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/xds/xds_audit_logger_registry.h" |
||||
|
||||
#include <utility> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/types/optional.h" |
||||
#include "absl/types/variant.h" |
||||
#include "envoy/config/core/v3/extension.upb.h" |
||||
#include "envoy/config/rbac/v3/rbac.upb.h" |
||||
|
||||
#include "src/core/ext/xds/xds_common_types.h" |
||||
#include "src/core/lib/gprpp/validation_errors.h" |
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
namespace { |
||||
|
||||
class StdoutLoggerConfigFactory : public XdsAuditLoggerRegistry::ConfigFactory { |
||||
public: |
||||
Json::Object ConvertXdsAuditLoggerConfig( |
||||
const XdsResourceType::DecodeContext& /*context*/, |
||||
absl::string_view /*configuration*/, |
||||
ValidationErrors* /*errors*/) override { |
||||
return Json::Object{{"stdout_logger", Json::Object()}}; |
||||
} |
||||
|
||||
absl::string_view type() override { return Type(); } |
||||
|
||||
static absl::string_view Type() { |
||||
return "envoy.extensions.rbac.audit_loggers.stream.v3.StdoutAuditLog"; |
||||
} |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
XdsAuditLoggerRegistry::XdsAuditLoggerRegistry() { |
||||
audit_logger_config_factories_.emplace( |
||||
StdoutLoggerConfigFactory::Type(), |
||||
std::make_unique<StdoutLoggerConfigFactory>()); |
||||
} |
||||
|
||||
Json XdsAuditLoggerRegistry::ConvertXdsAuditLoggerConfig( |
||||
const XdsResourceType::DecodeContext& context, |
||||
const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* |
||||
logger_config, |
||||
ValidationErrors* errors) const { |
||||
const auto* typed_extension_config = |
||||
envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_audit_logger( |
||||
logger_config); |
||||
ValidationErrors::ScopedField field(errors, ".audit_logger"); |
||||
if (typed_extension_config == nullptr) { |
||||
errors->AddError("field not present"); |
||||
return Json(); // A null Json object.
|
||||
} else { |
||||
ValidationErrors::ScopedField field(errors, ".typed_config"); |
||||
const auto* typed_config = |
||||
envoy_config_core_v3_TypedExtensionConfig_typed_config( |
||||
typed_extension_config); |
||||
auto extension = ExtractXdsExtension(context, typed_config, errors); |
||||
if (!extension.has_value()) return Json(); |
||||
// Check for registered audit logger type.
|
||||
absl::string_view* serialized_value = |
||||
absl::get_if<absl::string_view>(&extension->value); |
||||
if (serialized_value != nullptr) { |
||||
auto config_factory_it = |
||||
audit_logger_config_factories_.find(extension->type); |
||||
if (config_factory_it != audit_logger_config_factories_.end()) { |
||||
// TODO(lwge): Parse the config with the gRPC audit logger registry.
|
||||
return config_factory_it->second->ConvertXdsAuditLoggerConfig( |
||||
context, *serialized_value, errors); |
||||
} |
||||
} |
||||
// TODO(lwge): Check for third-party audit logger type. For now, we disallow
|
||||
// it by rejecting TypedStruct entries.
|
||||
if (absl::get_if<Json>(&extension->value) != nullptr) { |
||||
errors->AddError("third-party audit logger is not supported"); |
||||
return Json(); |
||||
} |
||||
} |
||||
// Add validation error only if the config is not marked optional.
|
||||
if (!envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_is_optional( |
||||
logger_config)) { |
||||
errors->AddError("unsupported audit logger type"); |
||||
} |
||||
return Json(); |
||||
} |
||||
} // namespace grpc_core
|
@ -0,0 +1,65 @@ |
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_SRC_CORE_EXT_XDS_XDS_AUDIT_LOGGER_REGISTRY_H |
||||
#define GRPC_SRC_CORE_EXT_XDS_XDS_AUDIT_LOGGER_REGISTRY_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <map> |
||||
#include <memory> |
||||
|
||||
#include "absl/strings/string_view.h" |
||||
#include "envoy/config/rbac/v3/rbac.upb.h" |
||||
|
||||
#include "src/core/ext/xds/xds_resource_type.h" |
||||
#include "src/core/lib/gprpp/validation_errors.h" |
||||
#include "src/core/lib/json/json.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// A registry that maintains a set of converters that are able to map xDS
|
||||
// RBAC audit logger configuration to gRPC's JSON format.
|
||||
class XdsAuditLoggerRegistry { |
||||
public: |
||||
class ConfigFactory { |
||||
public: |
||||
virtual ~ConfigFactory() = default; |
||||
virtual Json::Object ConvertXdsAuditLoggerConfig( |
||||
const XdsResourceType::DecodeContext& context, |
||||
absl::string_view configuration, ValidationErrors* errors) = 0; |
||||
virtual absl::string_view type() = 0; |
||||
}; |
||||
|
||||
XdsAuditLoggerRegistry(); |
||||
|
||||
Json ConvertXdsAuditLoggerConfig( |
||||
const XdsResourceType::DecodeContext& context, |
||||
const envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig* |
||||
logger_config, |
||||
ValidationErrors* errors) const; |
||||
|
||||
private: |
||||
// A map of config factories that goes from the type of the audit logging
|
||||
// config to the config factory.
|
||||
std::map<absl::string_view /* Owned by ConfigFactory */, |
||||
std::unique_ptr<ConfigFactory>> |
||||
audit_logger_config_factories_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_EXT_XDS_XDS_AUDIT_LOGGER_REGISTRY_H
|
@ -0,0 +1,30 @@ |
||||
// Copyright 2023 The gRPC Authors |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
// Local copy of Envoy xDS proto file, used for testing only. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package envoy.extensions.rbac.audit_loggers.stream.v3; |
||||
|
||||
option java_package = "io.envoyproxy.envoy.extensions.rbac.audit_loggers.stream.v3"; |
||||
option java_outer_classname = "StreamProto"; |
||||
option java_multiple_files = true; |
||||
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/rbac/audit_loggers/stream/v3;streamv3"; |
||||
|
||||
// Custom configuration for the RBAC audit logger that writes log entries |
||||
// directly to the operating system's standard output. |
||||
// The logger outputs in JSON format and is currently not configurable. |
||||
message StdoutAuditLog { |
||||
} |
@ -0,0 +1,160 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2023 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#include "src/core/ext/xds/xds_audit_logger_registry.h" |
||||
|
||||
#include <string> |
||||
|
||||
#include <google/protobuf/any.pb.h> |
||||
|
||||
#include "absl/status/status.h" |
||||
#include "absl/status/statusor.h" |
||||
#include "envoy/config/rbac/v3/rbac.upb.h" |
||||
#include "gtest/gtest.h" |
||||
#include "upb/reflection/def.hpp" |
||||
#include "upb/upb.hpp" |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include "src/core/ext/xds/xds_bootstrap_grpc.h" |
||||
#include "src/core/lib/json/json_writer.h" |
||||
#include "src/proto/grpc/testing/xds/v3/audit_logger_stream.pb.h" |
||||
#include "src/proto/grpc/testing/xds/v3/extension.pb.h" |
||||
#include "src/proto/grpc/testing/xds/v3/rbac.pb.h" |
||||
#include "src/proto/grpc/testing/xds/v3/typed_struct.pb.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
using AuditLoggerConfigProto = |
||||
::envoy::config::rbac::v3::RBAC::AuditLoggingOptions::AuditLoggerConfig; |
||||
using ::envoy::extensions::rbac::audit_loggers::stream::v3::StdoutAuditLog; |
||||
using ::xds::type::v3::TypedStruct; |
||||
|
||||
absl::StatusOr<std::string> ConvertAuditLoggerConfig( |
||||
const AuditLoggerConfigProto& config) { |
||||
std::string serialized_config = config.SerializeAsString(); |
||||
upb::Arena arena; |
||||
upb::SymbolTable symtab; |
||||
XdsResourceType::DecodeContext context = {nullptr, |
||||
GrpcXdsBootstrap::GrpcXdsServer(), |
||||
nullptr, symtab.ptr(), arena.ptr()}; |
||||
auto* upb_config = |
||||
envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_parse( |
||||
serialized_config.data(), serialized_config.size(), arena.ptr()); |
||||
ValidationErrors errors; |
||||
auto config_json = XdsAuditLoggerRegistry().ConvertXdsAuditLoggerConfig( |
||||
context, upb_config, &errors); |
||||
if (!errors.ok()) { |
||||
return errors.status(absl::StatusCode::kInvalidArgument, |
||||
"validation errors"); |
||||
} |
||||
return JsonDump(config_json); |
||||
} |
||||
|
||||
//
|
||||
// StdoutLoggerTest
|
||||
//
|
||||
|
||||
TEST(StdoutLoggerTest, Basic) { |
||||
AuditLoggerConfigProto config; |
||||
config.mutable_audit_logger()->mutable_typed_config()->PackFrom( |
||||
StdoutAuditLog()); |
||||
auto result = ConvertAuditLoggerConfig(config); |
||||
ASSERT_TRUE(result.ok()) << result.status(); |
||||
EXPECT_EQ(*result, "{\"stdout_logger\":{}}"); |
||||
} |
||||
|
||||
//
|
||||
// ThirdPartyLoggerTest
|
||||
//
|
||||
|
||||
TEST(XdsAuditLoggerRegistryTest, ThirdPartyLogger) { |
||||
AuditLoggerConfigProto config; |
||||
TypedStruct logger; |
||||
logger.set_type_url("myorg/foo/bar/test.UnknownAuditLogger"); |
||||
config.mutable_audit_logger()->mutable_typed_config()->PackFrom(logger); |
||||
auto result = ConvertAuditLoggerConfig(config); |
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); |
||||
EXPECT_EQ(result.status().message(), |
||||
"validation errors: " |
||||
"[field:audit_logger.typed_config.value" |
||||
"[xds.type.v3.TypedStruct].value[test.UnknownAuditLogger] " |
||||
"error:third-party audit logger is not supported]") |
||||
<< result.status(); |
||||
} |
||||
|
||||
//
|
||||
// XdsAuditLoggerRegistryTest
|
||||
//
|
||||
|
||||
TEST(XdsAuditLoggerRegistryTest, EmptyAuditLoggerConfig) { |
||||
auto result = ConvertAuditLoggerConfig(AuditLoggerConfigProto()); |
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); |
||||
EXPECT_EQ(result.status().message(), |
||||
"validation errors: [field:audit_logger error:field not present]") |
||||
<< result.status(); |
||||
} |
||||
|
||||
TEST(XdsAuditLoggerRegistryTest, MissingTypedConfig) { |
||||
AuditLoggerConfigProto config; |
||||
config.mutable_audit_logger(); |
||||
auto result = ConvertAuditLoggerConfig(config); |
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); |
||||
EXPECT_EQ(result.status().message(), |
||||
"validation errors: [field:audit_logger.typed_config error:field " |
||||
"not present]") |
||||
<< result.status(); |
||||
} |
||||
|
||||
TEST(XdsAuditLoggerRegistryTest, NoSupportedType) { |
||||
AuditLoggerConfigProto config; |
||||
config.mutable_audit_logger()->mutable_typed_config()->PackFrom( |
||||
AuditLoggerConfigProto()); |
||||
auto result = ConvertAuditLoggerConfig(config); |
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument); |
||||
EXPECT_EQ(result.status().message(), |
||||
"validation errors: [field:audit_logger error:unsupported audit " |
||||
"logger type]") |
||||
<< result.status(); |
||||
} |
||||
|
||||
TEST(XdsAuditLoggerRegistryTest, NoSupportedTypeButIsOptional) { |
||||
AuditLoggerConfigProto config; |
||||
config.mutable_audit_logger()->mutable_typed_config()->PackFrom( |
||||
AuditLoggerConfigProto()); |
||||
config.set_is_optional(true); |
||||
auto result = ConvertAuditLoggerConfig(config); |
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kOk); |
||||
EXPECT_EQ(*result, "null"); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc::testing::TestEnvironment env(&argc, argv); |
||||
grpc_init(); |
||||
auto result = RUN_ALL_TESTS(); |
||||
grpc_shutdown(); |
||||
return result; |
||||
} |
Loading…
Reference in new issue