import (
uuid "github.com/hashicorp/go-uuid"
vaultapi "github.com/hashicorp/vault/api"
+ smslogger "sms/log"
"errors"
"fmt"
- "log"
"strings"
"sync"
"time"
// Vault is the main Struct used in Backend to initialize the struct
type Vault struct {
- vaultAddress string
- vaultToken string
- vaultMount string
- vaultTempToken string
-
- vaultClient *vaultapi.Client
engineType string
+ initRoleDone bool
policyName string
roleID string
secretID string
+ tokenLock sync.Mutex
+ vaultAddress string
+ vaultClient *vaultapi.Client
+ vaultMount string
vaultTempTokenTTL time.Time
-
- tokenLock sync.Mutex
+ vaultToken string
}
// Init will initialize the vault connection
vaultCFG.Address = v.vaultAddress
client, err := vaultapi.NewClient(vaultCFG)
if err != nil {
- return err
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to create new vault client")
}
v.engineType = "kv"
+ v.initRoleDone = false
v.policyName = "smsvaultpolicy"
- v.vaultMount = "sms"
v.vaultClient = client
+ v.vaultMount = "sms"
- // Check if vault is ready and unsealed
- seal, err := v.GetStatus()
+ err = v.initRole()
if err != nil {
- return err
- }
- if seal == true {
- return fmt.Errorf("Vault is still sealed. Unseal before use")
+ smslogger.WriteError(err.Error())
+ smslogger.WriteInfo("InitRole will try again later")
}
- v.initRole()
- v.checkToken()
return nil
}
sys := v.vaultClient.Sys()
sealStatus, err := sys.SealStatus()
if err != nil {
- return false, err
+ smslogger.WriteError(err.Error())
+ return false, errors.New("Error getting status")
}
return sealStatus.Sealed, nil
}
-// GetSecretDomain returns any information related to the secretDomain
-// More information can be added in the future with updates to the struct
-func (v *Vault) GetSecretDomain(name string) (SecretDomain, error) {
- return SecretDomain{}, nil
+// Unseal is a passthrough API that allows any
+// unseal or initialization processes for the backend
+func (v *Vault) Unseal(shard string) error {
+ sys := v.vaultClient.Sys()
+ _, err := sys.Unseal(shard)
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to execute unseal operation with specified shard")
+ }
+
+ return nil
}
// GetSecret returns a secret mounted on a particular domain name
func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
err := v.checkToken()
if err != nil {
- return Secret{}, errors.New("Token check returned error: " + err.Error())
+ smslogger.WriteError(err.Error())
+ return Secret{}, errors.New("Token check failed")
}
dom = v.vaultMount + "/" + dom
sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
if err != nil {
+ smslogger.WriteError(err.Error())
return Secret{}, errors.New("Unable to read Secret at provided path")
}
// sec and err are nil in the case where a path does not exist
if sec == nil {
+ smslogger.WriteWarn("Vault read was empty. Invalid Path")
return Secret{}, errors.New("Secret not found at the provided path")
}
func (v *Vault) ListSecret(dom string) ([]string, error) {
err := v.checkToken()
if err != nil {
- return nil, errors.New("Token check returned error: " + err.Error())
+ smslogger.WriteError(err.Error())
+ return nil, errors.New("Token check failed")
}
dom = v.vaultMount + "/" + dom
sec, err := v.vaultClient.Logical().List(dom)
if err != nil {
+ smslogger.WriteError(err.Error())
return nil, errors.New("Unable to read Secret at provided path")
}
// sec and err are nil in the case where a path does not exist
if sec == nil {
+ smslogger.WriteWarn("Vaultclient returned empty data")
return nil, errors.New("Secret not found at the provided path")
}
val, ok := sec.Data["keys"].([]interface{})
if !ok {
+ smslogger.WriteError("Secret not found at the provided path")
return nil, errors.New("Secret not found at the provided path")
}
// Check if token is still valid
err := v.checkToken()
if err != nil {
- return SecretDomain{}, err
+ smslogger.WriteError(err.Error())
+ return SecretDomain{}, errors.New("Token Check failed")
}
name = strings.TrimSpace(name)
err = v.vaultClient.Sys().Mount(mountPath, mountInput)
if err != nil {
- return SecretDomain{}, err
+ smslogger.WriteError(err.Error())
+ return SecretDomain{}, errors.New("Unable to create Secret Domain")
}
uuid, _ := uuid.GenerateUUID()
func (v *Vault) CreateSecret(dom string, sec Secret) error {
err := v.checkToken()
if err != nil {
- return errors.New("Token checking returned an error" + err.Error())
+ smslogger.WriteError(err.Error())
+ return errors.New("Token check failed")
}
dom = v.vaultMount + "/" + dom
// Vault return is empty on successful write
+ // TODO: Check if values is not empty
_, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
if err != nil {
+ smslogger.WriteError(err.Error())
return errors.New("Unable to create Secret at provided path")
}
// DeleteSecretDomain deletes a secret domain which translates to
// an unmount operation on the given path in Vault
func (v *Vault) DeleteSecretDomain(name string) error {
+ err := v.checkToken()
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Token Check Failed")
+ }
+
+ name = strings.TrimSpace(name)
+ mountPath := v.vaultMount + "/" + name
+
+ err = v.vaultClient.Sys().Unmount(mountPath)
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to delete domain specified")
+ }
return nil
}
func (v *Vault) DeleteSecret(dom string, name string) error {
err := v.checkToken()
if err != nil {
- return errors.New("Token checking returned an error" + err.Error())
+ smslogger.WriteError(err.Error())
+ return errors.New("Token check failed")
}
dom = v.vaultMount + "/" + dom
// Vault return is empty on successful delete
_, err = v.vaultClient.Logical().Delete(dom + "/" + name)
if err != nil {
+ smslogger.WriteError(err.Error())
return errors.New("Unable to delete Secret at provided path")
}
rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
- v.vaultClient.Sys().PutPolicy(v.policyName, rules)
+ err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to create policy for approle creation")
+ }
rName := v.vaultMount + "-role"
data := map[string]interface{}{
"policies": [2]string{"default", v.policyName},
}
- // Delete role if it already exists
- v.vaultClient.Logical().Delete("auth/approle/role/" + rName)
-
- //Check if approle is mounted
+ //Check if applrole is mounted
authMounts, err := v.vaultClient.Sys().ListAuth()
if err != nil {
- return err
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to get mounted auth backends")
}
approleMounted := false
v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
if err != nil {
- log.Fatal(err)
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to create role ID for approle")
}
v.roleID = sec.Data["role_id"].(string)
// Create a secret-id to go with it
- sec, _ = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
+ sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
map[string]interface{}{})
- v.secretID = sec.Data["secret_id"].(string)
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to create secret ID for role")
+ }
+ v.secretID = sec.Data["secret_id"].(string)
+ v.initRoleDone = true
return nil
}
v.tokenLock.Lock()
defer v.tokenLock.Unlock()
+ // Init Role if it is not yet done
+ // Role needs to be created before token can be created
+ if v.initRoleDone == false {
+ err := v.initRole()
+ if err != nil {
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to initRole in checkToken")
+ }
+ }
+
// Return immediately if token still has life
if v.vaultClient.Token() != "" &&
time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
out, err := v.vaultClient.Logical().Write("auth/approle/login",
map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
if err != nil {
- return err
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to create Temporary Token for Role")
}
tok, err := out.TokenID()
- v.vaultTempToken = tok
v.vaultTempTokenTTL = time.Now()
- v.vaultClient.SetToken(v.vaultTempToken)
+ v.vaultClient.SetToken(tok)
return nil
}