From ce74c5ac55fdb51582268046632943e510a19f00 Mon Sep 17 00:00:00 2001 From: Kiran Kamineni Date: Wed, 21 Mar 2018 09:13:26 -0700 Subject: Adding secure init code for backend Changes to allow quorum client to SMS communication Introducing a registration api for quorum clients to get their shard piece in PGP encrypted form from SMS Tested with 3 quorum clients. This is now ready for review. Issue-ID: AAF-168 Change-Id: I7a6ade792c1e5ebcf00cbc8c4a1f1942c006e7c7 Signed-off-by: Kiran Kamineni --- sms-service/src/quorumclient/Gopkg.toml | 24 +++++ sms-service/src/quorumclient/Makefile | 7 +- sms-service/src/quorumclient/config.json | 10 +-- sms-service/src/quorumclient/quorumclient.go | 127 +++++++++++++++++++++------ 4 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 sms-service/src/quorumclient/Gopkg.toml (limited to 'sms-service/src/quorumclient') diff --git a/sms-service/src/quorumclient/Gopkg.toml b/sms-service/src/quorumclient/Gopkg.toml new file mode 100644 index 0000000..7641eef --- /dev/null +++ b/sms-service/src/quorumclient/Gopkg.toml @@ -0,0 +1,24 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + +[[constraint]] + branch = "master" + name = "github.com/hashicorp/go-uuid" \ No newline at end of file diff --git a/sms-service/src/quorumclient/Makefile b/sms-service/src/quorumclient/Makefile index 720be29..00e12a7 100644 --- a/sms-service/src/quorumclient/Makefile +++ b/sms-service/src/quorumclient/Makefile @@ -1,13 +1,14 @@ GOPATH := $(shell realpath "$(CURDIR)/../../") BINARY := quorumclient PLATFORM := linux +DEPENDENCIES := github.com/golang/dep/cmd/dep export GOPATH ... all: test build deploy: test build -build: format +build: deps format CGO_ENABLED=0 GOOS=$(PLATFORM) go build -a \ -ldflags '-extldflags "-static"' \ -o $(GOPATH)/target/$(BINARY) -v quorumclient.go @@ -22,4 +23,8 @@ test: format: go fmt ./... +deps: + go get -u $(DEPENDENCIES) + $(GOPATH)/bin/dep ensure + .PHONY: test diff --git a/sms-service/src/quorumclient/config.json b/sms-service/src/quorumclient/config.json index 89979d5..a096968 100644 --- a/sms-service/src/quorumclient/config.json +++ b/sms-service/src/quorumclient/config.json @@ -1,9 +1,7 @@ { - "url":"https://localhost:10443/", - "cafile": "selfsignedca.pem", - "clientcert":"client.crt", + "url":"https://aaf-sms:10443", + "cafile": "auth/selfsignedca.pem", + "clientcert":"client.cert", "clientkey":"client.key", - "key":"UHFFY0l6WDhZVlErbGxvWitFVWpUL3FCV083NXRra1B2TDVBblN4VE5mYz0=", - "timeout":"60s", - "disable_tls":false + "timeout":"10s" } \ No newline at end of file diff --git a/sms-service/src/quorumclient/quorumclient.go b/sms-service/src/quorumclient/quorumclient.go index e3e6e40..7244720 100644 --- a/sms-service/src/quorumclient/quorumclient.go +++ b/sms-service/src/quorumclient/quorumclient.go @@ -19,8 +19,8 @@ package main import ( "crypto/tls" "crypto/x509" - "encoding/base64" "encoding/json" + uuid "github.com/hashicorp/go-uuid" "io/ioutil" "log" "net/http" @@ -31,11 +31,72 @@ import ( "time" ) +func loadPGPKeys(prKeyPath string, pbKeyPath string) (string, string, error) { + + var pbkey, prkey string + generated := false + prkey, err := smsauth.ReadFromFile(prKeyPath) + if err != nil { + smslogger.WriteWarn("No Private Key found. Generating...") + pbkey, prkey, _ = smsauth.GeneratePGPKeyPair() + generated = true + } else { + pbkey, err = smsauth.ReadFromFile(pbKeyPath) + if err != nil { + smslogger.WriteWarn("No Public Key found. Generating...") + pbkey, prkey, _ = smsauth.GeneratePGPKeyPair() + generated = true + } + } + + // Storing the keys to file to allow for recovery during restarts + if generated { + smsauth.WriteToFile(prkey, prKeyPath) + smsauth.WriteToFile(pbkey, pbKeyPath) + } + + return pbkey, prkey, nil + +} + //This application checks the backend status and //calls necessary initialization endpoints on the //SMS webservice func main() { - smslogger.Init("quorumclient.log") + idFilePath := "auth/myid" + pbKeyPath := "auth/pbkey" + prKeyPath := "auth/prkey" + shardPath := "auth/shard" + + smslogger.Init("") + smslogger.WriteInfo("Starting Log for Quorum Client") + + /* + myID is used to uniquely identify the quorum client + Using any other information such as hostname is not + guaranteed to be unique. + In Kubernetes, pod restarts will also change the hostname + */ + myID, err := smsauth.ReadFromFile(idFilePath) + if err != nil { + smslogger.WriteWarn("Unable to find an ID for this client. Generating...") + myID, _ = uuid.GenerateUUID() + smsauth.WriteToFile(myID, idFilePath) + } + + /* + readMyShard will read the shard from disk when this client + instance restarts. It will return err when a shard is not found. + This is the case for first startup + */ + registrationDone := true + myShard, err := smsauth.ReadFromFile(shardPath) + if err != nil { + smslogger.WriteWarn("Unable to find a shard file. Registering with SMS...") + registrationDone = false + } + + pbkey, prkey, _ := loadPGPKeys(prKeyPath, pbKeyPath) //Struct to read json configuration file type config struct { @@ -43,7 +104,6 @@ func main() { CAFile string `json:"cafile"` ClientCert string `json:"clientcert"` ClientKey string `json:"clientkey"` - B64Key string `json:"key"` TimeOut string `json:"timeout"` DisableTLS bool `json:"disable_tls"` } @@ -55,15 +115,14 @@ func main() { } cfg := config{} - decoder := json.NewDecoder(vcf) - err = decoder.Decode(&cfg) + err = json.NewDecoder(vcf).Decode(&cfg) if err != nil { log.Fatalf("Error while parsing config file %v", err) } transport := http.Transport{} - if cfg.DisableTLS { + if cfg.DisableTLS == false { // Read the CA cert. This can be the self-signed CA // or CA cert provided by AAF caCert, err := ioutil.ReadFile(cfg.CAFile) @@ -75,14 +134,16 @@ func main() { caCertPool.AppendCertsFromPEM(caCert) // Load the client certificate files - cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey) - if err != nil { - log.Fatalf("Error while loading key pair %v ", err) - } + //cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey) + //if err != nil { + // log.Fatalf("Error while loading key pair %v ", err) + //} transport.TLSClientConfig = &tls.Config{ - RootCAs: caCertPool, - Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + RootCAs: caCertPool, + //Enable once we have proper client certificates + //Certificates: []tls.Certificate{cert}, } } @@ -90,36 +151,50 @@ func main() { Transport: &transport, } - smsauth.GeneratePGPKeyPair() - duration, _ := time.ParseDuration(cfg.TimeOut) ticker := time.NewTicker(duration) for _ = range ticker.C { //URL and Port is configured in config file - response, err := client.Get(cfg.BackEndURL + "/v1/sms/status") + response, err := client.Get(cfg.BackEndURL + "/v1/sms/quorum/status") if err != nil { - log.Fatalf("Error while connecting to SMS webservice %v", err) + smslogger.WriteError("Unable to connect to SMS. Retrying...") + continue } - responseData, err := ioutil.ReadAll(response.Body) - if err != nil { - log.Fatalf("Error while reading response %v", err) + var data struct { + Seal bool `json:"sealstatus"` } + err = json.NewDecoder(response.Body).Decode(&data) - var data map[string]interface{} - json.Unmarshal(responseData, &data) - sealed := data["sealed"].(bool) + sealed := data.Seal // Unseal the vault if sealed if sealed { - decdB64Key, _ := base64.StdEncoding.DecodeString(cfg.B64Key) - body := strings.NewReader(`{"key":"` + string(decdB64Key) + `"}`) + //Register with SMS if not already done so + if !registrationDone { + body := strings.NewReader(`{"pgpkey":"` + pbkey + `","quorumid":"` + myID + `"}`) + res, err := client.Post(cfg.BackEndURL+"/v1/sms/quorum/register", "application/json", body) + if err != nil { + smslogger.WriteError("Ran into error during registration. Retrying...") + continue + } + registrationDone = true + var data struct { + Shard string `json:"shard"` + } + json.NewDecoder(res.Body).Decode(&data) + myShard = data.Shard + smsauth.WriteToFile(myShard, shardPath) + } + + decShard, err := smsauth.DecryptPGPString(myShard, prkey) + body := strings.NewReader(`{"unsealshard":"` + decShard + `"}`) //URL and PORT is configured via config file - response, err = client.Post(cfg.BackEndURL+"/v1/sms/unseal", "application/json", body) + response, err = client.Post(cfg.BackEndURL+"/v1/sms/quorum/unseal", "application/json", body) if err != nil { - log.Fatalf("Error while unsealing %v", err) + smslogger.WriteError("Error unsealing vault. Retrying... " + err.Error()) } } } -- cgit 1.2.3-korg