2015-03-11 22:33:25 +00:00
|
|
|
package vault
|
|
|
|
|
|
|
|
import (
|
2018-01-19 06:44:44 +00:00
|
|
|
"context"
|
2016-05-26 16:55:00 +00:00
|
|
|
"encoding/json"
|
2015-03-11 22:33:25 +00:00
|
|
|
"reflect"
|
2016-08-09 07:43:03 +00:00
|
|
|
"strings"
|
2015-03-11 22:33:25 +00:00
|
|
|
"testing"
|
2015-04-02 18:47:44 +00:00
|
|
|
"time"
|
|
|
|
|
2020-10-27 15:24:43 +00:00
|
|
|
metrics "github.com/armon/go-metrics"
|
2019-02-14 19:55:32 +00:00
|
|
|
"github.com/go-test/deep"
|
2016-05-26 17:38:51 +00:00
|
|
|
"github.com/hashicorp/vault/audit"
|
2020-10-27 15:24:43 +00:00
|
|
|
"github.com/hashicorp/vault/helper/metricsutil"
|
2019-04-13 07:44:06 +00:00
|
|
|
"github.com/hashicorp/vault/helper/namespace"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/compressutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/jsonutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2015-03-11 22:33:25 +00:00
|
|
|
)
|
|
|
|
|
2018-02-09 19:04:25 +00:00
|
|
|
func TestMount_ReadOnlyViewDuringMount(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2018-02-09 19:04:25 +00:00
|
|
|
c.logicalBackends["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")
|
|
|
|
}
|
|
|
|
return &NoopBackend{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
me := &MountEntry{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo",
|
|
|
|
Type: "noop",
|
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
err := c.mount(namespace.RootContext(nil), me)
|
2018-02-09 19:04:25 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-27 15:24:43 +00:00
|
|
|
func TestLogicalMountMetrics(t *testing.T) {
|
|
|
|
c, _, _, _ := TestCoreUnsealedWithMetrics(t)
|
|
|
|
c.logicalBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) {
|
|
|
|
return &NoopBackend{
|
|
|
|
BackendType: logical.TypeLogical,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
mountKeyName := "core.mount_table.num_entries.type|logical||local|false||"
|
|
|
|
mountMetrics := &c.metricsHelper.LoopMetrics.Metrics
|
|
|
|
loadMetric, ok := mountMetrics.Load(mountKeyName)
|
|
|
|
var numEntriesMetric metricsutil.GaugeMetric = loadMetric.(metricsutil.GaugeMetric)
|
|
|
|
|
|
|
|
// 3 default nonlocal logical backends
|
|
|
|
if !ok || numEntriesMetric.Value != 3 {
|
|
|
|
t.Fatalf("Auth values should be: %+v", numEntriesMetric)
|
|
|
|
}
|
|
|
|
me := &MountEntry{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo",
|
|
|
|
Type: "noop",
|
|
|
|
}
|
|
|
|
err := c.mount(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 != 4 {
|
|
|
|
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 != "logical" ||
|
|
|
|
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|logical||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 != "logical" ||
|
|
|
|
sizeMetric.Labels[1].Name != "local" ||
|
|
|
|
sizeMetric.Labels[1].Value != "false" {
|
|
|
|
t.Fatalf("mount metrics for size have wrong labels")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-11 22:33:25 +00:00
|
|
|
func TestCore_DefaultMountTable(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, keys, _ := TestCoreUnsealed(t)
|
2019-02-14 19:55:32 +00:00
|
|
|
verifyDefaultTable(t, c.mounts, 4)
|
2015-03-11 22:33:25 +00:00
|
|
|
|
|
|
|
// Start a second core with same physical
|
2020-10-27 15:24:43 +00:00
|
|
|
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
|
2015-04-29 01:12:57 +00:00
|
|
|
conf := &CoreConfig{
|
2020-10-27 15:24:43 +00:00
|
|
|
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
|
|
|
}
|
2015-03-11 22:33:25 +00:00
|
|
|
c2, err := NewCore(conf)
|
|
|
|
if err != nil {
|
2015-03-12 01:29:49 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2021-02-12 20:04:48 +00:00
|
|
|
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")
|
|
|
|
}
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2019-02-15 22:00:14 +00:00
|
|
|
if diff := deep.Equal(c.mounts.sortEntriesByPath(), c2.mounts.sortEntriesByPath()); len(diff) > 0 {
|
|
|
|
t.Fatalf("mismatch: %v", diff)
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
|
|
|
func TestCore_Mount(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, keys, _ := TestCoreUnsealed(t)
|
2015-03-12 01:29:49 +00:00
|
|
|
me := &MountEntry{
|
2016-05-26 16:55:00 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo",
|
2017-09-15 13:02:29 +00:00
|
|
|
Type: "kv",
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
err := c.mount(namespace.RootContext(nil), me)
|
2015-03-12 01:29:49 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:11:32 +00:00
|
|
|
match := c.router.MatchingMount(namespace.RootContext(nil), "foo/bar")
|
2015-03-12 01:29:49 +00:00
|
|
|
if match != "foo/" {
|
|
|
|
t.Fatalf("missing mount")
|
|
|
|
}
|
|
|
|
|
2022-08-11 01:02:05 +00:00
|
|
|
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
|
|
|
|
conf := &CoreConfig{
|
|
|
|
Physical: c.physical,
|
|
|
|
DisableMlock: true,
|
|
|
|
BuiltinRegistry: NewMockBuiltinRegistry(),
|
|
|
|
MetricSink: metricsutil.NewClusterMetricSink("test-cluster", inmemSink),
|
|
|
|
MetricsHelper: metricsutil.NewMetricsHelper(inmemSink, false),
|
|
|
|
}
|
|
|
|
c2, err := NewCore(conf)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
defer c2.Shutdown()
|
|
|
|
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 diff := deep.Equal(c.mounts.sortEntriesByPath(), c2.mounts.sortEntriesByPath()); len(diff) > 0 {
|
|
|
|
t.Fatalf("mismatch: %v", diff)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCore_Mount_kv_generic tests that we can successfully mount kv using the
|
|
|
|
// kv alias "generic"
|
|
|
|
func TestCore_Mount_kv_generic(t *testing.T) {
|
|
|
|
c, keys, _ := TestCoreUnsealed(t)
|
|
|
|
me := &MountEntry{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo",
|
|
|
|
Type: "generic",
|
|
|
|
}
|
|
|
|
err := c.mount(namespace.RootContext(nil), me)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
match := c.router.MatchingMount(namespace.RootContext(nil), "foo/bar")
|
|
|
|
if match != "foo/" {
|
|
|
|
t.Fatalf("missing mount")
|
|
|
|
}
|
|
|
|
|
2020-10-27 15:24:43 +00:00
|
|
|
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
|
2015-04-29 01:12:57 +00:00
|
|
|
conf := &CoreConfig{
|
2020-10-27 15:24:43 +00:00
|
|
|
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
|
|
|
}
|
2015-03-12 01:29:49 +00:00
|
|
|
c2, err := NewCore(conf)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2021-02-12 20:04:48 +00:00
|
|
|
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")
|
|
|
|
}
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify matching mount tables
|
2019-02-15 22:00:14 +00:00
|
|
|
if diff := deep.Equal(c.mounts.sortEntriesByPath(), c2.mounts.sortEntriesByPath()); len(diff) > 0 {
|
|
|
|
t.Fatalf("mismatch: %v", diff)
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-17 01:13:19 +00:00
|
|
|
// 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_Mount_Local(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2017-02-17 01:13:19 +00:00
|
|
|
|
|
|
|
c.mounts = &MountTable{
|
|
|
|
Type: mountTableType,
|
|
|
|
Entries: []*MountEntry{
|
2021-04-08 16:43:39 +00:00
|
|
|
{
|
2018-03-21 19:04:27 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "noop/",
|
|
|
|
Type: "kv",
|
|
|
|
UUID: "abcd",
|
|
|
|
Accessor: "kv-abcd",
|
|
|
|
BackendAwareUUID: "abcde",
|
2018-09-18 03:03:00 +00:00
|
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
|
|
namespace: namespace.RootNamespace,
|
2017-02-17 01:13:19 +00:00
|
|
|
},
|
2021-04-08 16:43:39 +00:00
|
|
|
{
|
2018-03-21 19:04:27 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "noop2/",
|
|
|
|
Type: "kv",
|
|
|
|
UUID: "bcde",
|
|
|
|
Accessor: "kv-bcde",
|
|
|
|
BackendAwareUUID: "bcdea",
|
2018-09-18 03:03:00 +00:00
|
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
|
|
namespace: namespace.RootNamespace,
|
2017-02-17 01:13:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Both should set up successfully
|
2018-11-05 16:11:32 +00:00
|
|
|
err := c.setupMounts(namespace.RootContext(nil))
|
2017-02-17 01:13:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(c.mounts.Entries) != 2 {
|
|
|
|
t.Fatalf("expected two entries, got %d", len(c.mounts.Entries))
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
rawLocal, err := c.barrier.Get(context.Background(), coreLocalMountConfigPath)
|
2017-02-17 01:13:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if rawLocal == nil {
|
|
|
|
t.Fatal("expected non-nil local mounts")
|
|
|
|
}
|
|
|
|
localMountsTable := &MountTable{}
|
|
|
|
if err := jsonutil.DecodeJSON(rawLocal.Value, localMountsTable); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-02-18 18:51:05 +00:00
|
|
|
if len(localMountsTable.Entries) != 1 || localMountsTable.Entries[0].Type != "cubbyhole" {
|
|
|
|
t.Fatalf("expected only cubbyhole entry in local mount table, got %#v", localMountsTable)
|
2017-02-17 01:13:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c.mounts.Entries[1].Local = true
|
2018-04-11 18:32:55 +00:00
|
|
|
if err := c.persistMounts(context.Background(), c.mounts, nil); err != nil {
|
2017-02-17 01:13:19 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
rawLocal, err = c.barrier.Get(context.Background(), coreLocalMountConfigPath)
|
2017-02-17 01:13:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if rawLocal == nil {
|
|
|
|
t.Fatal("expected non-nil local mount")
|
|
|
|
}
|
|
|
|
localMountsTable = &MountTable{}
|
|
|
|
if err := jsonutil.DecodeJSON(rawLocal.Value, localMountsTable); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-02-18 18:51:05 +00:00
|
|
|
// This requires some explanation: because we're directly munging the mount
|
|
|
|
// table, the table initially when core unseals contains cubbyhole as per
|
|
|
|
// above, but then we overwrite it with our own table with one local entry,
|
|
|
|
// so we should now only expect the noop2 entry
|
|
|
|
if len(localMountsTable.Entries) != 1 || localMountsTable.Entries[0].Path != "noop2/" {
|
2017-02-17 01:13:19 +00:00
|
|
|
t.Fatalf("expected one entry in local mount table, got %#v", localMountsTable)
|
|
|
|
}
|
|
|
|
|
|
|
|
oldMounts := c.mounts
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := c.loadMounts(context.Background()); err != nil {
|
2017-02-17 01:13:19 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
compEntries := c.mounts.Entries[:0]
|
|
|
|
// Filter out required mounts
|
|
|
|
for _, v := range c.mounts.Entries {
|
2017-09-15 13:02:29 +00:00
|
|
|
if v.Type == "kv" {
|
2017-02-17 01:13:19 +00:00
|
|
|
compEntries = append(compEntries, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.mounts.Entries = compEntries
|
|
|
|
|
|
|
|
if !reflect.DeepEqual(oldMounts, c.mounts) {
|
|
|
|
t.Fatalf("expected\n%#v\ngot\n%#v\n", oldMounts, c.mounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.mounts.Entries) != 2 {
|
|
|
|
t.Fatalf("expected two mount entries, got %#v", localMountsTable)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-29 20:15:29 +00:00
|
|
|
func TestCore_FindOps(t *testing.T) {
|
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
|
|
|
uuid1 := "80DA741F-3997-4179-B531-EBD7371DFA86"
|
|
|
|
uuid2 := "0178594D-F267-445A-89A3-5B5DFC4A4C0F"
|
|
|
|
path1 := "kv1"
|
|
|
|
path2 := "kv2"
|
|
|
|
|
|
|
|
c.mounts = &MountTable{
|
|
|
|
Type: mountTableType,
|
|
|
|
Entries: []*MountEntry{
|
|
|
|
{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: path1,
|
|
|
|
Type: "kv",
|
|
|
|
UUID: "abcd",
|
|
|
|
Accessor: "kv-abcd",
|
|
|
|
BackendAwareUUID: uuid1,
|
|
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
|
|
namespace: namespace.RootNamespace,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: path2,
|
|
|
|
Type: "kv",
|
|
|
|
UUID: "bcde",
|
|
|
|
Accessor: "kv-bcde",
|
|
|
|
BackendAwareUUID: uuid2,
|
|
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
|
|
namespace: namespace.RootNamespace,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Both should set up successfully
|
|
|
|
if err := c.setupMounts(namespace.RootContext(nil)); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(c.mounts.Entries) != 2 {
|
|
|
|
t.Fatalf("expected two entries, got %d", len(c.mounts.Entries))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unknown uuids/paths should return nil, nil
|
|
|
|
entry, err := c.mounts.findByBackendUUID(namespace.RootContext(nil), "unknown")
|
|
|
|
if err != nil || entry != nil {
|
|
|
|
t.Fatalf("expected no errors nor matches got, error: %#v entry: %#v", err, entry)
|
|
|
|
}
|
|
|
|
entry, err = c.mounts.find(namespace.RootContext(nil), "unknown")
|
|
|
|
if err != nil || entry != nil {
|
|
|
|
t.Fatalf("expected no errors nor matches got, error: %#v entry: %#v", err, entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find our entry by its uuid
|
|
|
|
entry, err = c.mounts.findByBackendUUID(namespace.RootContext(nil), uuid1)
|
|
|
|
if err != nil || entry == nil {
|
|
|
|
t.Fatalf("failed finding entry by uuid error: %#v entry: %#v", err, entry)
|
|
|
|
}
|
|
|
|
if entry.Path != path1 {
|
|
|
|
t.Fatalf("found incorrect entry by uuid, entry should had a path of '%s': %#v", path1, entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find another entry by its path
|
|
|
|
entry, err = c.mounts.find(namespace.RootContext(nil), path2)
|
|
|
|
if err != nil || entry == nil {
|
|
|
|
t.Fatalf("failed finding entry by path error: %#v entry: %#v", err, entry)
|
|
|
|
}
|
|
|
|
if entry.BackendAwareUUID != uuid2 {
|
|
|
|
t.Fatalf("found incorrect entry by path, entry should had a uuid of '%s': %#v", uuid2, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
|
|
|
func TestCore_Unmount(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, keys, _ := TestCoreUnsealed(t)
|
2018-11-05 16:11:32 +00:00
|
|
|
err := c.unmount(namespace.RootContext(nil), "secret")
|
2017-07-13 17:57:14 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-03-12 01:29:49 +00:00
|
|
|
}
|
|
|
|
|
2018-11-05 16:11:32 +00:00
|
|
|
match := c.router.MatchingMount(namespace.RootContext(nil), "secret/foo")
|
2015-03-12 01:29:49 +00:00
|
|
|
if match != "" {
|
|
|
|
t.Fatalf("backend present")
|
|
|
|
}
|
|
|
|
|
2020-10-27 15:24:43 +00:00
|
|
|
inmemSink := metrics.NewInmemSink(1000000*time.Hour, 2000000*time.Hour)
|
2015-04-29 01:12:57 +00:00
|
|
|
conf := &CoreConfig{
|
2020-10-27 15:24:43 +00:00
|
|
|
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
|
|
|
}
|
2015-03-12 01:29:49 +00:00
|
|
|
c2, err := NewCore(conf)
|
|
|
|
if err != nil {
|
2015-03-11 22:33:25 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2021-02-12 20:04:48 +00:00
|
|
|
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")
|
|
|
|
}
|
2015-03-11 22:33:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify matching mount tables
|
2019-02-15 19:13:26 +00:00
|
|
|
if diff := deep.Equal(c.mounts, c2.mounts); len(diff) > 0 {
|
|
|
|
t.Fatalf("mismatch: %v", diff)
|
2015-03-11 22:33:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-02 18:47:44 +00:00
|
|
|
func TestCore_Unmount_Cleanup(t *testing.T) {
|
2019-06-04 17:33:36 +00:00
|
|
|
testCore_Unmount_Cleanup(t, false)
|
|
|
|
testCore_Unmount_Cleanup(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testCore_Unmount_Cleanup(t *testing.T, causeFailure bool) {
|
2015-04-02 18:47:44 +00:00
|
|
|
noop := &NoopBackend{}
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, root := TestCoreUnsealed(t)
|
2018-01-19 06:44:44 +00:00
|
|
|
c.logicalBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
2015-04-02 18:47:44 +00:00
|
|
|
return noop, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount the noop backend
|
|
|
|
me := &MountEntry{
|
2016-05-26 16:55:00 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "test/",
|
|
|
|
Type: "noop",
|
2015-04-02 18:47:44 +00:00
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
if err := c.mount(namespace.RootContext(nil), me); err != nil {
|
2015-04-02 18:47:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the view
|
2018-11-05 16:11:32 +00:00
|
|
|
view := c.router.MatchingStorageByAPIPath(namespace.RootContext(nil), "test/")
|
2015-04-02 18:47:44 +00:00
|
|
|
|
|
|
|
// Inject data
|
|
|
|
se := &logical.StorageEntry{
|
|
|
|
Key: "plstodelete",
|
|
|
|
Value: []byte("test"),
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := view.Put(context.Background(), se); err != nil {
|
2015-04-02 18:47:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup response
|
|
|
|
resp := &logical.Response{
|
|
|
|
Secret: &logical.Secret{
|
2015-04-09 19:14:04 +00:00
|
|
|
LeaseOptions: logical.LeaseOptions{
|
2015-08-21 00:47:17 +00:00
|
|
|
TTL: time.Hour,
|
2015-04-09 19:14:04 +00:00
|
|
|
},
|
2015-04-02 18:47:44 +00:00
|
|
|
},
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"foo": "bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
noop.Response = resp
|
|
|
|
|
|
|
|
// Generate leased secret
|
|
|
|
r := &logical.Request{
|
|
|
|
Operation: logical.ReadOperation,
|
|
|
|
Path: "test/foo",
|
|
|
|
ClientToken: root,
|
|
|
|
}
|
2018-10-15 16:56:24 +00:00
|
|
|
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
2018-11-05 16:11:32 +00:00
|
|
|
resp, err := c.HandleRequest(namespace.RootContext(nil), r)
|
2015-04-02 18:47:44 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-04-08 20:35:32 +00:00
|
|
|
if resp.Secret.LeaseID == "" {
|
2015-04-02 18:47:44 +00:00
|
|
|
t.Fatalf("bad: %#v", resp)
|
|
|
|
}
|
|
|
|
|
2019-06-04 17:33:36 +00:00
|
|
|
if causeFailure {
|
|
|
|
view.(*BarrierView).setReadOnlyErr(logical.ErrSetupReadOnly)
|
|
|
|
}
|
|
|
|
|
2015-04-02 18:47:44 +00:00
|
|
|
// Unmount, this should cleanup
|
2019-06-04 17:33:36 +00:00
|
|
|
err = c.unmount(namespace.RootContext(nil), "test/")
|
|
|
|
switch {
|
|
|
|
case err != nil && causeFailure:
|
|
|
|
case err == nil && causeFailure:
|
|
|
|
t.Fatal("expected error")
|
|
|
|
case err != nil:
|
2017-07-13 17:57:14 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
2015-04-02 18:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback should be invoked
|
|
|
|
if noop.Requests[1].Operation != logical.RollbackOperation {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revoke should be invoked
|
|
|
|
if noop.Requests[2].Operation != logical.RevokeOperation {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
if noop.Requests[2].Path != "foo" {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
|
|
|
|
// View should be empty
|
2018-01-19 06:44:44 +00:00
|
|
|
out, err := logical.CollectKeys(context.Background(), view)
|
2015-04-02 18:47:44 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2019-06-04 17:33:36 +00:00
|
|
|
switch {
|
|
|
|
case len(out) == 1 && causeFailure:
|
|
|
|
case len(out) == 0 && causeFailure:
|
|
|
|
t.Fatal("expected a value")
|
|
|
|
case len(out) != 0:
|
2015-04-02 18:47:44 +00:00
|
|
|
t.Fatalf("bad: %#v", out)
|
2019-06-04 17:33:36 +00:00
|
|
|
case !causeFailure:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this point just in the failure case, check mounting
|
|
|
|
if err := c.mount(namespace.RootContext(nil), me); err == nil {
|
|
|
|
t.Fatal("expected error")
|
|
|
|
} else {
|
|
|
|
if !strings.Contains(err.Error(), "path is already in use at") {
|
|
|
|
t.Fatalf("expected a path is already in use error, got %v", err)
|
|
|
|
}
|
2015-04-02 18:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-08 16:43:39 +00:00
|
|
|
|
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
|
|
|
func TestCore_Remount(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, keys, _ := TestCoreUnsealed(t)
|
2022-02-17 20:17:59 +00:00
|
|
|
err := c.remountSecretsEngineCurrentNamespace(namespace.RootContext(nil), "secret", "foo", true)
|
2015-03-12 19:09:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-11-05 16:11:32 +00:00
|
|
|
match := c.router.MatchingMount(namespace.RootContext(nil), "foo/bar")
|
2015-03-12 19:09:30 +00:00
|
|
|
if match != "foo/" {
|
|
|
|
t.Fatalf("failed remount")
|
|
|
|
}
|
|
|
|
|
2022-02-18 16:04:21 +00:00
|
|
|
c.sealInternal()
|
2017-01-17 20:43:10 +00:00
|
|
|
for i, key := range keys {
|
2022-02-18 16:04:21 +00:00
|
|
|
unseal, err := TestCoreUnseal(c, key)
|
2017-01-17 20:43:10 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if i+1 == len(keys) && !unseal {
|
|
|
|
t.Fatalf("should be unsealed")
|
|
|
|
}
|
2015-03-12 19:09:30 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 16:04:21 +00:00
|
|
|
match = c.router.MatchingMount(namespace.RootContext(nil), "foo/bar")
|
|
|
|
if match != "foo/" {
|
|
|
|
t.Fatalf("failed remount")
|
2015-03-12 19:09:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-02 19:04:37 +00:00
|
|
|
func TestCore_Remount_Cleanup(t *testing.T) {
|
|
|
|
noop := &NoopBackend{}
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, root := TestCoreUnsealed(t)
|
2018-01-19 06:44:44 +00:00
|
|
|
c.logicalBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
2015-04-02 19:04:37 +00:00
|
|
|
return noop, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount the noop backend
|
|
|
|
me := &MountEntry{
|
2016-05-26 16:55:00 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "test/",
|
|
|
|
Type: "noop",
|
2015-04-02 19:04:37 +00:00
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
if err := c.mount(namespace.RootContext(nil), me); err != nil {
|
2015-04-02 19:04:37 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the view
|
2018-11-05 16:11:32 +00:00
|
|
|
view := c.router.MatchingStorageByAPIPath(namespace.RootContext(nil), "test/")
|
2015-04-02 19:04:37 +00:00
|
|
|
|
|
|
|
// Inject data
|
|
|
|
se := &logical.StorageEntry{
|
|
|
|
Key: "plstokeep",
|
|
|
|
Value: []byte("test"),
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := view.Put(context.Background(), se); err != nil {
|
2015-04-02 19:04:37 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup response
|
|
|
|
resp := &logical.Response{
|
|
|
|
Secret: &logical.Secret{
|
2015-04-09 19:14:04 +00:00
|
|
|
LeaseOptions: logical.LeaseOptions{
|
2015-08-21 00:47:17 +00:00
|
|
|
TTL: time.Hour,
|
2015-04-09 19:14:04 +00:00
|
|
|
},
|
2015-04-02 19:04:37 +00:00
|
|
|
},
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"foo": "bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
noop.Response = resp
|
|
|
|
|
|
|
|
// Generate leased secret
|
|
|
|
r := &logical.Request{
|
|
|
|
Operation: logical.ReadOperation,
|
|
|
|
Path: "test/foo",
|
|
|
|
ClientToken: root,
|
|
|
|
}
|
2018-10-15 16:56:24 +00:00
|
|
|
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
2018-11-05 16:11:32 +00:00
|
|
|
resp, err := c.HandleRequest(namespace.RootContext(nil), r)
|
2015-04-02 19:04:37 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-04-08 20:35:32 +00:00
|
|
|
if resp.Secret.LeaseID == "" {
|
2015-04-02 19:04:37 +00:00
|
|
|
t.Fatalf("bad: %#v", resp)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remount, this should cleanup
|
2022-02-17 20:17:59 +00:00
|
|
|
if err := c.remountSecretsEngineCurrentNamespace(namespace.RootContext(nil), "test/", "new/", true); err != nil {
|
2015-04-02 19:04:37 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback should be invoked
|
|
|
|
if noop.Requests[1].Operation != logical.RollbackOperation {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revoke should be invoked
|
|
|
|
if noop.Requests[2].Operation != logical.RevokeOperation {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
if noop.Requests[2].Path != "foo" {
|
|
|
|
t.Fatalf("bad: %#v", noop.Requests)
|
|
|
|
}
|
|
|
|
|
|
|
|
// View should not be empty
|
2018-01-19 06:44:44 +00:00
|
|
|
out, err := logical.CollectKeys(context.Background(), view)
|
2015-04-02 19:04:37 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(out) != 1 && out[0] != "plstokeep" {
|
|
|
|
t.Fatalf("bad: %#v", out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
vault: make Mount related core functions public
/cc @armon - So I know the conversation we had related to this about
auth, but I think we still need to export these and do auth only at the
external API layer. If you're writing to the internal API, then all bets
are off.
The reason is simply that if you have access to the code, you can
already work around it anyways (you can disable auth or w/e), so a
compromised Vault source/binary is already a failure, and that is the
only thing that our previous unexported methods were protecting against.
If you write an external tool to access a Vault, it still needs to be
unsealed so _that_ is the primary security mechanism from an API
perspective. Once it is unsealed then the core API has full access to
the Vault, and identity/auth is only done at the external API layer, not
at the internal API layer.
The benefits of this approach is that it lets us still treat the "sys"
mount specially but at least have sys adopt helper/backend and use that
machinery and it can still be the only backend which actually has a
reference to *vault.Core to do core things (a key difference). So, an
AWS backend still will never be able to muck with things it can't, but
we're explicitly giving Sys (via struct initialization in Go itself)
a reference to *vault.Core.
2015-03-15 00:26:59 +00:00
|
|
|
func TestCore_Remount_Protected(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2022-02-17 20:17:59 +00:00
|
|
|
err := c.remountSecretsEngineCurrentNamespace(namespace.RootContext(nil), "sys", "foo", true)
|
2018-04-06 00:43:29 +00:00
|
|
|
if err.Error() != `cannot remount "sys/"` {
|
2015-03-12 19:09:30 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-11 22:33:25 +00:00
|
|
|
func TestDefaultMountTable(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2017-06-26 17:14:36 +00:00
|
|
|
table := c.defaultMountTable()
|
2019-02-14 19:55:32 +00:00
|
|
|
verifyDefaultTable(t, table, 3)
|
2015-03-11 22:33:25 +00:00
|
|
|
}
|
|
|
|
|
2016-05-26 16:55:00 +00:00
|
|
|
func TestCore_MountTable_UpgradeToTyped(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2016-05-26 16:55:00 +00:00
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig) (audit.Backend, error) {
|
2016-05-26 17:38:51 +00:00
|
|
|
return &NoopAudit{
|
|
|
|
Config: config,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
me := &MountEntry{
|
|
|
|
Table: auditTableType,
|
|
|
|
Path: "foo",
|
|
|
|
Type: "noop",
|
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
err := c.enableAudit(namespace.RootContext(nil), me, true)
|
2016-05-26 17:38:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
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
|
2016-05-26 17:38:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
me = &MountEntry{
|
|
|
|
Table: credentialTableType,
|
|
|
|
Path: "foo",
|
|
|
|
Type: "noop",
|
|
|
|
}
|
2018-11-05 16:11:32 +00:00
|
|
|
err = c.enableCredential(namespace.RootContext(nil), me)
|
2016-05-26 17:38:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
testCore_MountTable_UpgradeToTyped_Common(t, c, "mounts")
|
|
|
|
testCore_MountTable_UpgradeToTyped_Common(t, c, "audits")
|
|
|
|
testCore_MountTable_UpgradeToTyped_Common(t, c, "credentials")
|
|
|
|
}
|
|
|
|
|
|
|
|
func testCore_MountTable_UpgradeToTyped_Common(
|
|
|
|
t *testing.T,
|
|
|
|
c *Core,
|
2022-01-27 18:06:34 +00:00
|
|
|
testType string,
|
|
|
|
) {
|
2016-05-26 17:38:51 +00:00
|
|
|
var path string
|
|
|
|
var mt *MountTable
|
|
|
|
switch testType {
|
|
|
|
case "mounts":
|
|
|
|
path = coreMountConfigPath
|
|
|
|
mt = c.mounts
|
|
|
|
case "audits":
|
|
|
|
path = coreAuditConfigPath
|
|
|
|
mt = c.audit
|
|
|
|
case "credentials":
|
|
|
|
path = coreAuthConfigPath
|
|
|
|
mt = c.auth
|
|
|
|
}
|
|
|
|
|
2017-02-18 18:51:05 +00:00
|
|
|
// We filter out local entries here since the logic is rather dumb
|
|
|
|
// (straight JSON comparison) and doesn't seal well with the separate
|
|
|
|
// locations
|
|
|
|
newEntries := mt.Entries[:0]
|
|
|
|
for _, entry := range mt.Entries {
|
|
|
|
if !entry.Local {
|
|
|
|
newEntries = append(newEntries, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mt.Entries = newEntries
|
|
|
|
|
2016-05-26 16:55:00 +00:00
|
|
|
// Save the expected table
|
2016-05-26 17:38:51 +00:00
|
|
|
goodJson, err := json.Marshal(mt)
|
2016-05-26 16:55:00 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a pre-typed version
|
2016-05-26 17:38:51 +00:00
|
|
|
mt.Type = ""
|
|
|
|
for _, entry := range mt.Entries {
|
2016-05-26 16:55:00 +00:00
|
|
|
entry.Table = ""
|
|
|
|
}
|
|
|
|
|
2016-05-26 17:38:51 +00:00
|
|
|
raw, err := json.Marshal(mt)
|
2016-05-26 16:55:00 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if reflect.DeepEqual(raw, goodJson) {
|
|
|
|
t.Fatalf("bad: values here should be different")
|
|
|
|
}
|
|
|
|
|
2019-01-31 14:25:18 +00:00
|
|
|
entry := &logical.StorageEntry{
|
2016-05-26 17:38:51 +00:00
|
|
|
Key: path,
|
2016-05-26 16:55:00 +00:00
|
|
|
Value: raw,
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
if err := c.barrier.Put(context.Background(), entry); err != nil {
|
2016-05-26 16:55:00 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-04-11 18:32:55 +00:00
|
|
|
var persistFunc func(context.Context, *MountTable, *bool) error
|
2016-05-26 17:38:51 +00:00
|
|
|
|
2016-05-26 16:55:00 +00:00
|
|
|
// It should load successfully and be upgraded and persisted
|
2016-05-26 17:38:51 +00:00
|
|
|
switch testType {
|
|
|
|
case "mounts":
|
2018-01-19 06:44:44 +00:00
|
|
|
err = c.loadMounts(context.Background())
|
2016-05-26 17:38:51 +00:00
|
|
|
persistFunc = c.persistMounts
|
|
|
|
mt = c.mounts
|
|
|
|
case "credentials":
|
2018-01-19 06:44:44 +00:00
|
|
|
err = c.loadCredentials(context.Background())
|
2016-05-26 17:38:51 +00:00
|
|
|
persistFunc = c.persistAuth
|
|
|
|
mt = c.auth
|
|
|
|
case "audits":
|
2018-01-19 06:44:44 +00:00
|
|
|
err = c.loadAudits(context.Background())
|
2018-04-11 18:32:55 +00:00
|
|
|
persistFunc = func(ctx context.Context, mt *MountTable, b *bool) error {
|
|
|
|
if b == nil {
|
|
|
|
b = new(bool)
|
|
|
|
*b = false
|
|
|
|
}
|
|
|
|
return c.persistAudit(ctx, mt, *b)
|
|
|
|
}
|
2016-05-26 17:38:51 +00:00
|
|
|
mt = c.audit
|
|
|
|
}
|
2016-05-26 16:55:00 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2018-01-19 06:44:44 +00:00
|
|
|
entry, err = c.barrier.Get(context.Background(), path)
|
2016-05-26 16:55:00 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2021-11-01 18:43:00 +00:00
|
|
|
if entry == nil {
|
|
|
|
t.Fatal("nil value")
|
|
|
|
}
|
2016-05-26 16:55:00 +00:00
|
|
|
|
2016-08-09 16:06:59 +00:00
|
|
|
decompressedBytes, uncompressed, err := compressutil.Decompress(entry.Value)
|
2016-08-09 07:43:03 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := decompressedBytes
|
|
|
|
if uncompressed {
|
|
|
|
actual = entry.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.TrimSpace(string(actual)) != strings.TrimSpace(string(goodJson)) {
|
|
|
|
t.Fatalf("bad: expected\n%s\nactual\n%s\n", string(goodJson), string(actual))
|
2016-05-26 16:55:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now try saving invalid versions
|
2016-05-26 17:38:51 +00:00
|
|
|
origTableType := mt.Type
|
|
|
|
mt.Type = "foo"
|
2018-04-11 18:32:55 +00:00
|
|
|
if err := persistFunc(context.Background(), mt, nil); err == nil {
|
2016-05-26 16:55:00 +00:00
|
|
|
t.Fatal("expected error")
|
|
|
|
}
|
|
|
|
|
2016-05-26 17:38:51 +00:00
|
|
|
if len(mt.Entries) > 0 {
|
|
|
|
mt.Type = origTableType
|
|
|
|
mt.Entries[0].Table = "bar"
|
2018-04-11 18:32:55 +00:00
|
|
|
if err := persistFunc(context.Background(), mt, nil); err == nil {
|
2016-05-26 17:38:51 +00:00
|
|
|
t.Fatal("expected error")
|
|
|
|
}
|
2016-05-26 16:55:00 +00:00
|
|
|
|
2016-05-26 17:38:51 +00:00
|
|
|
mt.Entries[0].Table = mt.Type
|
2018-04-11 18:32:55 +00:00
|
|
|
if err := persistFunc(context.Background(), mt, nil); err != nil {
|
2016-05-26 17:38:51 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-05-26 16:55:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-14 19:55:32 +00:00
|
|
|
func verifyDefaultTable(t *testing.T, table *MountTable, expected int) {
|
|
|
|
if len(table.Entries) != expected {
|
2015-03-11 22:33:25 +00:00
|
|
|
t.Fatalf("bad: %v", table.Entries)
|
|
|
|
}
|
2017-02-18 18:51:05 +00:00
|
|
|
table.sortEntriesByPath()
|
2017-10-11 17:21:20 +00:00
|
|
|
for _, entry := range table.Entries {
|
|
|
|
switch entry.Path {
|
|
|
|
case "cubbyhole/":
|
2017-02-18 18:51:05 +00:00
|
|
|
if entry.Type != "cubbyhole" {
|
2015-03-11 22:33:25 +00:00
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
2017-10-11 17:21:20 +00:00
|
|
|
case "secret/":
|
2017-09-15 13:02:29 +00:00
|
|
|
if entry.Type != "kv" {
|
2015-09-10 01:58:09 +00:00
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
2017-10-11 17:21:20 +00:00
|
|
|
case "sys/":
|
|
|
|
if entry.Type != "system" {
|
2015-03-11 22:33:25 +00:00
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
2022-02-04 21:06:32 +00:00
|
|
|
if !entry.SealWrap {
|
|
|
|
t.Fatalf("expected SealWrap to be enabled: %v", entry)
|
|
|
|
}
|
2017-10-11 17:21:20 +00:00
|
|
|
case "identity/":
|
|
|
|
if entry.Type != "identity" {
|
2015-03-11 22:33:25 +00:00
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
|
|
|
}
|
2016-05-26 16:55:00 +00:00
|
|
|
if entry.Table != mountTableType {
|
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
2015-03-11 22:33:25 +00:00
|
|
|
if entry.Description == "" {
|
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
|
|
|
if entry.UUID == "" {
|
|
|
|
t.Fatalf("bad: %v", entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-05 21:20:30 +00:00
|
|
|
|
|
|
|
func TestSingletonMountTableFunc(t *testing.T) {
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2017-05-05 21:20:30 +00:00
|
|
|
|
|
|
|
mounts, auth := c.singletonMountTables()
|
|
|
|
|
2017-10-11 17:21:20 +00:00
|
|
|
if len(mounts.Entries) != 2 {
|
2017-10-23 19:35:28 +00:00
|
|
|
t.Fatalf("length of mounts is wrong; expected 2, got %d", len(mounts.Entries))
|
2017-05-05 21:20:30 +00:00
|
|
|
}
|
2017-10-23 19:35:28 +00:00
|
|
|
|
2017-05-05 21:20:30 +00:00
|
|
|
for _, entry := range mounts.Entries {
|
|
|
|
switch entry.Type {
|
|
|
|
case "system":
|
2017-10-11 17:21:20 +00:00
|
|
|
case "identity":
|
2017-05-05 21:20:30 +00:00
|
|
|
default:
|
|
|
|
t.Fatalf("unknown type %s", entry.Type)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(auth.Entries) != 1 {
|
|
|
|
t.Fatal("length of auth is wrong")
|
|
|
|
}
|
|
|
|
|
|
|
|
if auth.Entries[0].Type != "token" {
|
|
|
|
t.Fatal("unexpected entry type for auth")
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 23:55:40 +00:00
|
|
|
|
|
|
|
func TestCore_MountInitialize(t *testing.T) {
|
|
|
|
{
|
|
|
|
backend := &InitializableBackend{
|
|
|
|
&NoopBackend{
|
|
|
|
BackendType: logical.TypeLogical,
|
2021-04-08 16:43:39 +00:00
|
|
|
}, false,
|
|
|
|
}
|
2019-07-05 23:55:40 +00:00
|
|
|
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2019-07-05 23:55:40 +00:00
|
|
|
c.logicalBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
|
|
|
return backend, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount the noop backend
|
|
|
|
me := &MountEntry{
|
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo/",
|
|
|
|
Type: "initable",
|
|
|
|
}
|
|
|
|
if err := c.mount(namespace.RootContext(nil), me); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !backend.isInitialized {
|
|
|
|
t.Fatal("backend is not initialized")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
backend := &InitializableBackend{
|
|
|
|
&NoopBackend{
|
|
|
|
BackendType: logical.TypeLogical,
|
2021-04-08 16:43:39 +00:00
|
|
|
}, false,
|
|
|
|
}
|
2019-07-05 23:55:40 +00:00
|
|
|
|
2020-11-11 23:35:09 +00:00
|
|
|
c, _, _ := TestCoreUnsealed(t)
|
2019-07-05 23:55:40 +00:00
|
|
|
c.logicalBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) {
|
|
|
|
return backend, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mounts = &MountTable{
|
|
|
|
Type: mountTableType,
|
|
|
|
Entries: []*MountEntry{
|
2021-04-08 16:43:39 +00:00
|
|
|
{
|
2019-07-05 23:55:40 +00:00
|
|
|
Table: mountTableType,
|
|
|
|
Path: "foo/",
|
|
|
|
Type: "initable",
|
|
|
|
UUID: "abcd",
|
|
|
|
Accessor: "initable-abcd",
|
|
|
|
BackendAwareUUID: "abcde",
|
|
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
|
|
namespace: namespace.RootNamespace,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := c.setupMounts(namespace.RootContext(nil))
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|