mirror of https://github.com/grpc/grpc-go.git
authz: End2End test for AuditLogger (#6304)
* Draft of e2e test * No Audit, Audit on Allow and Deny * Audit on Allow, Audit on Deny * fix typo * SPIFFE related testing * SPIFFE Id validation and certs creation script * Address PR comments * Wrap tests using grpctest.Tester * Address PR comments * Change package name to authz_test to fit other end2end tests * Add licence header, remove SPIFFE slice * Licence year change * Address PR comments part 1 * Address PR comments part 2 * Address PR comments part 3 * Address PR comments final part * Drop newline for a brace * Address PR comments, fix outdated function comment * Address PR comments * Fix typo * Remove unused var * Address PR comment, change most test error handling to Errorf * Address PR comments
This commit is contained in:
parent
2b1d70be02
commit
8edfa1a17b
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package audit_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/authz"
|
||||
"google.golang.org/grpc/authz/audit"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
"google.golang.org/grpc/internal/stubserver"
|
||||
testgrpc "google.golang.org/grpc/interop/grpc_testing"
|
||||
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/testdata"
|
||||
|
||||
_ "google.golang.org/grpc/authz/audit/stdout"
|
||||
)
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
type statAuditLogger struct {
|
||||
authzDecisionStat map[bool]int // Map to hold the counts of authorization decisions
|
||||
lastEvent *audit.Event // Field to store last received event
|
||||
}
|
||||
|
||||
func (s *statAuditLogger) Log(event *audit.Event) {
|
||||
s.authzDecisionStat[event.Authorized]++
|
||||
*s.lastEvent = *event
|
||||
}
|
||||
|
||||
type loggerBuilder struct {
|
||||
authzDecisionStat map[bool]int
|
||||
lastEvent *audit.Event
|
||||
}
|
||||
|
||||
func (loggerBuilder) Name() string {
|
||||
return "stat_logger"
|
||||
}
|
||||
|
||||
func (lb *loggerBuilder) Build(audit.LoggerConfig) audit.Logger {
|
||||
return &statAuditLogger{
|
||||
authzDecisionStat: lb.authzDecisionStat,
|
||||
lastEvent: lb.lastEvent,
|
||||
}
|
||||
}
|
||||
|
||||
func (*loggerBuilder) ParseLoggerConfig(config json.RawMessage) (audit.LoggerConfig, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TestAuditLogger examines audit logging invocations using four different
|
||||
// authorization policies. It covers scenarios including a disabled audit,
|
||||
// auditing both 'allow' and 'deny' outcomes, and separately auditing 'allow'
|
||||
// and 'deny' outcomes. Additionally, it checks if SPIFFE ID from a certificate
|
||||
// is propagated correctly.
|
||||
func (s) TestAuditLogger(t *testing.T) {
|
||||
// Each test data entry contains an authz policy for a grpc server,
|
||||
// how many 'allow' and 'deny' outcomes we expect (each test case makes 2
|
||||
// unary calls and one client-streaming call), and a structure to check if
|
||||
// the audit.Event fields are properly populated. Additionally, we specify
|
||||
// directly which authz outcome we expect from each type of call.
|
||||
tests := []struct {
|
||||
name string
|
||||
authzPolicy string
|
||||
wantAuthzOutcomes map[bool]int
|
||||
eventContent *audit.Event
|
||||
wantUnaryCallCode codes.Code
|
||||
wantStreamingCallCode codes.Code
|
||||
}{
|
||||
{
|
||||
name: "No audit",
|
||||
authzPolicy: `{
|
||||
"name": "authz",
|
||||
"allow_rules": [
|
||||
{
|
||||
"name": "allow_UnaryCall",
|
||||
"request": {
|
||||
"paths": [
|
||||
"/grpc.testing.TestService/UnaryCall"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"audit_logging_options": {
|
||||
"audit_condition": "NONE",
|
||||
"audit_loggers": [
|
||||
{
|
||||
"name": "stat_logger",
|
||||
"config": {},
|
||||
"is_optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
wantAuthzOutcomes: map[bool]int{true: 0, false: 0},
|
||||
wantUnaryCallCode: codes.OK,
|
||||
wantStreamingCallCode: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
name: "Allow All Deny Streaming - Audit All",
|
||||
authzPolicy: `{
|
||||
"name": "authz",
|
||||
"allow_rules": [
|
||||
{
|
||||
"name": "allow_all",
|
||||
"request": {
|
||||
"paths": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deny_rules": [
|
||||
{
|
||||
"name": "deny_all",
|
||||
"request": {
|
||||
"paths": [
|
||||
"/grpc.testing.TestService/StreamingInputCall"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"audit_logging_options": {
|
||||
"audit_condition": "ON_DENY_AND_ALLOW",
|
||||
"audit_loggers": [
|
||||
{
|
||||
"name": "stat_logger",
|
||||
"config": {},
|
||||
"is_optional": false
|
||||
},
|
||||
{
|
||||
"name": "stdout_logger",
|
||||
"is_optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
wantAuthzOutcomes: map[bool]int{true: 2, false: 1},
|
||||
eventContent: &audit.Event{
|
||||
FullMethodName: "/grpc.testing.TestService/StreamingInputCall",
|
||||
Principal: "spiffe://foo.bar.com/client/workload/1",
|
||||
PolicyName: "authz",
|
||||
MatchedRule: "authz_deny_all",
|
||||
Authorized: false,
|
||||
},
|
||||
wantUnaryCallCode: codes.OK,
|
||||
wantStreamingCallCode: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
name: "Allow Unary - Audit Allow",
|
||||
authzPolicy: `{
|
||||
"name": "authz",
|
||||
"allow_rules": [
|
||||
{
|
||||
"name": "allow_UnaryCall",
|
||||
"request": {
|
||||
"paths": [
|
||||
"/grpc.testing.TestService/UnaryCall"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"audit_logging_options": {
|
||||
"audit_condition": "ON_ALLOW",
|
||||
"audit_loggers": [
|
||||
{
|
||||
"name": "stat_logger",
|
||||
"config": {},
|
||||
"is_optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
wantAuthzOutcomes: map[bool]int{true: 2, false: 0},
|
||||
wantUnaryCallCode: codes.OK,
|
||||
wantStreamingCallCode: codes.PermissionDenied,
|
||||
},
|
||||
{
|
||||
name: "Allow Typo - Audit Deny",
|
||||
authzPolicy: `{
|
||||
"name": "authz",
|
||||
"allow_rules": [
|
||||
{
|
||||
"name": "allow_UnaryCall",
|
||||
"request": {
|
||||
"paths": [
|
||||
"/grpc.testing.TestService/UnaryCall_Z"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"audit_logging_options": {
|
||||
"audit_condition": "ON_DENY",
|
||||
"audit_loggers": [
|
||||
{
|
||||
"name": "stat_logger",
|
||||
"config": {},
|
||||
"is_optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}`,
|
||||
wantAuthzOutcomes: map[bool]int{true: 0, false: 3},
|
||||
wantUnaryCallCode: codes.PermissionDenied,
|
||||
wantStreamingCallCode: codes.PermissionDenied,
|
||||
},
|
||||
}
|
||||
// Construct the credentials for the tests and the stub server
|
||||
serverCreds := loadServerCreds(t)
|
||||
clientCreds := loadClientCreds(t)
|
||||
ss := &stubserver.StubServer{
|
||||
UnaryCallF: func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
|
||||
return &testpb.SimpleResponse{}, nil
|
||||
},
|
||||
FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
|
||||
_, err := stream.Recv()
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Setup test statAuditLogger, gRPC test server with authzPolicy, unary
|
||||
// and stream interceptors.
|
||||
lb := &loggerBuilder{
|
||||
authzDecisionStat: map[bool]int{true: 0, false: 0},
|
||||
lastEvent: &audit.Event{},
|
||||
}
|
||||
audit.RegisterLoggerBuilder(lb)
|
||||
i, _ := authz.NewStatic(test.authzPolicy)
|
||||
|
||||
s := grpc.NewServer(
|
||||
grpc.Creds(serverCreds),
|
||||
grpc.ChainUnaryInterceptor(i.UnaryInterceptor),
|
||||
grpc.ChainStreamInterceptor(i.StreamInterceptor))
|
||||
defer s.Stop()
|
||||
testgrpc.RegisterTestServiceServer(s, ss)
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Error listening: %v", err)
|
||||
}
|
||||
go s.Serve(lis)
|
||||
|
||||
// Setup gRPC test client with certificates containing a SPIFFE Id.
|
||||
clientConn, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(clientCreds))
|
||||
if err != nil {
|
||||
t.Fatalf("grpc.Dial(%v) failed: %v", lis.Addr().String(), err)
|
||||
}
|
||||
defer clientConn.Close()
|
||||
client := testgrpc.NewTestServiceClient(clientConn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != test.wantUnaryCallCode {
|
||||
t.Errorf("Unexpected UnaryCall fail: got %v want %v", err, test.wantUnaryCallCode)
|
||||
}
|
||||
if _, err := client.UnaryCall(ctx, &testpb.SimpleRequest{}); status.Code(err) != test.wantUnaryCallCode {
|
||||
t.Errorf("Unexpected UnaryCall fail: got %v want %v", err, test.wantUnaryCallCode)
|
||||
}
|
||||
stream, err := client.StreamingInputCall(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("StreamingInputCall failed:%v", err)
|
||||
}
|
||||
req := &testpb.StreamingInputCallRequest{
|
||||
Payload: &testpb.Payload{
|
||||
Body: []byte("hi"),
|
||||
},
|
||||
}
|
||||
if err := stream.Send(req); err != nil && err != io.EOF {
|
||||
t.Fatalf("stream.Send failed:%v", err)
|
||||
}
|
||||
if _, err := stream.CloseAndRecv(); status.Code(err) != test.wantStreamingCallCode {
|
||||
t.Errorf("Unexpected stream.CloseAndRecv fail: got %v want %v", err, test.wantStreamingCallCode)
|
||||
}
|
||||
|
||||
// Compare expected number of allows/denies with content of the internal
|
||||
// map of statAuditLogger.
|
||||
if diff := cmp.Diff(lb.authzDecisionStat, test.wantAuthzOutcomes); diff != "" {
|
||||
t.Errorf("Authorization decisions do not match\ndiff (-got +want):\n%s", diff)
|
||||
}
|
||||
// Compare last event received by statAuditLogger with expected event.
|
||||
if test.eventContent != nil {
|
||||
if diff := cmp.Diff(lb.lastEvent, test.eventContent); diff != "" {
|
||||
t.Errorf("Unexpected message\ndiff (-got +want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// loadServerCreds constructs TLS containing server certs and CA
|
||||
func loadServerCreds(t *testing.T) credentials.TransportCredentials {
|
||||
t.Helper()
|
||||
cert := loadKeys(t, "x509/server1_cert.pem", "x509/server1_key.pem")
|
||||
certPool := loadCACerts(t, "x509/client_ca_cert.pem")
|
||||
return credentials.NewTLS(&tls.Config{
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientCAs: certPool,
|
||||
})
|
||||
}
|
||||
|
||||
// loadClientCreds constructs TLS containing client certs and CA
|
||||
func loadClientCreds(t *testing.T) credentials.TransportCredentials {
|
||||
t.Helper()
|
||||
cert := loadKeys(t, "x509/client_with_spiffe_cert.pem", "x509/client_with_spiffe_key.pem")
|
||||
roots := loadCACerts(t, "x509/server_ca_cert.pem")
|
||||
return credentials.NewTLS(&tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: roots,
|
||||
ServerName: "x.test.example.com",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// loadKeys loads X509 key pair from the provided file paths.
|
||||
// It is used for loading both client and server certificates for the test
|
||||
func loadKeys(t *testing.T, certPath, key string) tls.Certificate {
|
||||
t.Helper()
|
||||
cert, err := tls.LoadX509KeyPair(testdata.Path(certPath), testdata.Path(key))
|
||||
if err != nil {
|
||||
t.Fatalf("tls.LoadX509KeyPair(%q, %q) failed: %v", certPath, key, err)
|
||||
}
|
||||
return cert
|
||||
}
|
||||
|
||||
// loadCACerts loads CA certificates and constructs x509.CertPool
|
||||
// It is used for loading both client and server CAs for the test
|
||||
func loadCACerts(t *testing.T, certPath string) *x509.CertPool {
|
||||
t.Helper()
|
||||
ca, err := os.ReadFile(testdata.Path(certPath))
|
||||
if err != nil {
|
||||
t.Fatalf("os.ReadFile(%q) failed: %v", certPath, err)
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(ca) {
|
||||
t.Fatal("Failed to append certificates")
|
||||
}
|
||||
return roots
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
eSCMKThlBQ57WubHTi2TbFBVKph/rP0=
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,17 @@
|
|||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
|
||||
[req_distinguished_name]
|
||||
|
||||
[req_attributes]
|
||||
|
||||
[test_client]
|
||||
basicConstraints = critical,CA:FALSE
|
||||
subjectKeyIdentifier = hash
|
||||
keyUsage = critical,nonRepudiation,digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = critical,clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
URI = spiffe://foo.bar.com/client/workload/1
|
|
@ -128,5 +128,24 @@ openssl req -x509 \
|
|||
-addext "subjectAltName = URI:spiffe://foo.bar.com/client/workload/1, URI:https://bar.baz.com/client" \
|
||||
-sha256
|
||||
|
||||
# Generate a cert with SPIFFE ID using client_with_spiffe_openssl.cnf
|
||||
openssl req -new \
|
||||
-key client_with_spiffe_key.pem \
|
||||
-out client_with_spiffe_csr.pem \
|
||||
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client1/ \
|
||||
-config ./client_with_spiffe_openssl.cnf \
|
||||
-reqexts test_client
|
||||
openssl x509 -req \
|
||||
-in client_with_spiffe_csr.pem \
|
||||
-CAkey client_ca_key.pem \
|
||||
-CA client_ca_cert.pem \
|
||||
-days 3650 \
|
||||
-set_serial 1000 \
|
||||
-out client_with_spiffe_cert.pem \
|
||||
-extfile ./client_with_spiffe_openssl.cnf \
|
||||
-extensions test_client \
|
||||
-sha256
|
||||
openssl verify -verbose -CAfile client_with_spiffe_cert.pem
|
||||
|
||||
# Cleanup the CSRs.
|
||||
rm *_csr.pem
|
||||
|
|
Loading…
Reference in New Issue