Skip to content

Commit

Permalink
Peering ACL support (#1343)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashwin Venkatesh authored Jul 27, 2022
1 parent 1cdfa29 commit cdf488f
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 92 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## UNRELEASED

FEATURES:
* [Experimental] Cluster Peering:
* Add support for ACLs and TLS. [[GH-1343](https://github.com/hashicorp/consul-k8s/pull/1343)] [[GH-1366](https://github.com/hashicorp/consul-k8s/pull/1366)]

## 0.46.1 (July 26, 2022)

IMPROVEMENTS:
Expand All @@ -13,7 +17,6 @@ IMPROVEMENTS:
* CLI
* Update minimum go version for project to 1.18 [[GH-1292](https://github.com/hashicorp/consul-k8s/pull/1292)]


FEATURES:
* [Experimental] Cluster Peering:
* Add support for secret watchers on the Peering Acceptor and Peering Dialer controllers. [[GH-1284](https://github.com/hashicorp/consul-k8s/pull/1284)]
Expand Down
97 changes: 47 additions & 50 deletions acceptance/tests/peering/peering_connect_namespaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ func TestPeering_ConnectNamespaces(t *testing.T) {
true,
false,
},
{
"default destination namespace",
defaultNamespace,
false,
true,
},
{
"single destination namespace",
staticServerNamespace,
false,
true,
},
{
"mirror k8s namespaces",
staticServerNamespace,
true,
true,
},
}

for _, c := range cases {
Expand All @@ -77,7 +95,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) {
"global.peering.enabled": "true",
"global.enableConsulNamespaces": "true",

"global.image": "thisisnotashwin/consul@sha256:446aad6e02f66e3027756dfc0d34e8e6e2b11ac6ec5637b134b34644ca7cda64",
"global.image": "thisisnotashwin/consul@sha256:b1d3f59406adf5fb9a3bee4ded058e619d3a186e83b2e2dc14d6da3f28a7073d",

"global.tls.enabled": "true",
"global.tls.httpsOnly": strconv.FormatBool(c.ACLsAndAutoEncryptEnabled),
Expand Down Expand Up @@ -266,61 +284,40 @@ func TestPeering_ConnectNamespaces(t *testing.T) {
})
}

logger.Log(t, "checking that connection is successful")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, fmt.Sprintf("http://static-server.virtual.%s.%s.consul", c.destinationNamespace, staticServerPeer))
} else {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, "http://localhost:1234")
}
if c.ACLsAndAutoEncryptEnabled {
logger.Log(t, "checking that the connection is not successful because there's no allow intention")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionMultipleFailureMessages(t, staticClientOpts, staticClientName, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", fmt.Sprintf("curl: (7) Failed to connect to static-server.%s port 80: Connection refused", c.destinationNamespace)}, "", fmt.Sprintf("http://static-server.virtual.%s.%s.consul", c.destinationNamespace, staticServerPeer))
} else {
k8s.CheckStaticServerConnectionFailing(t, staticClientOpts, staticClientName, "http://localhost:1234")
}

denyAllIntention := &api.ServiceIntentionsConfigEntry{
Name: "*",
Kind: api.ServiceIntentions,
Namespace: "*",
Sources: []*api.SourceIntention{
{
Name: "*",
Namespace: "*",
Action: api.IntentionActionDeny,
Peer: staticClientPeer,
intention := &api.ServiceIntentionsConfigEntry{
Name: staticServerName,
Kind: api.ServiceIntentions,
Namespace: staticServerNamespace,
Sources: []*api.SourceIntention{
{
Name: staticClientName,
Namespace: staticClientNamespace,
Action: api.IntentionActionAllow,
Peer: staticClientPeer,
},
},
},
}
_, _, err = staticServerPeerClient.ConfigEntries().Set(denyAllIntention, &api.WriteOptions{})
require.NoError(t, err)

logger.Log(t, "checking that the connection is not successful because there's no allow intention")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionMultipleFailureMessages(t, staticClientOpts, staticClientName, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, "", fmt.Sprintf("http://static-server.virtual.%s.%s.consul", c.destinationNamespace, staticServerPeer))
} else {
k8s.CheckStaticServerConnectionFailing(t, staticClientOpts, staticClientName, "http://localhost:1234")
}
}

intention := &api.ServiceIntentionsConfigEntry{
Name: staticServerName,
Kind: api.ServiceIntentions,
Namespace: staticServerNamespace,
Sources: []*api.SourceIntention{
{
Name: staticClientName,
Namespace: staticClientNamespace,
Action: api.IntentionActionAllow,
Peer: staticClientPeer,
},
},
}
// Set the destination namespace to be the same
// unless mirrorK8S is true.
if !c.mirrorK8S {
intention.Namespace = c.destinationNamespace
intention.Sources[0].Namespace = c.destinationNamespace
}

// Set the destination namespace to be the same
// unless mirrorK8S is true.
if !c.mirrorK8S {
intention.Namespace = c.destinationNamespace
intention.Sources[0].Namespace = c.destinationNamespace
logger.Log(t, "creating intentions in server peer")
_, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{})
require.NoError(t, err)
}

logger.Log(t, "creating intentions in server peer")
_, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{})
require.NoError(t, err)

logger.Log(t, "checking that connection is successful")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, fmt.Sprintf("http://static-server.virtual.%s.%s.consul", c.destinationNamespace, staticServerPeer))
Expand Down
66 changes: 26 additions & 40 deletions acceptance/tests/peering/peering_connect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func TestPeering_Connect(t *testing.T) {
"default installation",
false,
},
{
"secure installation",
true,
},
}

