mirror of https://github.com/status-im/consul.git
add traffic permissions excludes and tests (#20453)
* add traffic permissions tests * review fixes * Update internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go Co-authored-by: John Landa <jonathanlanda@gmail.com> --------- Co-authored-by: John Landa <jonathanlanda@gmail.com>
This commit is contained in:
parent
1bd253021b
commit
57bad0df85
|
@ -536,6 +536,7 @@ jobs:
|
||||||
-tags "${{ env.GOTAGS }}" \
|
-tags "${{ env.GOTAGS }}" \
|
||||||
-timeout=20m \
|
-timeout=20m \
|
||||||
-parallel=2 \
|
-parallel=2 \
|
||||||
|
-failfast \
|
||||||
-json \
|
-json \
|
||||||
`go list -tags "${{ env.GOTAGS }}" ./... | grep -v peering_commontopo | grep -v upgrade ` \
|
`go list -tags "${{ env.GOTAGS }}" ./... | grep -v peering_commontopo | grep -v upgrade ` \
|
||||||
--target-image ${{ env.CONSUL_LATEST_IMAGE_NAME }} \
|
--target-image ${{ env.CONSUL_LATEST_IMAGE_NAME }} \
|
||||||
|
|
|
@ -732,6 +732,58 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"v2-path-excludes": {
|
||||||
|
intentionDefaultAllow: false,
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathPrefix: "/admin"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DenyPermissions: []*pbproxystate.Permission{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v2-path-method-header-excludes": {
|
||||||
|
intentionDefaultAllow: false,
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/admin",
|
||||||
|
Methods: []string{"POST", "DELETE"},
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "experiment", Present: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DenyPermissions: []*pbproxystate.Permission{},
|
||||||
|
},
|
||||||
|
},
|
||||||
"v2-default-deny": {
|
"v2-default-deny": {
|
||||||
intentionDefaultAllow: false,
|
intentionDefaultAllow: false,
|
||||||
v2TrafficPermissions: &pbproxystate.TrafficPermissions{},
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{},
|
||||||
|
@ -784,6 +836,21 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
v1Intentions: sorted(
|
v1Intentions: sorted(
|
||||||
testSourcePermIntention("web", permSlashPrefix),
|
testSourcePermIntention("web", permSlashPrefix),
|
||||||
),
|
),
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{PathPrefix: "/"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DenyPermissions: []*pbproxystate.Permission{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"default-allow-path-deny": {
|
"default-allow-path-deny": {
|
||||||
intentionDefaultAllow: true,
|
intentionDefaultAllow: true,
|
||||||
|
@ -796,6 +863,7 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
v1Intentions: sorted(
|
v1Intentions: sorted(
|
||||||
testSourcePermIntention("web", permDenySlashPrefix),
|
testSourcePermIntention("web", permDenySlashPrefix),
|
||||||
),
|
),
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{},
|
||||||
},
|
},
|
||||||
// ========================
|
// ========================
|
||||||
"default-allow-deny-all-and-path-allow": {
|
"default-allow-deny-all-and-path-allow": {
|
||||||
|
@ -869,6 +937,7 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
),
|
),
|
||||||
testSourceIntention("*", structs.IntentionActionDeny),
|
testSourceIntention("*", structs.IntentionActionDeny),
|
||||||
),
|
),
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{},
|
||||||
},
|
},
|
||||||
// ========================
|
// ========================
|
||||||
"default-deny-two-path-deny-and-path-allow": {
|
"default-deny-two-path-deny-and-path-allow": {
|
||||||
|
@ -895,6 +964,26 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/admin"},
|
||||||
|
{PathExact: "/v1/secret"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"default-allow-two-path-deny-and-path-allow": {
|
"default-allow-two-path-deny-and-path-allow": {
|
||||||
intentionDefaultAllow: true,
|
intentionDefaultAllow: true,
|
||||||
|
@ -964,6 +1053,163 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
"v2-single-permission-with-excludes": {
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/v1",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-foo", Present: true},
|
||||||
|
},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/secret", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
{PathExact: "/v1/admin", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v2-single-permission-multiple-destination-rules": {
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/v1",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-foo", Present: true},
|
||||||
|
},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/secret", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
{PathExact: "/v1/admin", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathPrefix: "/v2",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-foo", Present: true},
|
||||||
|
},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v2/secret", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
{PathExact: "/v2/admin", Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
{Name: "x-bar", Present: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v2-single-permission-with-kitchen-sink-perms": {
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/v1",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/secret"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathRegex: "/v[123]",
|
||||||
|
Methods: []string{"GET", "HEAD", "OPTIONS"},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/secret"},
|
||||||
|
{PathExact: "/v1/admin", Methods: []string{"GET"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-foo", Present: true},
|
||||||
|
{Name: "x-bar", Exact: "xyz"},
|
||||||
|
{Name: "x-dib", Prefix: "gaz"},
|
||||||
|
{Name: "x-gir", Suffix: "zim"},
|
||||||
|
{Name: "x-zim", Regex: "gi[rR]"},
|
||||||
|
{Name: "z-foo", Present: true, Invert: true},
|
||||||
|
{Name: "z-bar", Exact: "xyz", Invert: true},
|
||||||
|
{Name: "z-dib", Prefix: "gaz", Invert: true},
|
||||||
|
{Name: "z-gir", Suffix: "zim", Invert: true},
|
||||||
|
{Name: "z-zim", Regex: "gi[rR]", Invert: true},
|
||||||
|
},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{PathExact: "/v1/secret"},
|
||||||
|
{Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-baz", Present: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"v2-L4-deny-L7-allow": {
|
||||||
|
v2TrafficPermissions: &pbproxystate.TrafficPermissions{
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/v1",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "x-foo", Present: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DenyPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: makeSpiffe("web", nil),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"default-allow-single-intention-with-kitchen-sink-perms": {
|
"default-allow-single-intention-with-kitchen-sink-perms": {
|
||||||
intentionDefaultAllow: true,
|
intentionDefaultAllow: true,
|
||||||
v1Intentions: sorted(
|
v1Intentions: sorted(
|
||||||
|
@ -1114,7 +1360,6 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
chain.Filters = filters
|
chain.Filters = filters
|
||||||
gotJSON = protoToJSON(t, chain)
|
gotJSON = protoToJSON(t, chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.JSONEq(t, goldenSimple(t, filepath.Join("rbac", name), gotJSON), gotJSON)
|
require.JSONEq(t, goldenSimple(t, filepath.Join("rbac", name), gotJSON), gotJSON)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1152,7 +1397,6 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) {
|
||||||
manager.HttpFilters = filters
|
manager.HttpFilters = filters
|
||||||
gotJSON = protoToJSON(t, manager)
|
gotJSON = protoToJSON(t, manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.JSONEq(t, goldenSimple(t, filepath.Join("rbac", name+"--httpfilter"), gotJSON), gotJSON)
|
require.JSONEq(t, goldenSimple(t, filepath.Join("rbac", name+"--httpfilter"), gotJSON), gotJSON)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -78,12 +78,3 @@ func MakeEnvoyRegexMatch(patt string) *envoy_matcher_v3.RegexMatcher {
|
||||||
Regex: patt,
|
Regex: patt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeEnvoyStringMatcher(patt string) *envoy_matcher_v3.StringMatcher {
|
|
||||||
return &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
|
||||||
SafeRegex: MakeEnvoyRegexMatch(patt),
|
|
||||||
},
|
|
||||||
IgnoreCase: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"httpFilters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"action": "DENY",
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer4": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"any": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"action": "DENY",
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer4": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"any": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": ":method",
|
||||||
|
"stringMatch": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "POST|DELETE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "experiment",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
183
agent/xds/testdata/rbac/v2-single-permission-multiple-destination-rules--httpfilter.golden
vendored
Normal file
183
agent/xds/testdata/rbac/v2-single-permission-multiple-destination-rules--httpfilter.golden
vendored
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/v2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v2/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v2/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
agent/xds/testdata/rbac/v2-single-permission-multiple-destination-rules.golden
vendored
Normal file
8
agent/xds/testdata/rbac/v2-single-permission-multiple-destination-rules.golden
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
104
agent/xds/testdata/rbac/v2-single-permission-with-excludes--httpfilter.golden
vendored
Normal file
104
agent/xds/testdata/rbac/v2-single-permission-with-excludes--httpfilter.golden
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
226
agent/xds/testdata/rbac/v2-single-permission-with-kitchen-sink-perms--httpfilter.golden
vendored
Normal file
226
agent/xds/testdata/rbac/v2-single-permission-with-kitchen-sink-perms--httpfilter.golden
vendored
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
|
||||||
|
"rules": {
|
||||||
|
"policies": {
|
||||||
|
"consul-intentions-layer7-0": {
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"prefix": "/v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "/v[123]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": ":method",
|
||||||
|
"stringMatch": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "GET|HEAD|OPTIONS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": ":method",
|
||||||
|
"stringMatch": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "GET"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"andRules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-bar",
|
||||||
|
"stringMatch": {
|
||||||
|
"exact": "xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-dib",
|
||||||
|
"stringMatch": {
|
||||||
|
"prefix": "gaz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-gir",
|
||||||
|
"stringMatch": {
|
||||||
|
"suffix": "zim"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"name": "x-zim",
|
||||||
|
"stringMatch": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "gi[rR]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"invertMatch": true,
|
||||||
|
"name": "z-foo",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"invertMatch": true,
|
||||||
|
"name": "z-bar",
|
||||||
|
"stringMatch": {
|
||||||
|
"exact": "xyz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"invertMatch": true,
|
||||||
|
"name": "z-dib",
|
||||||
|
"stringMatch": {
|
||||||
|
"prefix": "gaz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"invertMatch": true,
|
||||||
|
"name": "z-gir",
|
||||||
|
"stringMatch": {
|
||||||
|
"suffix": "zim"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"invertMatch": true,
|
||||||
|
"name": "z-zim",
|
||||||
|
"stringMatch": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "gi[rR]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"urlPath": {
|
||||||
|
"path": {
|
||||||
|
"exact": "/v1/secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notRule": {
|
||||||
|
"header": {
|
||||||
|
"name": "x-baz",
|
||||||
|
"presentMatch": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"principals": [
|
||||||
|
{
|
||||||
|
"authenticated": {
|
||||||
|
"principalName": {
|
||||||
|
"safeRegex": {
|
||||||
|
"regex": "^spiffe://test.consul/ns/default/dc/[^/]+/svc/web$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.rbac",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC",
|
||||||
|
"rules": {},
|
||||||
|
"statPrefix": "connect_authz"
|
||||||
|
}
|
||||||
|
}
|
|
@ -226,139 +226,179 @@ func makeL7RBACPolicy(p *pbproxystate.Permission) *envoy_rbac_v3.Policy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func translateRule(dr *pbproxystate.DestinationRule) *envoy_rbac_v3.Permission {
|
||||||
|
var perms []*envoy_rbac_v3.Permission
|
||||||
|
// paths
|
||||||
|
switch {
|
||||||
|
case dr.PathExact != "":
|
||||||
|
perms = append(perms, &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
||||||
|
UrlPath: &envoy_matcher_v3.PathMatcher{
|
||||||
|
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
||||||
|
Path: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
|
||||||
|
Exact: dr.PathExact,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case dr.PathPrefix != "":
|
||||||
|
perms = append(perms, &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
||||||
|
UrlPath: &envoy_matcher_v3.PathMatcher{
|
||||||
|
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
||||||
|
Path: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{
|
||||||
|
Prefix: dr.PathPrefix,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case dr.PathRegex != "":
|
||||||
|
perms = append(perms, &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
||||||
|
UrlPath: &envoy_matcher_v3.PathMatcher{
|
||||||
|
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
||||||
|
Path: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
||||||
|
SafeRegex: response.MakeEnvoyRegexMatch(dr.PathRegex),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// methods
|
||||||
|
if len(dr.Methods) > 0 {
|
||||||
|
methodHeaderRegex := strings.Join(dr.Methods, "|")
|
||||||
|
eh := &envoy_route_v3.HeaderMatcher{
|
||||||
|
Name: ":method",
|
||||||
|
HeaderMatchSpecifier: &envoy_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
||||||
|
SafeRegex: response.MakeEnvoyRegexMatch(methodHeaderRegex),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
perms = append(perms, &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_Header{
|
||||||
|
Header: eh,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// headers
|
||||||
|
for _, hdr := range dr.DestinationRuleHeader {
|
||||||
|
eh := &envoy_route_v3.HeaderMatcher{
|
||||||
|
Name: hdr.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case hdr.Exact != "":
|
||||||
|
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
|
||||||
|
Exact: hdr.Exact,
|
||||||
|
},
|
||||||
|
IgnoreCase: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case hdr.Regex != "":
|
||||||
|
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
||||||
|
SafeRegex: response.MakeEnvoyRegexMatch(hdr.Regex),
|
||||||
|
},
|
||||||
|
IgnoreCase: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case hdr.Prefix != "":
|
||||||
|
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{
|
||||||
|
Prefix: hdr.Prefix,
|
||||||
|
},
|
||||||
|
IgnoreCase: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case hdr.Suffix != "":
|
||||||
|
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_matcher_v3.StringMatcher_Suffix{
|
||||||
|
Suffix: hdr.Suffix,
|
||||||
|
},
|
||||||
|
IgnoreCase: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case hdr.Present:
|
||||||
|
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_PresentMatch{
|
||||||
|
PresentMatch: true,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue // skip this impossible situation
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.Invert {
|
||||||
|
eh.InvertMatch = true
|
||||||
|
}
|
||||||
|
|
||||||
|
perms = append(perms, &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_Header{
|
||||||
|
Header: eh,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return combineAndPermissions(perms)
|
||||||
|
}
|
||||||
|
|
||||||
func permissionsFromDestinationRules(drs []*pbproxystate.DestinationRule) []*envoy_rbac_v3.Permission {
|
func permissionsFromDestinationRules(drs []*pbproxystate.DestinationRule) []*envoy_rbac_v3.Permission {
|
||||||
var perms []*envoy_rbac_v3.Permission
|
var perms []*envoy_rbac_v3.Permission
|
||||||
|
|
||||||
for _, dr := range drs {
|
for _, dr := range drs {
|
||||||
// paths
|
subPerms := make([]*envoy_rbac_v3.Permission, len(dr.Exclude))
|
||||||
switch {
|
for i, er := range dr.Exclude {
|
||||||
case dr.PathExact != "":
|
translated := translateRule(&pbproxystate.DestinationRule{
|
||||||
perms = append(perms, &envoy_rbac_v3.Permission{
|
PathExact: er.PathExact,
|
||||||
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
PathPrefix: er.PathPrefix,
|
||||||
UrlPath: &envoy_matcher_v3.PathMatcher{
|
PathRegex: er.PathRegex,
|
||||||
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
Methods: er.Methods,
|
||||||
Path: &envoy_matcher_v3.StringMatcher{
|
DestinationRuleHeader: er.Headers,
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
|
|
||||||
Exact: dr.PathExact,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case dr.PathPrefix != "":
|
|
||||||
perms = append(perms, &envoy_rbac_v3.Permission{
|
|
||||||
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
|
||||||
UrlPath: &envoy_matcher_v3.PathMatcher{
|
|
||||||
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
|
||||||
Path: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{
|
|
||||||
Prefix: dr.PathPrefix,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case dr.PathRegex != "":
|
|
||||||
perms = append(perms, &envoy_rbac_v3.Permission{
|
|
||||||
Rule: &envoy_rbac_v3.Permission_UrlPath{
|
|
||||||
UrlPath: &envoy_matcher_v3.PathMatcher{
|
|
||||||
Rule: &envoy_matcher_v3.PathMatcher_Path{
|
|
||||||
Path: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
|
||||||
SafeRegex: response.MakeEnvoyRegexMatch(dr.PathRegex),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// methods
|
|
||||||
if len(dr.Methods) > 0 {
|
|
||||||
methodHeaderRegex := strings.Join(dr.Methods, "|")
|
|
||||||
eh := &envoy_route_v3.HeaderMatcher{
|
|
||||||
Name: ":method",
|
|
||||||
HeaderMatchSpecifier: &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
||||||
StringMatch: response.MakeEnvoyStringMatcher(methodHeaderRegex),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
perms = append(perms, &envoy_rbac_v3.Permission{
|
|
||||||
Rule: &envoy_rbac_v3.Permission_Header{
|
|
||||||
Header: eh,
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// headers
|
|
||||||
for _, hdr := range dr.DestinationRuleHeader {
|
|
||||||
eh := &envoy_route_v3.HeaderMatcher{
|
|
||||||
Name: hdr.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case hdr.Exact != "":
|
|
||||||
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
||||||
StringMatch: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
|
|
||||||
Exact: hdr.Exact,
|
|
||||||
},
|
|
||||||
IgnoreCase: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
case hdr.Regex != "":
|
|
||||||
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
||||||
StringMatch: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
|
||||||
SafeRegex: response.MakeEnvoyRegexMatch(hdr.Regex),
|
|
||||||
},
|
|
||||||
IgnoreCase: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
case hdr.Prefix != "":
|
|
||||||
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
||||||
StringMatch: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{
|
|
||||||
Prefix: hdr.Prefix,
|
|
||||||
},
|
|
||||||
IgnoreCase: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
case hdr.Suffix != "":
|
|
||||||
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
||||||
StringMatch: &envoy_matcher_v3.StringMatcher{
|
|
||||||
MatchPattern: &envoy_matcher_v3.StringMatcher_Suffix{
|
|
||||||
Suffix: hdr.Suffix,
|
|
||||||
},
|
|
||||||
IgnoreCase: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
case hdr.Present:
|
|
||||||
eh.HeaderMatchSpecifier = &envoy_route_v3.HeaderMatcher_PresentMatch{
|
|
||||||
PresentMatch: true,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
continue // skip this impossible situation
|
|
||||||
}
|
|
||||||
|
|
||||||
if hdr.Invert {
|
|
||||||
eh.InvertMatch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
perms = append(perms, &envoy_rbac_v3.Permission{
|
|
||||||
Rule: &envoy_rbac_v3.Permission_Header{
|
|
||||||
Header: eh,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
subPerms[i] = &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_NotRule{NotRule: translated},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
subPerms = append([]*envoy_rbac_v3.Permission{translateRule(dr)}, subPerms...)
|
||||||
|
perms = append(perms, combineAndPermissions(subPerms))
|
||||||
}
|
}
|
||||||
return perms
|
return perms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func combineAndPermissions(perms []*envoy_rbac_v3.Permission) *envoy_rbac_v3.Permission {
|
||||||
|
switch len(perms) {
|
||||||
|
case 0:
|
||||||
|
return anyPermission()
|
||||||
|
case 1:
|
||||||
|
return perms[0]
|
||||||
|
default:
|
||||||
|
return &envoy_rbac_v3.Permission{
|
||||||
|
Rule: &envoy_rbac_v3.Permission_AndRules{
|
||||||
|
AndRules: &envoy_rbac_v3.Permission_Set{
|
||||||
|
Rules: perms,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func toEnvoyPrincipal(p *pbproxystate.Principal) *envoy_rbac_v3.Principal {
|
func toEnvoyPrincipal(p *pbproxystate.Principal) *envoy_rbac_v3.Principal {
|
||||||
includePrincipal := principal(p.Spiffe)
|
includePrincipal := principal(p.Spiffe)
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,12 @@ package types
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errSourcesTenancy = errors.New("permissions sources may not specify partitions, peers, and sameness_groups together")
|
errSourcesTenancy = errors.New("permissions sources may not specify partitions, peers, and sameness_groups together")
|
||||||
errSourceWildcards = errors.New("permission sources may not have wildcard namespaces and explicit names.")
|
errSourceWildcards = errors.New("permission sources may not have wildcard namespaces and explicit names")
|
||||||
errSourceExcludes = errors.New("must be defined on wildcard sources")
|
errSourceExcludes = errors.New("must be defined on wildcard sources")
|
||||||
errInvalidPrefixValues = errors.New("prefix values, regex values, and explicit names must not combined")
|
errInvalidPrefixValues = errors.New("prefix values, regex values, and explicit names must not combined")
|
||||||
errHeaderRulesInvalid = errors.New("header rule must contain header name")
|
errInvalidRule = errors.New("rules must contain path, method, header, or port fields")
|
||||||
ErrWildcardNotSupported = errors.New("traffic permissions without explicit destinations are not yet supported")
|
errExclValuesMustBeSubset = errors.New("exclude permission rules must select a subset of ports and methods defined in the destination rule")
|
||||||
|
errHeaderRulesInvalid = errors.New("header rule must contain header name")
|
||||||
|
ErrWildcardNotSupported = errors.New("traffic permissions without explicit destinations are not yet supported")
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
@ -254,6 +256,12 @@ func validatePermission(p *pbauth.Permission, id *pbresource.ID, wrapErr func(er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if dest.IsEmpty() {
|
||||||
|
merr = multierror.Append(merr, wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "destination_rule",
|
||||||
|
Wrapped: errInvalidRule,
|
||||||
|
}))
|
||||||
|
}
|
||||||
if len(dest.Exclude) > 0 {
|
if len(dest.Exclude) > 0 {
|
||||||
for e, excl := range dest.Exclude {
|
for e, excl := range dest.Exclude {
|
||||||
wrapExclPermRuleErr := func(err error) error {
|
wrapExclPermRuleErr := func(err error) error {
|
||||||
|
@ -271,6 +279,43 @@ func validatePermission(p *pbauth.Permission, id *pbresource.ID, wrapErr func(er
|
||||||
Wrapped: errInvalidPrefixValues,
|
Wrapped: errInvalidPrefixValues,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
for eh, hdr := range excl.Headers {
|
||||||
|
wrapExclHeaderErr := func(err error) error {
|
||||||
|
return wrapDestRuleErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "exclude_permission_header_rules",
|
||||||
|
Index: eh,
|
||||||
|
Wrapped: err,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(hdr.Name) == 0 {
|
||||||
|
merr = multierror.Append(merr, wrapExclHeaderErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "exclude_permission_header_rule",
|
||||||
|
Wrapped: errHeaderRulesInvalid,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range excl.Methods {
|
||||||
|
if len(dest.Methods) != 0 && !slices.Contains(dest.Methods, m) {
|
||||||
|
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "exclude_permission_header_rule",
|
||||||
|
Wrapped: errExclValuesMustBeSubset,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, port := range excl.PortNames {
|
||||||
|
if len(dest.PortNames) != 0 && !slices.Contains(dest.PortNames, port) {
|
||||||
|
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "exclude_permission_header_rule",
|
||||||
|
Wrapped: errExclValuesMustBeSubset,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if excl.IsEmpty() {
|
||||||
|
merr = multierror.Append(merr, wrapExclPermRuleErr(resource.ErrInvalidListElement{
|
||||||
|
Name: "exclude_permission_rule",
|
||||||
|
Wrapped: errInvalidRule,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,11 @@ package types
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidateTrafficPermissionsActionCE(t *testing.T) {
|
func TestValidateTrafficPermissionsActionCE(t *testing.T) {
|
||||||
|
|
|
@ -42,6 +42,37 @@ func TestValidateTrafficPermissions(t *testing.T) {
|
||||||
Action: pbauth.Action_ACTION_ALLOW,
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ok-permissions": {
|
||||||
|
tp: &pbauth.TrafficPermissions{
|
||||||
|
Destination: &pbauth.Destination{IdentityName: "wi-1"},
|
||||||
|
Action: pbauth.Action_ACTION_ALLOW,
|
||||||
|
Permissions: []*pbauth.Permission{
|
||||||
|
{
|
||||||
|
Sources: []*pbauth.Source{
|
||||||
|
{
|
||||||
|
IdentityName: "wi-2",
|
||||||
|
Namespace: "default",
|
||||||
|
Partition: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IdentityName: "wi-1",
|
||||||
|
Namespace: "default",
|
||||||
|
Partition: "ap1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{{Name: "X-Consul-Token", Present: false, Invert: true}},
|
||||||
|
PortNames: []string{"https"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{PathExact: "/admin"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"unspecified-action": {
|
"unspecified-action": {
|
||||||
// Any type other than the TrafficPermissions type would work
|
// Any type other than the TrafficPermissions type would work
|
||||||
// to cause the error we are expecting
|
// to cause the error we are expecting
|
||||||
|
@ -441,6 +472,82 @@ func permissionsTestCases() map[string]permissionTestCase {
|
||||||
},
|
},
|
||||||
expectErr: `invalid element at index 0 of list "destination_rule": prefix values, regex values, and explicit names must not combined`,
|
expectErr: `invalid element at index 0 of list "destination_rule": prefix values, regex values, and explicit names must not combined`,
|
||||||
},
|
},
|
||||||
|
"destination-rule-empty": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "destination_rule": rules must contain path, method, header, or port fields`,
|
||||||
|
},
|
||||||
|
"destination-rule-only-empty-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{Exclude: []*pbauth.ExcludePermissionRule{{}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "exclude_permission_rules": invalid element at index 0 of list "exclude_permission_rule": rules must contain path, method, header, or port fields`,
|
||||||
|
},
|
||||||
|
"destination-rule-empty-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathExact: "/",
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "exclude_permission_rules": invalid element at index 0 of list "exclude_permission_rule": rules must contain path, method, header, or port fields`,
|
||||||
|
},
|
||||||
|
"destination-rule-mismatched-ports-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PortNames: []string{"foo"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{PortNames: []string{"bar"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "exclude_permission_rules": invalid element at index 0 of list "exclude_permission_header_rule": exclude permission rules must select a subset of ports and methods defined in the destination rule`,
|
||||||
|
},
|
||||||
|
"destination-rule-ports-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{PortNames: []string{"bar"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"destination-rule-invalid-headers-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{{Name: "auth"}},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{Headers: []*pbauth.DestinationRuleHeader{{}}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "exclude_permission_header_rules": invalid element at index 0 of list "exclude_permission_header_rule": header rule must contain header name`,
|
||||||
|
},
|
||||||
|
"destination-rule-mismatched-methods-exclude": {
|
||||||
|
p: &pbauth.Permission{
|
||||||
|
Sources: []*pbauth.Source{{IdentityName: "i1"}},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
Methods: []string{"post"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{{Methods: []string{"patch"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: `invalid element at index 0 of list "destination_rules": invalid element at index 0 of list "exclude_permission_rules": invalid element at index 0 of list "exclude_permission_header_rule": exclude permission rules must select a subset of ports and methods defined in the destination rule`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,60 +135,85 @@ func destinationRulesByPort(allPorts []string, destinationRules []*pbauth.Destin
|
||||||
for _, p := range allPorts {
|
for _, p := range allPorts {
|
||||||
out[p] = nil
|
out[p] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, destinationRule := range destinationRules {
|
for _, destinationRule := range destinationRules {
|
||||||
ports, dr := convertDestinationRule(allPorts, destinationRule)
|
portRules := convertDestinationRule(allPorts, destinationRule)
|
||||||
for _, p := range ports {
|
for p, pr := range portRules {
|
||||||
if dr == nil {
|
if pr.rule == nil {
|
||||||
out[p] = nil
|
out[p] = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out[p] = append(out[p], dr)
|
out[p] = append(out[p], pr.rule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:unparam
|
type PortRule struct {
|
||||||
func convertDestinationRule(allPorts []string, dr *pbauth.DestinationRule) ([]string, *pbproxystate.DestinationRule) {
|
rule *pbproxystate.DestinationRule
|
||||||
ports := make(map[string]struct{})
|
}
|
||||||
|
|
||||||
|
func convertDestinationRule(allPorts []string, dr *pbauth.DestinationRule) map[string]*PortRule {
|
||||||
|
portRules := make(map[string]*PortRule)
|
||||||
|
targetPorts := allPorts
|
||||||
if len(dr.PortNames) > 0 {
|
if len(dr.PortNames) > 0 {
|
||||||
for _, p := range dr.PortNames {
|
targetPorts = dr.PortNames
|
||||||
ports[p] = struct{}{}
|
}
|
||||||
}
|
for _, p := range targetPorts {
|
||||||
} else {
|
if dr.PortsOnly() {
|
||||||
for _, p := range allPorts {
|
portRules[p] = &PortRule{}
|
||||||
ports[p] = struct{}{}
|
for _, exclude := range dr.Exclude {
|
||||||
|
for _, ep := range exclude.PortNames {
|
||||||
|
delete(portRules, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
portRules[p] = makePortRule(dr, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return portRules
|
||||||
|
}
|
||||||
|
|
||||||
for _, exclude := range dr.Exclude {
|
func makePortRule(dr *pbauth.DestinationRule, p string) *PortRule {
|
||||||
for _, p := range exclude.PortNames {
|
|
||||||
delete(ports, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var out []string
|
|
||||||
for p := range ports {
|
|
||||||
out = append(out, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(dr.String()) == 0 {
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
psdr := &pbproxystate.DestinationRule{
|
psdr := &pbproxystate.DestinationRule{
|
||||||
PathExact: dr.PathExact,
|
PathExact: dr.PathExact,
|
||||||
PathPrefix: dr.PathPrefix,
|
PathPrefix: dr.PathPrefix,
|
||||||
PathRegex: dr.PathRegex,
|
PathRegex: dr.PathRegex,
|
||||||
Methods: dr.Methods,
|
Methods: dr.Methods,
|
||||||
}
|
}
|
||||||
hrs := make([]*pbproxystate.DestinationRuleHeader, len(dr.Headers))
|
psdr.DestinationRuleHeader = destinationRuleHeaders(dr.Headers)
|
||||||
for i, hr := range dr.Headers {
|
|
||||||
|
var excls []*pbproxystate.ExcludePermissionRule
|
||||||
|
for _, ex := range dr.Exclude {
|
||||||
|
if len(ex.PortNames) == 0 || listContains(ex.PortNames, p) {
|
||||||
|
excls = append(excls, &pbproxystate.ExcludePermissionRule{
|
||||||
|
PathExact: ex.PathExact,
|
||||||
|
PathPrefix: ex.PathPrefix,
|
||||||
|
PathRegex: ex.PathRegex,
|
||||||
|
Methods: ex.Methods,
|
||||||
|
Headers: destinationRuleHeaders(ex.Headers),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
psdr.Exclude = excls
|
||||||
|
return &PortRule{psdr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listContains(list []string, str string) bool {
|
||||||
|
for _, item := range list {
|
||||||
|
if item == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func destinationRuleHeaders(headers []*pbauth.DestinationRuleHeader) []*pbproxystate.DestinationRuleHeader {
|
||||||
|
hrs := make([]*pbproxystate.DestinationRuleHeader, len(headers))
|
||||||
|
for i, hr := range headers {
|
||||||
hrs[i] = &pbproxystate.DestinationRuleHeader{
|
hrs[i] = &pbproxystate.DestinationRuleHeader{
|
||||||
Name: hr.Name,
|
Name: hr.Name,
|
||||||
Present: hr.Present,
|
Present: hr.Present,
|
||||||
|
@ -199,13 +224,7 @@ func convertDestinationRule(allPorts []string, dr *pbauth.DestinationRule) ([]st
|
||||||
Invert: hr.Invert,
|
Invert: hr.Invert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
psdr.DestinationRuleHeader = hrs
|
return hrs
|
||||||
|
|
||||||
if len(psdr.String()) > 0 {
|
|
||||||
return out, psdr
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePrincipals(trustDomain string, perm *pbauth.Permission) []*pbproxystate.Principal {
|
func makePrincipals(trustDomain string, perm *pbauth.Permission) []*pbproxystate.Principal {
|
||||||
|
|
|
@ -704,6 +704,392 @@ func TestBuildTrafficPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"destination rules l7, exclude rules for one port": {
|
||||||
|
defaultAllow: true,
|
||||||
|
workloadPorts: map[string]*pbcatalog.WorkloadPort{
|
||||||
|
"p1": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctp: &pbauth.ComputedTrafficPermissions{
|
||||||
|
AllowPermissions: []*pbauth.Permission{
|
||||||
|
{
|
||||||
|
Sources: []*pbauth.Source{
|
||||||
|
{
|
||||||
|
IdentityName: "foo",
|
||||||
|
Partition: tenancy.Partition,
|
||||||
|
Namespace: tenancy.Namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{{Name: "restricted"}},
|
||||||
|
PortNames: []string{"p2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]*pbproxystate.TrafficPermissions{
|
||||||
|
"p1": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{{Name: "restricted"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"destination rules l7, rules by port": {
|
||||||
|
defaultAllow: true,
|
||||||
|
workloadPorts: map[string]*pbcatalog.WorkloadPort{
|
||||||
|
"p1": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
"p3": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctp: &pbauth.ComputedTrafficPermissions{
|
||||||
|
AllowPermissions: []*pbauth.Permission{
|
||||||
|
{
|
||||||
|
Sources: []*pbauth.Source{
|
||||||
|
{
|
||||||
|
IdentityName: "foo",
|
||||||
|
Partition: tenancy.Partition,
|
||||||
|
Namespace: tenancy.Namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
PortNames: []string{"p1", "p2"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{{Name: "restricted"}},
|
||||||
|
PortNames: []string{"p2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PortNames: []string{"p3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]*pbproxystate.TrafficPermissions{
|
||||||
|
"p1": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Methods: []string{"get", "post"},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{{Name: "restricted"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"p3": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"destination rules l7, exclude infer ports": {
|
||||||
|
defaultAllow: true,
|
||||||
|
workloadPorts: map[string]*pbcatalog.WorkloadPort{
|
||||||
|
"p1": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctp: &pbauth.ComputedTrafficPermissions{
|
||||||
|
AllowPermissions: []*pbauth.Permission{
|
||||||
|
{
|
||||||
|
Sources: []*pbauth.Source{
|
||||||
|
{
|
||||||
|
IdentityName: "foo",
|
||||||
|
Partition: tenancy.Partition,
|
||||||
|
Namespace: tenancy.Namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]*pbproxystate.TrafficPermissions{
|
||||||
|
"p1": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"destination rules l7, headers and methods exclude": {
|
||||||
|
defaultAllow: true,
|
||||||
|
workloadPorts: map[string]*pbcatalog.WorkloadPort{
|
||||||
|
"p1": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctp: &pbauth.ComputedTrafficPermissions{
|
||||||
|
AllowPermissions: []*pbauth.Permission{
|
||||||
|
{
|
||||||
|
Sources: []*pbauth.Source{
|
||||||
|
{
|
||||||
|
IdentityName: "foo",
|
||||||
|
Partition: tenancy.Partition,
|
||||||
|
Namespace: tenancy.Namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbauth.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{
|
||||||
|
{Name: "header1", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST, GET"},
|
||||||
|
Exclude: []*pbauth.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{
|
||||||
|
{Name: "header2", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathExact: "/config",
|
||||||
|
Headers: []*pbauth.DestinationRuleHeader{
|
||||||
|
{Name: "header3", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: map[string]*pbproxystate.TrafficPermissions{
|
||||||
|
"p1": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header1", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST, GET"},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header2", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathExact: "/config",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header3", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"p2": {
|
||||||
|
DefaultAllow: false,
|
||||||
|
AllowPermissions: []*pbproxystate.Permission{
|
||||||
|
{
|
||||||
|
Principals: []*pbproxystate.Principal{
|
||||||
|
{
|
||||||
|
Spiffe: &pbproxystate.Spiffe{Regex: fmt.Sprintf("^spiffe://test.consul/ap/%s/ns/%s/identity/foo$", tenancy.Partition, tenancy.Namespace)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DestinationRules: []*pbproxystate.DestinationRule{
|
||||||
|
{
|
||||||
|
PathPrefix: "/",
|
||||||
|
DestinationRuleHeader: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header1", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST, GET"},
|
||||||
|
Exclude: []*pbproxystate.ExcludePermissionRule{
|
||||||
|
{
|
||||||
|
PathExact: "/secret",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header2", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PathExact: "/config",
|
||||||
|
Headers: []*pbproxystate.DestinationRuleHeader{
|
||||||
|
{Name: "header3", Present: true},
|
||||||
|
},
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package authv2beta1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTrafficPermissions_PortsOnly(t *testing.T) {
|
||||||
|
t.Run("nil", func(t *testing.T) {
|
||||||
|
var dr *DestinationRule
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("empty", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{}
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("ports", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
PortNames: []string{"foo"},
|
||||||
|
}
|
||||||
|
require.True(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("excl-ports", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
Exclude: []*ExcludePermissionRule{{PortNames: []string{"foo"}}},
|
||||||
|
}
|
||||||
|
require.True(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("ports-and-excl-ports", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
PortNames: []string{"foo", "bar"},
|
||||||
|
Exclude: []*ExcludePermissionRule{{PortNames: []string{"foo"}}},
|
||||||
|
}
|
||||||
|
require.True(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("methods", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
Methods: []string{"put"},
|
||||||
|
}
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("path", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
PathRegex: "*",
|
||||||
|
PortNames: []string{"foo"},
|
||||||
|
}
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("headers", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
Exclude: []*ExcludePermissionRule{{Headers: []*DestinationRuleHeader{{Name: "Authorization"}}, PortNames: []string{"foo"}}},
|
||||||
|
}
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
t.Run("path-and-exclports", func(t *testing.T) {
|
||||||
|
dr := &DestinationRule{
|
||||||
|
PathExact: "/",
|
||||||
|
Exclude: []*ExcludePermissionRule{{PortNames: []string{"foo"}}},
|
||||||
|
}
|
||||||
|
require.False(t, dr.PortsOnly())
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package authv2beta1
|
||||||
|
|
||||||
|
// IsEmpty returns true if a destination rule has no fields defined.
|
||||||
|
func (d *DestinationRule) IsEmpty() bool {
|
||||||
|
if d == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return len(d.PathExact) == 0 &&
|
||||||
|
len(d.PathPrefix) == 0 &&
|
||||||
|
len(d.PathRegex) == 0 &&
|
||||||
|
len(d.Methods) == 0 &&
|
||||||
|
len(d.Headers) == 0 &&
|
||||||
|
len(d.PortNames) == 0 &&
|
||||||
|
len(d.Exclude) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if an exclude permission has no fields defined.
|
||||||
|
func (e *ExcludePermissionRule) IsEmpty() bool {
|
||||||
|
if e == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return len(e.PathExact) == 0 &&
|
||||||
|
len(e.PathPrefix) == 0 &&
|
||||||
|
len(e.PathRegex) == 0 &&
|
||||||
|
len(e.Methods) == 0 &&
|
||||||
|
len(e.Headers) == 0 &&
|
||||||
|
len(e.PortNames) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortsOnly returns true if a destination rule only specifies port criteria
|
||||||
|
func (d *DestinationRule) PortsOnly() bool {
|
||||||
|
if d.IsEmpty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
excludePortsOnly := true
|
||||||
|
for _, e := range d.Exclude {
|
||||||
|
if !e.PortsOnly() {
|
||||||
|
excludePortsOnly = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(d.PathExact) == 0 &&
|
||||||
|
len(d.PathPrefix) == 0 &&
|
||||||
|
len(d.PathRegex) == 0 &&
|
||||||
|
len(d.Methods) == 0 &&
|
||||||
|
len(d.Headers) == 0 &&
|
||||||
|
(len(d.PortNames) != 0 || excludePortsOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortsOnly returns true if an exclude rule only specifies port criteria
|
||||||
|
func (e *ExcludePermissionRule) PortsOnly() bool {
|
||||||
|
if e == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(e.PathExact) == 0 &&
|
||||||
|
len(e.PathPrefix) == 0 &&
|
||||||
|
len(e.PathRegex) == 0 &&
|
||||||
|
len(e.Methods) == 0 &&
|
||||||
|
len(e.Headers) == 0 &&
|
||||||
|
len(e.PortNames) != 0
|
||||||
|
}
|
|
@ -66,3 +66,13 @@ func (msg *DestinationRuleHeader) MarshalBinary() ([]byte, error) {
|
||||||
func (msg *DestinationRuleHeader) UnmarshalBinary(b []byte) error {
|
func (msg *DestinationRuleHeader) UnmarshalBinary(b []byte) error {
|
||||||
return proto.Unmarshal(b, msg)
|
return proto.Unmarshal(b, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *ExcludePermissionRule) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *ExcludePermissionRule) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@ type DestinationRule struct {
|
||||||
PathRegex string `protobuf:"bytes,3,opt,name=path_regex,json=pathRegex,proto3" json:"path_regex,omitempty"`
|
PathRegex string `protobuf:"bytes,3,opt,name=path_regex,json=pathRegex,proto3" json:"path_regex,omitempty"`
|
||||||
Methods []string `protobuf:"bytes,4,rep,name=methods,proto3" json:"methods,omitempty"`
|
Methods []string `protobuf:"bytes,4,rep,name=methods,proto3" json:"methods,omitempty"`
|
||||||
DestinationRuleHeader []*DestinationRuleHeader `protobuf:"bytes,5,rep,name=destination_rule_header,json=destinationRuleHeader,proto3" json:"destination_rule_header,omitempty"`
|
DestinationRuleHeader []*DestinationRuleHeader `protobuf:"bytes,5,rep,name=destination_rule_header,json=destinationRuleHeader,proto3" json:"destination_rule_header,omitempty"`
|
||||||
|
Exclude []*ExcludePermissionRule `protobuf:"bytes,6,rep,name=exclude,proto3" json:"exclude,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DestinationRule) Reset() {
|
func (x *DestinationRule) Reset() {
|
||||||
|
@ -338,6 +339,13 @@ func (x *DestinationRule) GetDestinationRuleHeader() []*DestinationRuleHeader {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *DestinationRule) GetExclude() []*ExcludePermissionRule {
|
||||||
|
if x != nil {
|
||||||
|
return x.Exclude
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type DestinationRuleHeader struct {
|
type DestinationRuleHeader struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -433,6 +441,85 @@ func (x *DestinationRuleHeader) GetInvert() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExcludePermissionRule struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
PathExact string `protobuf:"bytes,1,opt,name=path_exact,json=pathExact,proto3" json:"path_exact,omitempty"`
|
||||||
|
PathPrefix string `protobuf:"bytes,2,opt,name=path_prefix,json=pathPrefix,proto3" json:"path_prefix,omitempty"`
|
||||||
|
PathRegex string `protobuf:"bytes,3,opt,name=path_regex,json=pathRegex,proto3" json:"path_regex,omitempty"`
|
||||||
|
Methods []string `protobuf:"bytes,4,rep,name=methods,proto3" json:"methods,omitempty"`
|
||||||
|
Headers []*DestinationRuleHeader `protobuf:"bytes,5,rep,name=headers,proto3" json:"headers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) Reset() {
|
||||||
|
*x = ExcludePermissionRule{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ExcludePermissionRule) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_msgTypes[6]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ExcludePermissionRule.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ExcludePermissionRule) Descriptor() ([]byte, []int) {
|
||||||
|
return file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) GetPathExact() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PathExact
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) GetPathPrefix() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PathPrefix
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) GetPathRegex() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PathRegex
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) GetMethods() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Methods
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ExcludePermissionRule) GetHeaders() []*DestinationRuleHeader {
|
||||||
|
if x != nil {
|
||||||
|
return x.Headers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto protoreflect.FileDescriptor
|
var File_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc = []byte{
|
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc = []byte{
|
||||||
|
@ -486,7 +573,7 @@ var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc = []byte{
|
||||||
0x0a, 0x06, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65,
|
0x0a, 0x06, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65,
|
||||||
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1d,
|
0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1d,
|
||||||
0x0a, 0x0a, 0x78, 0x66, 0x63, 0x63, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01,
|
0x0a, 0x0a, 0x78, 0x66, 0x63, 0x63, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x28, 0x09, 0x52, 0x09, 0x78, 0x66, 0x63, 0x63, 0x52, 0x65, 0x67, 0x65, 0x78, 0x22, 0x85, 0x02,
|
0x28, 0x09, 0x52, 0x09, 0x78, 0x66, 0x63, 0x63, 0x52, 0x65, 0x67, 0x65, 0x78, 0x22, 0xe2, 0x02,
|
||||||
0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c,
|
0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c,
|
||||||
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18,
|
0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74,
|
||||||
|
@ -503,41 +590,62 @@ var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc = []byte{
|
||||||
0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
|
0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x15,
|
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x15,
|
||||||
0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48,
|
0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48,
|
||||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0xb9, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e,
|
0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65,
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
|
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||||
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
|
0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76,
|
||||||
0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x02,
|
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74,
|
||||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a,
|
0x61, 0x74, 0x65, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69,
|
||||||
0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x78,
|
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75,
|
||||||
0x61, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20,
|
0x64, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73,
|
0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x66,
|
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x66, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01,
|
0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76,
|
0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78,
|
||||||
0x65, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72,
|
0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74,
|
||||||
0x74, 0x42, 0xdd, 0x02, 0x0a, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e,
|
0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66,
|
||||||
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73,
|
0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72,
|
0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a,
|
0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74,
|
||||||
0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68,
|
0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x22, 0xed,
|
||||||
0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f,
|
0x01, 0x0a, 0x15, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
|
||||||
0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68,
|
0x73, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68,
|
||||||
0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
0x5f, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61,
|
||||||
0x73, 0x74, 0x61, 0x74, 0x65, 0xa2, 0x02, 0x05, 0x48, 0x43, 0x4d, 0x56, 0x50, 0xaa, 0x02, 0x2a,
|
0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x5f,
|
||||||
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
|
0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61,
|
||||||
0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x62,
|
0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68,
|
||||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xca, 0x02, 0x2a, 0x48, 0x61, 0x73,
|
0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61,
|
||||||
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65,
|
0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f,
|
||||||
0x73, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f,
|
0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
|
||||||
0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xe2, 0x02, 0x36, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63,
|
0x73, 0x12, 0x5b, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03,
|
||||||
|
0x28, 0x0b, 0x32, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63,
|
||||||
|
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74,
|
||||||
|
0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e,
|
||||||
|
0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48,
|
||||||
|
0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x42, 0xdd,
|
||||||
|
0x02, 0x0a, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x32, 0x62,
|
||||||
|
0x65, 0x74, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74,
|
||||||
|
0x65, 0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
|
||||||
|
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x44, 0x67, 0x69,
|
||||||
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||||
|
0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d,
|
||||||
|
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x32,
|
||||||
|
0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61,
|
||||||
|
0x74, 0x65, 0xa2, 0x02, 0x05, 0x48, 0x43, 0x4d, 0x56, 0x50, 0xaa, 0x02, 0x2a, 0x48, 0x61, 0x73,
|
||||||
|
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65,
|
||||||
|
0x73, 0x68, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x62, 0x70, 0x72, 0x6f,
|
||||||
|
0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xca, 0x02, 0x2a, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||||
0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c,
|
0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c,
|
||||||
0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73,
|
0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
|
0x74, 0x61, 0x74, 0x65, 0xe2, 0x02, 0x36, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
||||||
0xea, 0x02, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f,
|
0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x32, 0x62,
|
||||||
0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65,
|
0x65, 0x74, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74,
|
||||||
0x74, 0x61, 0x31, 0x3a, 0x3a, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74,
|
0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x2e,
|
||||||
0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75,
|
||||||
|
0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
|
||||||
|
0x3a, 0x3a, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -552,7 +660,7 @@ func file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDescGZIP() []
|
||||||
return file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDescData
|
return file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_goTypes = []interface{}{
|
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_goTypes = []interface{}{
|
||||||
(*TrafficPermissions)(nil), // 0: hashicorp.consul.mesh.v2beta1.pbproxystate.TrafficPermissions
|
(*TrafficPermissions)(nil), // 0: hashicorp.consul.mesh.v2beta1.pbproxystate.TrafficPermissions
|
||||||
(*Permission)(nil), // 1: hashicorp.consul.mesh.v2beta1.pbproxystate.Permission
|
(*Permission)(nil), // 1: hashicorp.consul.mesh.v2beta1.pbproxystate.Permission
|
||||||
|
@ -560,6 +668,7 @@ var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_goTypes = []inter
|
||||||
(*Spiffe)(nil), // 3: hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
(*Spiffe)(nil), // 3: hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
||||||
(*DestinationRule)(nil), // 4: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRule
|
(*DestinationRule)(nil), // 4: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRule
|
||||||
(*DestinationRuleHeader)(nil), // 5: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRuleHeader
|
(*DestinationRuleHeader)(nil), // 5: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRuleHeader
|
||||||
|
(*ExcludePermissionRule)(nil), // 6: hashicorp.consul.mesh.v2beta1.pbproxystate.ExcludePermissionRule
|
||||||
}
|
}
|
||||||
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_depIdxs = []int32{
|
var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_depIdxs = []int32{
|
||||||
1, // 0: hashicorp.consul.mesh.v2beta1.pbproxystate.TrafficPermissions.allow_permissions:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Permission
|
1, // 0: hashicorp.consul.mesh.v2beta1.pbproxystate.TrafficPermissions.allow_permissions:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Permission
|
||||||
|
@ -569,11 +678,13 @@ var file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_depIdxs = []int32
|
||||||
3, // 4: hashicorp.consul.mesh.v2beta1.pbproxystate.Principal.spiffe:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
3, // 4: hashicorp.consul.mesh.v2beta1.pbproxystate.Principal.spiffe:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
||||||
3, // 5: hashicorp.consul.mesh.v2beta1.pbproxystate.Principal.exclude_spiffes:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
3, // 5: hashicorp.consul.mesh.v2beta1.pbproxystate.Principal.exclude_spiffes:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.Spiffe
|
||||||
5, // 6: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRule.destination_rule_header:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRuleHeader
|
5, // 6: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRule.destination_rule_header:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRuleHeader
|
||||||
7, // [7:7] is the sub-list for method output_type
|
6, // 7: hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRule.exclude:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.ExcludePermissionRule
|
||||||
7, // [7:7] is the sub-list for method input_type
|
5, // 8: hashicorp.consul.mesh.v2beta1.pbproxystate.ExcludePermissionRule.headers:type_name -> hashicorp.consul.mesh.v2beta1.pbproxystate.DestinationRuleHeader
|
||||||
7, // [7:7] is the sub-list for extension type_name
|
9, // [9:9] is the sub-list for method output_type
|
||||||
7, // [7:7] is the sub-list for extension extendee
|
9, // [9:9] is the sub-list for method input_type
|
||||||
0, // [0:7] is the sub-list for field type_name
|
9, // [9:9] is the sub-list for extension type_name
|
||||||
|
9, // [9:9] is the sub-list for extension extendee
|
||||||
|
0, // [0:9] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_init() }
|
func init() { file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_init() }
|
||||||
|
@ -654,6 +765,18 @@ func file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ExcludePermissionRule); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
|
@ -661,7 +784,7 @@ func file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_init() {
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc,
|
RawDescriptor: file_pbmesh_v2beta1_pbproxystate_traffic_permissions_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 6,
|
NumMessages: 7,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,7 @@ message DestinationRule {
|
||||||
string path_regex = 3;
|
string path_regex = 3;
|
||||||
repeated string methods = 4;
|
repeated string methods = 4;
|
||||||
repeated DestinationRuleHeader destination_rule_header = 5;
|
repeated DestinationRuleHeader destination_rule_header = 5;
|
||||||
|
repeated ExcludePermissionRule exclude = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DestinationRuleHeader {
|
message DestinationRuleHeader {
|
||||||
|
@ -53,3 +54,11 @@ message DestinationRuleHeader {
|
||||||
string regex = 6;
|
string regex = 6;
|
||||||
bool invert = 7;
|
bool invert = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ExcludePermissionRule {
|
||||||
|
string path_exact = 1;
|
||||||
|
string path_prefix = 2;
|
||||||
|
string path_regex = 3;
|
||||||
|
repeated string methods = 4;
|
||||||
|
repeated DestinationRuleHeader headers = 5;
|
||||||
|
}
|
||||||
|
|
|
@ -130,3 +130,24 @@ func (in *DestinationRuleHeader) DeepCopy() *DestinationRuleHeader {
|
||||||
func (in *DestinationRuleHeader) DeepCopyInterface() interface{} {
|
func (in *DestinationRuleHeader) DeepCopyInterface() interface{} {
|
||||||
return in.DeepCopy()
|
return in.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto supports using ExcludePermissionRule within kubernetes types, where deepcopy-gen is used.
|
||||||
|
func (in *ExcludePermissionRule) DeepCopyInto(out *ExcludePermissionRule) {
|
||||||
|
proto.Reset(out)
|
||||||
|
proto.Merge(out, proto.Clone(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludePermissionRule. Required by controller-gen.
|
||||||
|
func (in *ExcludePermissionRule) DeepCopy() *ExcludePermissionRule {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ExcludePermissionRule)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new ExcludePermissionRule. Required by controller-gen.
|
||||||
|
func (in *ExcludePermissionRule) DeepCopyInterface() interface{} {
|
||||||
|
return in.DeepCopy()
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,17 @@ func (this *DestinationRuleHeader) UnmarshalJSON(b []byte) error {
|
||||||
return TrafficPermissionsUnmarshaler.Unmarshal(b, this)
|
return TrafficPermissionsUnmarshaler.Unmarshal(b, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a custom marshaler for ExcludePermissionRule
|
||||||
|
func (this *ExcludePermissionRule) MarshalJSON() ([]byte, error) {
|
||||||
|
str, err := TrafficPermissionsMarshaler.Marshal(this)
|
||||||
|
return []byte(str), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON is a custom unmarshaler for ExcludePermissionRule
|
||||||
|
func (this *ExcludePermissionRule) UnmarshalJSON(b []byte) error {
|
||||||
|
return TrafficPermissionsUnmarshaler.Unmarshal(b, this)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TrafficPermissionsMarshaler = &protojson.MarshalOptions{}
|
TrafficPermissionsMarshaler = &protojson.MarshalOptions{}
|
||||||
TrafficPermissionsUnmarshaler = &protojson.UnmarshalOptions{DiscardUnknown: false}
|
TrafficPermissionsUnmarshaler = &protojson.UnmarshalOptions{DiscardUnknown: false}
|
||||||
|
|
|
@ -0,0 +1,459 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
package catalogv2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
|
||||||
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
||||||
|
"github.com/hashicorp/consul/test-integ/topoutil"
|
||||||
|
libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert"
|
||||||
|
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
|
||||||
|
"github.com/hashicorp/consul/testing/deployer/sprawl/sprawltest"
|
||||||
|
"github.com/hashicorp/consul/testing/deployer/topology"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
permissions []*permission
|
||||||
|
result []*testResult
|
||||||
|
}
|
||||||
|
|
||||||
|
type permission struct {
|
||||||
|
allow bool
|
||||||
|
excludeSource bool
|
||||||
|
includeSourceTenancy bool
|
||||||
|
excludeSourceTenancy bool
|
||||||
|
destRules []*destRules
|
||||||
|
}
|
||||||
|
|
||||||
|
type destRules struct {
|
||||||
|
values *ruleValues
|
||||||
|
excludes []*ruleValues
|
||||||
|
}
|
||||||
|
|
||||||
|
type ruleValues struct {
|
||||||
|
portNames []string
|
||||||
|
path string
|
||||||
|
pathPref string
|
||||||
|
pathReg string
|
||||||
|
headers []string
|
||||||
|
methods []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testResult struct {
|
||||||
|
fail bool
|
||||||
|
port string
|
||||||
|
path string
|
||||||
|
headers map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTrafficPermissions(p *permission, srcTenancy *pbresource.Tenancy) *pbauth.TrafficPermissions {
|
||||||
|
sources := []*pbauth.Source{{
|
||||||
|
IdentityName: "static-client",
|
||||||
|
Namespace: srcTenancy.Namespace,
|
||||||
|
Partition: srcTenancy.Partition,
|
||||||
|
}}
|
||||||
|
destinationRules := []*pbauth.DestinationRule{}
|
||||||
|
if p != nil {
|
||||||
|
srcId := "static-client"
|
||||||
|
if p.includeSourceTenancy {
|
||||||
|
srcId = ""
|
||||||
|
}
|
||||||
|
if p.excludeSource {
|
||||||
|
sources = []*pbauth.Source{{
|
||||||
|
IdentityName: srcId,
|
||||||
|
Namespace: srcTenancy.Namespace,
|
||||||
|
Partition: srcTenancy.Partition,
|
||||||
|
Exclude: []*pbauth.ExcludeSource{{
|
||||||
|
IdentityName: "static-client",
|
||||||
|
Namespace: srcTenancy.Namespace,
|
||||||
|
Partition: srcTenancy.Partition,
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
} else {
|
||||||
|
sources = []*pbauth.Source{{
|
||||||
|
IdentityName: srcId,
|
||||||
|
Namespace: srcTenancy.Namespace,
|
||||||
|
Partition: srcTenancy.Partition,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
for _, dr := range p.destRules {
|
||||||
|
destRule := &pbauth.DestinationRule{}
|
||||||
|
if dr.values != nil {
|
||||||
|
destRule.PathExact = dr.values.path
|
||||||
|
destRule.PathPrefix = dr.values.pathPref
|
||||||
|
destRule.PathRegex = dr.values.pathReg
|
||||||
|
destRule.Methods = dr.values.methods
|
||||||
|
destRule.PortNames = dr.values.portNames
|
||||||
|
destRule.Headers = []*pbauth.DestinationRuleHeader{}
|
||||||
|
for _, h := range dr.values.headers {
|
||||||
|
destRule.Headers = append(destRule.Headers, &pbauth.DestinationRuleHeader{
|
||||||
|
Name: h,
|
||||||
|
Present: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var excludePermissions []*pbauth.ExcludePermissionRule
|
||||||
|
for _, e := range dr.excludes {
|
||||||
|
eRule := &pbauth.ExcludePermissionRule{
|
||||||
|
PathExact: e.path,
|
||||||
|
PathPrefix: e.pathPref,
|
||||||
|
PathRegex: e.pathReg,
|
||||||
|
Methods: e.methods,
|
||||||
|
PortNames: e.portNames,
|
||||||
|
}
|
||||||
|
eRule.Headers = []*pbauth.DestinationRuleHeader{}
|
||||||
|
for _, h := range e.headers {
|
||||||
|
eRule.Headers = append(eRule.Headers, &pbauth.DestinationRuleHeader{
|
||||||
|
Name: h,
|
||||||
|
Present: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
excludePermissions = append(excludePermissions, eRule)
|
||||||
|
}
|
||||||
|
destRule.Exclude = excludePermissions
|
||||||
|
destinationRules = append(destinationRules, destRule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action := pbauth.Action_ACTION_ALLOW
|
||||||
|
if !p.allow {
|
||||||
|
action = pbauth.Action_ACTION_DENY
|
||||||
|
}
|
||||||
|
return &pbauth.TrafficPermissions{
|
||||||
|
Destination: &pbauth.Destination{
|
||||||
|
IdentityName: "static-server",
|
||||||
|
},
|
||||||
|
Action: action,
|
||||||
|
Permissions: []*pbauth.Permission{{
|
||||||
|
Sources: sources,
|
||||||
|
DestinationRules: destinationRules,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This tests runs a gauntlet of traffic permissions updates and validates that the request status codes match the intended rules
|
||||||
|
func TestL7TrafficPermissions(t *testing.T) {
|
||||||
|
testcases := map[string]testCase{
|
||||||
|
// L4 permissions
|
||||||
|
"basic": {permissions: []*permission{{allow: true}}, result: []*testResult{{fail: false}}},
|
||||||
|
"client-exclude": {permissions: []*permission{{allow: true, includeSourceTenancy: true, excludeSource: true}}, result: []*testResult{{fail: true}}},
|
||||||
|
"allow-all-client-in-tenancy": {permissions: []*permission{{allow: true, includeSourceTenancy: true}}, result: []*testResult{{fail: false}}},
|
||||||
|
"only-one-port": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{portNames: []string{"http"}}}}}}, result: []*testResult{{fail: true, port: "http2"}}},
|
||||||
|
"exclude-port": {permissions: []*permission{{allow: true, destRules: []*destRules{{excludes: []*ruleValues{{portNames: []string{"http"}}}}}}}, result: []*testResult{{fail: true, port: "http"}}},
|
||||||
|
// L7 permissions
|
||||||
|
"methods": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{methods: []string{"POST", "PUT", "PATCH", "DELETE", "CONNECT", "HEAD", "OPTIONS", "TRACE"}, pathPref: "/"}}}}},
|
||||||
|
// fortio fetch2 is configured to GET
|
||||||
|
result: []*testResult{{fail: true}}},
|
||||||
|
"headers": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{headers: []string{"a", "b"}, pathPref: "/"}}}}},
|
||||||
|
result: []*testResult{{fail: true}, {fail: true, headers: map[string]string{"a": "1"}}, {fail: false, headers: map[string]string{"a": "1", "b": "2"}}}},
|
||||||
|
"path-prefix-all": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/", methods: []string{"GET"}}}}}}, result: []*testResult{{fail: false}}},
|
||||||
|
"method-exclude": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/"}, excludes: []*ruleValues{{methods: []string{"GET"}}}}}}},
|
||||||
|
// fortio fetch2 is configured to GET
|
||||||
|
result: []*testResult{{fail: true}}},
|
||||||
|
"exclude-paths-and-headers": {permissions: []*permission{{allow: true, destRules: []*destRules{
|
||||||
|
{
|
||||||
|
values: &ruleValues{pathPref: "/f", headers: []string{"a"}},
|
||||||
|
excludes: []*ruleValues{{headers: []string{"b"}, path: "/foobar"}},
|
||||||
|
}}}},
|
||||||
|
result: []*testResult{
|
||||||
|
{fail: false, path: "foobar", headers: map[string]string{"a": "1"}},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: true, path: "foobar", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"b": "2"}},
|
||||||
|
{fail: true, path: "baz", headers: map[string]string{"a": "1"}},
|
||||||
|
}},
|
||||||
|
"exclude-paths-or-headers": {permissions: []*permission{{allow: true, destRules: []*destRules{
|
||||||
|
{values: &ruleValues{pathPref: "/f", headers: []string{"a"}}, excludes: []*ruleValues{{headers: []string{"b"}}, {path: "/foobar"}}}}}},
|
||||||
|
result: []*testResult{
|
||||||
|
{fail: true, path: "foobar", headers: map[string]string{"a": "1"}},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: true, path: "foobar", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
{fail: true, path: "baz", port: "http", headers: map[string]string{"a": "1"}},
|
||||||
|
}},
|
||||||
|
"path-or-header": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/bar"}}, {values: &ruleValues{headers: []string{"b"}}}}}},
|
||||||
|
result: []*testResult{
|
||||||
|
{fail: false, path: "bar"},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: false, path: "bar", headers: map[string]string{"b": "2"}},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
}},
|
||||||
|
"path-and-header": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/bar", headers: []string{"b"}}}}}},
|
||||||
|
result: []*testResult{
|
||||||
|
{fail: true, path: "bar"},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: false, path: "bar", headers: map[string]string{"b": "2"}},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
}},
|
||||||
|
"path-regex-exclude": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/"}, excludes: []*ruleValues{{pathReg: ".*dns.*"}}}}}},
|
||||||
|
result: []*testResult{{fail: true, path: "fortio/rest/dns"}, {fail: false, path: "fortio/rest/status"}}},
|
||||||
|
"header-include-exclude-by-port": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/", headers: []string{"experiment1", "experiment2"}}, excludes: []*ruleValues{{portNames: []string{"http2"}, headers: []string{"experiment1"}}}}}}},
|
||||||
|
result: []*testResult{{fail: true, port: "http2", headers: map[string]string{"experiment1": "a", "experiment2": "b"}},
|
||||||
|
{fail: false, port: "http", headers: map[string]string{"experiment1": "a", "experiment2": "b"}},
|
||||||
|
{fail: true, port: "http2", headers: map[string]string{"experiment2": "b"}},
|
||||||
|
{fail: true, port: "http", headers: map[string]string{"experiment3": "c"}},
|
||||||
|
}},
|
||||||
|
"two-tp-or": {permissions: []*permission{{allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/bar"}}}}, {allow: true, destRules: []*destRules{{values: &ruleValues{headers: []string{"b"}}}}}},
|
||||||
|
result: []*testResult{
|
||||||
|
{fail: false, path: "bar"},
|
||||||
|
{fail: false, path: "foo", headers: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
{fail: false, path: "bar", headers: map[string]string{"b": "2"}},
|
||||||
|
{fail: true, path: "foo", headers: map[string]string{"a": "1"}},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
if utils.IsEnterprise() {
|
||||||
|
// DENY and ALLOW permissions
|
||||||
|
testcases["deny-cancel-allow"] = testCase{permissions: []*permission{{allow: true}, {allow: false}}, result: []*testResult{{fail: true}}}
|
||||||
|
testcases["l4-deny-l7-allow"] = testCase{permissions: []*permission{{allow: false}, {allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/"}}}}}, result: []*testResult{{fail: true}, {fail: true, path: "test"}}}
|
||||||
|
testcases["l7-deny-l4-allow"] = testCase{permissions: []*permission{{allow: true}, {allow: true, destRules: []*destRules{{values: &ruleValues{pathPref: "/"}}}}, {allow: false, destRules: []*destRules{{values: &ruleValues{pathPref: "/foo"}}}}},
|
||||||
|
result: []*testResult{{fail: false}, {fail: false, path: "test"}, {fail: true, path: "foo-bar"}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
tenancies := []*pbresource.Tenancy{
|
||||||
|
{
|
||||||
|
Partition: "default",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if utils.IsEnterprise() {
|
||||||
|
tenancies = append(tenancies, &pbresource.Tenancy{
|
||||||
|
Partition: "ap1",
|
||||||
|
Namespace: "ns1",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cfg := testL7TrafficPermissionsCreator{tenancies}.NewConfig(t)
|
||||||
|
targetImage := utils.TargetImages()
|
||||||
|
imageName := targetImage.Consul
|
||||||
|
if utils.IsEnterprise() {
|
||||||
|
imageName = targetImage.ConsulEnterprise
|
||||||
|
}
|
||||||
|
t.Log("running with target image: " + imageName)
|
||||||
|
|
||||||
|
sp := sprawltest.Launch(t, cfg)
|
||||||
|
|
||||||
|
asserter := topoutil.NewAsserter(sp)
|
||||||
|
|
||||||
|
topo := sp.Topology()
|
||||||
|
cluster := topo.Clusters["dc1"]
|
||||||
|
ships := topo.ComputeRelationships()
|
||||||
|
|
||||||
|
clientV2 := sp.ResourceServiceClientForCluster(cluster.Name)
|
||||||
|
|
||||||
|
// Make sure services exist
|
||||||
|
for _, tenancy := range tenancies {
|
||||||
|
for _, name := range []string{
|
||||||
|
"static-server",
|
||||||
|
"static-client",
|
||||||
|
} {
|
||||||
|
libassert.CatalogV2ServiceHasEndpointCount(t, clientV2, name, tenancy, len(tenancies))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var initialTrafficPerms []*pbresource.Resource
|
||||||
|
for testName, tc := range testcases {
|
||||||
|
// Delete old TP and write new one for a new test case
|
||||||
|
mustDeleteTestResources(t, clientV2, initialTrafficPerms)
|
||||||
|
initialTrafficPerms = []*pbresource.Resource{}
|
||||||
|
for _, st := range tenancies {
|
||||||
|
for _, dt := range tenancies {
|
||||||
|
for i, p := range tc.permissions {
|
||||||
|
newTrafficPerms := sprawltest.MustSetResourceData(t, &pbresource.Resource{
|
||||||
|
Id: &pbresource.ID{
|
||||||
|
Type: pbauth.TrafficPermissionsType,
|
||||||
|
Name: "static-server-perms" + strconv.Itoa(i) + "-" + st.Namespace + "-" + st.Partition,
|
||||||
|
Tenancy: dt,
|
||||||
|
},
|
||||||
|
}, newTrafficPermissions(p, st))
|
||||||
|
mustWriteTestResource(t, clientV2, newTrafficPerms)
|
||||||
|
initialTrafficPerms = append(initialTrafficPerms, newTrafficPerms)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Log(initialTrafficPerms)
|
||||||
|
// Wait for the resource updates to go through and Envoy to be ready
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
// Check the default server workload envoy config for RBAC filters matching testcase criteria
|
||||||
|
serverWorkload := cluster.WorkloadsByID(topology.ID{
|
||||||
|
Partition: "default",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "static-server",
|
||||||
|
})
|
||||||
|
asserter.AssertEnvoyHTTPrbacFiltersContainIntentions(t, serverWorkload[0])
|
||||||
|
// Check relationships
|
||||||
|
for _, ship := range ships {
|
||||||
|
t.Run("case: "+testName+":"+ship.Destination.PortName+":("+ship.Caller.ID.Partition+"/"+ship.Caller.ID.Namespace+
|
||||||
|
")("+ship.Destination.ID.Partition+"/"+ship.Destination.ID.Namespace+")", func(t *testing.T) {
|
||||||
|
var (
|
||||||
|
wrk = ship.Caller
|
||||||
|
dest = ship.Destination
|
||||||
|
)
|
||||||
|
for _, res := range tc.result {
|
||||||
|
if res.port != "" && res.port != ship.Destination.PortName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dest.ID.Name = "static-server"
|
||||||
|
destClusterPrefix := clusterPrefix(dest.PortName, dest.ID, dest.Cluster)
|
||||||
|
asserter.DestinationEndpointStatus(t, wrk, destClusterPrefix+".", "HEALTHY", len(tenancies))
|
||||||
|
status := http.StatusForbidden
|
||||||
|
if res.fail == false {
|
||||||
|
status = http.StatusOK
|
||||||
|
}
|
||||||
|
t.Log("Test request:"+res.path, res.headers, status)
|
||||||
|
asserter.FortioFetch2ServiceStatusCodes(t, wrk, dest, res.path, res.headers, []int{status})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustWriteTestResource(t *testing.T, client pbresource.ResourceServiceClient, res *pbresource.Resource) {
|
||||||
|
retryer := &retry.Timer{Timeout: time.Minute, Wait: time.Second}
|
||||||
|
rsp, err := client.Write(context.Background(), &pbresource.WriteRequest{Resource: res})
|
||||||
|
require.NoError(t, err)
|
||||||
|
retry.RunWith(retryer, t, func(r *retry.R) {
|
||||||
|
readRsp, err := client.Read(context.Background(), &pbresource.ReadRequest{Id: rsp.Resource.Id})
|
||||||
|
require.NoError(r, err, "error reading %s", rsp.Resource.Id.Name)
|
||||||
|
require.NotNil(r, readRsp)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustDeleteTestResources(t *testing.T, client pbresource.ResourceServiceClient, resources []*pbresource.Resource) {
|
||||||
|
if len(resources) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
retryer := &retry.Timer{Timeout: time.Minute, Wait: time.Second}
|
||||||
|
for _, res := range resources {
|
||||||
|
retry.RunWith(retryer, t, func(r *retry.R) {
|
||||||
|
_, err := client.Delete(context.Background(), &pbresource.DeleteRequest{Id: res.Id})
|
||||||
|
if status.Code(err) == codes.NotFound {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil && status.Code(err) != codes.Aborted {
|
||||||
|
r.Stop(fmt.Errorf("failed to delete the resource: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(r, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testL7TrafficPermissionsCreator struct {
|
||||||
|
tenancies []*pbresource.Tenancy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c testL7TrafficPermissionsCreator) NewConfig(t *testing.T) *topology.Config {
|
||||||
|
const clusterName = "dc1"
|
||||||
|
|
||||||
|
servers := topoutil.NewTopologyServerSet(clusterName+"-server", 1, []string{clusterName, "wan"}, nil)
|
||||||
|
|
||||||
|
cluster := &topology.Cluster{
|
||||||
|
Enterprise: utils.IsEnterprise(),
|
||||||
|
Name: clusterName,
|
||||||
|
Nodes: servers,
|
||||||
|
}
|
||||||
|
|
||||||
|
lastNode := 0
|
||||||
|
nodeName := func() string {
|
||||||
|
lastNode++
|
||||||
|
return fmt.Sprintf("%s-box%d", clusterName, lastNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range c.tenancies {
|
||||||
|
for _, dt := range c.tenancies {
|
||||||
|
c.topologyConfigAddNodes(cluster, nodeName, st, dt)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &topology.Config{
|
||||||
|
Images: utils.TargetImages(),
|
||||||
|
Networks: []*topology.Network{
|
||||||
|
{Name: clusterName},
|
||||||
|
{Name: "wan", Type: "wan"},
|
||||||
|
},
|
||||||
|
Clusters: []*topology.Cluster{
|
||||||
|
cluster,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c testL7TrafficPermissionsCreator) topologyConfigAddNodes(
|
||||||
|
cluster *topology.Cluster,
|
||||||
|
nodeName func() string,
|
||||||
|
sourceTenancy *pbresource.Tenancy,
|
||||||
|
destinationTenancy *pbresource.Tenancy,
|
||||||
|
) {
|
||||||
|
clusterName := cluster.Name
|
||||||
|
|
||||||
|
newID := func(name string, tenancy *pbresource.Tenancy) topology.ID {
|
||||||
|
return topology.ID{
|
||||||
|
Partition: tenancy.Partition,
|
||||||
|
Namespace: tenancy.Namespace,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverNode := &topology.Node{
|
||||||
|
Kind: topology.NodeKindDataplane,
|
||||||
|
Version: topology.NodeVersionV2,
|
||||||
|
Partition: destinationTenancy.Partition,
|
||||||
|
Name: nodeName(),
|
||||||
|
Workloads: []*topology.Workload{
|
||||||
|
topoutil.NewFortioWorkloadWithDefaults(
|
||||||
|
clusterName,
|
||||||
|
newID("static-server", destinationTenancy),
|
||||||
|
topology.NodeVersionV2,
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clientNode := &topology.Node{
|
||||||
|
Kind: topology.NodeKindDataplane,
|
||||||
|
Version: topology.NodeVersionV2,
|
||||||
|
Partition: sourceTenancy.Partition,
|
||||||
|
Name: nodeName(),
|
||||||
|
Workloads: []*topology.Workload{
|
||||||
|
topoutil.NewFortioWorkloadWithDefaults(
|
||||||
|
clusterName,
|
||||||
|
newID("static-client", sourceTenancy),
|
||||||
|
topology.NodeVersionV2,
|
||||||
|
func(wrk *topology.Workload) {
|
||||||
|
wrk.Destinations = append(wrk.Destinations, &topology.Destination{
|
||||||
|
ID: newID("static-server", destinationTenancy),
|
||||||
|
PortName: "http",
|
||||||
|
LocalAddress: "0.0.0.0", // needed for an assertion
|
||||||
|
LocalPort: 5000,
|
||||||
|
},
|
||||||
|
&topology.Destination{
|
||||||
|
ID: newID("static-server", destinationTenancy),
|
||||||
|
PortName: "http2",
|
||||||
|
LocalAddress: "0.0.0.0", // needed for an assertion
|
||||||
|
LocalPort: 5001,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
wrk.WorkloadIdentity = "static-client"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.Nodes = append(cluster.Nodes,
|
||||||
|
clientNode,
|
||||||
|
serverNode,
|
||||||
|
)
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -127,6 +128,41 @@ func (a *Asserter) AssertEnvoyPresentsCertURIWithClient(t *testing.T, workload *
|
||||||
libassert.AssertEnvoyPresentsCertURIWithClient(t, client, addr, workload.ID.Name)
|
libassert.AssertEnvoyPresentsCertURIWithClient(t, client, addr, workload.ID.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Asserter) AssertEnvoyHTTPrbacFiltersContainIntentions(t *testing.T, workload *topology.Workload) {
|
||||||
|
t.Helper()
|
||||||
|
client, addr := a.getEnvoyClient(t, workload)
|
||||||
|
var (
|
||||||
|
dump string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
failer := func() *retry.Timer {
|
||||||
|
return &retry.Timer{Timeout: 30 * time.Second, Wait: 1 * time.Second}
|
||||||
|
}
|
||||||
|
|
||||||
|
retry.RunWith(failer(), t, func(r *retry.R) {
|
||||||
|
dump, _, err = libassert.GetEnvoyOutputWithClient(client, addr, "config_dump", map[string]string{})
|
||||||
|
if err != nil {
|
||||||
|
r.Fatal("could not fetch envoy configuration")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// the steps below validate that the json result from envoy config dump configured active listeners with rbac and http filters
|
||||||
|
filter := `.configs[2].dynamic_listeners[].active_state.listener | "\(.name) \( .filter_chains[].filters[] | select(.name == "envoy.filters.network.http_connection_manager") | .typed_config.http_filters | map(.name) | join(","))"`
|
||||||
|
errorStateFilter := `.configs[2].dynamic_listeners[].error_state"`
|
||||||
|
configErrorStates, _ := utils.JQFilter(dump, errorStateFilter)
|
||||||
|
require.Nil(t, configErrorStates, "there should not be any error states on listener configuration")
|
||||||
|
results, err := utils.JQFilter(dump, filter)
|
||||||
|
require.NoError(t, err, "could not parse envoy configuration")
|
||||||
|
var filteredResult []string
|
||||||
|
for _, result := range results {
|
||||||
|
parts := strings.Split(strings.ReplaceAll(result, `,`, " "), " ")
|
||||||
|
sanitizedResult := append(parts[:0], parts[1:]...)
|
||||||
|
filteredResult = append(filteredResult, sanitizedResult...)
|
||||||
|
}
|
||||||
|
require.Contains(t, filteredResult, "envoy.filters.http.rbac")
|
||||||
|
require.Contains(t, filteredResult, "envoy.filters.http.router")
|
||||||
|
require.Contains(t, dump, "intentions")
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPServiceEchoes verifies that a post to the given ip/port combination
|
// HTTPServiceEchoes verifies that a post to the given ip/port combination
|
||||||
// returns the data in the response body. Optional path can be provided to
|
// returns the data in the response body. Optional path can be provided to
|
||||||
// differentiate requests.
|
// differentiate requests.
|
||||||
|
@ -243,7 +279,7 @@ func (a *Asserter) fortioFetch2Destination(
|
||||||
) (body []byte, res *http.Response) {
|
) (body []byte, res *http.Response) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
err, res := getFortioFetch2DestinationResponse(t, client, addr, dest, path)
|
err, res := getFortioFetch2DestinationResponse(t, client, addr, dest, path, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
@ -258,7 +294,7 @@ func (a *Asserter) fortioFetch2Destination(
|
||||||
return body, res
|
return body, res
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFortioFetch2DestinationResponse(t testutil.TestingTB, client *http.Client, addr string, dest *topology.Destination, path string) (error, *http.Response) {
|
func getFortioFetch2DestinationResponse(t testutil.TestingTB, client *http.Client, addr string, dest *topology.Destination, path string, headers map[string]string) (error, *http.Response) {
|
||||||
var actualURL string
|
var actualURL string
|
||||||
if dest.Implied {
|
if dest.Implied {
|
||||||
actualURL = fmt.Sprintf("http://%s--%s--%s.virtual.consul:%d/%s",
|
actualURL = fmt.Sprintf("http://%s--%s--%s.virtual.consul:%d/%s",
|
||||||
|
@ -279,6 +315,9 @@ func getFortioFetch2DestinationResponse(t testutil.TestingTB, client *http.Clien
|
||||||
req, err := http.NewRequest(http.MethodPost, url, nil)
|
req, err := http.NewRequest(http.MethodPost, url, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for h, v := range headers {
|
||||||
|
req.Header.Set(h, v)
|
||||||
|
}
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return err, res
|
return err, res
|
||||||
|
@ -339,13 +378,16 @@ func (a *Asserter) FortioFetch2FortioName(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FortioFetch2ServiceUnavailable uses the /fortio/fetch2 endpoint to do a header echo check against an destination
|
|
||||||
// fortio and asserts that the service is unavailable (503)
|
|
||||||
func (a *Asserter) FortioFetch2ServiceUnavailable(t *testing.T, fortioWrk *topology.Workload, dest *topology.Destination) {
|
func (a *Asserter) FortioFetch2ServiceUnavailable(t *testing.T, fortioWrk *topology.Workload, dest *topology.Destination) {
|
||||||
const kPassphrase = "x-passphrase"
|
const kPassphrase = "x-passphrase"
|
||||||
const passphrase = "hello"
|
const passphrase = "hello"
|
||||||
path := (fmt.Sprintf("/?header=%s:%s", kPassphrase, passphrase))
|
path := (fmt.Sprintf("/?header=%s:%s", kPassphrase, passphrase))
|
||||||
|
a.FortioFetch2ServiceStatusCodes(t, fortioWrk, dest, path, nil, []int{http.StatusServiceUnavailable})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FortioFetch2ServiceStatusCodes uses the /fortio/fetch2 endpoint to do a header echo check against a destination
|
||||||
|
// fortio and asserts that the returned status code matches the desired one(s)
|
||||||
|
func (a *Asserter) FortioFetch2ServiceStatusCodes(t *testing.T, fortioWrk *topology.Workload, dest *topology.Destination, path string, headers map[string]string, statuses []int) {
|
||||||
var (
|
var (
|
||||||
node = fortioWrk.Node
|
node = fortioWrk.Node
|
||||||
addr = fmt.Sprintf("%s:%d", node.LocalAddress(), fortioWrk.PortOrDefault(dest.PortName))
|
addr = fmt.Sprintf("%s:%d", node.LocalAddress(), fortioWrk.PortOrDefault(dest.PortName))
|
||||||
|
@ -353,9 +395,9 @@ func (a *Asserter) FortioFetch2ServiceUnavailable(t *testing.T, fortioWrk *topol
|
||||||
)
|
)
|
||||||
|
|
||||||
retry.RunWith(&retry.Timer{Timeout: 60 * time.Second, Wait: time.Millisecond * 500}, t, func(r *retry.R) {
|
retry.RunWith(&retry.Timer{Timeout: 60 * time.Second, Wait: time.Millisecond * 500}, t, func(r *retry.R) {
|
||||||
_, res := getFortioFetch2DestinationResponse(r, client, addr, dest, path)
|
_, res := getFortioFetch2DestinationResponse(r, client, addr, dest, path, headers)
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
require.Equal(r, http.StatusServiceUnavailable, res.StatusCode)
|
require.Contains(r, statuses, res.StatusCode)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,41 +198,6 @@ func AssertEnvoyMetricAtLeast(t *testing.T, adminPort int, prefix, metric string
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnvoyHTTPrbacFilters validates that proxy was configured with an http connection manager
|
|
||||||
// AssertEnvoyHTTPrbacFilters validates that proxy was configured with an http connection manager
|
|
||||||
// this assertion is currently unused current tests use http protocol
|
|
||||||
func AssertEnvoyHTTPrbacFilters(t *testing.T, port int) {
|
|
||||||
var (
|
|
||||||
dump string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
failer := func() *retry.Timer {
|
|
||||||
return &retry.Timer{Timeout: 30 * time.Second, Wait: 1 * time.Second}
|
|
||||||
}
|
|
||||||
|
|
||||||
retry.RunWith(failer(), t, func(r *retry.R) {
|
|
||||||
dump, _, err = GetEnvoyOutput(port, "config_dump", map[string]string{})
|
|
||||||
if err != nil {
|
|
||||||
r.Fatal("could not fetch envoy configuration")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// the steps below validate that the json result from envoy config dump configured active listeners with rbac and http filters
|
|
||||||
filter := `.configs[2].dynamic_listeners[].active_state.listener | "\(.name) \( .filter_chains[0].filters[] | select(.name == "envoy.filters.network.http_connection_manager") | .typed_config.http_filters | map(.name) | join(","))"`
|
|
||||||
results, err := utils.JQFilter(dump, filter)
|
|
||||||
require.NoError(t, err, "could not parse envoy configuration")
|
|
||||||
require.Len(t, results, 1, "static-server proxy should have been configured with two listener filters.")
|
|
||||||
|
|
||||||
var filteredResult []string
|
|
||||||
for _, result := range results {
|
|
||||||
sanitizedResult := sanitizeResult(result)
|
|
||||||
filteredResult = append(filteredResult, sanitizedResult...)
|
|
||||||
}
|
|
||||||
require.Contains(t, filteredResult, "envoy.filters.http.rbac")
|
|
||||||
assert.Contains(t, filteredResult, "envoy.filters.http.header_to_metadata")
|
|
||||||
assert.Contains(t, filteredResult, "envoy.filters.http.router")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssertEnvoyPresentsCertURI makes GET request to /certs endpoint and validates that
|
// AssertEnvoyPresentsCertURI makes GET request to /certs endpoint and validates that
|
||||||
// two certificates URI is available in the response
|
// two certificates URI is available in the response
|
||||||
func AssertEnvoyPresentsCertURI(t *testing.T, port int, serviceName string) {
|
func AssertEnvoyPresentsCertURI(t *testing.T, port int, serviceName string) {
|
||||||
|
|
Loading…
Reference in New Issue