mirror of https://github.com/hashicorp/consul
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>pull/20523/head
parent
1bd253021b
commit
57bad0df85
@ -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"
|
||||
}
|
||||
}
|
@ -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$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
)
|
||||
}
|
Loading…
Reference in new issue