summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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"
}