mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 21:35:52 +00:00
95d9b2c7e4
* xdsv2: support l7 by adding xfcc policy/headers, tweaking routes, and make a bunch of listeners l7 tests pass * sidecarproxycontroller: add l7 local app support * trafficpermissions: make l4 traffic permissions work on l7 workloads * rename route name field for consistency with l4 cluster name field * resolve conflicts and rebase * fix: ensure route name is used in l7 destination route name as well. previously it was only in the route names themselves, now the route name and l7 destination route name line up
257 lines
7.0 KiB
Go
257 lines
7.0 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package xdsv2
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
|
envoy_rbac_v3 "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
|
|
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
|
envoy_http_rbac_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3"
|
|
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
|
envoy_network_rbac_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3"
|
|
envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
|
|
|
"github.com/hashicorp/consul/agent/xds/response"
|
|
"github.com/hashicorp/consul/proto-public/pbmesh/v2beta1/pbproxystate"
|
|
)
|
|
|
|
const (
|
|
baseL4PermissionKey = "consul-intentions-layer4"
|
|
)
|
|
|
|
// MakeL4RBAC returns the envoy deny and allow rules from the traffic permissions. After calling this function these
|
|
// rules can be put into a network rbac filter or http rbac filter depending on the local app port protocol.
|
|
func MakeL4RBAC(trafficPermissions *pbproxystate.TrafficPermissions) (deny *envoy_rbac_v3.RBAC, allow *envoy_rbac_v3.RBAC, err error) {
|
|
var denyRBAC *envoy_rbac_v3.RBAC
|
|
var allowRBAC *envoy_rbac_v3.RBAC
|
|
|
|
if trafficPermissions == nil {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
if len(trafficPermissions.DenyPermissions) > 0 {
|
|
denyRBAC = &envoy_rbac_v3.RBAC{
|
|
Action: envoy_rbac_v3.RBAC_DENY,
|
|
Policies: make(map[string]*envoy_rbac_v3.Policy),
|
|
}
|
|
denyRBAC.Policies = makeRBACPolicies(trafficPermissions.DenyPermissions)
|
|
}
|
|
|
|
// Only include the allow RBAC when Consul is in default deny.
|
|
if !trafficPermissions.DefaultAllow {
|
|
allowRBAC = &envoy_rbac_v3.RBAC{
|
|
Action: envoy_rbac_v3.RBAC_ALLOW,
|
|
Policies: make(map[string]*envoy_rbac_v3.Policy),
|
|
}
|
|
|
|
allowRBAC.Policies = makeRBACPolicies(trafficPermissions.AllowPermissions)
|
|
}
|
|
|
|
return denyRBAC, allowRBAC, nil
|
|
}
|
|
|
|
// MakeRBACNetworkFilters calls MakeL4RBAC and wraps the result in envoy network filters meant for L4 protocols.
|
|
func MakeRBACNetworkFilters(trafficPermissions *pbproxystate.TrafficPermissions) ([]*envoy_listener_v3.Filter, error) {
|
|
var filters []*envoy_listener_v3.Filter
|
|
|
|
deny, allow, err := MakeL4RBAC(trafficPermissions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if deny != nil {
|
|
filter, err := makeRBACFilter(deny)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
filters = append(filters, filter)
|
|
}
|
|
|
|
if allow != nil {
|
|
filter, err := makeRBACFilter(allow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
filters = append(filters, filter)
|
|
|
|
}
|
|
|
|
return filters, nil
|
|
}
|
|
|
|
// MakeRBACHTTPFilters calls MakeL4RBAC and wraps the result in envoy http filters meant for L7 protocols. Eventually
|
|
// this will need to also accumulate any L7 traffic permissions when that is implemented.
|
|
func MakeRBACHTTPFilters(trafficPermissions *pbproxystate.TrafficPermissions) ([]*envoy_http_v3.HttpFilter, error) {
|
|
var httpFilters []*envoy_http_v3.HttpFilter
|
|
|
|
deny, allow, err := MakeL4RBAC(trafficPermissions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if deny != nil {
|
|
filter, err := makeRBACHTTPFilter(deny)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
httpFilters = append(httpFilters, filter)
|
|
}
|
|
|
|
if allow != nil {
|
|
filter, err := makeRBACHTTPFilter(allow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
httpFilters = append(httpFilters, filter)
|
|
|
|
}
|
|
|
|
return httpFilters, nil
|
|
}
|
|
|
|
const (
|
|
envoyNetworkRBACFilterKey = "envoy.filters.network.rbac"
|
|
envoyHTTPRBACFilterKey = "envoy.filters.http.rbac"
|
|
)
|
|
|
|
func makeRBACFilter(rbac *envoy_rbac_v3.RBAC) (*envoy_listener_v3.Filter, error) {
|
|
cfg := &envoy_network_rbac_v3.RBAC{
|
|
StatPrefix: "connect_authz",
|
|
Rules: rbac,
|
|
}
|
|
return makeEnvoyFilter(envoyNetworkRBACFilterKey, cfg)
|
|
}
|
|
|
|
func makeRBACHTTPFilter(rbac *envoy_rbac_v3.RBAC) (*envoy_http_v3.HttpFilter, error) {
|
|
cfg := &envoy_http_rbac_v3.RBAC{
|
|
Rules: rbac,
|
|
}
|
|
return makeEnvoyHTTPFilter(envoyHTTPRBACFilterKey, cfg)
|
|
}
|
|
|
|
func makeRBACPolicies(l4Permissions []*pbproxystate.Permission) map[string]*envoy_rbac_v3.Policy {
|
|
policyLabel := func(i int) string {
|
|
if len(l4Permissions) == 1 {
|
|
return baseL4PermissionKey
|
|
}
|
|
return fmt.Sprintf("%s-%d", baseL4PermissionKey, i)
|
|
}
|
|
|
|
policies := make(map[string]*envoy_rbac_v3.Policy, len(l4Permissions))
|
|
|
|
for i, permission := range l4Permissions {
|
|
policy := makeRBACPolicy(permission)
|
|
if policy != nil {
|
|
policies[policyLabel(i)] = policy
|
|
}
|
|
}
|
|
|
|
return policies
|
|
}
|
|
|
|
func makeRBACPolicy(p *pbproxystate.Permission) *envoy_rbac_v3.Policy {
|
|
if len(p.Principals) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var principals []*envoy_rbac_v3.Principal
|
|
|
|
for _, p := range p.Principals {
|
|
principals = append(principals, toEnvoyPrincipal(p))
|
|
}
|
|
|
|
return &envoy_rbac_v3.Policy{
|
|
Principals: principals,
|
|
Permissions: []*envoy_rbac_v3.Permission{anyPermission()},
|
|
}
|
|
}
|
|
|
|
func toEnvoyPrincipal(p *pbproxystate.Principal) *envoy_rbac_v3.Principal {
|
|
includePrincipal := principal(p.Spiffe)
|
|
|
|
if len(p.ExcludeSpiffes) == 0 {
|
|
return includePrincipal
|
|
}
|
|
|
|
principals := make([]*envoy_rbac_v3.Principal, 0, len(p.ExcludeSpiffes)+1)
|
|
principals = append(principals, includePrincipal)
|
|
for _, s := range p.ExcludeSpiffes {
|
|
principals = append(principals, negatePrincipal(principal(s)))
|
|
}
|
|
return andPrincipals(principals)
|
|
}
|
|
|
|
func principal(spiffe *pbproxystate.Spiffe) *envoy_rbac_v3.Principal {
|
|
var andIDs []*envoy_rbac_v3.Principal
|
|
andIDs = append(andIDs, idPrincipal(spiffe.Regex))
|
|
|
|
if len(spiffe.XfccRegex) > 0 {
|
|
andIDs = append(andIDs, xfccPrincipal(spiffe.XfccRegex))
|
|
}
|
|
|
|
return andPrincipals(andIDs)
|
|
}
|
|
|
|
func negatePrincipal(p *envoy_rbac_v3.Principal) *envoy_rbac_v3.Principal {
|
|
return &envoy_rbac_v3.Principal{
|
|
Identifier: &envoy_rbac_v3.Principal_NotId{
|
|
NotId: p,
|
|
},
|
|
}
|
|
}
|
|
|
|
func idPrincipal(spiffeID string) *envoy_rbac_v3.Principal {
|
|
return &envoy_rbac_v3.Principal{
|
|
Identifier: &envoy_rbac_v3.Principal_Authenticated_{
|
|
Authenticated: &envoy_rbac_v3.Principal_Authenticated{
|
|
PrincipalName: &envoy_matcher_v3.StringMatcher{
|
|
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
|
SafeRegex: response.MakeEnvoyRegexMatch(spiffeID),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func andPrincipals(ids []*envoy_rbac_v3.Principal) *envoy_rbac_v3.Principal {
|
|
switch len(ids) {
|
|
case 1:
|
|
return ids[0]
|
|
default:
|
|
return &envoy_rbac_v3.Principal{
|
|
Identifier: &envoy_rbac_v3.Principal_AndIds{
|
|
AndIds: &envoy_rbac_v3.Principal_Set{
|
|
Ids: ids,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
func xfccPrincipal(spiffeID string) *envoy_rbac_v3.Principal {
|
|
return &envoy_rbac_v3.Principal{
|
|
Identifier: &envoy_rbac_v3.Principal_Header{
|
|
Header: &envoy_route_v3.HeaderMatcher{
|
|
Name: "x-forwarded-client-cert",
|
|
HeaderMatchSpecifier: &envoy_route_v3.HeaderMatcher_StringMatch{
|
|
StringMatch: &envoy_matcher_v3.StringMatcher{
|
|
MatchPattern: &envoy_matcher_v3.StringMatcher_SafeRegex{
|
|
SafeRegex: response.MakeEnvoyRegexMatch(spiffeID),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func anyPermission() *envoy_rbac_v3.Permission {
|
|
return &envoy_rbac_v3.Permission{
|
|
Rule: &envoy_rbac_v3.Permission_Any{Any: true},
|
|
}
|
|
}
|