2015-03-18 22:30:31 +00:00
package vault
2015-03-18 22:46:07 +00:00
import (
"errors"
"fmt"
2015-03-19 16:54:57 +00:00
"strings"
2015-03-18 22:46:07 +00:00
2015-12-16 17:56:20 +00:00
"github.com/hashicorp/go-uuid"
2016-07-06 16:25:40 +00:00
"github.com/hashicorp/vault/helper/jsonutil"
2015-03-31 01:07:05 +00:00
"github.com/hashicorp/vault/logical"
2015-03-18 22:46:07 +00:00
)
const (
// coreAuthConfigPath is used to store the auth configuration.
// Auth configuration is protected within the Vault itself, which means it
// can only be viewed or modified after an unseal.
coreAuthConfigPath = "core/auth"
2017-02-17 04:09:39 +00:00
// coreLocalAuthConfigPath is used to store credential configuration for
// local (non-replicated) mounts
coreLocalAuthConfigPath = "core/local-auth"
2015-03-18 22:46:07 +00:00
// credentialBarrierPrefix is the prefix to the UUID used in the
// barrier view for the credential backends.
credentialBarrierPrefix = "auth/"
2015-03-19 16:56:39 +00:00
// credentialRoutePrefix is the mount prefix used for the router
credentialRoutePrefix = "auth/"
2016-05-26 17:38:51 +00:00
// credentialTableType is the value we expect to find for the credential
// table and corresponding entries
credentialTableType = "auth"
2015-03-18 22:46:07 +00:00
)
var (
2015-11-02 16:01:00 +00:00
// errLoadAuthFailed if loadCredentials encounters an error
2015-09-09 19:42:29 +00:00
errLoadAuthFailed = errors . New ( "failed to setup auth table" )
2017-04-24 19:15:50 +00:00
// credentialAliases maps old backend names to new backend names, allowing us
// to move/rename backends but maintain backwards compatibility
credentialAliases = map [ string ] string { "aws-ec2" : "aws" }
2015-03-18 22:46:07 +00:00
)
2015-03-19 02:36:17 +00:00
// enableCredential is used to enable a new credential backend
2017-05-09 21:51:09 +00:00
func ( c * Core ) enableCredential ( entry * MountEntry ) error {
2015-04-03 21:24:00 +00:00
// Ensure we end the path in a slash
if ! strings . HasSuffix ( entry . Path , "/" ) {
entry . Path += "/"
}
2015-03-19 02:36:17 +00:00
// Ensure there is a name
2015-04-03 21:24:00 +00:00
if entry . Path == "/" {
2015-03-19 16:54:57 +00:00
return fmt . Errorf ( "backend path must be specified" )
}
2015-03-19 02:36:17 +00:00
2015-11-11 16:44:07 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2015-03-19 02:36:17 +00:00
// Look for matching name
for _ , ent := range c . auth . Entries {
2015-04-03 21:24:00 +00:00
switch {
// Existing is oauth/github/ new is oauth/ or
// existing is oauth/ and new is oauth/github/
case strings . HasPrefix ( ent . Path , entry . Path ) :
fallthrough
case strings . HasPrefix ( entry . Path , ent . Path ) :
2015-08-13 17:17:04 +00:00
return logical . CodedError ( 409 , "path is already in use" )
2015-03-19 02:36:17 +00:00
}
}
// Ensure the token backend is a singleton
if entry . Type == "token" {
return fmt . Errorf ( "token credential backend cannot be instantiated" )
}
2017-01-17 18:02:29 +00:00
if match := c . router . MatchingMount ( credentialRoutePrefix + entry . Path ) ; match != "" {
return logical . CodedError ( 409 , fmt . Sprintf ( "existing mount at %s" , match ) )
}
2015-03-19 02:36:17 +00:00
// Generate a new UUID and view
2017-02-17 04:09:39 +00:00
if entry . UUID == "" {
entryUUID , err := uuid . GenerateUUID ( )
if err != nil {
return err
}
entry . UUID = entryUUID
2016-01-13 18:40:08 +00:00
}
2017-06-26 17:14:36 +00:00
if entry . Accessor == "" {
accessor , err := c . generateMountAccessor ( "auth_" + entry . Type )
if err != nil {
return err
}
entry . Accessor = accessor
}
2017-02-17 04:09:39 +00:00
viewPath := credentialBarrierPrefix + entry . UUID + "/"
view := NewBarrierView ( c . barrier , viewPath )
sysView := c . mountEntrySysView ( entry )
2015-03-19 02:36:17 +00:00
2015-07-01 00:30:43 +00:00
// Create the new backend
2017-02-17 04:09:39 +00:00
backend , err := c . newCredentialBackend ( entry . Type , sysView , view , nil )
2015-07-01 00:30:43 +00:00
if err != nil {
return err
}
2017-03-04 21:35:41 +00:00
if backend == nil {
return fmt . Errorf ( "nil backend returned from %q factory" , entry . Type )
}
2015-07-01 00:30:43 +00:00
2017-05-09 21:51:09 +00:00
if err := backend . Initialize ( ) ; err != nil {
return err
2017-02-17 04:09:39 +00:00
}
2015-03-19 02:36:17 +00:00
// Update the auth table
2016-09-13 15:50:14 +00:00
newTable := c . auth . shallowClone ( )
2015-03-19 02:36:17 +00:00
newTable . Entries = append ( newTable . Entries , entry )
2017-03-02 19:37:59 +00:00
if err := c . persistAuth ( newTable , entry . Local ) ; err != nil {
2015-03-19 02:36:17 +00:00
return errors . New ( "failed to update auth table" )
}
2015-11-11 16:44:07 +00:00
2015-03-19 02:36:17 +00:00
c . auth = newTable
2017-01-17 20:15:28 +00:00
path := credentialRoutePrefix + entry . Path
if err := c . router . Mount ( backend , path , entry , view ) ; err != nil {
return err
}
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
c . logger . Info ( "core: enabled credential backend" , "path" , entry . Path , "type" , entry . Type )
}
2015-03-19 02:36:17 +00:00
return nil
}
2016-09-19 17:02:25 +00:00
// disableCredential is used to disable an existing credential backend; the
// boolean indicates if it existed
func ( c * Core ) disableCredential ( path string ) ( bool , error ) {
2015-04-03 21:24:00 +00:00
// Ensure we end the path in a slash
if ! strings . HasSuffix ( path , "/" ) {
path += "/"
}
2015-03-19 02:36:17 +00:00
// Ensure the token backend is not affected
2015-04-03 21:24:00 +00:00
if path == "token/" {
2016-09-19 17:02:25 +00:00
return true , fmt . Errorf ( "token credential backend cannot be disabled" )
2015-03-19 02:36:17 +00:00
}
2015-04-03 23:07:45 +00:00
// Store the view for this backend
fullPath := credentialRoutePrefix + path
2015-09-15 16:27:22 +00:00
view := c . router . MatchingStorageView ( fullPath )
2015-04-03 23:09:06 +00:00
if view == nil {
2017-02-17 04:09:39 +00:00
return false , fmt . Errorf ( "no matching backend %s" , fullPath )
2015-04-03 23:09:06 +00:00
}
2015-04-03 23:07:45 +00:00
// Mark the entry as tainted
if err := c . taintCredEntry ( path ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-04-03 23:07:45 +00:00
}
// Taint the router path to prevent routing
if err := c . router . Taint ( fullPath ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-04-03 23:07:45 +00:00
}
// Revoke credentials from this path
if err := c . expiration . RevokePrefix ( fullPath ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-04-03 23:07:45 +00:00
}
2017-03-04 21:35:41 +00:00
// Call cleanup function if it exists
backend := c . router . MatchingBackend ( fullPath )
if backend != nil {
backend . Cleanup ( )
}
2015-04-03 23:07:45 +00:00
// Unmount the backend
if err := c . router . Unmount ( fullPath ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-04-03 23:07:45 +00:00
}
// Clear the data in the view
if view != nil {
2017-01-06 20:42:18 +00:00
if err := logical . ClearView ( view ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-03-19 02:36:17 +00:00
}
}
2015-04-03 23:07:45 +00:00
// Remove the mount table entry
if err := c . removeCredEntry ( path ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , err
2015-04-03 23:07:45 +00:00
}
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
c . logger . Info ( "core: disabled credential backend" , "path" , path )
}
2016-09-19 17:02:25 +00:00
return true , nil
2015-04-03 23:07:45 +00:00
}
// removeCredEntry is used to remove an entry in the auth table
func ( c * Core ) removeCredEntry ( path string ) error {
2017-01-17 18:02:29 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2015-04-03 23:07:45 +00:00
// Taint the entry from the auth table
2016-09-13 15:50:14 +00:00
newTable := c . auth . shallowClone ( )
2017-03-02 19:37:59 +00:00
entry := newTable . remove ( path )
if entry == nil {
c . logger . Error ( "core: nil entry found removing entry in auth table" , "path" , path )
return logical . CodedError ( 500 , "failed to remove entry in auth table" )
}
2015-04-03 23:07:45 +00:00
// Update the auth table
2017-03-02 19:37:59 +00:00
if err := c . persistAuth ( newTable , entry . Local ) ; err != nil {
2015-04-03 23:07:45 +00:00
return errors . New ( "failed to update auth table" )
}
2015-11-11 16:44:07 +00:00
2015-04-03 23:07:45 +00:00
c . auth = newTable
2015-11-11 16:44:07 +00:00
2015-04-03 23:07:45 +00:00
return nil
}
// taintCredEntry is used to mark an entry in the auth table as tainted
func ( c * Core ) taintCredEntry ( path string ) error {
2017-01-17 18:02:29 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2015-04-03 23:07:45 +00:00
// Taint the entry from the auth table
2015-11-11 16:44:07 +00:00
// We do this on the original since setting the taint operates
// on the entries which a shallow clone shares anyways
2017-03-02 19:37:59 +00:00
entry := c . auth . setTaint ( path , true )
2015-04-03 23:07:45 +00:00
2015-03-19 02:36:17 +00:00
// Ensure there was a match
2017-03-02 19:37:59 +00:00
if entry == nil {
2015-03-19 02:36:17 +00:00
return fmt . Errorf ( "no matching backend" )
}
// Update the auth table
2017-03-02 19:37:59 +00:00
if err := c . persistAuth ( c . auth , entry . Local ) ; err != nil {
2015-03-19 02:36:17 +00:00
return errors . New ( "failed to update auth table" )
}
2015-11-11 16:44:07 +00:00
2015-03-19 02:36:17 +00:00
return nil
}
2015-03-18 22:46:07 +00:00
// loadCredentials is invoked as part of postUnseal to load the auth table
func ( c * Core ) loadCredentials ( ) error {
2015-11-11 16:44:07 +00:00
authTable := & MountTable { }
2017-02-17 04:09:39 +00:00
localAuthTable := & MountTable { }
2015-03-18 22:46:07 +00:00
// Load the existing mount table
raw , err := c . barrier . Get ( coreAuthConfigPath )
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to read auth table" , "error" , err )
2015-09-09 19:42:29 +00:00
return errLoadAuthFailed
2015-03-18 22:46:07 +00:00
}
2017-02-17 04:09:39 +00:00
rawLocal , err := c . barrier . Get ( coreLocalAuthConfigPath )
if err != nil {
c . logger . Error ( "core: failed to read local auth table" , "error" , err )
return errLoadAuthFailed
}
2015-11-11 16:44:07 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2015-03-18 22:46:07 +00:00
if raw != nil {
2016-07-06 16:25:40 +00:00
if err := jsonutil . DecodeJSON ( raw . Value , authTable ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to decode auth table" , "error" , err )
2015-09-09 19:42:29 +00:00
return errLoadAuthFailed
2015-03-18 22:46:07 +00:00
}
2015-11-11 16:44:07 +00:00
c . auth = authTable
2015-03-18 22:46:07 +00:00
}
2017-02-17 04:09:39 +00:00
if rawLocal != nil {
if err := jsonutil . DecodeJSON ( rawLocal . Value , localAuthTable ) ; err != nil {
c . logger . Error ( "core: failed to decode local auth table" , "error" , err )
return errLoadAuthFailed
}
c . auth . Entries = append ( c . auth . Entries , localAuthTable . Entries ... )
}
2015-03-18 22:46:07 +00:00
// Done if we have restored the auth table
if c . auth != nil {
2016-05-26 17:38:51 +00:00
needPersist := false
// Upgrade to typed auth table
if c . auth . Type == "" {
c . auth . Type = credentialTableType
needPersist = true
}
// Upgrade to table-scoped entries
for _ , entry := range c . auth . Entries {
if entry . Table == "" {
entry . Table = c . auth . Type
needPersist = true
}
2017-06-26 17:14:36 +00:00
if entry . Accessor == "" {
accessor , err := c . generateMountAccessor ( "auth_" + entry . Type )
if err != nil {
return err
}
entry . Accessor = accessor
needPersist = true
}
2016-05-26 17:38:51 +00:00
}
2017-03-02 19:37:59 +00:00
if ! needPersist {
return nil
2016-05-26 17:38:51 +00:00
}
2017-03-02 19:37:59 +00:00
} else {
2017-06-26 17:14:36 +00:00
c . auth = c . defaultAuthTable ( )
2015-03-18 22:46:07 +00:00
}
2017-03-02 19:37:59 +00:00
if err := c . persistAuth ( c . auth , false ) ; err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to persist auth table" , "error" , err )
2015-09-09 19:42:29 +00:00
return errLoadAuthFailed
2015-03-18 22:46:07 +00:00
}
return nil
}
// persistAuth is used to persist the auth table after modification
2017-03-02 19:37:59 +00:00
func ( c * Core ) persistAuth ( table * MountTable , localOnly bool ) error {
2016-05-26 17:38:51 +00:00
if table . Type != credentialTableType {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: given table to persist has wrong type" , "actual_type" , table . Type , "expected_type" , credentialTableType )
2016-05-26 17:38:51 +00:00
return fmt . Errorf ( "invalid table type given, not persisting" )
}
for _ , entry := range table . Entries {
if entry . Table != table . Type {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: given entry to persist in auth table has wrong table value" , "path" , entry . Path , "entry_table_type" , entry . Table , "actual_type" , table . Type )
2016-05-26 17:38:51 +00:00
return fmt . Errorf ( "invalid auth entry found, not persisting" )
}
}
2017-02-17 04:09:39 +00:00
nonLocalAuth := & MountTable {
Type : credentialTableType ,
}
localAuth := & MountTable {
Type : credentialTableType ,
}
for _ , entry := range table . Entries {
if entry . Local {
localAuth . Entries = append ( localAuth . Entries , entry )
} else {
nonLocalAuth . Entries = append ( nonLocalAuth . Entries , entry )
}
}
2017-03-02 19:37:59 +00:00
if ! localOnly {
// Marshal the table
compressedBytes , err := jsonutil . EncodeJSONAndCompress ( nonLocalAuth , nil )
if err != nil {
c . logger . Error ( "core: failed to encode and/or compress auth table" , "error" , err )
return err
}
2015-03-18 22:46:07 +00:00
2017-03-02 19:37:59 +00:00
// Create an entry
entry := & Entry {
Key : coreAuthConfigPath ,
Value : compressedBytes ,
}
2015-03-18 22:46:07 +00:00
2017-03-02 19:37:59 +00:00
// Write to the physical backend
if err := c . barrier . Put ( entry ) ; err != nil {
c . logger . Error ( "core: failed to persist auth table" , "error" , err )
return err
}
2015-03-18 22:46:07 +00:00
}
2017-02-17 04:09:39 +00:00
// Repeat with local auth
2017-03-02 19:37:59 +00:00
compressedBytes , err := jsonutil . EncodeJSONAndCompress ( localAuth , nil )
2017-02-17 04:09:39 +00:00
if err != nil {
c . logger . Error ( "core: failed to encode and/or compress local auth table" , "error" , err )
return err
}
2017-03-02 19:37:59 +00:00
entry := & Entry {
2017-02-17 04:09:39 +00:00
Key : coreLocalAuthConfigPath ,
Value : compressedBytes ,
}
if err := c . barrier . Put ( entry ) ; err != nil {
c . logger . Error ( "core: failed to persist local auth table" , "error" , err )
return err
}
2015-03-18 22:46:07 +00:00
return nil
}
// setupCredentials is invoked after we've loaded the auth table to
2015-03-18 22:30:31 +00:00
// initialize the credential backends and setup the router
func ( c * Core ) setupCredentials ( ) error {
2015-03-31 01:07:05 +00:00
var backend logical . Backend
2015-03-18 22:46:07 +00:00
var view * BarrierView
var err error
2016-05-25 21:53:45 +00:00
var persistNeeded bool
2015-11-11 16:44:07 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2015-03-18 22:46:07 +00:00
for _ , entry := range c . auth . Entries {
2016-05-25 21:53:45 +00:00
// Work around some problematic code that existed in master for a while
if strings . HasPrefix ( entry . Path , credentialRoutePrefix ) {
entry . Path = strings . TrimPrefix ( entry . Path , credentialRoutePrefix )
persistNeeded = true
}
2015-07-01 00:30:43 +00:00
// Create a barrier view using the UUID
2017-02-17 04:09:39 +00:00
viewPath := credentialBarrierPrefix + entry . UUID + "/"
view = NewBarrierView ( c . barrier , viewPath )
sysView := c . mountEntrySysView ( entry )
2015-07-01 00:30:43 +00:00
2015-03-18 22:46:07 +00:00
// Initialize the backend
2017-02-17 04:09:39 +00:00
backend , err = c . newCredentialBackend ( entry . Type , sysView , view , nil )
2015-03-18 22:46:07 +00:00
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to create credential entry" , "path" , entry . Path , "error" , err )
2015-09-09 19:42:29 +00:00
return errLoadAuthFailed
2015-03-18 22:46:07 +00:00
}
2017-03-04 21:35:41 +00:00
if backend == nil {
return fmt . Errorf ( "nil backend returned from %q factory" , entry . Type )
}
2015-03-18 22:46:07 +00:00
2017-02-17 04:09:39 +00:00
if err := backend . Initialize ( ) ; err != nil {
return err
}
2015-03-18 22:46:07 +00:00
// Mount the backend
2015-04-03 21:24:00 +00:00
path := credentialRoutePrefix + entry . Path
2015-09-04 20:58:12 +00:00
err = c . router . Mount ( backend , path , entry , view )
2015-03-18 22:46:07 +00:00
if err != nil {
2016-08-19 20:45:17 +00:00
c . logger . Error ( "core: failed to mount auth entry" , "path" , entry . Path , "error" , err )
2015-09-09 19:42:29 +00:00
return errLoadAuthFailed
2015-03-18 22:46:07 +00:00
}
2015-03-23 20:41:05 +00:00
2015-04-03 23:07:45 +00:00
// Ensure the path is tainted if set in the mount table
if entry . Tainted {
c . router . Taint ( path )
}
2015-03-23 20:41:05 +00:00
// Check if this is the token store
if entry . Type == "token" {
c . tokenStore = backend . ( * TokenStore )
2015-09-15 16:27:22 +00:00
// this is loaded *after* the normal mounts, including cubbyhole
2015-09-21 21:54:36 +00:00
c . router . tokenStoreSalt = c . tokenStore . salt
2015-09-15 17:49:53 +00:00
c . tokenStore . cubbyholeBackend = c . router . MatchingBackend ( "cubbyhole/" ) . ( * CubbyholeBackend )
2015-03-23 20:41:05 +00:00
}
2015-03-18 22:46:07 +00:00
}
2016-05-25 21:53:45 +00:00
if persistNeeded {
2017-03-02 19:37:59 +00:00
return c . persistAuth ( c . auth , false )
2016-05-25 21:53:45 +00:00
}
2015-03-18 22:30:31 +00:00
return nil
}
// teardownCredentials is used before we seal the vault to reset the credential
// backends to their unloaded state. This is reversed by loadCredentials.
func ( c * Core ) teardownCredentials ( ) error {
2015-11-11 16:44:07 +00:00
c . authLock . Lock ( )
defer c . authLock . Unlock ( )
2016-10-28 19:32:32 +00:00
if c . auth != nil {
authTable := c . auth . shallowClone ( )
for _ , e := range authTable . Entries {
2017-03-04 21:35:41 +00:00
backend := c . router . MatchingBackend ( credentialRoutePrefix + e . Path )
if backend != nil {
backend . Cleanup ( )
2016-10-28 19:32:32 +00:00
}
}
}
2015-03-18 22:46:07 +00:00
c . auth = nil
2015-03-23 20:41:05 +00:00
c . tokenStore = nil
2015-03-18 22:30:31 +00:00
return nil
}
2015-03-18 22:46:07 +00:00
// newCredentialBackend is used to create and configure a new credential backend by name
2015-03-31 01:07:05 +00:00
func ( c * Core ) newCredentialBackend (
2015-09-04 20:58:12 +00:00
t string , sysView logical . SystemView , view logical . Storage , conf map [ string ] string ) ( logical . Backend , error ) {
2017-04-24 19:15:50 +00:00
if alias , ok := credentialAliases [ t ] ; ok {
t = alias
}
2015-03-18 22:46:07 +00:00
f , ok := c . credentialBackends [ t ]
if ! ok {
return nil , fmt . Errorf ( "unknown backend type: %s" , t )
}
2015-03-31 01:07:05 +00:00
2015-07-01 00:30:43 +00:00
config := & logical . BackendConfig {
2015-09-09 19:42:29 +00:00
StorageView : view ,
Logger : c . logger ,
Config : conf ,
2015-09-10 01:58:09 +00:00
System : sysView ,
2015-07-01 00:30:43 +00:00
}
b , err := f ( config )
if err != nil {
return nil , err
}
2015-09-04 20:58:12 +00:00
2015-07-01 00:30:43 +00:00
return b , nil
2015-03-18 22:46:07 +00:00
}
// defaultAuthTable creates a default auth table
2017-06-26 17:14:36 +00:00
func ( c * Core ) defaultAuthTable ( ) * MountTable {
2016-05-26 17:38:51 +00:00
table := & MountTable {
Type : credentialTableType ,
}
2016-01-13 18:40:08 +00:00
tokenUUID , err := uuid . GenerateUUID ( )
if err != nil {
panic ( fmt . Sprintf ( "could not generate UUID for default auth table token entry: %v" , err ) )
}
2017-06-26 17:14:36 +00:00
tokenAccessor , err := c . generateMountAccessor ( "auth_token" )
if err != nil {
panic ( fmt . Sprintf ( "could not generate accessor for default auth table token entry: %v" , err ) )
}
2015-03-19 16:54:57 +00:00
tokenAuth := & MountEntry {
2016-05-26 17:38:51 +00:00
Table : credentialTableType ,
2015-04-03 21:24:00 +00:00
Path : "token/" ,
2015-03-18 22:46:07 +00:00
Type : "token" ,
Description : "token based credentials" ,
2016-01-13 18:40:08 +00:00
UUID : tokenUUID ,
2017-06-26 17:14:36 +00:00
Accessor : tokenAccessor ,
2015-03-18 22:46:07 +00:00
}
table . Entries = append ( table . Entries , tokenAuth )
return table
}