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] Audit logging config translation by rbac service config parser #33145

Merged
merged 23 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7ac6a4d
audit logging in service config parser
rockspore May 5, 2023
72487d0
remove unused using decl and hdrs
rockspore May 5, 2023
d2f15ec
Automated change: Fix sanity tests
rockspore May 5, 2023
bbe16bf
Merge pull request #20 from rockspore/create-pull-request/patch-72487d0
rockspore May 5, 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
50d7ad7
Merge branch 'stdout-logger' of github.com:rockspore/grpc into servic…
rockspore May 5, 2023
1c224f3
Merge pull request #21 from rockspore/create-pull-request/patch-f8f5ee1
rockspore May 5, 2023
296ac6c
Merge branch 'stdout-logger' of github.com:rockspore/grpc into servic…
rockspore May 5, 2023
0e598e9
remove fixture by using stdout logger
rockspore May 5, 2023
361798a
merge from origin
rockspore May 5, 2023
67e5571
Automated change: Fix sanity tests
rockspore May 5, 2023
769dbc5
Merge pull request #22 from rockspore/create-pull-request/patch-361798a
rockspore May 5, 2023
9864edf
Merge branch 'master' of github.com:grpc/grpc into service-config-parser
rockspore May 16, 2023
6adbc3f
public json hdr
rockspore May 16, 2023
7a556e7
Automated change: Fix sanity tests
rockspore May 16, 2023
0aa5024
Merge pull request #25 from rockspore/create-pull-request/patch-6adbc3f
rockspore May 16, 2023
a438bbb
address PR comments
rockspore May 17, 2023
a3a3a64
Merge branch 'service-config-parser' of github.com:rockspore/grpc int…
rockspore May 17, 2023
935af66
address PR comments
rockspore May 17, 2023
ccc6633
iwyu, clang_format and PR comments
rockspore May 17, 2023
0577455
fix test with json bug fixed
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 @@ -3699,6 +3699,7 @@ grpc_cc_library(
"channel_fwd",
"closure",
"error",
"grpc_audit_logging",
"grpc_authorization_base",
"grpc_matchers",
"grpc_rbac_engine",
Expand Down
105 changes: 97 additions & 8 deletions src/core/ext/filters/rbac/rbac_service_config_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,29 @@

#include <cstdint>
#include <map>
#include <memory>
#include <string>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/optional.h"

#include <grpc/support/log.h>

#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/json/json_args.h"
#include "src/core/lib/json/json_object_loader.h"
#include "src/core/lib/matchers/matchers.h"
#include "src/core/lib/security/authorization/audit_logging.h"

namespace grpc_core {

namespace {

using experimental::AuditLoggerFactory;
using experimental::AuditLoggerRegistry;

// RbacConfig: one or more RbacPolicy structs
struct RbacConfig {
// RbacPolicy: optional Rules
Expand Down Expand Up @@ -179,16 +187,29 @@ struct RbacConfig {
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
};

// AuditLogger: the name of logger and its config in json
struct AuditLogger {
std::string name;
Json::Object config;

static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&,
ValidationErrors* errors);
};

int action;
std::map<std::string, Policy> policies;
// Defaults to 0 since its json field is optional.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/0/kNone/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Rbac::AuditCondition audit_condition;
markdroth marked this conversation as resolved.
Show resolved Hide resolved
std::vector<std::unique_ptr<AuditLoggerFactory::Config>> logger_configs;

Rules() = default;
Rules(const Rules&) = delete;
Rules& operator=(const Rules&) = delete;
Rules(Rules&&) = default;
Rules& operator=(Rules&&) = default;

Rbac TakeAsRbac();
Rbac TakeAsRbac(std::string name);
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors);
};
Expand Down Expand Up @@ -716,32 +737,60 @@ const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::Policy::JsonLoader(
return loader;
}

//
// RbacConfig::RbacPolicy::Rules::AuditLogger
//

const JsonLoaderInterface*
RbacConfig::RbacPolicy::Rules::AuditLogger::JsonLoader(const JsonArgs&) {
// All fields handled in JsonPostLoad().
static const auto* loader = JsonObjectLoader<AuditLogger>().Finish();
return loader;
}

void RbacConfig::RbacPolicy::Rules::AuditLogger::JsonPostLoad(
const Json& json, const JsonArgs& args, ValidationErrors* errors) {
// Should have exactly one field as the logger name.
if (json.object().size() != 1) {
errors->AddError("audit logger should have exactly one field");
return;
}
name = json.object().begin()->first;
auto config_or =
LoadJsonObjectField<Json::Object>(json.object(), args, name, errors);
if (config_or.has_value()) {
config = std::move(*config_or);
}
}

//
// RbacConfig::RbacPolicy::Rules
//

Rbac RbacConfig::RbacPolicy::Rules::TakeAsRbac() {
Rbac RbacConfig::RbacPolicy::Rules::TakeAsRbac(std::string name) {
Rbac rbac;
// TODO(lwge): This is to fix msan failure for now. Add proper conversion once
// audit logging support is added.
rbac.audit_condition = Rbac::AuditCondition::kNone;
rbac.name = std::move(name);
rbac.action = static_cast<Rbac::Action>(action);
rbac.audit_condition = audit_condition;
for (auto& p : policies) {
rbac.policies.emplace(p.first, p.second.TakeAsRbacPolicy());
}
rbac.logger_configs = std::move(logger_configs);
return rbac;
}

const JsonLoaderInterface* RbacConfig::RbacPolicy::Rules::JsonLoader(
const JsonArgs&) {
// Audit logger configs handled in post load.
static const auto* loader = JsonObjectLoader<Rules>()
.Field("action", &Rules::action)
.OptionalField("policies", &Rules::policies)
.Finish();
return loader;
}

void RbacConfig::RbacPolicy::Rules::JsonPostLoad(const Json&, const JsonArgs&,
void RbacConfig::RbacPolicy::Rules::JsonPostLoad(const Json& json,
const JsonArgs& args,
ValidationErrors* errors) {
// Validate action field.
auto rbac_action = static_cast<Rbac::Action>(action);
Expand All @@ -750,6 +799,47 @@ void RbacConfig::RbacPolicy::Rules::JsonPostLoad(const Json&, const JsonArgs&,
ValidationErrors::ScopedField field(errors, ".action");
errors->AddError("unknown action");
}
// Parse and validate audit_condition field.
auto it = json.object().find("audit_condition");
markdroth marked this conversation as resolved.
Show resolved Hide resolved
int condition = static_cast<int>(Rbac::AuditCondition::kNone);
if (it != json.object().end()) {
if (it->second.type() != Json::Type::kNumber) {
ValidationErrors::ScopedField field(errors, ".audit_condition");
errors->AddError("is not a number");
} else {
GPR_ASSERT(absl::SimpleAtoi(it->second.string(), &condition));
}
}
switch (condition) {
case static_cast<int>(Rbac::AuditCondition::kNone):
case static_cast<int>(Rbac::AuditCondition::kOnAllow):
case static_cast<int>(Rbac::AuditCondition::kOnDeny):
case static_cast<int>(Rbac::AuditCondition::kOnDenyAndAllow):
break;
default: {
ValidationErrors::ScopedField field(errors, ".audit_condition");
errors->AddError("unknown audit condition");
}
}
audit_condition = static_cast<Rbac::AuditCondition>(condition);
if (json.object().find("audit_loggers") == json.object().end()) return;
// Parse and validate audit logger configs.
markdroth marked this conversation as resolved.
Show resolved Hide resolved
auto configs = LoadJsonObjectField<std::vector<AuditLogger>>(
json.object(), args, "audit_loggers", errors);
if (configs.has_value()) {
for (size_t i = 0; i < configs->size(); ++i) {
auto& logger = (*configs)[i];
auto config = AuditLoggerRegistry::ParseConfig(
logger.name, Json::FromObject(std::move(logger.config)));
if (!config.ok()) {
ValidationErrors::ScopedField field(
errors, absl::StrCat(".audit_loggers[", i, "]"));
errors->AddError(config.status().message());
continue;
}
logger_configs.push_back(std::move(*config));
}
}
}

//
Expand All @@ -762,8 +852,7 @@ Rbac RbacConfig::RbacPolicy::TakeAsRbac() {
// is equivalent to no enforcing.
return Rbac(std::move(name), Rbac::Action::kDeny, {});
}
// TODO(lwge): This also needs to take the name.
return rules->TakeAsRbac();
return rules->TakeAsRbac(std::move(name));
}

const JsonLoaderInterface* RbacConfig::RbacPolicy::JsonLoader(const JsonArgs&) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class GrpcAuthorizationEngine : public AuthorizationEngine {
// Required only for testing purpose.
size_t num_policies() const { return policies_.size(); }

// Required only for testing purpose.
Rbac::AuditCondition audit_condition() const { return audit_condition_; }

// Required only for testing purpose.
size_t num_audit_loggers() const { return audit_loggers_.size(); }
markdroth marked this conversation as resolved.
Show resolved Hide resolved

// Evaluates incoming request against RBAC policy and makes a decision to
// whether allow/deny this request.
Decision Evaluate(const EvaluateArgs& args) const override;
Expand Down