vault: support remount

This commit is contained in:
Armon Dadgar 2015-03-12 12:09:30 -07:00
parent 3ed3e23d93
commit b17607e51f
4 changed files with 203 additions and 0 deletions

View File

@ -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{}

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}
}