mirror of https://github.com/status-im/consul.git
[NET-3091] Update service intentions to support jwt provider references (#17037)
* [NET-3090] Add new JWT provider config entry * Add initial test cases * update validations for jwt-provider config entry fields * more validation * start improving tests * more tests * Normalize * Improve tests and move validate fns * usage test update * Add split between ent and oss for partitions * fix lint issues * Added retry backoff, fixed tests, removed unused defaults * take into account default partitions * use countTrue and add aliases * omit audiences if empty * fix failing tests * add omit-entry * Add JWT intentions * generate proto * fix deep copy issues * remove extra field * added some tests * more tests * add validation for creating existing jwt * fix nil issue * More tests, fix conflicts and improve memdb call * fix namespace * add aliases * consolidate errors, skip duplicate memdb calls * reworked iteration over config entries * logic improvements from review --------- Co-authored-by: Ronald Ekambi <ronekambi@gmail.com>
This commit is contained in:
parent
ac200cfec8
commit
f4406e69b9
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
memdb "github.com/hashicorp/go-memdb"
|
memdb "github.com/hashicorp/go-memdb"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/configentry"
|
"github.com/hashicorp/consul/agent/configentry"
|
||||||
|
@ -592,6 +593,13 @@ func validateProposedConfigEntryInGraph(
|
||||||
}
|
}
|
||||||
case structs.SamenessGroup:
|
case structs.SamenessGroup:
|
||||||
case structs.ServiceIntentions:
|
case structs.ServiceIntentions:
|
||||||
|
if newEntry != nil {
|
||||||
|
err := validateJWTProvidersExist(tx, kindName, newEntry)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
case structs.MeshConfig:
|
case structs.MeshConfig:
|
||||||
case structs.ExportedServices:
|
case structs.ExportedServices:
|
||||||
case structs.APIGateway: // TODO Consider checkGatewayClash
|
case structs.APIGateway: // TODO Consider checkGatewayClash
|
||||||
|
@ -608,6 +616,92 @@ func validateProposedConfigEntryInGraph(
|
||||||
return validateProposedConfigEntryInServiceGraph(tx, kindName, newEntry)
|
return validateProposedConfigEntryInServiceGraph(tx, kindName, newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getExistingJWTProvidersByName(tx ReadTxn, kn configentry.KindName) (map[string]*structs.JWTProviderConfigEntry, error) {
|
||||||
|
meta := acl.NewEnterpriseMetaWithPartition(
|
||||||
|
kn.EnterpriseMeta.PartitionOrDefault(),
|
||||||
|
acl.DefaultNamespaceName,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, configEntries, err := configEntriesByKindTxn(tx, nil, structs.JWTProvider, &meta)
|
||||||
|
|
||||||
|
providerNames := make(map[string]*structs.JWTProviderConfigEntry)
|
||||||
|
|
||||||
|
for i := range configEntries {
|
||||||
|
entry, ok := configEntries[i].(*structs.JWTProviderConfigEntry)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Invalid type of jwt-provider config entry: %T", configEntries[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := providerNames[entry.Name]; !ok {
|
||||||
|
providerNames[entry.Name] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerNames, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateJWTProvider(existingProviderNames map[string]*structs.JWTProviderConfigEntry, referencedProviderNames map[string]struct{}) error {
|
||||||
|
var result error
|
||||||
|
|
||||||
|
for referencedProvider := range referencedProviderNames {
|
||||||
|
_, found := existingProviderNames[referencedProvider]
|
||||||
|
if !found {
|
||||||
|
result = multierror.Append(result, fmt.Errorf("Referenced JWT Provider does not exist. Provider Name: %s", referencedProvider)).ErrorOrNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReferencedProviderNames(j *structs.IntentionJWTRequirement, s []*structs.SourceIntention) map[string]struct{} {
|
||||||
|
providerNames := make(map[string]struct{})
|
||||||
|
|
||||||
|
if j != nil {
|
||||||
|
for _, provider := range j.Providers {
|
||||||
|
if _, ok := providerNames[provider.Name]; !ok {
|
||||||
|
providerNames[provider.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, src := range s {
|
||||||
|
for _, perm := range src.Permissions {
|
||||||
|
if perm.JWT != nil {
|
||||||
|
for _, provider := range perm.JWT.Providers {
|
||||||
|
if _, ok := providerNames[provider.Name]; !ok {
|
||||||
|
providerNames[provider.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// This fetches all the jwt-providers config entries and iterates over them
|
||||||
|
// to validate that any provider referenced exists.
|
||||||
|
// This is okay because we assume there are very few jwt-providers per partition
|
||||||
|
func validateJWTProvidersExist(tx ReadTxn, kn configentry.KindName, ce structs.ConfigEntry) error {
|
||||||
|
var result error
|
||||||
|
entry, ok := ce.(*structs.ServiceIntentionsConfigEntry)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Invalid service intention config entry: %T", entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
referencedProvidersNames := getReferencedProviderNames(entry.JWT, entry.Sources)
|
||||||
|
|
||||||
|
if len(referencedProvidersNames) > 0 {
|
||||||
|
jwtProvidersNames, err := getExistingJWTProvidersByName(tx, kn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed retrieval of jwt config entries with err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = multierror.Append(result, validateJWTProvider(jwtProvidersNames, referencedProvidersNames)).ErrorOrNil()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// checkMutualTLSMode validates the MutualTLSMode (in proxy-defaults or
|
// checkMutualTLSMode validates the MutualTLSMode (in proxy-defaults or
|
||||||
// service-defaults) against the AllowEnablingPermissiveMutualTLS setting in the
|
// service-defaults) against the AllowEnablingPermissiveMutualTLS setting in the
|
||||||
// mesh config entry, as follows:
|
// mesh config entry, as follows:
|
||||||
|
|
|
@ -87,6 +87,7 @@ var _ configEntryIndexable = (*structs.ServiceResolverConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.ServiceRouterConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.ServiceRouterConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.ServiceSplitterConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.ServiceSplitterConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.TerminatingGatewayConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.TerminatingGatewayConfigEntry)(nil)
|
||||||
|
var _ configEntryIndexable = (*structs.JWTProviderConfigEntry)(nil)
|
||||||
|
|
||||||
func indexFromConfigEntry(c structs.ConfigEntry) ([]byte, error) {
|
func indexFromConfigEntry(c structs.ConfigEntry) ([]byte, error) {
|
||||||
if c.GetName() == "" || c.GetKind() == "" {
|
if c.GetName() == "" || c.GetKind() == "" {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/lib/stringslice"
|
"github.com/hashicorp/consul/lib/stringslice"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +21,8 @@ type ServiceIntentionsConfigEntry struct {
|
||||||
|
|
||||||
Sources []*SourceIntention
|
Sources []*SourceIntention
|
||||||
|
|
||||||
|
JWT *IntentionJWTRequirement `json:",omitempty"`
|
||||||
|
|
||||||
Meta map[string]string `json:",omitempty"` // formerly Intention.Meta
|
Meta map[string]string `json:",omitempty"` // formerly Intention.Meta
|
||||||
|
|
||||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` // formerly DestinationNS
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` // formerly DestinationNS
|
||||||
|
@ -57,6 +60,10 @@ func (e *ServiceIntentionsConfigEntry) Clone() *ServiceIntentionsConfigEntry {
|
||||||
e2.Sources[i] = src.Clone()
|
e2.Sources[i] = src.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.JWT != nil {
|
||||||
|
e2.JWT = e.JWT.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
return &e2
|
return &e2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +138,7 @@ func (e *ServiceIntentionsConfigEntry) ToIntention(src *SourceIntention) *Intent
|
||||||
SourceNS: src.NamespaceOrDefault(),
|
SourceNS: src.NamespaceOrDefault(),
|
||||||
SourceName: src.Name,
|
SourceName: src.Name,
|
||||||
SourceType: src.Type,
|
SourceType: src.Type,
|
||||||
|
JWT: e.JWT,
|
||||||
Action: src.Action,
|
Action: src.Action,
|
||||||
Permissions: src.Permissions,
|
Permissions: src.Permissions,
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
|
@ -268,6 +276,79 @@ type SourceIntention struct {
|
||||||
Peer string `json:",omitempty"`
|
Peer string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IntentionJWTRequirement struct {
|
||||||
|
// Providers is a list of providers to consider when verifying a JWT.
|
||||||
|
Providers []*IntentionJWTProvider `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntentionJWTRequirement) Clone() *IntentionJWTRequirement {
|
||||||
|
e2 := *e
|
||||||
|
|
||||||
|
e2.Providers = make([]*IntentionJWTProvider, len(e.Providers))
|
||||||
|
for i, src := range e.Providers {
|
||||||
|
e2.Providers[i] = src.Clone()
|
||||||
|
}
|
||||||
|
return &e2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *IntentionJWTProvider) Validate() error {
|
||||||
|
if p.Name == "" {
|
||||||
|
return fmt.Errorf("JWT provider name is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntentionJWTRequirement) Validate() error {
|
||||||
|
var result error
|
||||||
|
|
||||||
|
for _, provider := range e.Providers {
|
||||||
|
if err := provider.Validate(); err != nil {
|
||||||
|
result = multierror.Append(result, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntentionJWTProvider struct {
|
||||||
|
// Name is the name of the JWT provider. There MUST be a corresponding
|
||||||
|
// "jwt-provider" config entry with this name.
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
|
||||||
|
// VerifyClaims is a list of additional claims to verify in a JWT's payload.
|
||||||
|
VerifyClaims []*IntentionJWTClaimVerification `json:",omitempty" alias:"verify_claims"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntentionJWTProvider) Clone() *IntentionJWTProvider {
|
||||||
|
e2 := *e
|
||||||
|
|
||||||
|
e2.VerifyClaims = make([]*IntentionJWTClaimVerification, len(e.VerifyClaims))
|
||||||
|
for i, src := range e.VerifyClaims {
|
||||||
|
e2.VerifyClaims[i] = src.Clone()
|
||||||
|
}
|
||||||
|
return &e2
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntentionJWTClaimVerification struct {
|
||||||
|
// Path is the path to the claim in the token JSON.
|
||||||
|
Path []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Value is the expected value at the given path:
|
||||||
|
// - If the type at the path is a list then we verify
|
||||||
|
// that this value is contained in the list.
|
||||||
|
//
|
||||||
|
// - If the type at the path is a string then we verify
|
||||||
|
// that this value matches.
|
||||||
|
Value string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *IntentionJWTClaimVerification) Clone() *IntentionJWTClaimVerification {
|
||||||
|
e2 := *e
|
||||||
|
|
||||||
|
e2.Path = stringslice.CloneStringSlice(e.Path)
|
||||||
|
return &e2
|
||||||
|
}
|
||||||
|
|
||||||
type IntentionPermission struct {
|
type IntentionPermission struct {
|
||||||
Action IntentionAction // required: allow|deny
|
Action IntentionAction // required: allow|deny
|
||||||
|
|
||||||
|
@ -281,6 +362,8 @@ type IntentionPermission struct {
|
||||||
|
|
||||||
// If we ever add Sentinel support, this is one place we may
|
// If we ever add Sentinel support, this is one place we may
|
||||||
// wish to add it.
|
// wish to add it.
|
||||||
|
|
||||||
|
JWT *IntentionJWTRequirement `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *IntentionPermission) Clone() *IntentionPermission {
|
func (p *IntentionPermission) Clone() *IntentionPermission {
|
||||||
|
@ -288,9 +371,21 @@ func (p *IntentionPermission) Clone() *IntentionPermission {
|
||||||
if p.HTTP != nil {
|
if p.HTTP != nil {
|
||||||
p2.HTTP = p.HTTP.Clone()
|
p2.HTTP = p.HTTP.Clone()
|
||||||
}
|
}
|
||||||
|
if p.JWT != nil {
|
||||||
|
p2.JWT = p.JWT.Clone()
|
||||||
|
}
|
||||||
return &p2
|
return &p2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *IntentionPermission) Validate() error {
|
||||||
|
var result error
|
||||||
|
if p.JWT != nil {
|
||||||
|
result = p.JWT.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
type IntentionHTTPPermission struct {
|
type IntentionHTTPPermission struct {
|
||||||
// PathExact, PathPrefix, and PathRegex are mutually exclusive.
|
// PathExact, PathPrefix, and PathRegex are mutually exclusive.
|
||||||
PathExact string `json:",omitempty" alias:"path_exact"`
|
PathExact string `json:",omitempty" alias:"path_exact"`
|
||||||
|
@ -562,6 +657,12 @@ func (e *ServiceIntentionsConfigEntry) validate(legacyWrite bool) error {
|
||||||
|
|
||||||
destIsWild := e.HasWildcardDestination()
|
destIsWild := e.HasWildcardDestination()
|
||||||
|
|
||||||
|
if e.JWT != nil {
|
||||||
|
if err := e.JWT.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if legacyWrite {
|
if legacyWrite {
|
||||||
if len(e.Meta) > 0 {
|
if len(e.Meta) > 0 {
|
||||||
return fmt.Errorf("Meta must be omitted for legacy intention writes")
|
return fmt.Errorf("Meta must be omitted for legacy intention writes")
|
||||||
|
@ -762,6 +863,10 @@ func (e *ServiceIntentionsConfigEntry) validate(legacyWrite bool) error {
|
||||||
if permParts == 0 {
|
if permParts == 0 {
|
||||||
return fmt.Errorf(errorPrefix+" should not be empty", i, j)
|
return fmt.Errorf(errorPrefix+" should not be empty", i, j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := perm.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
psn := PeeredServiceName{Peer: src.Peer, ServiceName: src.SourceServiceName()}
|
psn := PeeredServiceName{Peer: src.Peer, ServiceName: src.SourceServiceName()}
|
||||||
|
|
|
@ -1265,6 +1265,153 @@ func TestServiceIntentionsConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
validateErr: `Sources[1] defines peer("peer1") "` + fooName.String() + `" more than once`,
|
validateErr: `Sources[1] defines peer("peer1") "` + fooName.String() + `" more than once`,
|
||||||
},
|
},
|
||||||
|
"JWT - missing provider name": {
|
||||||
|
entry: &ServiceIntentionsConfigEntry{
|
||||||
|
Kind: ServiceIntentions,
|
||||||
|
Name: "test",
|
||||||
|
JWT: &IntentionJWTRequirement{
|
||||||
|
Providers: []*IntentionJWTProvider{
|
||||||
|
{
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Value: "api.apps.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `JWT provider name is required`,
|
||||||
|
},
|
||||||
|
"JWT - missing 1 provider name with multiple providers": {
|
||||||
|
entry: &ServiceIntentionsConfigEntry{
|
||||||
|
Kind: ServiceIntentions,
|
||||||
|
Name: "test",
|
||||||
|
JWT: &IntentionJWTRequirement{
|
||||||
|
Providers: []*IntentionJWTProvider{
|
||||||
|
{
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Path: []string{"aud"},
|
||||||
|
Value: "another-api.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "okta",
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Path: []string{"aud"},
|
||||||
|
Value: "api.apps.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `JWT provider name is required`,
|
||||||
|
},
|
||||||
|
"JWT - missing provider name under permissions": {
|
||||||
|
entry: &ServiceIntentionsConfigEntry{
|
||||||
|
Kind: ServiceIntentions,
|
||||||
|
Name: "test",
|
||||||
|
Sources: []*SourceIntention{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Permissions: []*IntentionPermission{
|
||||||
|
{
|
||||||
|
Action: IntentionActionAllow,
|
||||||
|
HTTP: &IntentionHTTPPermission{
|
||||||
|
PathPrefix: "/foo",
|
||||||
|
Header: []IntentionHTTPHeaderPermission{
|
||||||
|
{
|
||||||
|
Name: "x-abc",
|
||||||
|
Exact: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "x-xyz",
|
||||||
|
Present: true,
|
||||||
|
Invert: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST", "PUT", "GET"},
|
||||||
|
},
|
||||||
|
JWT: &IntentionJWTRequirement{
|
||||||
|
Providers: []*IntentionJWTProvider{
|
||||||
|
{
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Path: []string{"aud"},
|
||||||
|
Value: "another-api.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `JWT provider name is required`,
|
||||||
|
},
|
||||||
|
"valid JWTs": {
|
||||||
|
entry: &ServiceIntentionsConfigEntry{
|
||||||
|
Kind: ServiceIntentions,
|
||||||
|
Name: "test",
|
||||||
|
JWT: &IntentionJWTRequirement{
|
||||||
|
Providers: []*IntentionJWTProvider{
|
||||||
|
{
|
||||||
|
Name: "okta",
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Path: []string{"aud"},
|
||||||
|
Value: "another-api.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sources: []*SourceIntention{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Permissions: []*IntentionPermission{
|
||||||
|
{
|
||||||
|
Action: IntentionActionAllow,
|
||||||
|
HTTP: &IntentionHTTPPermission{
|
||||||
|
PathPrefix: "/foo",
|
||||||
|
Header: []IntentionHTTPHeaderPermission{
|
||||||
|
{
|
||||||
|
Name: "x-abc",
|
||||||
|
Exact: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "x-xyz",
|
||||||
|
Present: true,
|
||||||
|
Invert: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Methods: []string{"POST", "PUT", "GET"},
|
||||||
|
},
|
||||||
|
JWT: &IntentionJWTRequirement{
|
||||||
|
Providers: []*IntentionJWTProvider{
|
||||||
|
{
|
||||||
|
Name: "okta",
|
||||||
|
VerifyClaims: []*IntentionJWTClaimVerification{
|
||||||
|
{
|
||||||
|
Path: []string{"aud"},
|
||||||
|
Value: "another-api.test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
tc := tc
|
tc := tc
|
||||||
|
|
|
@ -78,6 +78,9 @@ type Intention struct {
|
||||||
// service-intentions config entry directly.
|
// service-intentions config entry directly.
|
||||||
Permissions []*IntentionPermission `bexpr:"-" json:",omitempty"`
|
Permissions []*IntentionPermission `bexpr:"-" json:",omitempty"`
|
||||||
|
|
||||||
|
// JWT specifies JWT authn that applies to incoming requests.
|
||||||
|
JWT *IntentionJWTRequirement `bexpr:"-" json:",omitempty"`
|
||||||
|
|
||||||
// DefaultAddr is not used.
|
// DefaultAddr is not used.
|
||||||
// Deprecated: DefaultAddr is not used and may be removed in a future version.
|
// Deprecated: DefaultAddr is not used and may be removed in a future version.
|
||||||
DefaultAddr string `bexpr:"-" codec:",omitempty" json:",omitempty"`
|
DefaultAddr string `bexpr:"-" codec:",omitempty" json:",omitempty"`
|
||||||
|
|
|
@ -565,6 +565,34 @@ func (o *Intention) DeepCopy() *Intention {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if o.JWT != nil {
|
||||||
|
cp.JWT = new(IntentionJWTRequirement)
|
||||||
|
*cp.JWT = *o.JWT
|
||||||
|
if o.JWT.Providers != nil {
|
||||||
|
cp.JWT.Providers = make([]*IntentionJWTProvider, len(o.JWT.Providers))
|
||||||
|
copy(cp.JWT.Providers, o.JWT.Providers)
|
||||||
|
for i4 := range o.JWT.Providers {
|
||||||
|
if o.JWT.Providers[i4] != nil {
|
||||||
|
cp.JWT.Providers[i4] = new(IntentionJWTProvider)
|
||||||
|
*cp.JWT.Providers[i4] = *o.JWT.Providers[i4]
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims = make([]*IntentionJWTClaimVerification, len(o.JWT.Providers[i4].VerifyClaims))
|
||||||
|
copy(cp.JWT.Providers[i4].VerifyClaims, o.JWT.Providers[i4].VerifyClaims)
|
||||||
|
for i7 := range o.JWT.Providers[i4].VerifyClaims {
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims[i7] != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims[i7] = new(IntentionJWTClaimVerification)
|
||||||
|
*cp.JWT.Providers[i4].VerifyClaims[i7] = *o.JWT.Providers[i4].VerifyClaims[i7]
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims[i7].Path != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims[i7].Path = make([]string, len(o.JWT.Providers[i4].VerifyClaims[i7].Path))
|
||||||
|
copy(cp.JWT.Providers[i4].VerifyClaims[i7].Path, o.JWT.Providers[i4].VerifyClaims[i7].Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if o.Meta != nil {
|
if o.Meta != nil {
|
||||||
cp.Meta = make(map[string]string, len(o.Meta))
|
cp.Meta = make(map[string]string, len(o.Meta))
|
||||||
for k2, v2 := range o.Meta {
|
for k2, v2 := range o.Meta {
|
||||||
|
@ -593,6 +621,34 @@ func (o *IntentionPermission) DeepCopy() *IntentionPermission {
|
||||||
copy(cp.HTTP.Methods, o.HTTP.Methods)
|
copy(cp.HTTP.Methods, o.HTTP.Methods)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if o.JWT != nil {
|
||||||
|
cp.JWT = new(IntentionJWTRequirement)
|
||||||
|
*cp.JWT = *o.JWT
|
||||||
|
if o.JWT.Providers != nil {
|
||||||
|
cp.JWT.Providers = make([]*IntentionJWTProvider, len(o.JWT.Providers))
|
||||||
|
copy(cp.JWT.Providers, o.JWT.Providers)
|
||||||
|
for i4 := range o.JWT.Providers {
|
||||||
|
if o.JWT.Providers[i4] != nil {
|
||||||
|
cp.JWT.Providers[i4] = new(IntentionJWTProvider)
|
||||||
|
*cp.JWT.Providers[i4] = *o.JWT.Providers[i4]
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims = make([]*IntentionJWTClaimVerification, len(o.JWT.Providers[i4].VerifyClaims))
|
||||||
|
copy(cp.JWT.Providers[i4].VerifyClaims, o.JWT.Providers[i4].VerifyClaims)
|
||||||
|
for i7 := range o.JWT.Providers[i4].VerifyClaims {
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims[i7] != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims[i7] = new(IntentionJWTClaimVerification)
|
||||||
|
*cp.JWT.Providers[i4].VerifyClaims[i7] = *o.JWT.Providers[i4].VerifyClaims[i7]
|
||||||
|
if o.JWT.Providers[i4].VerifyClaims[i7].Path != nil {
|
||||||
|
cp.JWT.Providers[i4].VerifyClaims[i7].Path = make([]string, len(o.JWT.Providers[i4].VerifyClaims[i7].Path))
|
||||||
|
copy(cp.JWT.Providers[i4].VerifyClaims[i7].Path, o.JWT.Providers[i4].VerifyClaims[i7].Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ type ServiceIntentionsConfigEntry struct {
|
||||||
Namespace string `json:",omitempty"`
|
Namespace string `json:",omitempty"`
|
||||||
|
|
||||||
Sources []*SourceIntention
|
Sources []*SourceIntention
|
||||||
|
JWT *IntentionJWTRequirement `json:",omitempty"`
|
||||||
|
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ func (e *ServiceIntentionsConfigEntry) GetModifyIndex() uint64 { return e.Mo
|
||||||
type IntentionPermission struct {
|
type IntentionPermission struct {
|
||||||
Action IntentionAction
|
Action IntentionAction
|
||||||
HTTP *IntentionHTTPPermission `json:",omitempty"`
|
HTTP *IntentionHTTPPermission `json:",omitempty"`
|
||||||
|
JWT *IntentionJWTRequirement `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntentionHTTPPermission struct {
|
type IntentionHTTPPermission struct {
|
||||||
|
@ -68,3 +70,30 @@ type IntentionHTTPHeaderPermission struct {
|
||||||
Regex string `json:",omitempty"`
|
Regex string `json:",omitempty"`
|
||||||
Invert bool `json:",omitempty"`
|
Invert bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IntentionJWTRequirement struct {
|
||||||
|
// Providers is a list of providers to consider when verifying a JWT.
|
||||||
|
Providers []*IntentionJWTProvider `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntentionJWTProvider struct {
|
||||||
|
// Name is the name of the JWT provider. There MUST be a corresponding
|
||||||
|
// "jwt-provider" config entry with this name.
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
|
||||||
|
// VerifyClaims is a list of additional claims to verify in a JWT's payload.
|
||||||
|
VerifyClaims []*IntentionJWTClaimVerification `json:",omitempty" alias:"verify_claims"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntentionJWTClaimVerification struct {
|
||||||
|
// Path is the path to the claim in the token JSON.
|
||||||
|
Path []string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Value is the expected value at the given path:
|
||||||
|
// - If the type at the path is a list then we verify
|
||||||
|
// that this value is contained in the list.
|
||||||
|
//
|
||||||
|
// - If the type at the path is a string then we verify
|
||||||
|
// that this value matches.
|
||||||
|
Value string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
|
@ -944,6 +944,82 @@ func IntentionHTTPPermissionFromStructs(t *structs.IntentionHTTPPermission, s *I
|
||||||
}
|
}
|
||||||
s.Methods = t.Methods
|
s.Methods = t.Methods
|
||||||
}
|
}
|
||||||
|
func IntentionJWTClaimVerificationToStructs(s *IntentionJWTClaimVerification, t *structs.IntentionJWTClaimVerification) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Path = s.Path
|
||||||
|
t.Value = s.Value
|
||||||
|
}
|
||||||
|
func IntentionJWTClaimVerificationFromStructs(t *structs.IntentionJWTClaimVerification, s *IntentionJWTClaimVerification) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Path = t.Path
|
||||||
|
s.Value = t.Value
|
||||||
|
}
|
||||||
|
func IntentionJWTProviderToStructs(s *IntentionJWTProvider, t *structs.IntentionJWTProvider) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Name = s.Name
|
||||||
|
{
|
||||||
|
t.VerifyClaims = make([]*structs.IntentionJWTClaimVerification, len(s.VerifyClaims))
|
||||||
|
for i := range s.VerifyClaims {
|
||||||
|
if s.VerifyClaims[i] != nil {
|
||||||
|
var x structs.IntentionJWTClaimVerification
|
||||||
|
IntentionJWTClaimVerificationToStructs(s.VerifyClaims[i], &x)
|
||||||
|
t.VerifyClaims[i] = &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func IntentionJWTProviderFromStructs(t *structs.IntentionJWTProvider, s *IntentionJWTProvider) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Name = t.Name
|
||||||
|
{
|
||||||
|
s.VerifyClaims = make([]*IntentionJWTClaimVerification, len(t.VerifyClaims))
|
||||||
|
for i := range t.VerifyClaims {
|
||||||
|
if t.VerifyClaims[i] != nil {
|
||||||
|
var x IntentionJWTClaimVerification
|
||||||
|
IntentionJWTClaimVerificationFromStructs(t.VerifyClaims[i], &x)
|
||||||
|
s.VerifyClaims[i] = &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func IntentionJWTRequirementToStructs(s *IntentionJWTRequirement, t *structs.IntentionJWTRequirement) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
{
|
||||||
|
t.Providers = make([]*structs.IntentionJWTProvider, len(s.Providers))
|
||||||
|
for i := range s.Providers {
|
||||||
|
if s.Providers[i] != nil {
|
||||||
|
var x structs.IntentionJWTProvider
|
||||||
|
IntentionJWTProviderToStructs(s.Providers[i], &x)
|
||||||
|
t.Providers[i] = &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func IntentionJWTRequirementFromStructs(t *structs.IntentionJWTRequirement, s *IntentionJWTRequirement) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
{
|
||||||
|
s.Providers = make([]*IntentionJWTProvider, len(t.Providers))
|
||||||
|
for i := range t.Providers {
|
||||||
|
if t.Providers[i] != nil {
|
||||||
|
var x IntentionJWTProvider
|
||||||
|
IntentionJWTProviderFromStructs(t.Providers[i], &x)
|
||||||
|
s.Providers[i] = &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func IntentionPermissionToStructs(s *IntentionPermission, t *structs.IntentionPermission) {
|
func IntentionPermissionToStructs(s *IntentionPermission, t *structs.IntentionPermission) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return
|
||||||
|
@ -954,6 +1030,11 @@ func IntentionPermissionToStructs(s *IntentionPermission, t *structs.IntentionPe
|
||||||
IntentionHTTPPermissionToStructs(s.HTTP, &x)
|
IntentionHTTPPermissionToStructs(s.HTTP, &x)
|
||||||
t.HTTP = &x
|
t.HTTP = &x
|
||||||
}
|
}
|
||||||
|
if s.JWT != nil {
|
||||||
|
var x structs.IntentionJWTRequirement
|
||||||
|
IntentionJWTRequirementToStructs(s.JWT, &x)
|
||||||
|
t.JWT = &x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func IntentionPermissionFromStructs(t *structs.IntentionPermission, s *IntentionPermission) {
|
func IntentionPermissionFromStructs(t *structs.IntentionPermission, s *IntentionPermission) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
@ -965,6 +1046,11 @@ func IntentionPermissionFromStructs(t *structs.IntentionPermission, s *Intention
|
||||||
IntentionHTTPPermissionFromStructs(t.HTTP, &x)
|
IntentionHTTPPermissionFromStructs(t.HTTP, &x)
|
||||||
s.HTTP = &x
|
s.HTTP = &x
|
||||||
}
|
}
|
||||||
|
if t.JWT != nil {
|
||||||
|
var x IntentionJWTRequirement
|
||||||
|
IntentionJWTRequirementFromStructs(t.JWT, &x)
|
||||||
|
s.JWT = &x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func LeastRequestConfigToStructs(s *LeastRequestConfig, t *structs.LeastRequestConfig) {
|
func LeastRequestConfigToStructs(s *LeastRequestConfig, t *structs.LeastRequestConfig) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
@ -1350,6 +1436,11 @@ func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.JWT != nil {
|
||||||
|
var x structs.IntentionJWTRequirement
|
||||||
|
IntentionJWTRequirementToStructs(s.JWT, &x)
|
||||||
|
t.JWT = &x
|
||||||
|
}
|
||||||
t.Meta = s.Meta
|
t.Meta = s.Meta
|
||||||
}
|
}
|
||||||
func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *ServiceIntentions) {
|
func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *ServiceIntentions) {
|
||||||
|
@ -1366,6 +1457,11 @@ func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *Se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.JWT != nil {
|
||||||
|
var x IntentionJWTRequirement
|
||||||
|
IntentionJWTRequirementFromStructs(t.JWT, &x)
|
||||||
|
s.JWT = &x
|
||||||
|
}
|
||||||
s.Meta = t.Meta
|
s.Meta = t.Meta
|
||||||
}
|
}
|
||||||
func ServiceResolverToStructs(s *ServiceResolver, t *structs.ServiceResolverConfigEntry) {
|
func ServiceResolverToStructs(s *ServiceResolver, t *structs.ServiceResolverConfigEntry) {
|
||||||
|
|
|
@ -287,6 +287,36 @@ func (msg *ServiceIntentions) UnmarshalBinary(b []byte) error {
|
||||||
return proto.Unmarshal(b, msg)
|
return proto.Unmarshal(b, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *IntentionJWTRequirement) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *IntentionJWTRequirement) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *IntentionJWTProvider) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *IntentionJWTProvider) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *IntentionJWTClaimVerification) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *IntentionJWTClaimVerification) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary implements encoding.BinaryMarshaler
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
func (msg *SourceIntention) MarshalBinary() ([]byte, error) {
|
func (msg *SourceIntention) MarshalBinary() ([]byte, error) {
|
||||||
return proto.Marshal(msg)
|
return proto.Marshal(msg)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -374,6 +374,36 @@ message HTTPHeaderModifiers {
|
||||||
message ServiceIntentions {
|
message ServiceIntentions {
|
||||||
repeated SourceIntention Sources = 1;
|
repeated SourceIntention Sources = 1;
|
||||||
map<string, string> Meta = 2;
|
map<string, string> Meta = 2;
|
||||||
|
IntentionJWTRequirement JWT = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mog annotation:
|
||||||
|
//
|
||||||
|
// target=github.com/hashicorp/consul/agent/structs.IntentionJWTRequirement
|
||||||
|
// output=config_entry.gen.go
|
||||||
|
// name=Structs
|
||||||
|
message IntentionJWTRequirement {
|
||||||
|
repeated IntentionJWTProvider Providers = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mog annotation:
|
||||||
|
//
|
||||||
|
// target=github.com/hashicorp/consul/agent/structs.IntentionJWTProvider
|
||||||
|
// output=config_entry.gen.go
|
||||||
|
// name=Structs
|
||||||
|
message IntentionJWTProvider {
|
||||||
|
string Name = 1;
|
||||||
|
repeated IntentionJWTClaimVerification VerifyClaims = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mog annotation:
|
||||||
|
//
|
||||||
|
// target=github.com/hashicorp/consul/agent/structs.IntentionJWTClaimVerification
|
||||||
|
// output=config_entry.gen.go
|
||||||
|
// name=Structs
|
||||||
|
message IntentionJWTClaimVerification {
|
||||||
|
repeated string Path = 1;
|
||||||
|
string Value = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mog annotation:
|
// mog annotation:
|
||||||
|
@ -420,6 +450,7 @@ message IntentionPermission {
|
||||||
// mog: func-to=intentionActionToStructs func-from=intentionActionFromStructs
|
// mog: func-to=intentionActionToStructs func-from=intentionActionFromStructs
|
||||||
IntentionAction Action = 1;
|
IntentionAction Action = 1;
|
||||||
IntentionHTTPPermission HTTP = 2;
|
IntentionHTTPPermission HTTP = 2;
|
||||||
|
IntentionJWTRequirement JWT = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mog annotation:
|
// mog annotation:
|
||||||
|
|
Loading…
Reference in New Issue