1package approle 2 3import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/hashicorp/vault/helper/consts" 9 "github.com/hashicorp/vault/helper/locksutil" 10 "github.com/hashicorp/vault/helper/salt" 11 "github.com/hashicorp/vault/logical" 12 "github.com/hashicorp/vault/logical/framework" 13) 14 15const ( 16 secretIDPrefix = "secret_id/" 17 secretIDLocalPrefix = "secret_id_local/" 18 secretIDAccessorPrefix = "accessor/" 19 secretIDAccessorLocalPrefix = "accessor_local/" 20) 21 22type backend struct { 23 *framework.Backend 24 25 // The salt value to be used by the information to be accessed only 26 // by this backend. 27 salt *salt.Salt 28 saltMutex sync.RWMutex 29 30 // The view to use when creating the salt 31 view logical.Storage 32 33 // Guard to clean-up the expired SecretID entries 34 tidySecretIDCASGuard *uint32 35 36 // Locks to make changes to role entries. These will be initialized to a 37 // predefined number of locks when the backend is created, and will be 38 // indexed based on salted role names. 39 roleLocks []*locksutil.LockEntry 40 41 // Locks to make changes to the storage entries of RoleIDs generated. These 42 // will be initialized to a predefined number of locks when the backend is 43 // created, and will be indexed based on the salted RoleIDs. 44 roleIDLocks []*locksutil.LockEntry 45 46 // Locks to make changes to the storage entries of SecretIDs generated. 47 // These will be initialized to a predefined number of locks when the 48 // backend is created, and will be indexed based on the HMAC-ed SecretIDs. 49 secretIDLocks []*locksutil.LockEntry 50 51 // Locks to make changes to the storage entries of SecretIDAccessors 52 // generated. These will be initialized to a predefined number of locks 53 // when the backend is created, and will be indexed based on the 54 // SecretIDAccessors itself. 55 secretIDAccessorLocks []*locksutil.LockEntry 56 57 // secretIDListingLock is a dedicated lock for listing SecretIDAccessors 58 // for all the SecretIDs issued against an approle 59 secretIDListingLock sync.RWMutex 60 61 testTidyDelay time.Duration 62} 63 64func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { 65 b, err := Backend(conf) 66 if err != nil { 67 return nil, err 68 } 69 if err := b.Setup(ctx, conf); err != nil { 70 return nil, err 71 } 72 return b, nil 73} 74 75func Backend(conf *logical.BackendConfig) (*backend, error) { 76 // Create a backend object 77 b := &backend{ 78 view: conf.StorageView, 79 80 // Create locks to modify the registered roles 81 roleLocks: locksutil.CreateLocks(), 82 83 // Create locks to modify the generated RoleIDs 84 roleIDLocks: locksutil.CreateLocks(), 85 86 // Create locks to modify the generated SecretIDs 87 secretIDLocks: locksutil.CreateLocks(), 88 89 // Create locks to modify the generated SecretIDAccessors 90 secretIDAccessorLocks: locksutil.CreateLocks(), 91 92 tidySecretIDCASGuard: new(uint32), 93 } 94 95 // Attach the paths and secrets that are to be handled by the backend 96 b.Backend = &framework.Backend{ 97 // Register a periodic function that deletes the expired SecretID entries 98 PeriodicFunc: b.periodicFunc, 99 Help: backendHelp, 100 AuthRenew: b.pathLoginRenew, 101 PathsSpecial: &logical.Paths{ 102 Unauthenticated: []string{ 103 "login", 104 }, 105 LocalStorage: []string{ 106 secretIDLocalPrefix, 107 secretIDAccessorLocalPrefix, 108 }, 109 }, 110 Paths: framework.PathAppend( 111 rolePaths(b), 112 []*framework.Path{ 113 pathLogin(b), 114 pathTidySecretID(b), 115 }, 116 ), 117 Invalidate: b.invalidate, 118 BackendType: logical.TypeCredential, 119 } 120 return b, nil 121} 122 123func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) { 124 b.saltMutex.RLock() 125 if b.salt != nil { 126 defer b.saltMutex.RUnlock() 127 return b.salt, nil 128 } 129 b.saltMutex.RUnlock() 130 b.saltMutex.Lock() 131 defer b.saltMutex.Unlock() 132 if b.salt != nil { 133 return b.salt, nil 134 } 135 salt, err := salt.NewSalt(ctx, b.view, &salt.Config{ 136 HashFunc: salt.SHA256Hash, 137 Location: salt.DefaultLocation, 138 }) 139 if err != nil { 140 return nil, err 141 } 142 b.salt = salt 143 return salt, nil 144} 145 146func (b *backend) invalidate(_ context.Context, key string) { 147 switch key { 148 case salt.DefaultLocation: 149 b.saltMutex.Lock() 150 defer b.saltMutex.Unlock() 151 b.salt = nil 152 } 153} 154 155// periodicFunc of the backend will be invoked once a minute by the RollbackManager. 156// RoleRole backend utilizes this function to delete expired SecretID entries. 157// This could mean that the SecretID may live in the backend upto 1 min after its 158// expiration. The deletion of SecretIDs are not security sensitive and it is okay 159// to delay the removal of SecretIDs by a minute. 160func (b *backend) periodicFunc(ctx context.Context, req *logical.Request) error { 161 // Initiate clean-up of expired SecretID entries 162 if b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary|consts.ReplicationPerformanceStandby) { 163 b.tidySecretID(ctx, req) 164 } 165 return nil 166} 167 168const backendHelp = ` 169Any registered Role can authenticate itself with Vault. The credentials 170depends on the constraints that are set on the Role. One common required 171credential is the 'role_id' which is a unique identifier of the Role. 172It can be retrieved from the 'role/<appname>/role-id' endpoint. 173 174The default constraint configuration is 'bind_secret_id', which requires 175the credential 'secret_id' to be presented during login. Refer to the 176documentation for other types of constraints.` 177