mirror of https://github.com/hashicorp/consul
316 lines
11 KiB
Go
316 lines
11 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package proxycfg
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
)
|
|
|
|
func TestConfigSnapshot_AllowEmptyClusters(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
cfgSnapshot *ConfigSnapshot
|
|
expectedResult bool
|
|
}
|
|
testsCases := []testCase{
|
|
{
|
|
description: "Mesh proxies are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
description: "Ingress gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Terminating gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "API Gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Mesh Gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
|
|
expectedResult: true,
|
|
},
|
|
}
|
|
for _, tc := range testsCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyClusters())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigSnapshot_AllowEmptyListeners(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
cfgSnapshot *ConfigSnapshot
|
|
expectedResult bool
|
|
}
|
|
testsCases := []testCase{
|
|
{
|
|
description: "Mesh proxies are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
description: "Ingress gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Terminating gateways are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
description: "API Gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Mesh Gateways are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
|
|
expectedResult: false,
|
|
},
|
|
}
|
|
for _, tc := range testsCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyListeners())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigSnapshot_AllowEmptyRoutes(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
cfgSnapshot *ConfigSnapshot
|
|
expectedResult bool
|
|
}
|
|
testsCases := []testCase{
|
|
{
|
|
description: "Mesh proxies are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
description: "Ingress gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Terminating gateways are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
|
|
expectedResult: false,
|
|
},
|
|
{
|
|
description: "API Gateways are allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
|
|
expectedResult: true,
|
|
},
|
|
{
|
|
description: "Mesh Gateways are not allowed",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
|
|
expectedResult: false,
|
|
},
|
|
}
|
|
for _, tc := range testsCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.AllowEmptyRoutes())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigSnapshot_LoggerName(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
cfgSnapshot *ConfigSnapshot
|
|
expectedResult string
|
|
}
|
|
testsCases := []testCase{
|
|
{
|
|
description: "Mesh proxies have a logger named ''",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindConnectProxy},
|
|
expectedResult: "",
|
|
},
|
|
{
|
|
description: "Ingress gateways have a logger named 'ingress_gateway'",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindIngressGateway},
|
|
expectedResult: "ingress_gateway",
|
|
},
|
|
{
|
|
description: "Terminating gateways have a logger named 'terminating_gateway'",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindTerminatingGateway},
|
|
expectedResult: "terminating_gateway",
|
|
},
|
|
{
|
|
description: "API Gateways have a logger named ''",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindAPIGateway},
|
|
expectedResult: "",
|
|
},
|
|
{
|
|
description: "Mesh Gateways have a logger named 'mesh_gateway'",
|
|
cfgSnapshot: &ConfigSnapshot{Kind: structs.ServiceKindMeshGateway},
|
|
expectedResult: "mesh_gateway",
|
|
},
|
|
}
|
|
for _, tc := range testsCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
require.Equal(t, tc.expectedResult, tc.cfgSnapshot.LoggerName())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfigSnapshot_Authorize(t *testing.T) {
|
|
type testCase struct {
|
|
description string
|
|
cfgSnapshot *ConfigSnapshot
|
|
configureAuthorizer func(authorizer *acl.MockAuthorizer)
|
|
expectedErrorMessage string
|
|
}
|
|
testsCases := []testCase{
|
|
{
|
|
description: "ConnectProxy - if service write is allowed for the DestinationService then allow.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
Proxy: structs.ConnectProxyConfig{
|
|
DestinationServiceName: "DestinationServiceName",
|
|
},
|
|
},
|
|
expectedErrorMessage: "",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "DestinationServiceName", mock.Anything).Return(acl.Allow)
|
|
},
|
|
},
|
|
{
|
|
description: "ConnectProxy - if service write is not allowed for the DestinationService then deny.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindConnectProxy,
|
|
Proxy: structs.ConnectProxyConfig{
|
|
DestinationServiceName: "DestinationServiceName",
|
|
},
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"DestinationServiceName\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "DestinationServiceName", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "Mesh Gateway - if service write is allowed for the Service then allow.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindMeshGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Allow)
|
|
},
|
|
},
|
|
{
|
|
description: "Mesh Gateway - if service write is not allowed for the Service then deny.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindMeshGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "Terminating Gateway - if service write is allowed for the Service then allow.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "Terminating Gateway - if service write is not allowed for the Service then deny.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "Ingress Gateway - if service write is allowed for the Service then allow.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindIngressGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "Ingress Gateway - if service write is not allowed for the Service then deny.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindIngressGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "API Gateway - if service write is allowed for the Service then allow.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindAPIGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
{
|
|
description: "API Gateway - if service write is not allowed for the Service then deny.",
|
|
cfgSnapshot: &ConfigSnapshot{
|
|
Kind: structs.ServiceKindAPIGateway,
|
|
Service: "Service",
|
|
},
|
|
expectedErrorMessage: "rpc error: code = PermissionDenied desc = Permission denied: token with AccessorID '' lacks permission 'service:write' on \"Service\"",
|
|
configureAuthorizer: func(authz *acl.MockAuthorizer) {
|
|
authz.On("ServiceWrite", "Service", mock.Anything).Return(acl.Deny)
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range testsCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
authz := &acl.MockAuthorizer{}
|
|
authz.On("ToAllow").Return(acl.AllowAuthorizer{Authorizer: authz})
|
|
tc.configureAuthorizer(authz)
|
|
err := tc.cfgSnapshot.Authorize(authz)
|
|
errMsg := ""
|
|
if err != nil {
|
|
errMsg = err.Error()
|
|
}
|
|
// using contains because Enterprise tests append the parition and namespace
|
|
// information to the message.
|
|
require.True(t, strings.Contains(errMsg, tc.expectedErrorMessage))
|
|
})
|
|
}
|
|
}
|