Skip to content
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

[Audit Logging] Custom audit logger parsing in xDS registry. #32970

Merged
merged 35 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4e6e356
Add third_party logger config conversion in xDS audit logger registry.
rockspore Apr 28, 2023
4aec3b7
Automated change: Fix sanity tests
rockspore Apr 28, 2023
13f4442
Merge pull request #17 from rockspore/create-pull-request/patch-4e6e356
rockspore Apr 28, 2023
6d37605
return empty json on error
rockspore Apr 28, 2023
16bd834
stdout audit logger impl
rockspore May 5, 2023
f8f5ee1
generate projects
rockspore May 5, 2023
195f90f
Automated change: Fix sanity tests
rockspore May 5, 2023
1c224f3
Merge pull request #21 from rockspore/create-pull-request/patch-f8f5ee1
rockspore May 5, 2023
5cb67df
address PR comments
rockspore May 5, 2023
fc13177
constexpr
rockspore May 5, 2023
3cbd452
Merge branch 'master' of github.com:grpc/grpc into stdout-logger
rockspore May 12, 2023
1064479
switch to public json hdr
rockspore May 12, 2023
db15d5c
sanity
rockspore May 12, 2023
4ecbc71
Automated change: Fix sanity tests
rockspore May 12, 2023
5c0fc5b
Merge pull request #23 from rockspore/create-pull-request/patch-db15d5c
rockspore May 12, 2023
defee80
address PR comments
rockspore May 12, 2023
6092130
fix build
rockspore May 12, 2023
eedd236
Automated change: Fix sanity tests
rockspore May 12, 2023
2738e47
log in seconds
rockspore May 15, 2023
f6427bb
Merge pull request #24 from rockspore/create-pull-request/patch-6092130
rockspore May 15, 2023
7c765ee
address PR comments
rockspore May 15, 2023
aca75f2
nanosecond
rockspore May 15, 2023
82a2406
allow equal time
rockspore May 16, 2023
ebddd57
Merge branch 'master' of github.com:grpc/grpc into xds-registry
rockspore May 16, 2023
f333b8b
Merge branch 'stdout-logger' of github.com:rockspore/grpc into xds-re…
rockspore May 16, 2023
a89fa3f
use new Json APIs
rockspore May 16, 2023
fc994c5
Merge branch 'master' of github.com:grpc/grpc into xds-registry
rockspore May 16, 2023
b5e1962
validate config for built-in loggers
rockspore May 16, 2023
e9e96b6
Automated change: Fix sanity tests
rockspore May 16, 2023
803e4da
Merge pull request #26 from rockspore/create-pull-request/patch-b5e1962
rockspore May 16, 2023
1f58adf
use match
rockspore May 17, 2023
e76be97
Merge branch 'xds-registry' of github.com:rockspore/grpc into xds-reg…
rockspore May 17, 2023
8a0fa4e
fix test
rockspore May 17, 2023
9b21f69
Automated change: Fix sanity tests
rockspore May 17, 2023
cf7d08c
Merge pull request #27 from rockspore/create-pull-request/patch-8a0fa4e
rockspore May 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4067,6 +4067,7 @@ grpc_cc_library(
"envoy_type_upb",
"error",
"google_rpc_status_upb",
"grpc_audit_logging",
"grpc_fake_credentials",
"grpc_fault_injection_filter",
"grpc_lb_xds_channel_args",
Expand Down
83 changes: 48 additions & 35 deletions src/core/ext/xds/xds_audit_logger_registry.cc
markdroth marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,40 @@

#include "src/core/ext/xds/xds_audit_logger_registry.h"

#include <string>
#include <utility>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#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 <grpc/support/json.h>

#include "src/core/ext/xds/xds_common_types.h"
#include "src/core/lib/gprpp/match.h"
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/security/authorization/audit_logging.h"

namespace grpc_core {

namespace {

using experimental::AuditLoggerRegistry;

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::FromObject({})}};
// Stdout logger has no configuration right now. So we don't process the
// config protobuf.
return {};
}

absl::string_view type() override { return Type(); }
absl::string_view name() override { return "stdout_logger"; }

