vault: support remount
This commit is contained in:
parent
3ed3e23d93
commit
b17607e51f
|
@ -21,6 +21,11 @@ const (
|
|||
var (
|
||||
// loadMountsFailed if loadMounts encounters an error
|
||||
loadMountsFailed = errors.New("failed to setup mount table")
|
||||
|
||||
// protectedMounts cannot be remounted
|
||||
protectedMounts = []string{
|
||||
"sys/",
|
||||
}
|
||||
)
|
||||
|
||||
// MountTable is used to represent the internal mount table
|
||||
|
@ -223,6 +228,60 @@ func (c *Core) unmountPath(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// remountPath is used to remount a path
|
||||
func (c *Core) remountPath(src, dst string) error {
|
||||
c.mountsLock.Lock()
|
||||
defer c.mountsLock.Unlock()
|
||||
|
||||
// Ensure we end the path in a slash
|
||||
if !strings.HasSuffix(src, "/") {
|
||||
src += "/"
|
||||
}
|
||||
if !strings.HasSuffix(dst, "/") {
|
||||
dst += "/"
|
||||
}
|
||||
|
||||
// Prevent sys/ from being remounted
|
||||
for _, p := range protectedMounts {
|
||||
if src == p {
|
||||
return fmt.Errorf("cannot remount '%s'", p)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify exact match of the route
|
||||
match := c.router.MatchingMount(src)
|
||||
if match == "" || src != match {
|
||||
return fmt.Errorf("no matching mount at '%s'", src)
|
||||
}
|
||||
|
||||
// Verify there is no conflicting mount
|
||||
if match := c.router.MatchingMount(dst); match != "" {
|
||||
return fmt.Errorf("existing mount at '%s'", match)
|
||||
}
|
||||
|
||||
// Update the entry in the mount table
|
||||
newTable := c.mounts.Clone()
|
||||
for _, ent := range newTable.Entries {
|
||||
if ent.Path == src {
|
||||
ent.Path = dst
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Update the mount table
|
||||
if err := c.persistMounts(newTable); err != nil {
|
||||
return errors.New("failed to update mount table")
|
||||
}
|
||||
c.mounts = newTable
|
||||
|
||||
// Remount the backend
|
||||
if err := c.router.Remount(src, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
c.logger.Printf("[INFO] core: remounted '%s' to '%s'", src, dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultMountTable creates a default mount table
|
||||
func defaultMountTable() *MountTable {
|
||||
table := &MountTable{}
|
||||
|
|
|
@ -95,6 +95,45 @@ func TestCore_UnmountPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCore_RemountPath(t *testing.T) {
|
||||
c, key := testUnsealedCore(t)
|
||||
err := c.remountPath("secret", "foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
match := c.router.MatchingMount("foo/bar")
|
||||
if match != "foo/" {
|
||||
t.Fatalf("failed remount")
|
||||
}
|
||||
|
||||
conf := &CoreConfig{Physical: c.physical}
|
||||
c2, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
unseal, err := c2.Unseal(key)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !unseal {
|
||||
t.Fatalf("should be unsealed")
|
||||
}
|
||||
|
||||
// Verify matching mount tables
|
||||
if !reflect.DeepEqual(c.mounts, c2.mounts) {
|
||||
t.Fatalf("mismatch: %v %v", c.mounts, c2.mounts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_RemountPath_Protected(t *testing.T) {
|
||||
c, _ := testUnsealedCore(t)
|
||||
err := c.remountPath("sys", "foo")
|
||||
if err.Error() != "cannot remount 'sys/'" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMountTable(t *testing.T) {
|
||||
table := defaultMountTable()
|
||||
verifyDefaultTable(t, table)
|
||||
|
|
|
@ -16,6 +16,8 @@ func (s *SystemBackend) HandleRequest(req *Request) (*Response, error) {
|
|||
return s.handleMountTable(req)
|
||||
case strings.HasPrefix(req.Path, "mount/"):
|
||||
return s.handleMountOperation(req)
|
||||
case req.Path == "remount":
|
||||
return s.handleRemount(req)
|
||||
default:
|
||||
return nil, ErrUnsupportedPath
|
||||
}
|
||||
|
@ -24,6 +26,7 @@ func (s *SystemBackend) HandleRequest(req *Request) (*Response, error) {
|
|||
func (s *SystemBackend) RootPaths() []string {
|
||||
return []string{
|
||||
"mount/*",
|
||||
"remount",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,3 +112,28 @@ func (s *SystemBackend) handleUnmount(req *Request) (*Response, error) {
|
|||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// handleRemount is used to remount a path
|
||||
func (s *SystemBackend) handleRemount(req *Request) (*Response, error) {
|
||||
// Only accept write operations
|
||||
switch req.Operation {
|
||||
case WriteOperation:
|
||||
case HelpOperation:
|
||||
return HelpResponse("remount a backend path", []string{"sys/mount/", "sys/mounts"}), nil
|
||||
default:
|
||||
return nil, ErrUnsupportedOperation
|
||||
}
|
||||
|
||||
// Get the paths
|
||||
fromPath := req.GetString("from")
|
||||
toPath := req.GetString("to")
|
||||
if fromPath == "" || toPath == "" {
|
||||
return ErrorResponse("both 'from' and 'to' path must be specified as a string"), ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Attempt remount
|
||||
if err := s.core.remountPath(fromPath, toPath); err != nil {
|
||||
return ErrorResponse(err.Error()), ErrInvalidRequest
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ func TestSystem_verifyRoot(t *testing.T) {
|
|||
|
||||
root := []string{
|
||||
"sys/mount/prod/",
|
||||
"sys/remount",
|
||||
}
|
||||
nonRoot := []string{
|
||||
"sys/mounts",
|
||||
|
@ -159,3 +160,79 @@ func TestSystem_unmount_invalid(t *testing.T) {
|
|||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "secret",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount_invalid(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "unknown",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "no matching mount at 'unknown/'" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount_system(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: WriteOperation,
|
||||
Path: "remount",
|
||||
Data: map[string]interface{}{
|
||||
"from": "sys",
|
||||
"to": "foo",
|
||||
},
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != ErrInvalidRequest {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["error"] != "cannot remount 'sys/'" {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystem_remount_help(t *testing.T) {
|
||||
s := testSystem(t)
|
||||
|
||||
req := &Request{
|
||||
Operation: HelpOperation,
|
||||
Path: "remount",
|
||||
}
|
||||
resp, err := s.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Data["help"] != "remount a backend path" {
|
||||
t.Fatalf("got: %#v", resp.Data)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue