summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKiran <kiran.k.kamineni@intel.com>2018-02-21 13:16:53 -0800
committerKiran <kiran.k.kamineni@intel.com>2018-02-21 16:13:45 -0800
commit735839b4257e0341ab2225e763247e848bb31696 (patch)
tree528628a63496a4e0eb900efad391bed57a856a73
parent8caa4b65ffa9c1d086597e82087a3e523b60f305 (diff)
Adding token creation for operations
Secret domain creation and secret creation is controlled using approle authentication within the sms service A temporary token with a short ttl is created and used for adding domains and secrets into vault right now Root token is used only once during the initial bring up Also fixing unit test for backend.go Issue-ID: AAF-99 Change-Id: I1352dadb32b54caaef86c7795601bf04d657dc3b Signed-off-by: Kiran <kiran.k.kamineni@intel.com>
-rw-r--r--sms-service/src/sms/Gopkg.lock8
-rw-r--r--sms-service/src/sms/Gopkg.toml4
-rw-r--r--sms-service/src/sms/auth/auth_test.go6
-rw-r--r--sms-service/src/sms/backend/backend.go14
-rw-r--r--sms-service/src/sms/backend/backend_test.go14
-rw-r--r--sms-service/src/sms/backend/vault.go129
-rw-r--r--sms-service/src/sms/config/config.go4
-rw-r--r--sms-service/src/sms/handler/handler.go40
-rw-r--r--sms-service/src/sms/smsconfig.json7
9 files changed, 197 insertions, 29 deletions
diff --git a/sms-service/src/sms/Gopkg.lock b/sms-service/src/sms/Gopkg.lock
index da2fafd..b69eb59 100644
--- a/sms-service/src/sms/Gopkg.lock
+++ b/sms-service/src/sms/Gopkg.lock
@@ -51,6 +51,12 @@
[[projects]]
branch = "master"
+ name = "github.com/hashicorp/go-uuid"
+ packages = ["."]
+ revision = "64130c7a86d732268a38cb04cfbaf0cc987fda98"
+
+[[projects]]
+ branch = "master"
name = "github.com/hashicorp/hcl"
packages = [
".",
@@ -129,6 +135,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "04ef42d3e34fec943a6bbcbde4a3caea30a3ca59d53faf7c99aa63094bea4e8f"
+ inputs-digest = "c49b267ee83a36c520ae964867f725fc7d934296f38240152e6eec2842846ac0"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/sms-service/src/sms/Gopkg.toml b/sms-service/src/sms/Gopkg.toml
index af2ce87..6e40849 100644
--- a/sms-service/src/sms/Gopkg.toml
+++ b/sms-service/src/sms/Gopkg.toml
@@ -27,3 +27,7 @@
[[constraint]]
name = "github.com/hashicorp/vault"
version = "0.9.3"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/hashicorp/go-uuid"
diff --git a/sms-service/src/sms/auth/auth_test.go b/sms-service/src/sms/auth/auth_test.go
index 15f0cf3..1cacfe6 100644
--- a/sms-service/src/sms/auth/auth_test.go
+++ b/sms-service/src/sms/auth/auth_test.go
@@ -35,8 +35,8 @@ func TestGetTLSConfig(t *testing.T) {
if int(actual) != expected {
t.Errorf("Test Failed due to version mismatch")
}
- if tlsConfig == nil {
- t.Errorf("Test Failed due to GetTLSConfig returned nil")
- }
+ if tlsConfig == nil {
+ t.Errorf("Test Failed due to GetTLSConfig returned nil")
+ }
}
}
diff --git a/sms-service/src/sms/backend/backend.go b/sms-service/src/sms/backend/backend.go
index 5611f37..2536fe1 100644
--- a/sms-service/src/sms/backend/backend.go
+++ b/sms-service/src/sms/backend/backend.go
@@ -16,6 +16,10 @@
package backend
+import (
+ smsconfig "sms/config"
+)
+
// SecretDomain is where Secrets are stored.
// A single domain can have any number of secrets
type SecretDomain struct {
@@ -32,8 +36,8 @@ type SecretKeyValue struct {
// Secret is the struct that defines the structure of a secret
// A single Secret can have any number of SecretKeyValue pairs
type Secret struct {
- Name string `json:"name"`
- Values []SecretKeyValue `json:"values"`
+ Name string `json:"name"`
+ Values map[string]string `json:"values"`
}
// SecretBackend interface that will be implemented for various secret backends
@@ -53,7 +57,11 @@ type SecretBackend interface {
// InitSecretBackend returns an interface implementation
func InitSecretBackend() (SecretBackend, error) {
- backendImpl := &Vault{}
+ backendImpl := &Vault{
+ vaultAddress: smsconfig.SMSConfig.VaultAddress,
+ vaultToken: smsconfig.SMSConfig.VaultToken,
+ }
+
err := backendImpl.Init()
if err != nil {
return nil, err
diff --git a/sms-service/src/sms/backend/backend_test.go b/sms-service/src/sms/backend/backend_test.go
index a3a616c..92ca971 100644
--- a/sms-service/src/sms/backend/backend_test.go
+++ b/sms-service/src/sms/backend/backend_test.go
@@ -22,14 +22,16 @@ import (
)
func TestInitSecretBackend(t *testing.T) {
- smsconfig.SMSConfig = &smsconfig.SMSConfiguration{VaultAddress: "http://localhost:8200"}
+ smsconfig.SMSConfig = &smsconfig.SMSConfiguration{
+ VaultAddress: "http://localhost:8200",
+ }
sec, err := InitSecretBackend()
- // We don't expect an error to be returned as Init only creates a client
- // Does not expect backend to be running
- if err != nil {
+ // We expect an error to be returned as Init expects
+ // backend to be running
+ if err == nil {
t.Fatal("InitSecretBackend : error creating")
}
- if sec == nil {
- t.Fatal("InitSecretBackend: returned SecretBackend was nil")
+ if sec != nil {
+ t.Fatal("InitSecretBackend: returned SecretBackend was *NOT* nil, expected nil")
}
}
diff --git a/sms-service/src/sms/backend/vault.go b/sms-service/src/sms/backend/vault.go
index f3e2ac1..c912dae 100644
--- a/sms-service/src/sms/backend/vault.go
+++ b/sms-service/src/sms/backend/vault.go
@@ -17,28 +17,60 @@
package backend
import (
+ uuid "github.com/hashicorp/go-uuid"
vaultapi "github.com/hashicorp/vault/api"
- smsconfig "sms/config"
+ "fmt"
+ "log"
+ "strings"
+ "sync"
+ "time"
)
// Vault is the main Struct used in Backend to initialize the struct
type Vault struct {
- vaultClient *vaultapi.Client
+ vaultAddress string
+ vaultToken string
+ vaultMount string
+ vaultTempToken string
+
+ vaultClient *vaultapi.Client
+ engineType string
+ policyName string
+ roleID string
+ secretID string
+ vaultTempTokenTTL time.Time
+
+ tokenLock sync.Mutex
}
// Init will initialize the vault connection
+// It will also create the initial policy if it does not exist
// TODO: Check to see if we need to wait for vault to be running
func (v *Vault) Init() error {
vaultCFG := vaultapi.DefaultConfig()
- vaultCFG.Address = smsconfig.SMSConfig.VaultAddress
-
+ vaultCFG.Address = v.vaultAddress
client, err := vaultapi.NewClient(vaultCFG)
if err != nil {
return err
}
+ v.engineType = "kv"
+ v.policyName = "smsvaultpolicy"
+ v.vaultMount = "sms"
v.vaultClient = client
+
+ // Check if vault is ready and unsealed
+ seal, err := v.GetStatus()
+ if err != nil {
+ return err
+ }
+ if seal == true {
+ return fmt.Errorf("Vault is still sealed. Unseal before use")
+ }
+
+ v.initRole()
+ v.checkToken()
return nil
}
@@ -56,7 +88,6 @@ func (v *Vault) GetStatus() (bool, error) {
// 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
}
@@ -70,8 +101,29 @@ func (v *Vault) GetSecret(dom string, sec string) (Secret, error) {
// CreateSecretDomain mounts the kv backend on a path with the given name
func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) {
+ // Check if token is still valid
+ err := v.checkToken()
+ if err != nil {
+ return SecretDomain{}, err
+ }
- return SecretDomain{}, nil
+ name = strings.TrimSpace(name)
+ mountPath := v.vaultMount + "/" + name
+ mountInput := &vaultapi.MountInput{
+ Type: v.engineType,
+ Description: "Mount point for domain: " + name,
+ Local: false,
+ SealWrap: false,
+ Config: vaultapi.MountConfigInput{},
+ }
+
+ err = v.vaultClient.Sys().Mount(mountPath, mountInput)
+ if err != nil {
+ return SecretDomain{}, err
+ }
+
+ uuid, _ := uuid.GenerateUUID()
+ return SecretDomain{uuid, name}, nil
}
// CreateSecret creates a secret mounted on a particular domain name
@@ -93,3 +145,68 @@ func (v *Vault) DeleteSecret(dom string, name string) error {
return nil
}
+
+// initRole is called only once during the service bring up
+func (v *Vault) initRole() error {
+ // Use the root token once here
+ v.vaultClient.SetToken(v.vaultToken)
+ defer v.vaultClient.ClearToken()
+
+ rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
+ path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
+ v.vaultClient.Sys().PutPolicy(v.policyName, rules)
+
+ rName := v.vaultMount + "-role"
+ data := map[string]interface{}{
+ "token_ttl": "60m",
+ "policies": [2]string{"default", v.policyName},
+ }
+
+ // Delete role if it already exists
+ v.vaultClient.Logical().Delete("auth/approle/role/" + rName)
+
+ // Mount approle in case its not already mounted
+ v.vaultClient.Sys().EnableAuth("approle", "approle", "")
+
+ // Create a role-id
+ 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)
+ }
+ 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",
+ map[string]interface{}{})
+ v.secretID = sec.Data["secret_id"].(string)
+
+ return nil
+}
+
+// Function checkToken() gets called multiple times to create
+// temporary tokens
+func (v *Vault) checkToken() error {
+ v.tokenLock.Lock()
+ defer v.tokenLock.Unlock()
+
+ // Return immediately if token still has life
+ if v.vaultClient.Token() != "" &&
+ time.Since(v.vaultTempTokenTTL) < time.Minute*50 {
+ return nil
+ }
+
+ // Create a temporary token using our roleID and secretID
+ 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
+ }
+
+ tok, err := out.TokenID()
+
+ v.vaultTempToken = tok
+ v.vaultTempTokenTTL = time.Now()
+ v.vaultClient.SetToken(v.vaultTempToken)
+ return nil
+}
diff --git a/sms-service/src/sms/config/config.go b/sms-service/src/sms/config/config.go
index e1c1b86..b7f97d2 100644
--- a/sms-service/src/sms/config/config.go
+++ b/sms-service/src/sms/config/config.go
@@ -21,12 +21,16 @@ import (
"os"
)
+// SMSConfiguration loads up all the values that are used to configure
+// backend implementations
+// TODO: Review these and see if they can be created/discovered dynamically
type SMSConfiguration struct {
CAFile string `json:"cafile"`
ServerCert string `json:"servercert"`
ServerKey string `json:"serverkey"`
VaultAddress string `json:"vaultaddress"`
+ VaultToken string `json:"vaulttoken"`
}
// SMSConfig is the structure that stores the configuration
diff --git a/sms-service/src/sms/handler/handler.go b/sms-service/src/sms/handler/handler.go
index 7a3b7dc..f287263 100644
--- a/sms-service/src/sms/handler/handler.go
+++ b/sms-service/src/sms/handler/handler.go
@@ -21,19 +21,19 @@ import (
"github.com/gorilla/mux"
"net/http"
- "sms/backend"
+ smsbackend "sms/backend"
)
// handler stores two interface implementations that implement
// the backend functionality
type handler struct {
- secretBackend backend.SecretBackend
- loginBackend backend.LoginBackend
+ secretBackend smsbackend.SecretBackend
+ loginBackend smsbackend.LoginBackend
}
// createSecretDomainHandler creates a secret domain with a name provided
func (h handler) createSecretDomainHandler(w http.ResponseWriter, r *http.Request) {
- var d backend.SecretDomain
+ var d smsbackend.SecretDomain
err := json.NewDecoder(r.Body).Decode(&d)
if err != nil {
@@ -41,7 +41,17 @@ func (h handler) createSecretDomainHandler(w http.ResponseWriter, r *http.Reques
return
}
- h.secretBackend.CreateSecretDomain(d.Name)
+ dom, err := h.secretBackend.CreateSecretDomain(d.Name)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+
+ err = json.NewEncoder(w).Encode(dom)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
}
// getSecretDomainHandler returns list of secret domains
@@ -63,10 +73,12 @@ func (h handler) deleteSecretDomainHandler(w http.ResponseWriter, r *http.Reques
// createSecretHandler handles creation of secrets on a given domain name
func (h handler) createSecretHandler(w http.ResponseWriter, r *http.Request) {
+ // Get domain name from URL
vars := mux.Vars(r)
domName := vars["domName"]
- var b backend.Secret
+ // Get secrets to be stored from body
+ var b smsbackend.Secret
err := json.NewDecoder(r.Body).Decode(&b)
if err != nil {
http.Error(w, err.Error(), 400)
@@ -121,9 +133,19 @@ func (h handler) loginHandler(w http.ResponseWriter, r *http.Request) {
}
+// initSMSHandler
+func (h handler) initSMSHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
+// unsealHandler
+func (h handler) unsealHandler(w http.ResponseWriter, r *http.Request) {
+
+}
+
// CreateRouter returns an http.Handler for the registered URLs
// Takes an interface implementation as input
-func CreateRouter(b backend.SecretBackend) http.Handler {
+func CreateRouter(b smsbackend.SecretBackend) http.Handler {
h := handler{secretBackend: b}
// Create a new mux to handle URL endpoints
@@ -131,7 +153,11 @@ func CreateRouter(b backend.SecretBackend) http.Handler {
router.HandleFunc("/v1/sms/login", h.loginHandler).Methods("POST")
+ // Initialization APIs which will be used by quorum client
+ // to unseal and to provide root token to sms service
router.HandleFunc("/v1/sms/status", h.statusHandler).Methods("GET")
+ router.HandleFunc("/v1/sms/unseal", h.unsealHandler).Methods("POST")
+ router.HandleFunc("/v1/sms/init", h.initSMSHandler).Methods("POST")
router.HandleFunc("/v1/sms/domain", h.createSecretDomainHandler).Methods("POST")
router.HandleFunc("/v1/sms/domain/{domName}", h.getSecretDomainHandler).Methods("GET")
diff --git a/sms-service/src/sms/smsconfig.json b/sms-service/src/sms/smsconfig.json
index ddb89d3..b683bf8 100644
--- a/sms-service/src/sms/smsconfig.json
+++ b/sms-service/src/sms/smsconfig.json
@@ -1,7 +1,8 @@
{
- "cafile": "auth/selfsignedca.pem",
+ "cafile": "auth/selfsignedca.pem",
"servercert": "auth/server_cat.cert",
- "serverkey": "auth/server.key",
+ "serverkey": "auth/server.key",
- "vaultaddress": "http://localhost:8200"
+ "vaultaddress": "http://localhost:8200",
+ "vaulttoken": "1ee03564-80d8-2080-2c77-0bb097cba512"
}