Repository: grpc/grpc-go
File: credentials/credentials_ext_test.go (Go)

Code before:
// that the provided authority is correctly propagated to the server when using TLS.
// It covers both positive and negative cases:  correct authority and incorrect
// authority, expecting the RPC to fail with `UNAVAILABLE` status code error in
// the latter case.
func (s) TestAuthorityCallOptionsWithTLSCreds(t *testing.T) {
	tests := []struct {
		name         string
		expectedAuth string
		wantRPCError bool
	}{
		{
			name:         "CorrectAuthorityWithTLS",
			expectedAuth: "auth.test.example.com",
			wantRPCError: false,
		},
		{
			name:         "IncorrectAuthorityWithTLS",
			expectedAuth: "auth.example.com",
			wantRPCError: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ss := &stubserver.StubServer{
				EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
					return authorityChecker(ctx, tt.expectedAuth)
				},
			}
			if err := ss.StartServer(grpc.Creds(credentials.NewServerTLSFromCert(&cert))); err != nil {
				t.Fatalf("Error starting endpoint server: %v", err)
			}
			defer ss.Stop()

			cc, err := grpc.NewClient(ss.Address, grpc.WithTransportCredentials(creds))
			if err != nil {
				t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
			}
			defer cc.Close()

			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
			defer cancel()

			_, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(tt.expectedAuth))
			if tt.wantRPCError {
				checkUnavailableRPCError(t, err)
			} else if err != nil {
				t.Fatalf("EmptyCall() rpc failed: %v", err)
			}
		})
	}
}

Review comment (bug):
Hmmm.. I'm not convinced about checking the authority header on the server handler, because when the validation fails, the RPC will not even make it to the server. Maybe, checking from the server handler is OK when you actually expect validation to succeed on the client and expect the RPC to reach the server.

You know the certs you are using for the server. So, you can specify an authority override on the client that you expect to work and one that you dont expect to work, because it will fail validation with the peer certificate.

Code after:
	}
	defer cc.Close()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(authority)); status.Code(err) != codes.OK {
		t.Fatalf("EmptyCall() returned status %v, want %v", status.Code(err), codes.OK)
	}

}

// Tests the `grpc.CallAuthority` option with TLS credentials. This test verifies
// that the RPC fails with `UNAVAILABLE` status code and doesn't reach the server
// when an incorrect authority is used.
func (s) TestIncorrectAuthorityWithTLS(t *testing.T) {
	cert, err := tls.LoadX509KeyPair(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
	if err != nil {
		t.Fatalf("Failed to load key pair: %s", err)
	}
	creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
	if err != nil {
		t.Fatalf("Failed to create credentials %v", err)
	}

	serverCalled := make(chan struct{})
	ss := &stubserver.StubServer{
		EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
			close(serverCalled)
			return nil, nil
		},
	}
	if err := ss.StartServer(grpc.Creds(credentials.NewServerTLSFromCert(&cert))); err != nil {
		t.Fatalf("Error starting endpoint server: %v", err)
	}
	defer ss.Stop()

	cc, err := grpc.NewClient(ss.Address, grpc.WithTransportCredentials(creds))
	if err != nil {
		t.Fatalf("grpc.NewClient(%q) = %v", ss.Address, err)
	}
	defer cc.Close()

	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()

	const authority = "auth.example.com"
	if _, err = testgrpc.NewTestServiceClient(cc).EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(authority)); status.Code(err) != codes.Unavailable {
		t.Fatalf("EmptyCall() returned status %v, want %v", status.Code(err), codes.Unavailable)
	}
	select {