b2af154fb4
/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.
140 lines
3.7 KiB
Go
140 lines
3.7 KiB
Go
package vault
|
|
|
|
import "strings"
|
|
|
|
// SystemBackend implements the LogicalBackend interface but is used
|
|
// to interact with the core of the system. It acts like a "procfs"
|
|
// to provide a uniform interface to vault.
|
|
type SystemBackend struct {
|
|
core *Core
|
|
}
|
|
|
|
func (s *SystemBackend) HandleRequest(req *Request) (*Response, error) {
|
|
// Switch on the path to route to the appropriate handler
|
|
switch {
|
|
case req.Path == "mounts":
|
|
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
|
|
}
|
|
}
|
|
|
|
func (s *SystemBackend) RootPaths() []string {
|
|
return []string{
|
|
"mount/*",
|
|
"remount",
|
|
}
|
|
}
|
|
|
|
// handleMountTable handles the "mounts" endpoint to provide the mount table
|
|
func (s *SystemBackend) handleMountTable(req *Request) (*Response, error) {
|
|
switch req.Operation {
|
|
case ReadOperation:
|
|
case HelpOperation:
|
|
return HelpResponse("logical backend mount table", []string{"sys/mount/"}), nil
|
|
default:
|
|
return nil, ErrUnsupportedOperation
|
|
}
|
|
|
|
s.core.mountsLock.RLock()
|
|
defer s.core.mountsLock.RUnlock()
|
|
|
|
resp := &Response{
|
|
IsSecret: false,
|
|
Data: make(map[string]interface{}),
|
|
}
|
|
for _, entry := range s.core.mounts.Entries {
|
|
info := map[string]string{
|
|
"type": entry.Type,
|
|
"description": entry.Description,
|
|
}
|
|
resp.Data[entry.Path] = info
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// handleMountOperation is used to mount or unmount a path
|
|
func (s *SystemBackend) handleMountOperation(req *Request) (*Response, error) {
|
|
switch req.Operation {
|
|
case WriteOperation:
|
|
return s.handleMount(req)
|
|
case DeleteOperation:
|
|
return s.handleUnmount(req)
|
|
case HelpOperation:
|
|
return HelpResponse("used to mount or unmount a path", []string{"sys/mounts"}), nil
|
|
default:
|
|
return nil, ErrUnsupportedOperation
|
|
}
|
|
}
|
|
|
|
// handleMount is used to mount a new path
|
|
func (s *SystemBackend) handleMount(req *Request) (*Response, error) {
|
|
suffix := strings.TrimPrefix(req.Path, "mount/")
|
|
if len(suffix) == 0 {
|
|
return ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
|
}
|
|
|
|
// Get the type and description (optionally)
|
|
logicalType := req.GetString("type")
|
|
if logicalType == "" {
|
|
return ErrorResponse("backend type must be specified as a string"), ErrInvalidRequest
|
|
}
|
|
description := req.GetString("description")
|
|
|
|
// Create the mount entry
|
|
me := &MountEntry{
|
|
Path: suffix,
|
|
Type: logicalType,
|
|
Description: description,
|
|
}
|
|
|
|
// Attempt mount
|
|
if err := s.core.Mount(me); err != nil {
|
|
return ErrorResponse(err.Error()), ErrInvalidRequest
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// handleUnmount is used to unmount a path
|
|
func (s *SystemBackend) handleUnmount(req *Request) (*Response, error) {
|
|
suffix := strings.TrimPrefix(req.Path, "mount/")
|
|
if len(suffix) == 0 {
|
|
return ErrorResponse("path cannot be blank"), ErrInvalidRequest
|
|
}
|
|
|
|
// Attempt unmount
|
|
if err := s.core.Unmount(suffix); err != nil {
|
|
return ErrorResponse(err.Error()), ErrInvalidRequest
|
|
}
|
|
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.Remount(fromPath, toPath); err != nil {
|
|
return ErrorResponse(err.Error()), ErrInvalidRequest
|
|
}
|
|
return nil, nil
|
|
}
|