// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package proxycfgglue import ( "context" "testing" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" ) func TestServerIntentionUpstreams(t *testing.T) { const serviceName = "web" var index uint64 getIndex := func() uint64 { index++ return index } ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) store := state.NewStateStore(nil) disableLegacyIntentions(t, store) // Register api and db services. for _, service := range []string{"api", "db"} { err := store.EnsureRegistration(getIndex(), &structs.RegisterRequest{ Node: "node-1", Service: &structs.NodeService{ Service: service, }, }) require.NoError(t, err) } createIntention := func(destination string) { t.Helper() err := store.EnsureConfigEntry(getIndex(), &structs.ServiceIntentionsConfigEntry{ Name: destination, Sources: []*structs.SourceIntention{ { Name: serviceName, Action: structs.IntentionActionAllow, Type: structs.IntentionSourceConsul, }, }, }) require.NoError(t, err) } // Create an allow intention for the api service. This should be filtered out // because the ACL token doesn't have read access on it. createIntention("api") authz := policyAuthorizer(t, `service "db" { policy = "read" }`) dataSource := ServerIntentionUpstreams(ServerDataSourceDeps{ ACLResolver: newStaticResolver(authz), GetStore: func() Store { return store }, }, "") ch := make(chan proxycfg.UpdateEvent) err := dataSource.Notify(ctx, &structs.ServiceSpecificRequest{ServiceName: serviceName}, "", ch) require.NoError(t, err) result := getEventResult[*structs.IndexedServiceList](t, ch) require.Len(t, result.Services, 0) // Create an allow intention for the db service. This should *not* be filtered // out because the ACL token *does* have read access on it. createIntention("db") result = getEventResult[*structs.IndexedServiceList](t, ch) require.Len(t, result.Services, 1) require.Equal(t, "db", result.Services[0].Name) } // Variant of TestServerIntentionUpstreams where a default allow intention policy // returns "db" service as an IntentionUpstream even if there are no explicit // intentions for "db". func TestServerIntentionUpstreams_DefaultIntentionPolicy(t *testing.T) { const serviceName = "web" var index uint64 getIndex := func() uint64 { index++ return index } ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) store := state.NewStateStore(nil) disableLegacyIntentions(t, store) require.NoError(t, store.EnsureRegistration(getIndex(), &structs.RegisterRequest{ Node: "node-1", Service: &structs.NodeService{ Service: "db", }, })) // Ensures that "db" service will not be filtered due to ACLs authz := policyAuthorizer(t, `service "db" { policy = "read" }`) dataSource := ServerIntentionUpstreams(ServerDataSourceDeps{ ACLResolver: newStaticResolver(authz), GetStore: func() Store { return store }, }, "allow") ch := make(chan proxycfg.UpdateEvent) require.NoError(t, dataSource.Notify(ctx, &structs.ServiceSpecificRequest{ServiceName: serviceName}, "", ch)) result := getEventResult[*structs.IndexedServiceList](t, ch) require.Len(t, result.Services, 1) require.Equal(t, "db", result.Services[0].Name) } func disableLegacyIntentions(t *testing.T, store *state.Store) { t.Helper() require.NoError(t, store.SystemMetadataSet(0, &structs.SystemMetadataEntry{ Key: structs.SystemMetadataIntentionFormatKey, Value: structs.SystemMetadataIntentionFormatConfigValue, })) } func policyAuthorizer(t *testing.T, policyHCL string) acl.Authorizer { policy, err := acl.NewPolicyFromSource(policyHCL, nil, nil) require.NoError(t, err) authz, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) require.NoError(t, err) return authz }