for _, c := range cases {
Expand All @@ -50,7 +54,7 @@ func TestPeering_Connect(t *testing.T) {
commonHelmValues := map[string]string{
"global.peering.enabled": "true",

"global.image": "thisisnotashwin/consul@sha256:446aad6e02f66e3027756dfc0d34e8e6e2b11ac6ec5637b134b34644ca7cda64",
"global.image": "thisisnotashwin/consul@sha256:b1d3f59406adf5fb9a3bee4ded058e619d3a186e83b2e2dc14d6da3f28a7073d",

"global.tls.enabled": "true",
"global.tls.httpsOnly": strconv.FormatBool(c.ACLsAndAutoEncryptEnabled),
Expand Down Expand Up @@ -210,50 +214,32 @@ func TestPeering_Connect(t *testing.T) {
helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() {
k8s.KubectlDeleteK(t, staticServerPeerClusterContext.KubectlOptions(t), "../fixtures/cases/crd-peers/default")
})
logger.Log(t, "checking that connection is successful")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, fmt.Sprintf("http://static-server.virtual.%s.consul", staticServerPeer))
} else {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, "http://localhost:1234")
}

denyAllIntention := &api.ServiceIntentionsConfigEntry{
Name: "*",
Kind: api.ServiceIntentions,
Sources: []*api.SourceIntention{
{
Name: "*",
Action: api.IntentionActionDeny,
Peer: staticClientPeer,
if c.ACLsAndAutoEncryptEnabled {
logger.Log(t, "checking that the connection is not successful because there's no allow intention")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionMultipleFailureMessages(t, staticClientOpts, staticClientName, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server port 80: Connection refused"}, "", fmt.Sprintf("http://static-server.virtual.%s.consul", staticServerPeer))
} else {
k8s.CheckStaticServerConnectionFailing(t, staticClientOpts, staticClientName, "http://localhost:1234")
}

intention := &api.ServiceIntentionsConfigEntry{
Name: staticServerName,
Kind: api.ServiceIntentions,
Sources: []*api.SourceIntention{
{
Name: staticClientName,
Action: api.IntentionActionAllow,
Peer: staticClientPeer,
},
},
},
}
_, _, err = staticServerPeerClient.ConfigEntries().Set(denyAllIntention, &api.WriteOptions{})
require.NoError(t, err)

logger.Log(t, "checking that the connection is not successful because there's no allow intention")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionMultipleFailureMessages(t, staticClientOpts, staticClientName, false, []string{"curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server.ns1 port 80: Connection refused"}, "", fmt.Sprintf("http://static-server.virtual.%s.consul", staticServerPeer))
} else {
k8s.CheckStaticServerConnectionFailing(t, staticClientOpts, staticClientName, "http://localhost:1234")
}
}

intention := &api.ServiceIntentionsConfigEntry{
Name: staticServerName,
Kind: api.ServiceIntentions,
Sources: []*api.SourceIntention{
{
Name: staticClientName,
Action: api.IntentionActionAllow,
Peer: staticClientPeer,
},
},
logger.Log(t, "creating intentions in server peer")
_, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{})
require.NoError(t, err)
}

