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"
31 // Vault is the main Struct used in Backend to initialize the struct
40 vaultClient *vaultapi.Client
42 vaultTempTokenTTL time.Time
46 // Init will initialize the vault connection
47 // It will also create the initial policy if it does not exist
48 // TODO: Check to see if we need to wait for vault to be running
49 func (v *Vault) Init() error {
50 vaultCFG := vaultapi.DefaultConfig()
51 vaultCFG.Address = v.vaultAddress
52 client, err := vaultapi.NewClient(vaultCFG)
54 smslogger.WriteError(err.Error())
55 return errors.New("Unable to create new vault client")
59 v.initRoleDone = false
60 v.policyName = "smsvaultpolicy"
61 v.vaultClient = client
66 smslogger.WriteError(err.Error())
67 smslogger.WriteInfo("InitRole will try again later")
73 // GetStatus returns the current seal status of vault
74 func (v *Vault) GetStatus() (bool, error) {
75 sys := v.vaultClient.Sys()
76 sealStatus, err := sys.SealStatus()
78 smslogger.WriteError(err.Error())
79 return false, errors.New("Error getting status")
82 return sealStatus.Sealed, nil
85 // Unseal is a passthrough API that allows any
86 // unseal or initialization processes for the backend
87 func (v *Vault) Unseal(shard string) error {
88 sys := v.vaultClient.Sys()
89 _, err := sys.Unseal(shard)
91 smslogger.WriteError(err.Error())
92 return errors.New("Unable to execute unseal operation with specified shard")
98 // GetSecret returns a secret mounted on a particular domain name
99 // The secret itself is referenced via its name which translates to
100 // a mount path in vault
101 func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
102 err := v.checkToken()
104 smslogger.WriteError(err.Error())
105 return Secret{}, errors.New("Token check failed")
108 dom = v.vaultMount + "/" + dom
110 sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
112 smslogger.WriteError(err.Error())
113 return Secret{}, errors.New("Unable to read Secret at provided path")
116 // sec and err are nil in the case where a path does not exist
118 smslogger.WriteWarn("Vault read was empty. Invalid Path")
119 return Secret{}, errors.New("Secret not found at the provided path")
122 return Secret{Name: name, Values: sec.Data}, nil
125 // ListSecret returns a list of secret names on a particular domain
126 // The values of the secret are not returned
127 func (v *Vault) ListSecret(dom string) ([]string, error) {
128 err := v.checkToken()
130 smslogger.WriteError(err.Error())
131 return nil, errors.New("Token check failed")
134 dom = v.vaultMount + "/" + dom
136 sec, err := v.vaultClient.Logical().List(dom)
138 smslogger.WriteError(err.Error())
139 return nil, errors.New("Unable to read Secret at provided path")
142 // sec and err are nil in the case where a path does not exist
144 smslogger.WriteWarn("Vaultclient returned empty data")
145 return nil, errors.New("Secret not found at the provided path")
148 val, ok := sec.Data["keys"].([]interface{})
150 smslogger.WriteError("Secret not found at the provided path")
151 return nil, errors.New("Secret not found at the provided path")
154 retval := make([]string, len(val))
155 for i, v := range val {
156 retval[i] = fmt.Sprint(v)
162 // CreateSecretDomain mounts the kv backend on a path with the given name
163 func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
164 // Check if token is still valid
165 err := v.checkToken()
167 smslogger.WriteError(err.Error())
168 return SecretDomain{}, errors.New("Token Check failed")
171 name = strings.TrimSpace(name)
172 mountPath := v.vaultMount + "/" + name
173 mountInput := &vaultapi.MountInput{
175 Description: "Mount point for domain: " + name,
178 Config: vaultapi.MountConfigInput{},
181 err = v.vaultClient.Sys().Mount(mountPath, mountInput)
183 smslogger.WriteError(err.Error())
184 return SecretDomain{}, errors.New("Unable to create Secret Domain")
187 uuid, _ := uuid.GenerateUUID()
188 return SecretDomain{uuid, name}, nil
191 // CreateSecret creates a secret mounted on a particular domain name
192 // The secret itself is mounted on a path specified by name
193 func (v *Vault) CreateSecret(dom string, sec Secret) error {
194 err := v.checkToken()
196 smslogger.WriteError(err.Error())
197 return errors.New("Token check failed")
200 dom = v.vaultMount + "/" + dom
202 // Vault return is empty on successful write
203 // TODO: Check if values is not empty
204 _, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
206 smslogger.WriteError(err.Error())
207 return errors.New("Unable to create Secret at provided path")
213 // DeleteSecretDomain deletes a secret domain which translates to
214 // an unmount operation on the given path in Vault
215 func (v *Vault) DeleteSecretDomain(name string) error {
216 err := v.checkToken()
218 smslogger.WriteError(err.Error())
219 return errors.New("Token Check Failed")
222 name = strings.TrimSpace(name)
223 mountPath := v.vaultMount + "/" + name
225 err = v.vaultClient.Sys().Unmount(mountPath)
227 smslogger.WriteError(err.Error())
228 return errors.New("Unable to delete domain specified")
234 // DeleteSecret deletes a secret mounted on the path provided
235 func (v *Vault) DeleteSecret(dom string, name string) error {
236 err := v.checkToken()
238 smslogger.WriteError(err.Error())
239 return errors.New("Token check failed")
242 dom = v.vaultMount + "/" + dom
244 // Vault return is empty on successful delete
245 _, err = v.vaultClient.Logical().Delete(dom + "/" + name)
247 smslogger.WriteError(err.Error())
248 return errors.New("Unable to delete Secret at provided path")
254 // initRole is called only once during the service bring up
255 func (v *Vault) initRole() error {
256 // Use the root token once here
257 v.vaultClient.SetToken(v.vaultToken)
258 defer v.vaultClient.ClearToken()
260 rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
261 path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
262 err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
264 smslogger.WriteError(err.Error())
265 return errors.New("Unable to create policy for approle creation")
268 rName := v.vaultMount + "-role"
269 data := map[string]interface{}{
271 "policies": [2]string{"default", v.policyName},
274 //Check if applrole is mounted
275 authMounts, err := v.vaultClient.Sys().ListAuth()
277 smslogger.WriteError(err.Error())
278 return errors.New("Unable to get mounted auth backends")
281 approleMounted := false
282 for k, v := range authMounts {
283 if v.Type == "approle" && k == "approle/" {
284 approleMounted = true
289 // Mount approle in case its not already mounted
291 v.vaultClient.Sys().EnableAuth("approle", "approle", "")
295 v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
296 sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
298 smslogger.WriteError(err.Error())
299 return errors.New("Unable to create role ID for approle")
301 v.roleID = sec.Data["role_id"].(string)
303 // Create a secret-id to go with it
304 sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
305 map[string]interface{}{})
307 smslogger.WriteError(err.Error())
308 return errors.New("Unable to create secret ID for role")
311 v.secretID = sec.Data["secret_id"].(string)
312 v.initRoleDone = true
316 // Function checkToken() gets called multiple times to create
318 func (v *Vault) checkToken() error {
320 defer v.tokenLock.Unlock()
322 // Init Role if it is not yet done
323 // Role needs to be created before token can be created
324 if v.initRoleDone == false {
327 smslogger.WriteError(err.Error())
328 return errors.New("Unable to initRole in checkToken")
332 // Return immediately if token still has life
333 if v.vaultClient.Token() != "" &&
334 time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
338 // Create a temporary token using our roleID and secretID
339 out, err := v.vaultClient.Logical().Write("auth/approle/login",
340 map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
342 smslogger.WriteError(err.Error())
343 return errors.New("Unable to create Temporary Token for Role")
346 tok, err := out.TokenID()
348 v.vaultTempTokenTTL = time.Now()
349 v.vaultClient.SetToken(tok)