/* * Copyright 2018 TechMahindra * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package main import ( "crypto/tls" "crypto/x509" "encoding/json" uuid "github.com/hashicorp/go-uuid" "io/ioutil" "log" "net/http" "os" "path/filepath" smsauth "sms/auth" smslogger "sms/log" "strings" "time" ) func loadPGPKeys(prKeyPath string, pbKeyPath string) (string, string, error) { var pbkey, prkey string generated := false prkey, err := smsauth.ReadFromFile(prKeyPath) if smslogger.CheckError(err, "LoadPGP Private Key") != nil { smslogger.WriteInfo("No Private Key found. Generating...") pbkey, prkey, _ = smsauth.GeneratePGPKeyPair() generated = true } else { pbkey, err = smsauth.ReadFromFile(pbKeyPath) if smslogger.CheckError(err, "LoadPGP Public Key") != 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() { folderName := filepath.Join("auth", os.Getenv("HOSTNAME")) //Make sure to create the folder. It is not guaranteed to exist os.MkdirAll(folderName, 0700) idFilePath := filepath.Join(folderName, "id") pbKeyPath := filepath.Join(folderName, "pbkey") prKeyPath := filepath.Join(folderName, "prkey") shardPath := filepath.Join(folderName, "shard") smslogger.Init("quorum.log") 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 smslogger.CheckError(err, "Read ID") != 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 smslogger.CheckError(err, "Read Shard") != 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 { BackEndURL string `json:"url"` BackendServerName string `json:"servername"` CAFile string `json:"cafile"` ClientCert string `json:"clientcert"` ClientKey string `json:"clientkey"` TimeOut string `json:"timeout"` DisableTLS bool `json:"disable_tls"` } //Load the config File for reading vcf, err := os.Open("config.json") if err != nil { log.Fatalf("Error reading config file %v", err) } cfg := config{} err = json.NewDecoder(vcf).Decode(&cfg) if err != nil { log.Fatalf("Error while parsing config file %v", err) } transport := http.Transport{} 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) if err != nil { log.Fatalf("Error while reading CA file %v ", err) } caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) /* Support Client certificates once we have auto generated certs 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) } */ transport.TLSClientConfig = &tls.Config{ MinVersion: tls.VersionTLS12, RootCAs: caCertPool, //Enable once we have proper client certificates //Certificates: []tls.Certificate{cert}, } } // Allow https connection in k8s where servername does not match // certificate server name if cfg.BackendServerName != "" { transport.TLSClientConfig.ServerName = cfg.BackendServerName } client := &http.Client{ Transport: &transport, } 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/quorum/status") if smslogger.CheckError(err, "Connect to SMS") != nil { continue } var data struct { Seal bool `json:"sealstatus"` } err = json.NewDecoder(response.Body).Decode(&data) sealed := data.Seal // Unseal the vault if sealed if sealed { //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 smslogger.CheckError(err, "Register with SMS") != nil { 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/quorum/unseal", "application/json", body) if smslogger.CheckError(err, "Unsealing Vault") != nil { continue } } } }