2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-28 18:39:22 +00:00
|
|
|
|
2022-07-01 15:15:49 +00:00
|
|
|
package proxycfgglue
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-07-12 10:34:14 +00:00
|
|
|
"sync"
|
2022-07-01 15:15:49 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
|
|
"github.com/hashicorp/consul/acl/resolver"
|
|
|
|
"github.com/hashicorp/consul/agent/consul/state"
|
|
|
|
"github.com/hashicorp/consul/agent/proxycfg"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2022-07-12 10:34:14 +00:00
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
2022-07-01 15:15:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestServerIntentions(t *testing.T) {
|
2023-04-20 16:16:04 +00:00
|
|
|
nextIndex := indexGenerator()
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
t.Cleanup(cancel)
|
|
|
|
|
|
|
|
store := state.NewStateStore(nil)
|
|
|
|
|
2022-07-01 15:15:49 +00:00
|
|
|
const (
|
|
|
|
serviceName = "web"
|
|
|
|
index = 1
|
|
|
|
)
|
2023-04-20 16:16:04 +00:00
|
|
|
require.NoError(t, store.SystemMetadataSet(1, &structs.SystemMetadataEntry{
|
|
|
|
Key: structs.SystemMetadataIntentionFormatKey,
|
|
|
|
Value: structs.SystemMetadataIntentionFormatConfigValue,
|
|
|
|
}))
|
|
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceIntentionsConfigEntry{
|
|
|
|
Name: serviceName,
|
|
|
|
Sources: []*structs.SourceIntention{
|
|
|
|
{
|
|
|
|
Name: "db",
|
|
|
|
Action: structs.IntentionActionAllow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}))
|
2022-07-01 15:15:49 +00:00
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
authz := policyAuthorizer(t, `
|
|
|
|
service "web" { policy = "read" }
|
|
|
|
`)
|
2022-07-01 15:15:49 +00:00
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
logger := hclog.NewNullLogger()
|
2022-07-01 15:15:49 +00:00
|
|
|
|
|
|
|
intentions := ServerIntentions(ServerDataSourceDeps{
|
2023-04-20 16:16:04 +00:00
|
|
|
ACLResolver: newStaticResolver(authz),
|
|
|
|
Logger: logger,
|
|
|
|
GetStore: func() Store { return store },
|
2022-07-01 15:15:49 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
eventCh := make(chan proxycfg.UpdateEvent)
|
|
|
|
require.NoError(t, intentions.Notify(ctx, &structs.ServiceSpecificRequest{
|
|
|
|
ServiceName: serviceName,
|
|
|
|
EnterpriseMeta: *acl.DefaultEnterpriseMeta(),
|
|
|
|
}, "", eventCh))
|
|
|
|
|
2022-07-12 10:34:14 +00:00
|
|
|
testutil.RunStep(t, "initial snapshot", func(t *testing.T) {
|
2023-04-20 16:16:04 +00:00
|
|
|
result := getEventResult[structs.SimplifiedIntentions](t, eventCh)
|
2022-07-01 15:15:49 +00:00
|
|
|
require.Len(t, result, 1)
|
|
|
|
|
|
|
|
intention := result[0]
|
|
|
|
require.Equal(t, intention.DestinationName, serviceName)
|
|
|
|
require.Equal(t, intention.SourceName, "db")
|
2022-07-12 10:34:14 +00:00
|
|
|
})
|
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
testutil.RunStep(t, "updating an intention", func(t *testing.T) {
|
|
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceIntentionsConfigEntry{
|
|
|
|
Name: serviceName,
|
|
|
|
Sources: []*structs.SourceIntention{
|
|
|
|
{
|
|
|
|
Name: "api",
|
|
|
|
Action: structs.IntentionActionAllow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "db",
|
|
|
|
Action: structs.IntentionActionAllow,
|
2022-07-01 15:15:49 +00:00
|
|
|
},
|
|
|
|
},
|
2023-04-20 16:16:04 +00:00
|
|
|
}))
|
2022-07-01 15:15:49 +00:00
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
result := getEventResult[structs.SimplifiedIntentions](t, eventCh)
|
2022-07-01 15:15:49 +00:00
|
|
|
require.Len(t, result, 2)
|
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
for i, src := range []string{"api", "db"} {
|
|
|
|
intention := result[i]
|
|
|
|
require.Equal(t, intention.DestinationName, serviceName)
|
|
|
|
require.Equal(t, intention.SourceName, src)
|
|
|
|
}
|
2022-07-12 10:34:14 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
testutil.RunStep(t, "publishing a delete event", func(t *testing.T) {
|
2023-04-20 16:16:04 +00:00
|
|
|
require.NoError(t, store.DeleteConfigEntry(nextIndex(), structs.ServiceIntentions, serviceName, nil))
|
|
|
|
|
|
|
|
result := getEventResult[structs.SimplifiedIntentions](t, eventCh)
|
|
|
|
require.Len(t, result, 0)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerIntentions_ACLDeny(t *testing.T) {
|
|
|
|
nextIndex := indexGenerator()
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
t.Cleanup(cancel)
|
|
|
|
|
|
|
|
store := state.NewStateStore(nil)
|
|
|
|
|
|
|
|
const (
|
|
|
|
serviceName = "web"
|
|
|
|
index = 1
|
|
|
|
)
|
|
|
|
require.NoError(t, store.SystemMetadataSet(1, &structs.SystemMetadataEntry{
|
|
|
|
Key: structs.SystemMetadataIntentionFormatKey,
|
|
|
|
Value: structs.SystemMetadataIntentionFormatConfigValue,
|
|
|
|
}))
|
|
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceIntentionsConfigEntry{
|
|
|
|
Name: serviceName,
|
|
|
|
Sources: []*structs.SourceIntention{
|
2022-07-12 10:34:14 +00:00
|
|
|
{
|
2023-04-20 16:16:04 +00:00
|
|
|
Name: "db",
|
|
|
|
Action: structs.IntentionActionAllow,
|
2022-07-01 15:15:49 +00:00
|
|
|
},
|
2023-04-20 16:16:04 +00:00
|
|
|
},
|
|
|
|
}))
|
2022-07-01 15:15:49 +00:00
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
authz := policyAuthorizer(t, ``)
|
|
|
|
|
|
|
|
logger := hclog.NewNullLogger()
|
|
|
|
|
|
|
|
intentions := ServerIntentions(ServerDataSourceDeps{
|
|
|
|
ACLResolver: newStaticResolver(authz),
|
|
|
|
Logger: logger,
|
|
|
|
GetStore: func() Store { return store },
|
2022-07-12 10:34:14 +00:00
|
|
|
})
|
|
|
|
|
2023-04-20 16:16:04 +00:00
|
|
|
eventCh := make(chan proxycfg.UpdateEvent)
|
|
|
|
require.NoError(t, intentions.Notify(ctx, &structs.ServiceSpecificRequest{
|
|
|
|
ServiceName: serviceName,
|
|
|
|
EnterpriseMeta: *acl.DefaultEnterpriseMeta(),
|
|
|
|
}, "", eventCh))
|
|
|
|
|
|
|
|
testutil.RunStep(t, "initial snapshot", func(t *testing.T) {
|
|
|
|
result := getEventResult[structs.SimplifiedIntentions](t, eventCh)
|
|
|
|
require.Len(t, result, 0)
|
|
|
|
})
|
2022-07-01 15:15:49 +00:00
|
|
|
}
|
|
|
|
|
2022-07-01 15:18:33 +00:00
|
|
|
type staticResolver struct {
|
2022-07-12 10:34:14 +00:00
|
|
|
mu sync.Mutex
|
2022-07-01 15:18:33 +00:00
|
|
|
authorizer acl.Authorizer
|
|
|
|
}
|
2022-07-01 15:15:49 +00:00
|
|
|
|
2022-07-12 10:34:14 +00:00
|
|
|
func newStaticResolver(authz acl.Authorizer) *staticResolver {
|
|
|
|
resolver := new(staticResolver)
|
|
|
|
resolver.SwapAuthorizer(authz)
|
|
|
|
return resolver
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *staticResolver) SwapAuthorizer(authz acl.Authorizer) {
|
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
|
|
|
|
|
|
|
r.authorizer = authz
|
|
|
|
}
|
|
|
|
|
2022-09-26 16:50:17 +00:00
|
|
|
func (r *staticResolver) ResolveTokenAndDefaultMeta(_ string, entMeta *acl.EnterpriseMeta, authzContext *acl.AuthorizerContext) (resolver.Result, error) {
|
|
|
|
entMeta.FillAuthzContext(authzContext)
|
|
|
|
|
2022-07-12 10:34:14 +00:00
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
2022-07-01 15:18:33 +00:00
|
|
|
return resolver.Result{Authorizer: r.authorizer}, nil
|
2022-07-01 15:15:49 +00:00
|
|
|
}
|