[Audit Logging] End2end test for audit logging in authorization policy (#33196)

I generated a new client key and cert where a Spiffe ID is added as the
URI SAN. As such, we are able to test the audit log contains the
principal correctly.

Update: I switched to use the test logger to verify the log content and
removed stdout logger here because one the failure of [RBE Windows Debug
C/C++](https://source.cloud.google.com/results/invocations/c3187f41-bb1f-44b3-b2b1-23f38e47386d).

Update again: Refactored the test logger in a util such that the authz
engine test also uses the same logger. Subsequently, xDS e2e test will
also use it.

---------

Co-authored-by: rockspore <rockspore@users.noreply.github.com>
pull/33240/head
Luwei Ge 2 years ago committed by GitHub
parent 4d85f514cb
commit de9d398e8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CMakeLists.txt
  2. 4
      build_autogenerated.yaml
  3. 2
      src/core/tsi/test_creds/BUILD
  4. 9
      src/core/tsi/test_creds/README
  5. 15
      src/core/tsi/test_creds/client-with-spiffe-openssl.cnf
  6. 28
      src/core/tsi/test_creds/client-with-spiffe.key
  7. 23
      src/core/tsi/test_creds/client-with-spiffe.pem
  8. 1
      test/core/security/BUILD
  9. 158
      test/core/security/grpc_authorization_engine_test.cc
  10. 16
      test/core/util/BUILD
  11. 76
      test/core/util/audit_logging_utils.cc
  12. 70
      test/core/util/audit_logging_utils.h
  13. 5
      test/cpp/end2end/BUILD
  14. 720
      test/cpp/end2end/grpc_authz_end2end_test.cc

2
CMakeLists.txt generated

@ -12111,6 +12111,7 @@ if(gRPC_BUILD_TESTS)
add_executable(grpc_authorization_engine_test
test/core/security/grpc_authorization_engine_test.cc
test/core/util/audit_logging_utils.cc
test/core/util/cmdline.cc
test/core/util/fuzzer_util.cc
test/core/util/grpc_profiler.cc
@ -12226,6 +12227,7 @@ add_executable(grpc_authz_end2end_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h
src/cpp/server/authorization_policy_provider.cc
test/core/util/audit_logging_utils.cc
test/cpp/end2end/grpc_authz_end2end_test.cc
test/cpp/end2end/test_service_impl.cc
third_party/googletest/googletest/src/gtest-all.cc

@ -8110,6 +8110,7 @@ targets:
build: test
language: c++
headers:
- test/core/util/audit_logging_utils.h
- test/core/util/cmdline.h
- test/core/util/evaluate_args_test_util.h
- test/core/util/fuzzer_util.h
@ -8125,6 +8126,7 @@ targets:
- test/core/util/tracer_util.h
src:
- test/core/security/grpc_authorization_engine_test.cc
- test/core/util/audit_logging_utils.cc
- test/core/util/cmdline.cc
- test/core/util/fuzzer_util.cc
- test/core/util/grpc_profiler.cc
@ -8179,6 +8181,7 @@ targets:
build: test
language: c++
headers:
- test/core/util/audit_logging_utils.h
- test/cpp/end2end/test_service_impl.h
src:
- src/proto/grpc/testing/echo.proto
@ -8186,6 +8189,7 @@ targets:
- src/proto/grpc/testing/simple_messages.proto
- src/proto/grpc/testing/xds/v3/orca_load_report.proto
- src/cpp/server/authorization_policy_provider.cc
- test/core/util/audit_logging_utils.cc
- test/cpp/end2end/grpc_authz_end2end_test.cc
- test/cpp/end2end/test_service_impl.cc
deps:

@ -22,6 +22,8 @@ exports_files([
"server0.pem",
"client.key",
"client.pem",
"client-with-spiffe.key",
"client-with-spiffe.pem",
"badserver.key",
"badserver.pem",
"badclient.key",

@ -62,6 +62,15 @@ common name which is set to testclient2.
$ openssl x509 -req -CA ca.pem -CAkey ca.key -CAcreateserial -in client2.csr \
-out client2.pem -days 3650
client-with-spiffe is issued by CA:
-----------------------
$ openssl genrsa -out client-with-spiffe.key.rsa 2048
$ openssl pkcs8 -topk8 -in client-with-spiffe.key.rsa -out client-with-spiffe.key -nocrypt
$ openssl req -new -key client-with-spiffe.key -out client-with-spiffe.csr -config client-with-spiffe-openssl.cnf
$ openssl x509 -req -CA ca.pem -CAkey ca.key -CAcreateserial -in client-with-spiffe.csr \
-out client-with-spiffe.pem -extensions v3_req -extfile client-with-spiffe-openssl.cnf -days 3650
server0 is issued by CA:
------------------------

@ -0,0 +1,15 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = testclient3
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
URI = spiffe://foo.com/bar/baz

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNLEJrQ4+tX7+I
vyMLYg1xlz0TBEnZqPM3A81QoB0w5XuE5/wYp2vLmqz3c8b3tIoxSjl+i0E9KPKG
4YgFkQCOQFu2UBTjzlt2+zlWsFwGPXkKsmCNRw8Ey8cJqH4krk974yw1TQVZ8S/h
0yiyEsCgxjSHR1+Zjv7IqNBVuw+zgwR5QyirxNuaddrwysxotESoROTlesXiURfK
ywHWcIGgaxmaOAszcLjIK4zNkSYziVf7AMq4NvDmZWH/fe40nBhh4AflufRkcI/j
xogrCF6zA1CWwJO4AtSbnLqw7okkZlWY4opKG8VQXB9JwztPBSadCEwYh48DDM5l
Gyk0QPQ7AgMBAAECggEAPUEPe1KqXIqPdlYKMmXNubH7a+gF3FPSeQRLkdybmnOI
0v8nhnzFc/3z0dTg/CcMcvbD/4G3sni9H4PJxk+qKM4SE3YJJpH/QKB0V+tDLiHA
N29V4Bfs5fOosrt5HZ5k4InMMwWRqW6WawdNyQxMMjO6jwDCC+hjLKkpuc6lO2RD
DxSbZ1qnoEFevhn/A6WUaq/aQ+S56NvczUksP/EDsGD0aPSDpYx/ghj1Ps8A/H75
K+TnDsWpdMgW3/BIAAvIGRm5AVROuISwoAAtgvB1+fm9lMGJ/EPCtmCm3UZwmtYO
+YFznC4zvTzNZ7QswstO9A1FRGm1X90G6Rkdr1CCcQKBgQD1nLMFS6WBBTZEDk74
7fOmf0wBWdPW7kzcJfSz8dcedBfsZSffBPo23ePFHamGGW8YZRk48lBfNjPEXT/v
Otn4CoQIbEWPq8xIWjkAEP8JPj+t1KPkxFcL2r5+XlkSj+lmrvTlzsncz/QcU++j
MsKq50jshX0R1nf9KkZHsqlz7QKBgQDV2bcR0T7LdYvNon5gPbHv046Oc3XEQLD4
qlUsCxoIiSJCMe/mVrUI6EJl5qUH6pUO9FEyUSgQcIHZ0cYl6LnUSTR0+v6VzSOL
fHVb470RmOEorcPVeTV3craMhyoFBWTBs5J1ipq7/1bjrt1mqqoIXtdaHARQ7g+j
l2m9T3pTxwKBgQCVjDP0pXrAdEv2ZD/xkfEpD5lXuTojRDXIVdQJPNee01E9vtno
ET/I6JInE0iFPjdGw0f4RJJrZrVeeujS4SKWxNBf0I0KTbC03TqXr8GZ/y0GytKA
GIxny2jxyMCzbKzQuPakdqSyrmwUU4c191JQfUMJwL3Gfq/Qlkz0mvRSgQKBgDLV
xVID9rRw5eHlHbuNDu5e0QWF5tnXMvp5qzOEFBUxXCZ2LnwH4pMSey40DMj796EN
bPvUFP0LcaKw3jSGxR30pPal77z2fdubo15LnddAC04bOHFXleEmMMTpIJD+/juZ
j1hHsY69HQ+UsubD7RV+Th0KifAfFzKhPK6W5RJjAoGADfvdJhDFh/EqlS0IwKcP
SkZeIEKZjXQoyQHQs9BSEKS/YljOh1T/bSUgHasvQBLru2DwBZtjOUOr3AnspUWc
Ds6esvYqCmrejbAgH339q3ljyUAszOE5sCsz924WhMDzSaGCG4diPqIdRvXmQEhs
MjLXW5+g+AJOBB2XhJaxmB8=
-----END PRIVATE KEY-----

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID1zCCAr+gAwIBAgIUM1A1YAb9yiRy8KEZ0Yw+Oqeyff0wDQYJKoZIhvcNAQEL
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIz
MDUxODE3MTYwNVoXDTMzMDUxNTE3MTYwNVowFjEUMBIGA1UEAwwLdGVzdGNsaWVu
dDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNLEJrQ4+tX7+IvyML
Yg1xlz0TBEnZqPM3A81QoB0w5XuE5/wYp2vLmqz3c8b3tIoxSjl+i0E9KPKG4YgF
kQCOQFu2UBTjzlt2+zlWsFwGPXkKsmCNRw8Ey8cJqH4krk974yw1TQVZ8S/h0yiy
EsCgxjSHR1+Zjv7IqNBVuw+zgwR5QyirxNuaddrwysxotESoROTlesXiURfKywHW
cIGgaxmaOAszcLjIK4zNkSYziVf7AMq4NvDmZWH/fe40nBhh4AflufRkcI/jxogr
CF6zA1CWwJO4AtSbnLqw7okkZlWY4opKG8VQXB9JwztPBSadCEwYh48DDM5lGyk0
QPQ7AgMBAAGjgdwwgdkwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwIwYDVR0RBBww
GoYYc3BpZmZlOi8vZm9vLmNvbS9iYXIvYmF6MB0GA1UdDgQWBBTnAcMwRonIKLo4
MPWq1QNGJFbt9TB7BgNVHSMEdDByoVqkWDBWMQswCQYDVQQGEwJBVTETMBEGA1UE
CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MQ8wDQYDVQQDDAZ0ZXN0Y2GCFFqz9Fbx3Mviz+lLmDbYi/YAYQ+aMA0GCSqGSIb3
DQEBCwUAA4IBAQA2rDFPPcQEbQbV8ywaKaSsAHP8te7GVxBC3F18TVD+i8HNL1UU
gYXdVXYMPPKaNgOTY2SXx/73J74T3rTBf4aL/GBe9qOiNtV1lwVJDvswBHZ5obtd
aeBq3o1z0af9lGX/Q6JjO97/uovAuqmDGXFSPafTZAcvepKmlvQcmJnEUantlUCG
UWchPMbyoaOFmgD+b1fmj2/A2jFUyaK1tk5KlJMYCrWK16tUtVZk4mWytjijhpaJ
QBYOaX2tE1+E7LaXIVg/ZmYi7yfXCakopKCLY8DKDbLpXZKtUFjQAA8xXCcRV2Zc
5/et+LMsmzeD8rn3m5KN7uzWNTbQl/pvJi1Y
-----END CERTIFICATE-----

@ -496,6 +496,7 @@ grpc_cc_test(
"//:gpr",
"//:grpc",
"//src/core:grpc_rbac_engine",
"//test/core/util:audit_logging_utils",
"//test/core/util:grpc_test_util",
"//test/core/util:grpc_test_util_base",
],

@ -26,85 +26,26 @@
#include "src/core/lib/json/json.h"
#include "src/core/lib/security/authorization/audit_logging.h"
#include "test/core/util/audit_logging_utils.h"
#include "test/core/util/evaluate_args_test_util.h"
namespace grpc_core {
namespace {
constexpr absl::string_view kLoggerName = "test_logger";
constexpr absl::string_view kPolicyName = "authz";
constexpr absl::string_view kSpiffeId = "spiffe://foo";
constexpr absl::string_view kRpcMethod = "/foo.Bar/Echo";
using experimental::AuditContext;
using experimental::AuditLogger;
using experimental::AuditLoggerFactory;
using experimental::AuditLoggerRegistry;
using experimental::RegisterAuditLoggerFactory;
// This test class copies the audit context.
struct TestAuditContext {
explicit TestAuditContext(const AuditContext& context)
: rpc_method(context.rpc_method()),
principal(context.principal()),
policy_name(context.policy_name()),
matched_rule(context.matched_rule()),
authorized(context.authorized()) {}
std::string rpc_method;
std::string principal;
std::string policy_name;
std::string matched_rule;
bool authorized;
};
class TestAuditLogger : public AuditLogger {
public:
explicit TestAuditLogger(
std::vector<std::unique_ptr<TestAuditContext>>* contexts)
: contexts_(contexts) {}
absl::string_view name() const override { return kLoggerName; }
void Log(const AuditContext& context) override {
contexts_->push_back(std::make_unique<TestAuditContext>(context));
}
private:
std::vector<std::unique_ptr<TestAuditContext>>* contexts_;
};
class TestAuditLoggerFactory : public AuditLoggerFactory {
public:
class TestAuditLoggerConfig : public AuditLoggerFactory::Config {
absl::string_view name() const override { return kLoggerName; }
std::string ToString() const override { return ""; }
};
explicit TestAuditLoggerFactory(
std::vector<std::unique_ptr<TestAuditContext>>* contexts)
: contexts_(contexts) {}
absl::string_view name() const override { return kLoggerName; }
absl::StatusOr<std::unique_ptr<AuditLoggerFactory::Config>>
ParseAuditLoggerConfig(const Json&) override {
Crash("unreachable");
return nullptr;
}
std::unique_ptr<AuditLogger> CreateAuditLogger(
std::unique_ptr<AuditLoggerFactory::Config>) override {
return std::make_unique<TestAuditLogger>(contexts_);
}
private:
std::vector<std::unique_ptr<TestAuditContext>>* contexts_;
};
using testing::TestAuditLoggerFactory;
class GrpcAuthorizationEngineTest : public ::testing::Test {
protected:
void SetUp() override {
RegisterAuditLoggerFactory(
std::make_unique<TestAuditLoggerFactory>(&contexts_));
std::make_unique<TestAuditLoggerFactory>(&audit_logs_));
evaluate_args_util_.AddPairToMetadata(":path", kRpcMethod.data());
evaluate_args_util_.AddPropertyToAuthContext(
GRPC_PEER_SPIFFE_ID_PROPERTY_NAME, kSpiffeId.data());
@ -112,7 +53,7 @@ class GrpcAuthorizationEngineTest : public ::testing::Test {
void TearDown() override { AuditLoggerRegistry::TestOnlyResetRegistry(); }
std::vector<std::unique_ptr<TestAuditContext>> contexts_;
std::vector<std::string> audit_logs_;
EvaluateArgsTestUtil evaluate_args_util_;
};
@ -209,13 +150,13 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerNoneNotInvokedOnAllowedRequest) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kNone;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kAllow);
EXPECT_EQ(decision.matching_policy_name, "policy1");
EXPECT_EQ(contexts_.size(), 0);
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthorizationEngineTest, AuditLoggerNoneNotInvokedOnDeniedRequest) {
@ -229,13 +170,13 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerNoneNotInvokedOnDeniedRequest) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kNone;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kDeny);
EXPECT_EQ(decision.matching_policy_name, "");
EXPECT_EQ(contexts_.size(), 0);
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnDenyNotInvoked) {
@ -247,13 +188,13 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnDenyNotInvoked) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnDeny;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kAllow);
EXPECT_EQ(decision.matching_policy_name, "policy1");
EXPECT_EQ(contexts_.size(), 0);
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnAllowNotInvoked) {
@ -267,13 +208,13 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnAllowNotInvoked) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnAllow;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kDeny);
EXPECT_EQ(decision.matching_policy_name, "");
EXPECT_EQ(contexts_.size(), 0);
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnAllowInvoked) {
@ -285,18 +226,17 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnAllowInvoked) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnAllow;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kAllow);
EXPECT_EQ(decision.matching_policy_name, "policy1");
ASSERT_EQ(contexts_.size(), 1);
EXPECT_EQ(contexts_[0]->rpc_method, kRpcMethod);
EXPECT_EQ(contexts_[0]->principal, kSpiffeId);
EXPECT_EQ(contexts_[0]->policy_name, kPolicyName);
EXPECT_EQ(contexts_[0]->matched_rule, "policy1");
EXPECT_EQ(contexts_[0]->authorized, true);
EXPECT_THAT(audit_logs_,
::testing::ElementsAre(absl::StrFormat(
"{\"authorized\":true,\"matched_rule\":\"policy1\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod)));
}
TEST_F(GrpcAuthorizationEngineTest,
@ -309,18 +249,17 @@ TEST_F(GrpcAuthorizationEngineTest,
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnDenyAndAllow;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kAllow);
EXPECT_EQ(decision.matching_policy_name, "policy1");
ASSERT_EQ(contexts_.size(), 1);
EXPECT_EQ(contexts_[0]->rpc_method, kRpcMethod);
EXPECT_EQ(contexts_[0]->principal, kSpiffeId);
EXPECT_EQ(contexts_[0]->policy_name, kPolicyName);
EXPECT_EQ(contexts_[0]->matched_rule, "policy1");
EXPECT_EQ(contexts_[0]->authorized, true);
EXPECT_THAT(audit_logs_,
::testing::ElementsAre(absl::StrFormat(
"{\"authorized\":true,\"matched_rule\":\"policy1\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod)));
}
TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnDenyInvoked) {
@ -334,18 +273,17 @@ TEST_F(GrpcAuthorizationEngineTest, AuditLoggerOnDenyInvoked) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnDeny;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kDeny);
EXPECT_EQ(decision.matching_policy_name, "");
ASSERT_EQ(contexts_.size(), 1);
EXPECT_EQ(contexts_[0]->rpc_method, kRpcMethod);
EXPECT_EQ(contexts_[0]->principal, kSpiffeId);
EXPECT_EQ(contexts_[0]->policy_name, kPolicyName);
EXPECT_EQ(contexts_[0]->matched_rule, "");
EXPECT_EQ(contexts_[0]->authorized, false);
EXPECT_THAT(audit_logs_,
::testing::ElementsAre(absl::StrFormat(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod)));
}
TEST_F(GrpcAuthorizationEngineTest,
@ -360,18 +298,17 @@ TEST_F(GrpcAuthorizationEngineTest,
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnDenyAndAllow;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kDeny);
EXPECT_EQ(decision.matching_policy_name, "");
ASSERT_EQ(contexts_.size(), 1);
EXPECT_EQ(contexts_[0]->rpc_method, kRpcMethod);
EXPECT_EQ(contexts_[0]->principal, kSpiffeId);
EXPECT_EQ(contexts_[0]->policy_name, kPolicyName);
EXPECT_EQ(contexts_[0]->matched_rule, "");
EXPECT_EQ(contexts_[0]->authorized, false);
EXPECT_THAT(audit_logs_,
::testing::ElementsAre(absl::StrFormat(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod)));
}
TEST_F(GrpcAuthorizationEngineTest, MultipleAuditLoggerInvoked) {
@ -385,22 +322,25 @@ TEST_F(GrpcAuthorizationEngineTest, MultipleAuditLoggerInvoked) {
std::move(policies));
rbac.audit_condition = Rbac::AuditCondition::kOnDenyAndAllow;
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
rbac.logger_configs.push_back(
std::make_unique<TestAuditLoggerFactory::TestAuditLoggerConfig>());
std::make_unique<TestAuditLoggerFactory::Config>());
GrpcAuthorizationEngine engine(std::move(rbac));
AuthorizationEngine::Decision decision =
engine.Evaluate(evaluate_args_util_.MakeEvaluateArgs());
EXPECT_EQ(decision.type, AuthorizationEngine::Decision::Type::kDeny);
EXPECT_EQ(decision.matching_policy_name, "");
ASSERT_EQ(contexts_.size(), 2);
for (const auto& context : contexts_) {
EXPECT_EQ(context->rpc_method, kRpcMethod);
EXPECT_EQ(context->principal, kSpiffeId);
EXPECT_EQ(context->policy_name, kPolicyName);
EXPECT_EQ(context->matched_rule, "");
EXPECT_EQ(context->authorized, false);
}
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
absl::StrFormat(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod),
absl::StrFormat(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_"
"name\":\"%s\",\"principal\":\"%s\",\"rpc_method\":\"%s\"}",
kPolicyName, kSpiffeId, kRpcMethod)));
}
} // namespace grpc_core

