diff --git a/go.mod b/go.mod index 82dfdcede3..d4b28e3a2c 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/cockroachdb/apd v1.1.0 // indirect github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c github.com/coreos/go-semver v0.2.0 + github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190412130859-3b1d194e553a github.com/dnaeon/go-vcr v1.0.1 // indirect github.com/dsnet/compress v0.0.1 // indirect @@ -47,6 +48,7 @@ require ( github.com/golang/protobuf v1.3.2 github.com/google/go-github v17.0.0+incompatible github.com/google/go-metrics-stackdriver v0.0.0-20190816035513-b52628e82e2a + github.com/google/go-querystring v1.0.0 // indirect github.com/hashicorp/consul-template v0.22.0 github.com/hashicorp/consul/api v1.1.0 github.com/hashicorp/errwrap v1.0.0 @@ -91,6 +93,7 @@ require ( github.com/joyent/triton-go v0.0.0-20190112182421-51ffac552869 github.com/keybase/go-crypto v0.0.0-20190403132359-d65b6b94177f github.com/kr/pretty v0.1.0 + github.com/kr/pty v1.1.3 // indirect github.com/kr/text v0.1.0 github.com/lib/pq v1.2.0 github.com/mattn/go-colorable v0.1.2 @@ -105,6 +108,7 @@ require ( github.com/ncw/swift v1.0.47 github.com/nwaples/rardecode v1.0.0 // indirect github.com/oklog/run v1.0.0 + github.com/onsi/ginkgo v1.7.0 // indirect github.com/oracle/oci-go-sdk v7.0.0+incompatible github.com/ory/dockertest v3.3.4+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible diff --git a/vault/auth.go b/vault/auth.go index 7c789ddf54..8b411899ae 100644 --- a/vault/auth.go +++ b/vault/auth.go @@ -48,7 +48,17 @@ var ( // enableCredential is used to enable a new credential backend func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { - return c.enableCredentialInternal(ctx, entry, MountTableUpdateStorage) + // Enable credential internally + if err := c.enableCredentialInternal(ctx, entry, MountTableUpdateStorage); err != nil { + return err + } + + // Re-evaluate filtered paths + if err := runFilteredPathsEvaluation(ctx, c); err != nil { + c.logger.Error("failed to evaluate filtered paths", "error", err) + return err + } + return nil } // enableCredential is used to enable a new credential backend @@ -128,6 +138,12 @@ func (c *Core) enableCredentialInternal(ctx context.Context, entry *MountEntry, viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) + // Singleton mounts cannot be filtered on a per-secondary basis + // from replication + if strutil.StrListContains(singletonMounts, entry.Type) { + addFilterablePath(c, viewPath) + } + nilMount, err := preprocessMount(c, entry, view) if err != nil { return err @@ -202,8 +218,7 @@ func (c *Core) enableCredentialInternal(ctx context.Context, entry *MountEntry, return nil } -// disableCredential is used to disable an existing credential backend; the -// boolean indicates if it existed +// disableCredential is used to disable an existing credential backend func (c *Core) disableCredential(ctx context.Context, path string) error { // Ensure we end the path in a slash if !strings.HasSuffix(path, "/") { @@ -369,6 +384,38 @@ func (c *Core) remountCredEntryForce(ctx context.Context, path string) error { return c.enableCredential(ctx, me) } +// remountCredEntryForceInternal takes a copy of the mount entry for the path and fully +// unmounts and remounts the backend to pick up any changes, such as filtered +// paths. This should be only used internal. +func (c *Core) remountCredEntryForceInternal(ctx context.Context, path string, updateStorage bool) error { + fullPath := credentialRoutePrefix + path + me := c.router.MatchingMountEntry(ctx, fullPath) + if me == nil { + return fmt.Errorf("cannot find mount for path %q", path) + } + + me, err := me.Clone() + if err != nil { + return err + } + + if err := c.disableCredentialInternal(ctx, path, updateStorage); err != nil { + return err + } + + // Enable credential internally + if err := c.enableCredentialInternal(ctx, me, updateStorage); err != nil { + return err + } + + // Re-evaluate filtered paths + if err := runFilteredPathsEvaluation(ctx, c); err != nil { + c.logger.Error("failed to evaluate filtered paths", "error", err) + return err + } + return nil +} + // taintCredEntry is used to mark an entry in the auth table as tainted func (c *Core) taintCredEntry(ctx context.Context, path string, updateStorage bool) error { c.authLock.Lock() diff --git a/vault/core.go b/vault/core.go index 644c6243cf..bdce09f62f 100644 --- a/vault/core.go +++ b/vault/core.go @@ -17,11 +17,11 @@ import ( "sync/atomic" "time" - "github.com/armon/go-metrics" + metrics "github.com/armon/go-metrics" "github.com/hashicorp/errwrap" log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-uuid" + multierror "github.com/hashicorp/go-multierror" + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/command/server" @@ -42,7 +42,7 @@ import ( "github.com/hashicorp/vault/vault/cluster" "github.com/hashicorp/vault/vault/seal" shamirseal "github.com/hashicorp/vault/vault/seal/shamir" - "github.com/patrickmn/go-cache" + cache "github.com/patrickmn/go-cache" "google.golang.org/grpc" ) @@ -92,16 +92,17 @@ var ( manualStepDownSleepPeriod = 10 * time.Second // Functions only in the Enterprise version - enterprisePostUnseal = enterprisePostUnsealImpl - enterprisePreSeal = enterprisePreSealImpl - startReplication = startReplicationImpl - stopReplication = stopReplicationImpl - LastWAL = lastWALImpl - LastPerformanceWAL = lastPerformanceWALImpl - PerformanceMerkleRoot = merkleRootImpl - DRMerkleRoot = merkleRootImpl - LastRemoteWAL = lastRemoteWALImpl - WaitUntilWALShipped = waitUntilWALShippedImpl + enterprisePostUnseal = enterprisePostUnsealImpl + enterprisePreSeal = enterprisePreSealImpl + enterpriseSetupFilteredPaths = enterpriseSetupFilteredPathsImpl + startReplication = startReplicationImpl + stopReplication = stopReplicationImpl + LastWAL = lastWALImpl + LastPerformanceWAL = lastPerformanceWALImpl + PerformanceMerkleRoot = merkleRootImpl + DRMerkleRoot = merkleRootImpl + LastRemoteWAL = lastRemoteWALImpl + WaitUntilWALShipped = waitUntilWALShippedImpl ) // NonFatalError is an error that can be returned during NewCore that should be @@ -1681,6 +1682,9 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c if err := c.loadMounts(ctx); err != nil { return err } + if err := enterpriseSetupFilteredPaths(c); err != nil { + return err + } if err := c.setupMounts(ctx); err != nil { return err } @@ -1696,6 +1700,9 @@ func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c if err := c.loadCredentials(ctx); err != nil { return err } + if err := enterpriseSetupFilteredPaths(c); err != nil { + return err + } if err := c.setupCredentials(ctx); err != nil { return err } @@ -1881,6 +1888,10 @@ func enterprisePreSealImpl(c *Core) error { return nil } +func enterpriseSetupFilteredPathsImpl(c *Core) error { + return nil +} + func startReplicationImpl(c *Core) error { return nil } diff --git a/vault/core_util.go b/vault/core_util.go index 8b909d75f7..11ea223932 100644 --- a/vault/core_util.go +++ b/vault/core_util.go @@ -105,7 +105,7 @@ func (c *Core) setupReplicatedClusterPrimary(*replication.Cluster) error { retur func (c *Core) perfStandbyCount() int { return 0 } -func (c *Core) removePrefixFromFilteredPaths(context.Context, string) error { +func (c *Core) removePathFromFilteredPaths(context.Context, string, string) error { return nil } diff --git a/vault/logical_system.go b/vault/logical_system.go index 07c25e22be..a04bfcdc60 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -18,13 +18,13 @@ import ( "sync" "time" - "github.com/hashicorp/go-multierror" + multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/physical/raft" "github.com/hashicorp/errwrap" log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-memdb" - "github.com/hashicorp/go-uuid" + memdb "github.com/hashicorp/go-memdb" + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/hostutil" "github.com/hashicorp/vault/helper/identity" "github.com/hashicorp/vault/helper/metricsutil" @@ -894,7 +894,7 @@ func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, d // Attempt mount if err := b.Core.mount(ctx, me); err != nil { - b.Backend.Logger().Error("mount failed", "path", me.Path, "error", err) + b.Backend.Logger().Error("error occurred during enable mount", "path", me.Path, "error", err) return handleError(err) } @@ -957,7 +957,7 @@ func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, return nil, nil } - prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, path) + _, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, path) if !found { b.Backend.Logger().Error("unable to find storage for path", "path", path) return handleError(fmt.Errorf("unable to find storage for path: %q", path)) @@ -969,8 +969,14 @@ func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, return handleError(err) } + // Get the view path if available + var viewPath string + if entry != nil { + viewPath = entry.ViewPath() + } + // Remove from filtered mounts - if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { + if err := b.Core.removePathFromFilteredPaths(ctx, ns.Path+path, viewPath); err != nil { b.Backend.Logger().Error("filtered path removal failed", path, "error", err) return handleError(err) } @@ -982,6 +988,11 @@ func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, func (b *SystemBackend) handleRemount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { repState := b.Core.ReplicationState() + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + // Get the paths fromPath := data.Get("from").(string) toPath := data.Get("to").(string) @@ -1005,6 +1016,18 @@ func (b *SystemBackend) handleRemount(ctx context.Context, req *logical.Request, return handleError(err) } + // Get the view path if available + var viewPath string + if entry != nil { + viewPath = entry.ViewPath() + } + + // Remove from filtered mounts and restart evaluation process + if err := b.Core.removePathFromFilteredPaths(ctx, ns.Path+fromPath, viewPath); err != nil { + b.Backend.Logger().Error("filtered path removal failed", fromPath, "error", err) + return handleError(err) + } + return nil, nil } @@ -1823,7 +1846,7 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque // Attempt enabling if err := b.Core.enableCredential(ctx, me); err != nil { - b.Backend.Logger().Error("enable auth mount failed", "path", me.Path, "error", err) + b.Backend.Logger().Error("error occurred during enable credential", "path", me.Path, "error", err) return handleError(err) } return nil, nil @@ -1857,7 +1880,7 @@ func (b *SystemBackend) handleDisableAuth(ctx context.Context, req *logical.Requ return nil, nil } - prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, fullPath) + _, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, fullPath) if !found { b.Backend.Logger().Error("unable to find storage for path", "path", fullPath) return handleError(fmt.Errorf("unable to find storage for path: %q", fullPath)) @@ -1869,8 +1892,14 @@ func (b *SystemBackend) handleDisableAuth(ctx context.Context, req *logical.Requ return handleError(err) } + // Get the view path if available + var viewPath string + if entry != nil { + viewPath = entry.ViewPath() + } + // Remove from filtered mounts - if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { + if err := b.Core.removePathFromFilteredPaths(ctx, fullPath, viewPath); err != nil { b.Backend.Logger().Error("filtered path removal failed", path, "error", err) return handleError(err) } diff --git a/vault/mount.go b/vault/mount.go index 6532a57159..166e36edf5 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -374,7 +374,19 @@ func (c *Core) mount(ctx context.Context, entry *MountEntry) error { return logical.CodedError(403, fmt.Sprintf("mount type of %q is not mountable", entry.Type)) } } - return c.mountInternal(ctx, entry, MountTableUpdateStorage) + + // Mount internally + if err := c.mountInternal(ctx, entry, MountTableUpdateStorage); err != nil { + return err + } + + // Re-evaluate filtered paths + if err := runFilteredPathsEvaluation(ctx, c); err != nil { + c.logger.Error("failed to evaluate filtered paths", "error", err) + return err + } + + return nil } func (c *Core) mountInternal(ctx context.Context, entry *MountEntry, updateStorage bool) error { @@ -443,8 +455,8 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry, updateStora viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) - // Singleton mounts cannot be filtered on a per-secondary basis - // from replication + // Singleton mounts cannot be filtered manually on a per-secondary basis + // from replication. if strutil.StrListContains(singletonMounts, entry.Type) { addFilterablePath(c, viewPath) } @@ -727,6 +739,37 @@ func (c *Core) remountForce(ctx context.Context, path string) error { return c.mount(ctx, me) } +// remountForceInternal takes a copy of the mount entry for the path and fully unmounts +// and remounts the backend to pick up any changes, such as filtered paths. +// Should be only used for internal usage. +func (c *Core) remountForceInternal(ctx context.Context, path string, updateStorage bool) error { + me := c.router.MatchingMountEntry(ctx, path) + if me == nil { + return fmt.Errorf("cannot find mount for path %q", path) + } + + me, err := me.Clone() + if err != nil { + return err + } + + if err := c.unmountInternal(ctx, path, updateStorage); err != nil { + return err + } + + // Mount internally + if err := c.mountInternal(ctx, me, updateStorage); err != nil { + return err + } + + // Re-evaluate filtered paths + if err := runFilteredPathsEvaluation(ctx, c); err != nil { + c.logger.Error("failed to evaluate filtered paths", "error", err) + return err + } + return nil +} + // Remount is used to remount a path at a new mount point. func (c *Core) remount(ctx context.Context, src, dst string) error { ns, err := namespace.FromContext(ctx) @@ -1068,7 +1111,7 @@ func (c *Core) setupMounts(ctx context.Context) error { // Create a barrier view using the UUID view := NewBarrierView(c.barrier, barrierPath) - // Singleton mounts cannot be filtered on a per-secondary basis + // Singleton mounts cannot be filtered manually on a per-secondary basis // from replication if strutil.StrListContains(singletonMounts, entry.Type) { addFilterablePath(c, barrierPath) diff --git a/vault/mount_util.go b/vault/mount_util.go index 798eef21a2..65e5a3d5fd 100644 --- a/vault/mount_util.go +++ b/vault/mount_util.go @@ -18,6 +18,7 @@ func addFilterablePath(*Core, string) {} func preprocessMount(*Core, *MountEntry, *BarrierView) (bool, error) { return false, nil } func clearIgnoredPaths(context.Context, *Core, logical.Backend, string) error { return nil } func addLicenseCallback(*Core, logical.Backend) {} +func runFilteredPathsEvaluation(context.Context, *Core) error { return nil } // ViewPath returns storage prefix for the view func (e *MountEntry) ViewPath() string { diff --git a/vault/router.go b/vault/router.go index e8583e2421..eb42bc5cf6 100644 --- a/vault/router.go +++ b/vault/router.go @@ -10,7 +10,7 @@ import ( metrics "github.com/armon/go-metrics" radix "github.com/armon/go-radix" - "github.com/hashicorp/go-hclog" + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/salt"