mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
11 KiB
315 lines
11 KiB
// 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)) |
|
}) |
|
} |
|
}
|
|
|