@ -422,6 +422,22 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "audit_logging_utils",
srcs = ["audit_logging_utils.cc"],
hdrs = ["audit_logging_utils.h"],
external_deps = [
"absl/status:statusor",
"absl/strings",
],
deps = [
"//:gpr",
"//:gpr_platform",
"//:grpc",
"//src/core:json_writer",
],
)
grpc_proto_library(
name = "fuzzing_channel_args_proto",
srcs = ["fuzzing_channel_args.proto"],

@ -0,0 +1,76 @@
//
// 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 "test/core/util/audit_logging_utils.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc_audit_logging.h>
#include <grpc/support/json.h>
#include "src/core/lib/json/json_writer.h"
namespace grpc_core {
namespace testing {
namespace {
constexpr absl::string_view kLoggerName = "test_logger";
using experimental::AuditContext;
using experimental::AuditLogger;
using experimental::AuditLoggerFactory;
using experimental::Json;
} // namespace
absl::string_view TestAuditLogger::name() const { return kLoggerName; }
void TestAuditLogger::Log(const AuditContext& context) {
audit_logs_->push_back(JsonDump(Json::FromObject({
{"rpc_method", Json::FromString(std::string(context.rpc_method()))},
{"principal", Json::FromString(std::string(context.principal()))},
{"policy_name", Json::FromString(std::string(context.policy_name()))},
{"matched_rule", Json::FromString(std::string(context.matched_rule()))},
{"authorized", Json::FromBool(context.authorized())},
})));
}
absl::string_view TestAuditLoggerFactory::Config::name() const {
return kLoggerName;
}
absl::string_view TestAuditLoggerFactory::name() const { return kLoggerName; }
absl::StatusOr<std::unique_ptr<AuditLoggerFactory::Config>>
TestAuditLoggerFactory::ParseAuditLoggerConfig(const Json&) {
return std::make_unique<Config>();
}
std::unique_ptr<AuditLogger> TestAuditLoggerFactory::CreateAuditLogger(
std::unique_ptr<AuditLoggerFactory::Config>) {
return std::make_unique<TestAuditLogger>(audit_logs_);
}
} // namespace testing
} // namespace grpc_core

@ -0,0 +1,70 @@
//
// 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_TEST_CORE_UTIL_AUDIT_LOGGING_UTILS_H
#define GRPC_TEST_CORE_UTIL_AUDIT_LOGGING_UTILS_H
#include <grpc/support/port_platform.h>
#include <memory>
#include <string>
#include <vector>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include <grpc/grpc_audit_logging.h>
#include <grpc/support/json.h>
namespace grpc_core {
namespace testing {
class TestAuditLogger : public experimental::AuditLogger {
public:
explicit TestAuditLogger(std::vector<std::string>* audit_logs)
: audit_logs_(audit_logs) {}
absl::string_view name() const override;
void Log(const experimental::AuditContext& context) override;
private:
std::vector<std::string>* audit_logs_;
};
class TestAuditLoggerFactory : public experimental::AuditLoggerFactory {
public:
class Config : public AuditLoggerFactory::Config {
absl::string_view name() const override;
std::string ToString() const override { return "{}"; }
};
explicit TestAuditLoggerFactory(std::vector<std::string>* audit_logs)
: audit_logs_(audit_logs) {}
absl::string_view name() const override;
absl::StatusOr<std::unique_ptr<AuditLoggerFactory::Config>>
ParseAuditLoggerConfig(const experimental::Json&) override;
std::unique_ptr<experimental::AuditLogger> CreateAuditLogger(
std::unique_ptr<AuditLoggerFactory::Config>) override;
private:
std::vector<std::string>* audit_logs_;
};
} // namespace testing
} // namespace grpc_core
#endif // GRPC_TEST_CORE_UTIL_AUDIT_LOGGING_UTILS_H

@ -943,8 +943,8 @@ grpc_cc_test(
srcs = ["grpc_authz_end2end_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
"//src/core/tsi/test_creds:client.key",
"//src/core/tsi/test_creds:client.pem",
"//src/core/tsi/test_creds:client-with-spiffe.key",
"//src/core/tsi/test_creds:client-with-spiffe.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
@ -960,6 +960,7 @@ grpc_cc_test(
"//:grpc++_authorization_provider",
"//src/proto/grpc/testing:echo_messages_proto",
"//src/proto/grpc/testing:echo_proto",
"//test/core/util:audit_logging_utils",
"//test/core/util:grpc_test_util",
"//test/cpp/util:test_util",
],

@ -12,22 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/audit_logging.h>
#include <grpcpp/security/authorization_policy_provider.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/authorization/audit_logging.h"
#include "src/core/lib/security/authorization/grpc_authorization_policy_provider.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"
#include "src/cpp/client/secure_credentials.h"
#include "src/cpp/server/secure_server_credentials.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/core/util/audit_logging_utils.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#include "test/core/util/tls_utils.h"
@ -40,11 +45,17 @@ namespace {
constexpr char kCaCertPath[] = "src/core/tsi/test_creds/ca.pem";
constexpr char kServerCertPath[] = "src/core/tsi/test_creds/server1.pem";
constexpr char kServerKeyPath[] = "src/core/tsi/test_creds/server1.key";
constexpr char kClientCertPath[] = "src/core/tsi/test_creds/client.pem";
constexpr char kClientKeyPath[] = "src/core/tsi/test_creds/client.key";
constexpr char kClientCertPath[] =
"src/core/tsi/test_creds/client-with-spiffe.pem";
constexpr char kClientKeyPath[] =
"src/core/tsi/test_creds/client-with-spiffe.key";
constexpr char kMessage[] = "Hello";
using experimental::RegisterAuditLoggerFactory;
using grpc_core::experimental::AuditLoggerRegistry;
using grpc_core::testing::TestAuditLoggerFactory;
std::string ReadFile(const char* file_path) {
grpc_slice slice;
GPR_ASSERT(
@ -82,9 +93,14 @@ class GrpcAuthzEnd2EndTest : public ::testing::Test {
channel_options.watch_identity_key_cert_pairs();
channel_options.watch_root_certs();
channel_creds_ = grpc::experimental::TlsCredentials(channel_options);
RegisterAuditLoggerFactory(
std::make_unique<TestAuditLoggerFactory>(&audit_logs_));
}
~GrpcAuthzEnd2EndTest() override { server_->Shutdown(); }
~GrpcAuthzEnd2EndTest() override {
AuditLoggerRegistry::TestOnlyResetRegistry();
server_->Shutdown();
}
// Replaces existing credentials with insecure credentials.
void UseInsecureCredentials() {
@ -144,6 +160,7 @@ class GrpcAuthzEnd2EndTest : public ::testing::Test {
std::unique_ptr<Server> server_;
std::shared_ptr<ServerCredentials> server_creds_;
std::shared_ptr<ChannelCredentials> channel_creds_;
std::vector<std::string> audit_logs_;
};
TEST_F(GrpcAuthzEnd2EndTest,
@ -415,6 +432,658 @@ TEST_F(GrpcAuthzEnd2EndTest,
EXPECT_EQ(resp.message(), kMessage);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInAllowWithAuditLoggingNone) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"NONE\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the allow rule.
context.AddMetadata("key-foo", "foo");
status = SendRpc(channel, &context, &resp);
EXPECT_TRUE(status.ok());
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitNoMatchWithAuditLoggingNone) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"NONE\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Does not match the allow rule or deny rule.
context.AddMetadata("key-foo", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInDenyWithAuditLoggingNone) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"NONE\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the deny rule.
context.AddMetadata("key-bar", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInAllowWithAuditLoggingOnDeny) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the allow rule.
context.AddMetadata("key-foo", "foo");
status = SendRpc(channel, &context, &resp);
EXPECT_TRUE(status.ok());
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitNoMatchWithAuditLoggingOnDeny) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Does not match the allow rule or deny rule.
context.AddMetadata("key-foo", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_name\":"
"\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInDenyWithAuditLoggingOnDeny) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the deny rule.
context.AddMetadata("key-bar", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":false,\"matched_rule\":\"deny_bar\",\"policy_name\":"
"\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInAllowWithAuditLoggingOnAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the allow rule.
context.AddMetadata("key-foo", "foo");
status = SendRpc(channel, &context, &resp);
EXPECT_TRUE(status.ok());
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":true,\"matched_rule\":\"allow_foo\",\"policy_"
"name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitNoMatchWithAuditLoggingOnAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Does not match the allow rule. No audit log emitted.
context.AddMetadata("key-foo", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitMatchInDenyWithAuditLoggingOnAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the deny rule. No audit log emitted.
context.AddMetadata("key-bar", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_EQ(audit_logs_.size(), 0);
}
TEST_F(GrpcAuthzEnd2EndTest,
StaticInitMatchInAllowWithAuditLoggingOnDenyAndAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY_AND_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the allow rule.
context.AddMetadata("key-foo", "foo");
status = SendRpc(channel, &context, &resp);
EXPECT_TRUE(status.ok());
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":true,\"matched_rule\":\"allow_foo\",\"policy_"
"name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest, StaticInitNoMatchWithAuditLoggingOnDenyAndAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY_AND_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Does not match the allow rule.
context.AddMetadata("key-foo", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":false,\"matched_rule\":\"\",\"policy_"
"name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest,
StaticInitMatchInDenyWithAuditLoggingOnDenyAndAllow) {
std::string policy =
"{"
" \"name\": \"authz\","
" \"allow_rules\": ["
" {"
" \"name\": \"allow_foo\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-foo\","
" \"values\": [\"foo\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"deny_rules\": ["
" {"
" \"name\": \"deny_bar\","
" \"request\": {"
" \"headers\": ["
" {"
" \"key\": \"key-bar\","
" \"values\": [\"bar\"]"
" }"
" ]"
" }"
" }"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY_AND_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
InitServer(CreateStaticAuthzPolicyProvider(policy));
auto channel = BuildChannel();
grpc::testing::EchoResponse resp;
grpc::Status status;
ClientContext context;
// Matches the deny rule.
context.AddMetadata("key-bar", "bar");
status = SendRpc(channel, &context, &resp);
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":false,\"matched_rule\":\"deny_bar\",\"policy_"
"name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
}
TEST_F(GrpcAuthzEnd2EndTest,
FileWatcherInitAllowsRpcRequestNoMatchInDenyMatchInAllow) {
std::string policy =
@ -774,7 +1443,7 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherInvalidPolicyRefreshSkipsReload) {
->SetCallbackForTesting(nullptr);
}
TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
TEST_F(GrpcAuthzEnd2EndTest, FileWatcherWithAuditLoggingRecoversFromFailure) {
std::string policy =
"{"
" \"name\": \"authz\","
@ -787,7 +1456,15 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
" ]"
" }"
" }"
" ]"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_ALLOW\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
grpc_core::testing::TmpFile tmp_policy(policy);
auto provider = CreateFileWatcherAuthzPolicyProvider(tmp_policy.name(), 1);
@ -798,6 +1475,13 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
grpc::Status status = SendRpc(channel, &context1, &resp1);
EXPECT_TRUE(status.ok());
EXPECT_EQ(resp1.message(), kMessage);
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":true,\"matched_rule\":\"allow_echo\","
"\"policy_name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
audit_logs_.clear();
gpr_event on_first_reload_done;
gpr_event_init(&on_first_reload_done);
std::function<void(bool contents_changed, absl::Status status)> callback1 =
@ -823,6 +1507,13 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
status = SendRpc(channel, &context2, &resp2);
EXPECT_TRUE(status.ok());
EXPECT_EQ(resp2.message(), kMessage);
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":true,\"matched_rule\":\"allow_echo\","
"\"policy_name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
audit_logs_.clear();
gpr_event on_second_reload_done;
gpr_event_init(&on_second_reload_done);
std::function<void(bool contents_changed, absl::Status status)> callback2 =
@ -835,7 +1526,8 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
dynamic_cast<grpc_core::FileWatcherAuthorizationPolicyProvider*>(
provider->c_provider())
->SetCallbackForTesting(std::move(callback2));
// Replace the existing invalid policy with a valid authorization policy.
// Replace the existing invalid policy with a valid authorization
// policy.
policy =
"{"
" \"name\": \"authz\","
@ -858,7 +1550,15 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
" ]"
" }"
" }"
" ]"
" ],"
" \"audit_logging_options\": {"
" \"audit_condition\": \"ON_DENY\","
" \"audit_loggers\": ["
" {"
" \"name\": \"test_logger\""
" }"
" ]"
" }"
"}";
tmp_policy.RewriteFile(policy);
// Wait for the provider's refresh thread to read the updated files.
@ -871,6 +1571,12 @@ TEST_F(GrpcAuthzEnd2EndTest, FileWatcherRecoversFromFailure) {
EXPECT_EQ(status.error_code(), grpc::StatusCode::PERMISSION_DENIED);
EXPECT_EQ(status.error_message(), "Unauthorized RPC request rejected.");
EXPECT_TRUE(resp3.message().empty());
EXPECT_THAT(
audit_logs_,
::testing::ElementsAre(
"{\"authorized\":false,\"matched_rule\":\"deny_echo\","
"\"policy_name\":\"authz\",\"principal\":\"spiffe://foo.com/bar/"
"baz\",\"rpc_method\":\"/grpc.testing.EchoTestService/Echo\"}"));
dynamic_cast<grpc_core::FileWatcherAuthorizationPolicyProvider*>(
provider->c_provider())
->SetCallbackForTesting(nullptr);

Loading…
Cancel
Save