Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for consul namespaces and admin partitions #13850

Merged
merged 9 commits into from
Feb 9, 2022
280 changes: 268 additions & 12 deletions builtin/logical/consul/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/base64"
"fmt"
"log"
"os"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -38,7 +39,7 @@ func testBackendConfigAccess(t *testing.T, version string) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -87,7 +88,7 @@ func TestBackend_Renew_Revoke(t *testing.T) {
t.Parallel()
t.Run("legacy", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "")
testBackendRenewRevoke(t, "1.4.4")
})

testBackendRenewRevoke14(t, "")
Expand All @@ -103,7 +104,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -196,7 +197,7 @@ func testBackendRenewRevoke(t *testing.T, version string) {
Value: []byte("bar"),
}, nil)
if err == nil {
t.Fatal("expected error")
t.Fatal("err: expected error")
}
}

Expand All @@ -208,7 +209,7 @@ func testBackendRenewRevoke14(t *testing.T, version string) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -320,7 +321,7 @@ func TestBackend_LocalToken(t *testing.T) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, "")
cleanup, consulConfig := consul.PrepareTestContainer(t, "", false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -450,7 +451,7 @@ func TestBackend_Management(t *testing.T) {
})
t.Run("post-1.4.0", func(t *testing.T) {
t.Parallel()
testBackendManagement(t, "")
testBackendManagement(t, "1.4.4")
})
})
}
Expand All @@ -463,7 +464,7 @@ func testBackendManagement(t *testing.T, version string) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -492,10 +493,9 @@ func TestBackend_Basic(t *testing.T) {
t.Parallel()
t.Run("legacy", func(t *testing.T) {
t.Parallel()
testBackendRenewRevoke(t, "")
testBackendRenewRevoke(t, "1.4.4")
})

testBackendBasic(t, "")
testBackendBasic(t, "1.4.4")
})
})
}
Expand All @@ -508,7 +508,7 @@ func testBackendBasic(t *testing.T, version string) {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, version)
cleanup, consulConfig := consul.PrepareTestContainer(t, version, false)
defer cleanup()

connData := map[string]interface{}{
Expand Down Expand Up @@ -702,6 +702,262 @@ func testAccStepDeletePolicy(t *testing.T, name string) logicaltest.TestStep {
}
}

func TestBackend_Enterprise_Namespace(t *testing.T) {
if _, hasLicense := os.LookupEnv("CONSUL_LICENSE"); !hasLicense {
t.Skip("Skipping: No enterprise license found")
}

testBackendEntNamespace(t)
}

func TestBackend_Enterprise_Partition(t *testing.T) {
if _, hasLicense := os.LookupEnv("CONSUL_LICENSE"); !hasLicense {
t.Skip("Skipping: No enterprise license found")
}

testBackendEntPartition(t)
}

func testBackendEntNamespace(t *testing.T) {
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, "", true)
defer cleanup()

connData := map[string]interface{}{
"address": consulConfig.Address(),
"token": consulConfig.Token,
}

req := &logical.Request{
Storage: config.StorageView,
Operation: logical.UpdateOperation,
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

// Create the role in namespace "ns1"
req.Path = "roles/test-ns"
req.Data = map[string]interface{}{
"policies": []string{"ns-test"},
"lease": "6h",
"consul_namespace": "ns1",
}
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

req.Operation = logical.ReadOperation
req.Path = "creds/test-ns"
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("resp nil")
}
if resp.IsError() {
t.Fatalf("resp is error: %v", resp.Error())
}

generatedSecret := resp.Secret
generatedSecret.TTL = 6 * time.Hour

var d struct {
Token string `mapstructure:"token"`
Accessor string `mapstructure:"accessor"`
ConsulNamespace string `mapstructure:"consul_namespace"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
t.Fatal(err)
}
t.Logf("Generated namespace '%s' token: %s with accessor %s", d.ConsulNamespace, d.Token, d.Accessor)

if d.ConsulNamespace != "ns1" {
t.Fatalf("Failed to access namespace")
}

// Build a client and verify that the credentials work
consulapiConfig := consulapi.DefaultNonPooledConfig()
consulapiConfig.Address = connData["address"].(string)
consulapiConfig.Token = d.Token
client, err := consulapi.NewClient(consulapiConfig)
if err != nil {
t.Fatal(err)
}

t.Log("Verifying that the generated token works...")
_, err = client.Catalog(), nil
if err != nil {
t.Fatal(err)
}

req.Operation = logical.RenewOperation
req.Secret = generatedSecret
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}

req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

// Build a management client and verify that the token does not exist anymore
consulmgmtConfig := consulapi.DefaultNonPooledConfig()
consulmgmtConfig.Address = connData["address"].(string)
consulmgmtConfig.Token = connData["token"].(string)
mgmtclient, err := consulapi.NewClient(consulmgmtConfig)
if err != nil {
t.Fatal(err)
}
q := &consulapi.QueryOptions{
Datacenter: "DC1",
Namespace: "ns1",
}

t.Log("Verifying that the generated token does not exist...")
_, _, err = mgmtclient.ACL().TokenRead(d.Accessor, q)
if err == nil {
t.Fatal("err: expected error")
}
}

func testBackendEntPartition(t *testing.T) {
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Factory(context.Background(), config)
if err != nil {
t.Fatal(err)
}

cleanup, consulConfig := consul.PrepareTestContainer(t, "", true)
defer cleanup()

connData := map[string]interface{}{
"address": consulConfig.Address(),
"token": consulConfig.Token,
}

req := &logical.Request{
Storage: config.StorageView,
Operation: logical.UpdateOperation,
Path: "config/access",
Data: connData,
}
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

// Create the role in partition "part1"
req.Path = "roles/test-part"
req.Data = map[string]interface{}{
"policies": []string{"part-test"},
"lease": "6h",
"partition": "part1",
}
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

req.Operation = logical.ReadOperation
req.Path = "creds/test-part"
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("resp nil")
}
if resp.IsError() {
t.Fatalf("resp is error: %v", resp.Error())
}

generatedSecret := resp.Secret
generatedSecret.TTL = 6 * time.Hour

var d struct {
Token string `mapstructure:"token"`
Accessor string `mapstructure:"accessor"`
Partition string `mapstructure:"partition"`
}
if err := mapstructure.Decode(resp.Data, &d); err != nil {
t.Fatal(err)
}
t.Logf("Generated partition '%s' token: %s with accessor %s", d.Partition, d.Token, d.Accessor)

if d.Partition != "part1" {
t.Fatalf("Failed to access partition")
}

// Build a client and verify that the credentials work
consulapiConfig := consulapi.DefaultNonPooledConfig()
consulapiConfig.Address = connData["address"].(string)
consulapiConfig.Token = d.Token
client, err := consulapi.NewClient(consulapiConfig)
if err != nil {
t.Fatal(err)
}

t.Log("Verifying that the generated token works...")
_, err = client.Catalog(), nil
if err != nil {
t.Fatal(err)
}

req.Operation = logical.RenewOperation
req.Secret = generatedSecret
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("got nil response from renew")
}

req.Operation = logical.RevokeOperation
resp, err = b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatal(err)
}

// Build a management client and verify that the token does not exist anymore
consulmgmtConfig := consulapi.DefaultNonPooledConfig()
consulmgmtConfig.Address = connData["address"].(string)
consulmgmtConfig.Token = connData["token"].(string)
mgmtclient, err := consulapi.NewClient(consulmgmtConfig)
if err != nil {
t.Fatal(err)
}
q := &consulapi.QueryOptions{
Datacenter: "DC1",
Partition: "test1",
}

t.Log("Verifying that the generated token does not exist...")
_, _, err = mgmtclient.ACL().TokenRead(d.Accessor, q)
if err == nil {
t.Fatal("err: expected error")
}
}

const testPolicy = `
key "" {
policy = "write"
Expand Down
Loading