open-vault/vault/auth_test.go

581 lines
15 KiB
Go
Raw Normal View History

package vault
import (
"context"
"reflect"
"strings"
"testing"
"time"
metrics "github.com/armon/go-metrics"
"github.com/hashicorp/vault/helper/metricsutil"
2018-09-18 03:03:00 +00:00
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/helper/jsonutil"
"github.com/hashicorp/vault/sdk/logical"
)
func TestAuth_ReadOnlyViewDuringMount(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) {
err := config.StorageView.Put(ctx, &logical.StorageEntry{
Key: "bar",
Value: []byte("baz"),
})
if err == nil || !strings.Contains(err.Error(), logical.ErrSetupReadOnly.Error()) {
t.Fatalf("expected a read-only error")
}
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAuthMountMetrics(t *testing.T) {
c, _, _, _ := TestCoreUnsealedWithMetrics(t)
c.credentialBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) {
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
mountKeyName := "core.mount_table.num_entries.type|auth||local|false||"
mountMetrics := &c.metricsHelper.LoopMetrics.Metrics
loadMetric, ok := mountMetrics.Load(mountKeyName)
var numEntriesMetric metricsutil.GaugeMetric = loadMetric.(metricsutil.GaugeMetric)
// 1 default nonlocal auth backend
if !ok || numEntriesMetric.Value != 1 {
t.Fatalf("Auth values should be: %+v", numEntriesMetric)
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
mountMetrics = &c.metricsHelper.LoopMetrics.Metrics
loadMetric, ok = mountMetrics.Load(mountKeyName)
numEntriesMetric = loadMetric.(metricsutil.GaugeMetric)
if !ok || numEntriesMetric.Value != 2 {
t.Fatalf("mount metrics for num entries do not match true values")
}
if len(numEntriesMetric.Key) != 3 ||
numEntriesMetric.Key[0] != "core" ||
numEntriesMetric.Key[1] != "mount_table" ||
numEntriesMetric.Key[2] != "num_entries" {
t.Fatalf("mount metrics for num entries have wrong key")
}
if len(numEntriesMetric.Labels) != 2 ||
numEntriesMetric.Labels[0].Name != "type" ||
numEntriesMetric.Labels[0].Value != "auth" ||
numEntriesMetric.Labels[1].Name != "local" ||
numEntriesMetric.Labels[1].Value != "false" {
t.Fatalf("mount metrics for num entries have wrong labels")
}
mountSizeKeyName := "core.mount_table.size.type|auth||local|false||"
loadMetric, ok = mountMetrics.Load(mountSizeKeyName)
sizeMetric := loadMetric.(metricsutil.GaugeMetric)
if !ok {
t.Fatalf("mount metrics for size do not match exist")
}
if len(sizeMetric.Key) != 3 ||
sizeMetric.Key[0] != "core" ||
sizeMetric.Key[1] != "mount_table" ||
sizeMetric.Key[2] != "size" {
t.Fatalf("mount metrics for size have wrong key")
}
if len(sizeMetric.Labels) != 2 ||
sizeMetric.Labels[0].Name != "type" ||
sizeMetric.Labels[0].Value != "auth" ||
sizeMetric.Labels[1].Name != "local" ||
sizeMetric.Labels[1].Value != "false" {
t.Fatalf("mount metrics for size have wrong labels")
}
}
func TestCore_DefaultAuthTable(t *testing.T) {
c, keys, _ := TestCoreUnsealed(t)
verifyDefaultAuthTable(t, c.auth)
// Start a second core with same physical
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
2015-04-29 01:12:57 +00:00
conf := &CoreConfig{
Physical: c.physical,
DisableMlock: true,
BuiltinRegistry: NewMockBuiltinRegistry(),
MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink),
MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false),
2015-04-29 01:12:57 +00:00
}
c2, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %v", err)
}
defer c2.Shutdown()
2017-01-17 20:43:10 +00:00
for i, key := range keys {
unseal, err := TestCoreUnseal(c2, key)
if err != nil {
t.Fatalf("err: %v", err)
}
if i+1 == len(keys) && !unseal {
t.Fatalf("should be unsealed")
}
}
// Verify matching mount tables
if !reflect.DeepEqual(c.auth, c2.auth) {
t.Fatalf("mismatch: %v %v", c.auth, c2.auth)
}
}
func TestCore_EnableCredential(t *testing.T) {
c, keys, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
me := &MountEntry{
2016-05-26 17:38:51 +00:00
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "auth/foo/" {
2018-09-18 03:03:00 +00:00
t.Fatalf("missing mount, match: %q", match)
}
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
2015-04-29 01:12:57 +00:00
conf := &CoreConfig{
Physical: c.physical,
DisableMlock: true,
BuiltinRegistry: NewMockBuiltinRegistry(),
MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink),
MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false),
2015-04-29 01:12:57 +00:00
}
c2, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %v", err)
}
defer c2.Shutdown()
c2.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
2017-01-17 20:43:10 +00:00
for i, key := range keys {
unseal, err := TestCoreUnseal(c2, key)
if err != nil {
t.Fatalf("err: %v", err)
}
if i+1 == len(keys) && !unseal {
t.Fatalf("should be unsealed")
}
}
// Verify matching auth tables
if !reflect.DeepEqual(c.auth, c2.auth) {
t.Fatalf("mismatch: %v %v", c.auth, c2.auth)
}
}
// Test that the local table actually gets populated as expected with local
// entries, and that upon reading the entries from both are recombined
// correctly
func TestCore_EnableCredential_Local(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
c.auth = &MountTable{
Type: credentialTableType,
Entries: []*MountEntry{
&MountEntry{
Table: credentialTableType,
Path: "noop/",
Type: "noop",
UUID: "abcd",
Accessor: "noop-abcd",
BackendAwareUUID: "abcde",
2018-09-18 03:03:00 +00:00
NamespaceID: namespace.RootNamespaceID,
namespace: namespace.RootNamespace,
},
&MountEntry{
Table: credentialTableType,
Path: "noop2/",
Type: "noop",
UUID: "bcde",
Accessor: "noop-bcde",
BackendAwareUUID: "bcdea",
2018-09-18 03:03:00 +00:00
NamespaceID: namespace.RootNamespaceID,
namespace: namespace.RootNamespace,
},
},
}
// Both should set up successfully
err := c.setupCredentials(context.Background())
if err != nil {
t.Fatal(err)
}
rawLocal, err := c.barrier.Get(context.Background(), coreLocalAuthConfigPath)
if err != nil {
t.Fatal(err)
}
if rawLocal == nil {
t.Fatal("expected non-nil local credential")
}
localCredentialTable := &MountTable{}
if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil {
t.Fatal(err)
}
if len(localCredentialTable.Entries) > 0 {
t.Fatalf("expected no entries in local credential table, got %#v", localCredentialTable)
}
c.auth.Entries[1].Local = true
2018-04-11 18:32:55 +00:00
if err := c.persistAuth(context.Background(), c.auth, nil); err != nil {
t.Fatal(err)
}
rawLocal, err = c.barrier.Get(context.Background(), coreLocalAuthConfigPath)
if err != nil {
t.Fatal(err)
}
if rawLocal == nil {
t.Fatal("expected non-nil local credential")
}
localCredentialTable = &MountTable{}
if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil {
t.Fatal(err)
}
if len(localCredentialTable.Entries) != 1 {
t.Fatalf("expected one entry in local credential table, got %#v", localCredentialTable)
}
oldCredential := c.auth
if err := c.loadCredentials(context.Background()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(oldCredential, c.auth) {
t.Fatalf("expected\n%#v\ngot\n%#v\n", oldCredential, c.auth)
}
if len(c.auth.Entries) != 2 {
t.Fatalf("expected two credential entries, got %#v", localCredentialTable)
}
}
func TestCore_EnableCredential_twice_409(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
me := &MountEntry{
2016-05-26 17:38:51 +00:00
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
// 2nd should be a 409 error
err2 := c.enableCredential(namespace.RootContext(nil), me)
switch err2.(type) {
case logical.HTTPCodedError:
if err2.(logical.HTTPCodedError).Code() != 409 {
t.Fatalf("invalid code given")
}
default:
t.Fatalf("expected a different error type")
}
}
func TestCore_EnableCredential_Token(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
me := &MountEntry{
2016-05-26 17:38:51 +00:00
Table: credentialTableType,
Path: "foo",
Type: "token",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err.Error() != "token credential backend cannot be instantiated" {
t.Fatalf("err: %v", err)
}
}
func TestCore_DisableCredential(t *testing.T) {
c, keys, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
2018-11-07 01:21:24 +00:00
return &NoopBackend{
BackendType: logical.TypeCredential,
}, nil
}
err := c.disableCredential(namespace.RootContext(nil), "foo")
2018-09-18 03:03:00 +00:00
if err != nil && !strings.HasPrefix(err.Error(), "no matching mount") {
t.Fatal(err)
}
me := &MountEntry{
2016-05-26 17:38:51 +00:00
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err = c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
err = c.disableCredential(namespace.RootContext(nil), "foo")
if err != nil {
t.Fatalf("err: %v", err)
}
match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar")
if match != "" {
t.Fatalf("backend present")
}
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
2015-04-29 01:12:57 +00:00
conf := &CoreConfig{
Physical: c.physical,
DisableMlock: true,
BuiltinRegistry: NewMockBuiltinRegistry(),
MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink),
MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false),
2015-04-29 01:12:57 +00:00
}
c2, err := NewCore(conf)
if err != nil {
t.Fatalf("err: %v", err)
}
defer c2.Shutdown()
2017-01-17 20:43:10 +00:00
for i, key := range keys {
unseal, err := TestCoreUnseal(c2, key)
if err != nil {
t.Fatalf("err: %v", err)
}
if i+1 == len(keys) && !unseal {
t.Fatalf("should be unsealed")
}
}
// Verify matching mount tables
if !reflect.DeepEqual(c.auth, c2.auth) {
t.Fatalf("mismatch: %v %v", c.auth, c2.auth)
}
}
func TestCore_DisableCredential_Protected(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
err := c.disableCredential(namespace.RootContext(nil), "token")
if err.Error() != "token credential backend cannot be disabled" {
t.Fatalf("err: %v", err)
}
}
func TestCore_DisableCredential_Cleanup(t *testing.T) {
noop := &NoopBackend{
2018-11-07 01:21:24 +00:00
Login: []string{"login"},
BackendType: logical.TypeCredential,
}
c, _, _ := TestCoreUnsealed(t)
c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
return noop, nil
}
me := &MountEntry{
2016-05-26 17:38:51 +00:00
Table: credentialTableType,
Path: "foo",
Type: "noop",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
// Store the view
view := c.router.MatchingStorageByAPIPath(namespace.RootContext(nil), "auth/foo/")
// Inject data
se := &logical.StorageEntry{
Key: "plstodelete",
Value: []byte("test"),
}
if err := view.Put(context.Background(), se); err != nil {
t.Fatalf("err: %v", err)
}
// Generate a new token auth
noop.Response = &logical.Response{
Auth: &logical.Auth{
Policies: []string{"foo"},
},
}
r := &logical.Request{
Operation: logical.ReadOperation,
Path: "auth/foo/login",
}
resp, err := c.HandleRequest(namespace.RootContext(nil), r)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: %#v", resp)
}
// Disable should cleanup
err = c.disableCredential(namespace.RootContext(nil), "foo")
if err != nil {
t.Fatalf("err: %v", err)
}
// Token should be revoked
te, err := c.tokenStore.Lookup(namespace.RootContext(nil), resp.Auth.ClientToken)
if err != nil {
t.Fatalf("err: %v", err)
}
if te != nil {
t.Fatalf("bad: %#v", te)
}
// View should be empty
out, err := logical.CollectKeys(context.Background(), view)
if err != nil {
t.Fatalf("err: %v", err)
}
if len(out) != 0 {
t.Fatalf("bad: %#v", out)
}
}
func TestDefaultAuthTable(t *testing.T) {
c, _, _ := TestCoreUnsealed(t)
table := c.defaultAuthTable()
verifyDefaultAuthTable(t, table)
}
func verifyDefaultAuthTable(t *testing.T, table *MountTable) {
if len(table.Entries) != 1 {
t.Fatalf("bad: %v", table.Entries)
}
2016-05-26 17:38:51 +00:00
if table.Type != credentialTableType {
t.Fatalf("bad: %v", *table)
}
for idx, entry := range table.Entries {
switch idx {
case 0:
if entry.Path != "token/" {
t.Fatalf("bad: %v", entry)
}
if entry.Type != "token" {
t.Fatalf("bad: %v", entry)
}
}
if entry.Description == "" {
t.Fatalf("bad: %v", entry)
}
if entry.UUID == "" {
t.Fatalf("bad: %v", entry)
}
}
}
AWS upgrade role entries (#7025) * upgrade aws roles * test upgrade aws roles * Initialize aws credential backend at mount time * add a TODO * create end-to-end test for builtin/credential/aws * fix bug in initializer * improve comments * add Initialize() to logical.Backend * use Initialize() in Core.enableCredentialInternal() * use InitializeRequest to call Initialize() * improve unit testing for framework.Backend * call logical.Backend.Initialize() from all of the places that it needs to be called. * implement backend.proto changes for logical.Backend.Initialize() * persist current role storage version when upgrading aws roles * format comments correctly * improve comments * use postUnseal funcs to initialize backends * simplify test suite * improve test suite * simplify logic in aws role upgrade * simplify aws credential initialization logic * simplify logic in aws role upgrade * use the core's activeContext for initialization * refactor builtin/plugin/Backend * use a goroutine to upgrade the aws roles * misc improvements and cleanup * do not run AWS role upgrade on DR Secondary * always call logical.Backend.Initialize() when loading a plugin. * improve comments * on standbys and DR secondaries we do not want to run any kind of upgrade logic * fix awsVersion struct * clarify aws version upgrade * make the upgrade logic for aws auth more explicit * aws upgrade is now called from a switch * fix fallthrough bug * simplify logic * simplify logic * rename things * introduce currentAwsVersion const to track aws version * improve comments * rearrange things once more * conglomerate things into one function * stub out aws auth initialize e2e test * improve aws auth initialize e2e test * finish aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * fix typo in test suite * simplify logic a tad * rearrange assignment * Fix a few lifecycle related issues in #7025 (#7075) * Fix panic when plugin fails to load
2019-07-05 23:55:40 +00:00
func TestCore_CredentialInitialize(t *testing.T) {
{
backend := &InitializableBackend{
&NoopBackend{
BackendType: logical.TypeCredential,
}, false}
c, _, _ := TestCoreUnsealed(t)
AWS upgrade role entries (#7025) * upgrade aws roles * test upgrade aws roles * Initialize aws credential backend at mount time * add a TODO * create end-to-end test for builtin/credential/aws * fix bug in initializer * improve comments * add Initialize() to logical.Backend * use Initialize() in Core.enableCredentialInternal() * use InitializeRequest to call Initialize() * improve unit testing for framework.Backend * call logical.Backend.Initialize() from all of the places that it needs to be called. * implement backend.proto changes for logical.Backend.Initialize() * persist current role storage version when upgrading aws roles * format comments correctly * improve comments * use postUnseal funcs to initialize backends * simplify test suite * improve test suite * simplify logic in aws role upgrade * simplify aws credential initialization logic * simplify logic in aws role upgrade * use the core's activeContext for initialization * refactor builtin/plugin/Backend * use a goroutine to upgrade the aws roles * misc improvements and cleanup * do not run AWS role upgrade on DR Secondary * always call logical.Backend.Initialize() when loading a plugin. * improve comments * on standbys and DR secondaries we do not want to run any kind of upgrade logic * fix awsVersion struct * clarify aws version upgrade * make the upgrade logic for aws auth more explicit * aws upgrade is now called from a switch * fix fallthrough bug * simplify logic * simplify logic * rename things * introduce currentAwsVersion const to track aws version * improve comments * rearrange things once more * conglomerate things into one function * stub out aws auth initialize e2e test * improve aws auth initialize e2e test * finish aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * fix typo in test suite * simplify logic a tad * rearrange assignment * Fix a few lifecycle related issues in #7025 (#7075) * Fix panic when plugin fails to load
2019-07-05 23:55:40 +00:00
c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
return backend, nil
}
me := &MountEntry{
Table: credentialTableType,
Path: "foo/",
Type: "initable",
}
err := c.enableCredential(namespace.RootContext(nil), me)
if err != nil {
t.Fatalf("err: %v", err)
}
if !backend.isInitialized {
t.Fatal("backend is not initialized")
}
}
{
backend := &InitializableBackend{
&NoopBackend{
BackendType: logical.TypeCredential,
}, false}
c, _, _ := TestCoreUnsealed(t)
AWS upgrade role entries (#7025) * upgrade aws roles * test upgrade aws roles * Initialize aws credential backend at mount time * add a TODO * create end-to-end test for builtin/credential/aws * fix bug in initializer * improve comments * add Initialize() to logical.Backend * use Initialize() in Core.enableCredentialInternal() * use InitializeRequest to call Initialize() * improve unit testing for framework.Backend * call logical.Backend.Initialize() from all of the places that it needs to be called. * implement backend.proto changes for logical.Backend.Initialize() * persist current role storage version when upgrading aws roles * format comments correctly * improve comments * use postUnseal funcs to initialize backends * simplify test suite * improve test suite * simplify logic in aws role upgrade * simplify aws credential initialization logic * simplify logic in aws role upgrade * use the core's activeContext for initialization * refactor builtin/plugin/Backend * use a goroutine to upgrade the aws roles * misc improvements and cleanup * do not run AWS role upgrade on DR Secondary * always call logical.Backend.Initialize() when loading a plugin. * improve comments * on standbys and DR secondaries we do not want to run any kind of upgrade logic * fix awsVersion struct * clarify aws version upgrade * make the upgrade logic for aws auth more explicit * aws upgrade is now called from a switch * fix fallthrough bug * simplify logic * simplify logic * rename things * introduce currentAwsVersion const to track aws version * improve comments * rearrange things once more * conglomerate things into one function * stub out aws auth initialize e2e test * improve aws auth initialize e2e test * finish aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * tinker with aws auth initialize e2e test * fix typo in test suite * simplify logic a tad * rearrange assignment * Fix a few lifecycle related issues in #7025 (#7075) * Fix panic when plugin fails to load
2019-07-05 23:55:40 +00:00
c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
return backend, nil
}
c.auth = &MountTable{
Type: credentialTableType,
Entries: []*MountEntry{
&MountEntry{
Table: credentialTableType,
Path: "foo/",
Type: "initable",
UUID: "abcd",
Accessor: "initable-abcd",
BackendAwareUUID: "abcde",
NamespaceID: namespace.RootNamespaceID,
namespace: namespace.RootNamespace,
},
},
}
err := c.setupCredentials(context.Background())
if err != nil {
t.Fatal(err)
}
// run the postUnseal funcs, so that the backend will be inited
for _, f := range c.postUnsealFuncs {
f()
}
if !backend.isInitialized {
t.Fatal("backend is not initialized")
}
}
}