logger.Log(t, "creating intentions in server peer")
_, _, err = staticServerPeerClient.ConfigEntries().Set(intention, &api.WriteOptions{})
require.NoError(t, err)

logger.Log(t, "checking that connection is successful")
if cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionSuccessful(t, staticClientOpts, staticClientName, fmt.Sprintf("http://static-server.virtual.%s.consul", staticServerPeer))
Expand Down
3 changes: 3 additions & 0 deletions charts/consul/templates/server-acl-init-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ spec:
-enable-partitions=true \
-partition={{ .Values.global.adminPartitions.name }} \
{{- end }}
{{- if .Values.global.peering.enabled }}
-enable-peering=true \
{{- end }}
{{- if (or (and (ne (.Values.dns.enabled | toString) "-") .Values.dns.enabled) (and (eq (.Values.dns.enabled | toString) "-") .Values.global.enabled)) }}
-allow-dns=true \
{{- end }}
Expand Down
31 changes: 31 additions & 0 deletions charts/consul/test/unit/server-acl-init-job.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,37 @@ load _helpers
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# cluster peering

@test "serverACLInit/Job: cluster peering disabled by default" {
cd `chart_dir`
local object=$(helm template \
-s templates/server-acl-init-job.yaml \
--set 'global.acls.manageSystemACLs=true' \
. | tee /dev/stderr |
yq '.spec.template.spec.containers[0].command' | tee /dev/stderr)

local actual=$(echo $object |
yq 'any(contains("enable-peering"))' | tee /dev/stderr)
[ "${actual}" = "false" ]
}

@test "serverACLInit/Job: cluster peering enabled when peering is enabled" {
cd `chart_dir`
local object=$(helm template \
-s templates/server-acl-init-job.yaml \
--set 'global.acls.manageSystemACLs=true' \
--set 'global.peering.enabled=true' \
--set 'connectInject.enabled=true' \
. | tee /dev/stderr |
yq '.spec.template.spec.containers[0].command' | tee /dev/stderr)

local actual=$(echo $object |
yq 'any(contains("enable-peering"))' | tee /dev/stderr)
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# admin partitions

Expand Down
6 changes: 6 additions & 0 deletions control-plane/subcommand/server-acl-init/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ type Command struct {
flagPartitionName string // name of the Admin Partition
flagPartitionTokenFile string

// Flags to support peering.
flagEnablePeering bool // true if Cluster Peering is enabled

// Flags to support namespaces.
flagEnableNamespaces bool // Use namespacing on all components
flagConsulSyncDestinationNamespace string // Consul namespace to register all catalog sync services into if not mirroring
Expand Down Expand Up @@ -181,6 +184,9 @@ func (c *Command) init() {
c.flags.StringVar(&c.flagPartitionTokenFile, "partition-token-file", "",
"[Enterprise Only] Path to file containing ACL token to be used in non-default partitions.")

c.flags.BoolVar(&c.flagEnablePeering, "enable-peering", false,
"Enables Cluster Peering.")

c.flags.BoolVar(&c.flagEnableNamespaces, "enable-namespaces", false,
"[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored [Enterprise only feature]")
c.flags.StringVar(&c.flagConsulSyncDestinationNamespace, "consul-sync-destination-namespace", consulDefaultNamespace,
Expand Down
5 changes: 5 additions & 0 deletions control-plane/subcommand/server-acl-init/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

type rulesData struct {
EnablePartitions bool
EnablePeering bool
PartitionName string
EnableNamespaces bool
SyncConsulDestNS string
Expand Down Expand Up @@ -315,6 +316,9 @@ partition "{{ .PartitionName }}" {
{{- if .EnableNamespaces }}
operator = "write"
{{- end }}
{{- end }}
{{- if .EnablePeering }}
peering = "write"
{{- end }}
node_prefix "" {
policy = "write"
Expand Down Expand Up @@ -416,6 +420,7 @@ partition "{{ .PartitionName }}" {
func (c *Command) rulesData() rulesData {
return rulesData{
EnablePartitions: c.flagEnablePartitions,
EnablePeering: c.flagEnablePeering,
PartitionName: c.flagPartitionName,
EnableNamespaces: c.flagEnableNamespaces,
SyncConsulDestNS: c.flagConsulSyncDestinationNamespace,
Expand Down
Loading

0 comments on commit cdf488f

Please sign in to comment.