Log errors to log file
[aaf/sms.git] / sms-service / src / sms / backend / vault.go
index 6b9ad94..a4ebaaa 100644 (file)
@@ -19,10 +19,10 @@ package backend
 import (
        uuid "github.com/hashicorp/go-uuid"
        vaultapi "github.com/hashicorp/vault/api"
+       smslogger "sms/log"
 
        "errors"
        "fmt"
-       "log"
        "strings"
        "sync"
        "time"
@@ -30,19 +30,17 @@ import (
 
 // 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
@@ -53,25 +51,22 @@ func (v *Vault) Init() error {
        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
 }
 
@@ -80,16 +75,24 @@ func (v *Vault) GetStatus() (bool, error) {
        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
@@ -98,18 +101,21 @@ func (v *Vault) GetSecretDomain(name string) (SecretDomain, error) {
 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")
        }
 
@@ -121,23 +127,27 @@ func (v *Vault) GetSecret(dom string, name string) (Secret, error) {
 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")
        }
 
@@ -154,7 +164,8 @@ func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
        // 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)
@@ -169,7 +180,8 @@ func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
 
        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()
@@ -181,14 +193,17 @@ func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
 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")
        }
 
@@ -198,6 +213,20 @@ func (v *Vault) CreateSecret(dom string, sec Secret) error {
 // 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
 }
@@ -206,7 +235,8 @@ func (v *Vault) DeleteSecretDomain(name string) error {
 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
@@ -214,6 +244,7 @@ func (v *Vault) DeleteSecret(dom string, name string) error {
        // 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")
        }
 
@@ -228,7 +259,11 @@ func (v *Vault) initRole() error {
 
        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{}{
@@ -236,13 +271,11 @@ func (v *Vault) initRole() error {
                "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
@@ -262,15 +295,21 @@ func (v *Vault) initRole() error {
        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
 }
 
@@ -280,6 +319,16 @@ func (v *Vault) checkToken() error {
        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 {
@@ -290,13 +339,13 @@ func (v *Vault) checkToken() error {
        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
 }