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
40 vaultClient *vaultapi.Client
41 vaultMountPrefix string
43 internalDomainMounted bool
44 vaultTempTokenTTL time.Time
48 // Init will initialize the vault connection
49 // It will also create the initial policy if it does not exist
50 // TODO: Check to see if we need to wait for vault to be running
51 func (v *Vault) Init() error {
52 vaultCFG := vaultapi.DefaultConfig()
53 vaultCFG.Address = v.vaultAddress
54 client, err := vaultapi.NewClient(vaultCFG)
56 smslogger.WriteError(err.Error())
57 return errors.New("Unable to create new vault client")
60 v.initRoleDone = false
61 v.policyName = "smsvaultpolicy"
62 v.vaultClient = client
63 v.vaultMountPrefix = "sms"
64 v.internalDomain = "smsinternaldomain"
65 v.internalDomainMounted = false
69 smslogger.WriteError(err.Error())
70 smslogger.WriteInfo("InitRole will try again later")
76 // GetStatus returns the current seal status of vault
77 func (v *Vault) GetStatus() (bool, error) {
78 sys := v.vaultClient.Sys()
79 sealStatus, err := sys.SealStatus()
81 smslogger.WriteError(err.Error())
82 return false, errors.New("Error getting status")
84 return sealStatus.Sealed, nil
87 // Unseal is a passthrough API that allows any
88 // unseal or initialization processes for the backend
89 func (v *Vault) Unseal(shard string) error {
90 sys := v.vaultClient.Sys()
91 _, err := sys.Unseal(shard)
93 smslogger.WriteError(err.Error())
94 return errors.New("Unable to execute unseal operation with specified shard")
100 // GetSecret returns a secret mounted on a particular domain name
101 // The secret itself is referenced via its name which translates to
102 // a mount path in vault
103 func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
104 err := v.checkToken()
106 smslogger.WriteError(err.Error())
107 return Secret{}, errors.New("Token check failed")
110 dom = v.vaultMountPrefix + "/" + dom
112 sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
114 smslogger.WriteError(err.Error())
115 return Secret{}, errors.New("Unable to read Secret at provided path")
118 // sec and err are nil in the case where a path does not exist
120 smslogger.WriteWarn("Vault read was empty. Invalid Path")
121 return Secret{}, errors.New("Secret not found at the provided path")
124 return Secret{Name: name, Values: sec.Data}, nil
127 // ListSecret returns a list of secret names on a particular domain
128 // The values of the secret are not returned
129 func (v *Vault) ListSecret(dom string) ([]string, error) {
130 err := v.checkToken()
132 smslogger.WriteError(err.Error())
133 return nil, errors.New("Token check failed")
136 dom = v.vaultMountPrefix + "/" + dom
138 sec, err := v.vaultClient.Logical().List(dom)
140 smslogger.WriteError(err.Error())
141 return nil, errors.New("Unable to read Secret at provided path")
144 // sec and err are nil in the case where a path does not exist
146 smslogger.WriteWarn("Vaultclient returned empty data")
147 return nil, errors.New("Secret not found at the provided path")
150 val, ok := sec.Data["keys"].([]interface{})
152 smslogger.WriteError("Secret not found at the provided path")
153 return nil, errors.New("Secret not found at the provided path")
156 retval := make([]string, len(val))
157 for i, v := range val {
158 retval[i] = fmt.Sprint(v)
164 // Mounts the internal Domain if its not already mounted
165 func (v *Vault) mountInternalDomain(name string) error {
166 if v.internalDomainMounted {
170 name = strings.TrimSpace(name)
171 mountPath := v.vaultMountPrefix + "/" + name
172 mountInput := &vaultapi.MountInput{
174 Description: "Mount point for domain: " + name,
177 Config: vaultapi.MountConfigInput{},
180 err := v.vaultClient.Sys().Mount(mountPath, mountInput)
182 if strings.Contains(err.Error(), "existing mount") {
183 // It is already mounted
184 v.internalDomainMounted = true
187 // Ran into some other error mounting it.
188 smslogger.WriteError(err.Error())
189 return errors.New("Unable to mount internal Domain")
192 v.internalDomainMounted = true
196 // Stores the UUID created for secretdomain in vault
197 // under v.vaultMountPrefix / smsinternal domain
198 func (v *Vault) storeUUID(uuid string, name string) error {
199 // Check if token is still valid
200 err := v.checkToken()
202 smslogger.WriteError(err.Error())
203 return errors.New("Token Check failed")
206 err = v.mountInternalDomain(v.internalDomain)
208 smslogger.WriteError("Could not mount internal domain")
214 Values: map[string]interface{}{
219 err = v.CreateSecret(v.internalDomain, secret)
221 smslogger.WriteError("Unable to write UUID to internal domain")
228 // CreateSecretDomain mounts the kv backend on a path with the given name
229 func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
230 // Check if token is still valid
231 err := v.checkToken()
233 smslogger.WriteError(err.Error())
234 return SecretDomain{}, errors.New("Token Check failed")
237 name = strings.TrimSpace(name)
238 mountPath := v.vaultMountPrefix + "/" + name
239 mountInput := &vaultapi.MountInput{
241 Description: "Mount point for domain: " + name,
244 Config: vaultapi.MountConfigInput{},
247 err = v.vaultClient.Sys().Mount(mountPath, mountInput)
249 smslogger.WriteError(err.Error())
250 return SecretDomain{}, errors.New("Unable to create Secret Domain")
253 uuid, _ := uuid.GenerateUUID()
254 err = v.storeUUID(uuid, name)
256 // Mount was successful at this point.
257 // Rollback the mount operation since we could not
258 // store the UUID for the mount.
259 v.vaultClient.Sys().Unmount(mountPath)
260 return SecretDomain{}, errors.New("Unable to store Secret Domain UUID. Retry.")
263 return SecretDomain{uuid, name}, nil
266 // CreateSecret creates a secret mounted on a particular domain name
267 // The secret itself is mounted on a path specified by name
268 func (v *Vault) CreateSecret(dom string, sec Secret) error {
269 err := v.checkToken()
271 smslogger.WriteError(err.Error())
272 return errors.New("Token check failed")
275 dom = v.vaultMountPrefix + "/" + dom
277 // Vault return is empty on successful write
278 // TODO: Check if values is not empty
279 _, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
281 smslogger.WriteError(err.Error())
282 return errors.New("Unable to create Secret at provided path")
288 // DeleteSecretDomain deletes a secret domain which translates to
289 // an unmount operation on the given path in Vault
290 func (v *Vault) DeleteSecretDomain(name string) error {
291 err := v.checkToken()
293 smslogger.WriteError(err.Error())
294 return errors.New("Token Check Failed")
297 name = strings.TrimSpace(name)
298 mountPath := v.vaultMountPrefix + "/" + name
300 err = v.vaultClient.Sys().Unmount(mountPath)
302 smslogger.WriteError(err.Error())
303 return errors.New("Unable to delete domain specified")
309 // DeleteSecret deletes a secret mounted on the path provided
310 func (v *Vault) DeleteSecret(dom string, name string) error {
311 err := v.checkToken()
313 smslogger.WriteError(err.Error())
314 return errors.New("Token check failed")
317 dom = v.vaultMountPrefix + "/" + dom
319 // Vault return is empty on successful delete
320 _, err = v.vaultClient.Logical().Delete(dom + "/" + name)
322 smslogger.WriteError(err.Error())
323 return errors.New("Unable to delete Secret at provided path")
329 // initRole is called only once during the service bring up
330 func (v *Vault) initRole() error {
331 // Use the root token once here
332 v.vaultClient.SetToken(v.vaultToken)
333 defer v.vaultClient.ClearToken()
335 rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
336 path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
337 err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
339 smslogger.WriteError(err.Error())
340 return errors.New("Unable to create policy for approle creation")
343 rName := v.vaultMountPrefix + "-role"
344 data := map[string]interface{}{
346 "policies": [2]string{"default", v.policyName},
349 //Check if applrole is mounted
350 authMounts, err := v.vaultClient.Sys().ListAuth()
352 smslogger.WriteError(err.Error())
353 return errors.New("Unable to get mounted auth backends")
356 approleMounted := false
357 for k, v := range authMounts {
358 if v.Type == "approle" && k == "approle/" {
359 approleMounted = true
364 // Mount approle in case its not already mounted
366 v.vaultClient.Sys().EnableAuth("approle", "approle", "")
370 v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
371 sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
373 smslogger.WriteError(err.Error())
374 return errors.New("Unable to create role ID for approle")
376 v.roleID = sec.Data["role_id"].(string)
378 // Create a secret-id to go with it
379 sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
380 map[string]interface{}{})
382 smslogger.WriteError(err.Error())
383 return errors.New("Unable to create secret ID for role")
386 v.secretID = sec.Data["secret_id"].(string)
387 v.initRoleDone = true
391 // Function checkToken() gets called multiple times to create
393 func (v *Vault) checkToken() error {
397 // Init Role if it is not yet done
398 // Role needs to be created before token can be created
399 if v.initRoleDone == false {
402 smslogger.WriteError(err.Error())
403 return errors.New("Unable to initRole in checkToken")
407 // Return immediately if token still has life
408 if v.vaultClient.Token() != "" &&
409 time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
413 // Create a temporary token using our roleID and secretID
414 out, err := v.vaultClient.Logical().Write("auth/approle/login",
415 map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
417 smslogger.WriteError(err.Error())
418 return errors.New("Unable to create Temporary Token for Role")
421 tok, err := out.TokenID()
423 v.vaultTempTokenTTL = time.Now()
424 v.vaultClient.SetToken(tok)
428 // vaultInit() is used to initialize the vault in cases where it is not
429 // initialized. This happens once during intial bring up.
430 func (v *Vault) initializeVault() error {
431 initReq := &vaultapi.InitRequest{
436 pbkey, _, err := smsauth.GeneratePGPKeyPair()
438 smslogger.WriteError("Error Generating PGP Keys. Vault Init will not use encryption!")
440 initReq.PGPKeys = []string{pbkey, pbkey, pbkey, pbkey, pbkey}
441 initReq.RootTokenPGPKey = pbkey
444 resp, err := v.vaultClient.Sys().Init(initReq)
446 smslogger.WriteError(err.Error())
447 return errors.New("FATAL: Unable to initialize Vault")
451 //v.writeUnsealShards(resp.KeysB64)
452 v.vaultToken = resp.RootToken
456 return errors.New("FATAL: Init response was empty")