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