2 * Copyright 2018 Intel Corporation, Inc
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 uuid "github.com/hashicorp/go-uuid"
21 vaultapi "github.com/hashicorp/vault/api"
30 // Vault is the main Struct used in Backend to initialize the struct
39 vaultClient *vaultapi.Client
41 vaultTempTokenTTL time.Time
45 // Init will initialize the vault connection
46 // It will also create the initial policy if it does not exist
47 // TODO: Check to see if we need to wait for vault to be running
48 func (v *Vault) Init() error {
49 vaultCFG := vaultapi.DefaultConfig()
50 vaultCFG.Address = v.vaultAddress
51 client, err := vaultapi.NewClient(vaultCFG)
57 v.initRoleDone = false
58 v.policyName = "smsvaultpolicy"
59 v.vaultClient = client
64 //print error message and try to initrole later
70 // GetStatus returns the current seal status of vault
71 func (v *Vault) GetStatus() (bool, error) {
72 sys := v.vaultClient.Sys()
73 sealStatus, err := sys.SealStatus()
78 return sealStatus.Sealed, nil
81 // GetSecret returns a secret mounted on a particular domain name
82 // The secret itself is referenced via its name which translates to
83 // a mount path in vault
84 func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
87 return Secret{}, errors.New("Token check returned error: " + err.Error())
90 dom = v.vaultMount + "/" + dom
92 sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
94 return Secret{}, errors.New("Unable to read Secret at provided path")
97 // sec and err are nil in the case where a path does not exist
99 return Secret{}, errors.New("Secret not found at the provided path")
102 return Secret{Name: name, Values: sec.Data}, nil
105 // ListSecret returns a list of secret names on a particular domain
106 // The values of the secret are not returned
107 func (v *Vault) ListSecret(dom string) ([]string, error) {
108 err := v.checkToken()
110 return nil, errors.New("Token check returned error: " + err.Error())
113 dom = v.vaultMount + "/" + dom
115 sec, err := v.vaultClient.Logical().List(dom)
117 return nil, errors.New("Unable to read Secret at provided path")
120 // sec and err are nil in the case where a path does not exist
122 return nil, errors.New("Secret not found at the provided path")
125 val, ok := sec.Data["keys"].([]interface{})
127 return nil, errors.New("Secret not found at the provided path")
130 retval := make([]string, len(val))
131 for i, v := range val {
132 retval[i] = fmt.Sprint(v)
138 // CreateSecretDomain mounts the kv backend on a path with the given name
139 func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
140 // Check if token is still valid
141 err := v.checkToken()
143 return SecretDomain{}, err
146 name = strings.TrimSpace(name)
147 mountPath := v.vaultMount + "/" + name
148 mountInput := &vaultapi.MountInput{
150 Description: "Mount point for domain: " + name,
153 Config: vaultapi.MountConfigInput{},
156 err = v.vaultClient.Sys().Mount(mountPath, mountInput)
158 return SecretDomain{}, err
161 uuid, _ := uuid.GenerateUUID()
162 return SecretDomain{uuid, name}, nil
165 // CreateSecret creates a secret mounted on a particular domain name
166 // The secret itself is mounted on a path specified by name
167 func (v *Vault) CreateSecret(dom string, sec Secret) error {
168 err := v.checkToken()
170 return errors.New("Token checking returned an error" + err.Error())
173 dom = v.vaultMount + "/" + dom
175 // Vault return is empty on successful write
176 // TODO: Check if values is not empty
177 _, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
179 return errors.New("Unable to create Secret at provided path")
185 // DeleteSecretDomain deletes a secret domain which translates to
186 // an unmount operation on the given path in Vault
187 func (v *Vault) DeleteSecretDomain(name string) error {
188 err := v.checkToken()
193 name = strings.TrimSpace(name)
194 mountPath := v.vaultMount + "/" + name
196 err = v.vaultClient.Sys().Unmount(mountPath)
198 return errors.New("Unable to delete domain specified")
204 // DeleteSecret deletes a secret mounted on the path provided
205 func (v *Vault) DeleteSecret(dom string, name string) error {
206 err := v.checkToken()
208 return errors.New("Token checking returned an error" + err.Error())
211 dom = v.vaultMount + "/" + dom
213 // Vault return is empty on successful delete
214 _, err = v.vaultClient.Logical().Delete(dom + "/" + name)
216 return errors.New("Unable to delete Secret at provided path")
222 // initRole is called only once during the service bring up
223 func (v *Vault) initRole() error {
224 // Use the root token once here
225 v.vaultClient.SetToken(v.vaultToken)
226 defer v.vaultClient.ClearToken()
228 rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
229 path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
230 err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
232 return errors.New("Unable to create policy for approle creation")
235 rName := v.vaultMount + "-role"
236 data := map[string]interface{}{
238 "policies": [2]string{"default", v.policyName},
241 //Check if applrole is mounted
242 authMounts, err := v.vaultClient.Sys().ListAuth()
244 return errors.New("Unable to get mounted auth backends")
247 approleMounted := false
248 for k, v := range authMounts {
249 if v.Type == "approle" && k == "approle/" {
250 approleMounted = true
255 // Mount approle in case its not already mounted
257 v.vaultClient.Sys().EnableAuth("approle", "approle", "")
261 v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
262 sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
264 return errors.New("Unable to create role ID for approle")
266 v.roleID = sec.Data["role_id"].(string)
268 // Create a secret-id to go with it
269 sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
270 map[string]interface{}{})
272 return errors.New("Unable to create secret ID for role")
275 v.secretID = sec.Data["secret_id"].(string)
276 v.initRoleDone = true
280 // Function checkToken() gets called multiple times to create
282 func (v *Vault) checkToken() error {
284 defer v.tokenLock.Unlock()
286 // Init Role if it is not yet done
287 if v.initRoleDone == false {
294 // Return immediately if token still has life
295 if v.vaultClient.Token() != "" &&
296 time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
300 // Create a temporary token using our roleID and secretID
301 out, err := v.vaultClient.Logical().Write("auth/approle/login",
302 map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
307 tok, err := out.TokenID()
309 v.vaultTempTokenTTL = time.Now()
310 v.vaultClient.SetToken(tok)