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"
32 // Vault is the main Struct used in Backend to initialize the struct
41 vaultClient *vaultapi.Client
43 vaultTempTokenTTL time.Time
51 // Init will initialize the vault connection
52 // It will also create the initial policy if it does not exist
53 // TODO: Check to see if we need to wait for vault to be running
54 func (v *Vault) Init() error {
55 vaultCFG := vaultapi.DefaultConfig()
56 vaultCFG.Address = v.vaultAddress
57 client, err := vaultapi.NewClient(vaultCFG)
59 smslogger.WriteError(err.Error())
60 return errors.New("Unable to create new vault client")
64 v.initRoleDone = false
65 v.policyName = "smsvaultpolicy"
66 v.vaultClient = client
71 smslogger.WriteError(err.Error())
72 smslogger.WriteInfo("InitRole will try again later")
78 // GetStatus returns the current seal status of vault
79 func (v *Vault) GetStatus() (bool, error) {
80 sys := v.vaultClient.Sys()
81 sealStatus, err := sys.SealStatus()
83 smslogger.WriteError(err.Error())
84 return false, errors.New("Error getting status")
87 return sealStatus.Sealed, nil
90 // Unseal is a passthrough API that allows any
91 // unseal or initialization processes for the backend
92 func (v *Vault) Unseal(shard string) error {
93 sys := v.vaultClient.Sys()
94 _, err := sys.Unseal(shard)
96 smslogger.WriteError(err.Error())
97 return errors.New("Unable to execute unseal operation with specified shard")
103 // GetSecret returns a secret mounted on a particular domain name
104 // The secret itself is referenced via its name which translates to
105 // a mount path in vault
106 func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
107 err := v.checkToken()
109 smslogger.WriteError(err.Error())
110 return Secret{}, errors.New("Token check failed")
113 dom = v.vaultMount + "/" + dom
115 sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
117 smslogger.WriteError(err.Error())
118 return Secret{}, errors.New("Unable to read Secret at provided path")
121 // sec and err are nil in the case where a path does not exist
123 smslogger.WriteWarn("Vault read was empty. Invalid Path")
124 return Secret{}, errors.New("Secret not found at the provided path")
127 return Secret{Name: name, Values: sec.Data}, nil
130 // ListSecret returns a list of secret names on a particular domain
131 // The values of the secret are not returned
132 func (v *Vault) ListSecret(dom string) ([]string, error) {
133 err := v.checkToken()
135 smslogger.WriteError(err.Error())
136 return nil, errors.New("Token check failed")
139 dom = v.vaultMount + "/" + dom
141 sec, err := v.vaultClient.Logical().List(dom)
143 smslogger.WriteError(err.Error())
144 return nil, errors.New("Unable to read Secret at provided path")
147 // sec and err are nil in the case where a path does not exist
149 smslogger.WriteWarn("Vaultclient returned empty data")
150 return nil, errors.New("Secret not found at the provided path")
153 val, ok := sec.Data["keys"].([]interface{})
155 smslogger.WriteError("Secret not found at the provided path")
156 return nil, errors.New("Secret not found at the provided path")
159 retval := make([]string, len(val))
160 for i, v := range val {
161 retval[i] = fmt.Sprint(v)
167 // CreateSecretDomain mounts the kv backend on a path with the given name
168 func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
169 // Check if token is still valid
170 err := v.checkToken()
172 smslogger.WriteError(err.Error())
173 return SecretDomain{}, errors.New("Token Check failed")
176 name = strings.TrimSpace(name)
177 mountPath := v.vaultMount + "/" + name
178 mountInput := &vaultapi.MountInput{
180 Description: "Mount point for domain: " + name,
183 Config: vaultapi.MountConfigInput{},
186 err = v.vaultClient.Sys().Mount(mountPath, mountInput)
188 smslogger.WriteError(err.Error())
189 return SecretDomain{}, errors.New("Unable to create Secret Domain")
192 uuid, _ := uuid.GenerateUUID()
193 return SecretDomain{uuid, name}, nil
196 // CreateSecret creates a secret mounted on a particular domain name
197 // The secret itself is mounted on a path specified by name
198 func (v *Vault) CreateSecret(dom string, sec Secret) error {
199 err := v.checkToken()
201 smslogger.WriteError(err.Error())
202 return errors.New("Token check failed")
205 dom = v.vaultMount + "/" + dom
207 // Vault return is empty on successful write
208 // TODO: Check if values is not empty
209 _, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
211 smslogger.WriteError(err.Error())
212 return errors.New("Unable to create Secret at provided path")
218 // DeleteSecretDomain deletes a secret domain which translates to
219 // an unmount operation on the given path in Vault
220 func (v *Vault) DeleteSecretDomain(name string) error {
221 err := v.checkToken()
223 smslogger.WriteError(err.Error())
224 return errors.New("Token Check Failed")
227 name = strings.TrimSpace(name)
228 mountPath := v.vaultMount + "/" + name
230 err = v.vaultClient.Sys().Unmount(mountPath)
232 smslogger.WriteError(err.Error())
233 return errors.New("Unable to delete domain specified")
239 // DeleteSecret deletes a secret mounted on the path provided
240 func (v *Vault) DeleteSecret(dom string, name string) error {
241 err := v.checkToken()
243 smslogger.WriteError(err.Error())
244 return errors.New("Token check failed")
247 dom = v.vaultMount + "/" + dom
249 // Vault return is empty on successful delete
250 _, err = v.vaultClient.Logical().Delete(dom + "/" + name)
252 smslogger.WriteError(err.Error())
253 return errors.New("Unable to delete Secret at provided path")
259 // initRole is called only once during the service bring up
260 func (v *Vault) initRole() error {
261 // Use the root token once here
262 v.vaultClient.SetToken(v.vaultToken)
263 defer v.vaultClient.ClearToken()
265 rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
266 path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
267 err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
269 smslogger.WriteError(err.Error())
270 return errors.New("Unable to create policy for approle creation")
273 rName := v.vaultMount + "-role"
274 data := map[string]interface{}{
276 "policies": [2]string{"default", v.policyName},
279 //Check if applrole is mounted
280 authMounts, err := v.vaultClient.Sys().ListAuth()
282 smslogger.WriteError(err.Error())
283 return errors.New("Unable to get mounted auth backends")
286 approleMounted := false
287 for k, v := range authMounts {
288 if v.Type == "approle" && k == "approle/" {
289 approleMounted = true
294 // Mount approle in case its not already mounted
296 v.vaultClient.Sys().EnableAuth("approle", "approle", "")
300 v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
301 sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
303 smslogger.WriteError(err.Error())
304 return errors.New("Unable to create role ID for approle")
306 v.roleID = sec.Data["role_id"].(string)
308 // Create a secret-id to go with it
309 sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
310 map[string]interface{}{})
312 smslogger.WriteError(err.Error())
313 return errors.New("Unable to create secret ID for role")
316 v.secretID = sec.Data["secret_id"].(string)
317 v.initRoleDone = true
321 // Function checkToken() gets called multiple times to create
323 func (v *Vault) checkToken() error {
325 defer v.tokenLock.Unlock()
327 // Init Role if it is not yet done
328 // Role needs to be created before token can be created
329 if v.initRoleDone == false {
332 smslogger.WriteError(err.Error())
333 return errors.New("Unable to initRole in checkToken")
337 // Return immediately if token still has life
338 if v.vaultClient.Token() != "" &&
339 time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
343 // Create a temporary token using our roleID and secretID
344 out, err := v.vaultClient.Logical().Write("auth/approle/login",
345 map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
347 smslogger.WriteError(err.Error())
348 return errors.New("Unable to create Temporary Token for Role")
351 tok, err := out.TokenID()
353 v.vaultTempTokenTTL = time.Now()
354 v.vaultClient.SetToken(tok)
358 // vaultInit() is used to initialize the vault in cases where it is not
359 // initialized. This happens once during intial bring up.
360 func (v *Vault) initializeVault() error {
361 initReq := &vaultapi.InitRequest{
366 pbkey, prkey, err := smsauth.GeneratePGPKeyPair()
368 smslogger.WriteError("Error Generating PGP Keys. Vault Init will not use encryption!")
370 initReq.PGPKeys = []string{pbkey, pbkey, pbkey, pbkey, pbkey}
371 initReq.RootTokenPGPKey = pbkey
376 resp, err := v.vaultClient.Sys().Init(initReq)
378 smslogger.WriteError(err.Error())
379 return errors.New("FATAL: Unable to initialize Vault")
383 v.unsealShards = resp.KeysB64
384 v.rootToken = resp.RootToken
388 return errors.New("FATAL: Init response was empty")