static absl::string_view Type() {
return "envoy.extensions.rbac.audit_loggers.stream.v3.StdoutAuditLog";
Expand All @@ -72,38 +78,45 @@ Json XdsAuditLoggerRegistry::ConvertXdsAuditLoggerConfig(
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 Json::FromObject(
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();
}
ValidationErrors::ScopedField field2(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();
absl::string_view name;
Json config;
Match(
extension->value,
// Built-in logger types.
[&](absl::string_view serialized_value) {
auto it = audit_logger_config_factories_.find(extension->type);
if (it == audit_logger_config_factories_.end()) return;
name = it->second->name();
config = Json::FromObject(it->second->ConvertXdsAuditLoggerConfig(
context, serialized_value, errors));
},
// Custom logger types.
[&](Json json) {
if (!AuditLoggerRegistry::FactoryExists(extension->type)) return;
name = extension->type;
config = json;
});
// Config not found in either case if name is empty.
if (name.empty()) {
if (!envoy_config_rbac_v3_RBAC_AuditLoggingOptions_AuditLoggerConfig_is_optional(
logger_config)) {
errors->AddError("unsupported audit logger type");
}
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");
// Validate the converted config.
auto result = AuditLoggerRegistry::ParseConfig(name, config);
if (!result.ok()) {
errors->AddError(result.status().message());
return Json();
}
return Json();
return Json::FromObject({{std::string(name), std::move(config)}});
}
} // namespace grpc_core
3 changes: 3 additions & 0 deletions src/core/ext/xds/xds_audit_logger_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ class XdsAuditLoggerRegistry {
virtual Json::Object ConvertXdsAuditLoggerConfig(
const XdsResourceType::DecodeContext& context,
absl::string_view configuration, ValidationErrors* errors) = 0;
// The full proto message name for the logger config.
virtual absl::string_view type() = 0;
// The logger name used for the gRPC registry.
virtual absl::string_view name() = 0;
};

XdsAuditLoggerRegistry();
Expand Down
1 change: 1 addition & 0 deletions test/core/xds/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ grpc_cc_test(
deps = [
"//:gpr",
"//:grpc",
"//src/core:grpc_audit_logging",
"//src/proto/grpc/testing/xds/v3:audit_logger_stream_proto",
"//src/proto/grpc/testing/xds/v3:rbac_proto",
"//src/proto/grpc/testing/xds/v3:typed_struct_proto",
Expand Down
79 changes: 69 additions & 10 deletions test/core/xds/xds_audit_logger_registry_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,32 @@

#include "src/core/ext/xds/xds_audit_logger_registry.h"

#include <stdint.h>

#include <initializer_list>
#include <memory>
#include <string>

#include <google/protobuf/any.pb.h>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "envoy/config/rbac/v3/rbac.upb.h"
#include "google/protobuf/struct.pb.h"
#include "gtest/gtest.h"
#include "upb/reflection/def.hpp"
#include "upb/upb.hpp"

#include <grpc/grpc.h>
#include <grpc/grpc_audit_logging.h>

#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_writer.h"
#include "src/core/lib/security/authorization/audit_logging.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"
Expand All @@ -43,11 +54,16 @@ namespace grpc_core {
namespace testing {
namespace {

using experimental::AuditLogger;
using experimental::AuditLoggerFactory;
using experimental::AuditLoggerRegistry;
using AuditLoggerConfigProto =
::envoy::config::rbac::v3::RBAC::AuditLoggingOptions::AuditLoggerConfig;
using ::envoy::extensions::rbac::audit_loggers::stream::v3::StdoutAuditLog;
using ::xds::type::v3::TypedStruct;

constexpr absl::string_view kName = "test_logger";

absl::StatusOr<std::string> ConvertAuditLoggerConfig(
const AuditLoggerConfigProto& config) {
std::string serialized_config = config.SerializeAsString();
Expand All @@ -69,11 +85,38 @@ absl::StatusOr<std::string> ConvertAuditLoggerConfig(
return JsonDump(config_json);
}

class TestAuditLoggerFactory : public AuditLoggerFactory {
public:
absl::string_view name() const override { return kName; }
absl::StatusOr<std::unique_ptr<AuditLoggerFactory::Config>>
ParseAuditLoggerConfig(const Json& json) override {
if (json.object().find("bad") != json.object().end()) {
return absl::InvalidArgumentError("invalid test_logger config");
}
return nullptr;
}
std::unique_ptr<AuditLogger> CreateAuditLogger(
std::unique_ptr<AuditLoggerFactory::Config>) override {
Crash("unreachable");
return nullptr;
}
};

class XdsAuditLoggerRegistryTest : public ::testing::Test {
protected:
void SetUp() override {
AuditLoggerRegistry::RegisterFactory(
std::make_unique<TestAuditLoggerFactory>());
}

void TearDown() override { AuditLoggerRegistry::TestOnlyResetRegistry(); }
};

//
// StdoutLoggerTest
//

TEST(StdoutLoggerTest, Basic) {
TEST(StdoutLoggerTest, BasicStdoutLogger) {
AuditLoggerConfigProto config;
config.mutable_audit_logger()->mutable_typed_config()->PackFrom(
StdoutAuditLog());
Expand All @@ -86,34 +129,48 @@ TEST(StdoutLoggerTest, Basic) {
// ThirdPartyLoggerTest
//

TEST(XdsAuditLoggerRegistryTest, ThirdPartyLogger) {
TEST_F(XdsAuditLoggerRegistryTest, ValidThirdPartyLogger) {
AuditLoggerConfigProto config;
TypedStruct logger;
logger.set_type_url("myorg/foo/bar/test.UnknownAuditLogger");
logger.set_type_url(absl::StrFormat("myorg/foo/bar/%s", kName));
auto* fields = logger.mutable_value()->mutable_fields();
(*fields)["foo"].set_string_value("bar");
config.mutable_audit_logger()->mutable_typed_config()->PackFrom(logger);
auto result = ConvertAuditLoggerConfig(config);
ASSERT_TRUE(result.ok()) << result.status();
EXPECT_EQ(*result, "{\"test_logger\":{\"foo\":\"bar\"}}");
}

TEST_F(XdsAuditLoggerRegistryTest, InvalidThirdPartyLoggerConfig) {
AuditLoggerConfigProto config;
TypedStruct logger;
logger.set_type_url(absl::StrFormat("myorg/foo/bar/%s", kName));
auto* fields = logger.mutable_value()->mutable_fields();
(*fields)["bad"].set_string_value("true");
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]")
"[xds.type.v3.TypedStruct].value[test_logger] "
"error:invalid test_logger config]")
<< result.status();
}

//
// XdsAuditLoggerRegistryTest
//

TEST(XdsAuditLoggerRegistryTest, EmptyAuditLoggerConfig) {
TEST_F(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) {
TEST_F(XdsAuditLoggerRegistryTest, MissingTypedConfig) {
AuditLoggerConfigProto config;
config.mutable_audit_logger();
auto result = ConvertAuditLoggerConfig(config);
Expand All @@ -124,19 +181,21 @@ TEST(XdsAuditLoggerRegistryTest, MissingTypedConfig) {
<< result.status();
}

TEST(XdsAuditLoggerRegistryTest, NoSupportedType) {
TEST_F(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 "
"validation errors: "
"[field:audit_logger.typed_config.value[envoy.config.rbac.v3.RBAC."
"AuditLoggingOptions.AuditLoggerConfig] error:unsupported audit "
"logger type]")
<< result.status();
}

TEST(XdsAuditLoggerRegistryTest, NoSupportedTypeButIsOptional) {
TEST_F(XdsAuditLoggerRegistryTest, NoSupportedTypeButIsOptional) {
AuditLoggerConfigProto config;
config.mutable_audit_logger()->mutable_typed_config()->PackFrom(
AuditLoggerConfigProto());
Expand Down
12 changes: 6 additions & 6 deletions test/core/xds/xds_http_filters_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -987,12 +987,12 @@ TEST_P(XdsRbacFilterConfigTest, InvalidAuditLoggerConfig) {
"errors validating filter config");
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_EQ(status.message(),
absl::StrCat(
"errors validating filter config: ["
"field:",
FieldPrefix(),
".rules.audit_logging_options.logger_configs[0].audit_logger "
"error:unsupported audit logger type]"))
absl::StrCat("errors validating filter config: ["
"field:",
FieldPrefix(),
".rules.audit_logging_options.logger_configs[0].audit_"
"logger.typed_config.value[foo_logger] "
"error:unsupported audit logger type]"))
<< status;
}

Expand Down