2 * Copyright 2018 TechMahindra
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 uuid "github.com/hashicorp/go-uuid"
35 func loadPGPKeys(prKeyPath string, pbKeyPath string) (string, string, error) {
37 var pbkey, prkey string
39 prkey, err := smsauth.ReadFromFile(prKeyPath)
40 if smslogger.CheckError(err, "LoadPGP Private Key") != nil {
41 smslogger.WriteInfo("No Private Key found. Generating...")
42 pbkey, prkey, _ = smsauth.GeneratePGPKeyPair()
45 pbkey, err = smsauth.ReadFromFile(pbKeyPath)
46 if smslogger.CheckError(err, "LoadPGP Public Key") != nil {
47 smslogger.WriteWarn("No Public Key found. Generating...")
48 pbkey, prkey, _ = smsauth.GeneratePGPKeyPair()
53 // Storing the keys to file to allow for recovery during restarts
55 smsauth.WriteToFile(prkey, prKeyPath)
56 smsauth.WriteToFile(pbkey, pbKeyPath)
59 return pbkey, prkey, nil
63 //This application checks the backend status and
64 //calls necessary initialization endpoints on the
67 folderName := filepath.Join("auth", os.Getenv("HOSTNAME"))
68 //Make sure to create the folder. It is not guaranteed to exist
69 os.MkdirAll(folderName, 0700)
71 idFilePath := filepath.Join(folderName, "id")
72 pbKeyPath := filepath.Join(folderName, "pbkey")
73 prKeyPath := filepath.Join(folderName, "prkey")
74 shardPath := filepath.Join(folderName, "shard")
76 smslogger.Init("quorum.log")
77 smslogger.WriteInfo("Starting Log for Quorum Client")
80 myID is used to uniquely identify the quorum client
81 Using any other information such as hostname is not
82 guaranteed to be unique.
83 In Kubernetes, pod restarts will also change the hostname
85 myID, err := smsauth.ReadFromFile(idFilePath)
86 if smslogger.CheckError(err, "Read ID") != nil {
87 smslogger.WriteWarn("Unable to find an ID for this client. Generating...")
88 myID, _ = uuid.GenerateUUID()
89 smsauth.WriteToFile(myID, idFilePath)
93 readMyShard will read the shard from disk when this client
94 instance restarts. It will return err when a shard is not found.
95 This is the case for first startup
97 registrationDone := true
98 myShard, err := smsauth.ReadFromFile(shardPath)
99 if smslogger.CheckError(err, "Read Shard") != nil {
100 smslogger.WriteWarn("Unable to find a shard file. Registering with SMS...")
101 registrationDone = false
104 pbkey, prkey, _ := loadPGPKeys(prKeyPath, pbKeyPath)
106 //Struct to read json configuration file
108 BackEndURL string `json:"url"`
109 CAFile string `json:"cafile"`
110 ClientCert string `json:"clientcert"`
111 ClientKey string `json:"clientkey"`
112 TimeOut string `json:"timeout"`
113 DisableTLS bool `json:"disable_tls"`
116 //Load the config File for reading
117 vcf, err := os.Open("config.json")
119 log.Fatalf("Error reading config file %v", err)
123 err = json.NewDecoder(vcf).Decode(&cfg)
125 log.Fatalf("Error while parsing config file %v", err)
128 transport := http.Transport{}
130 if cfg.DisableTLS == false {
131 // Read the CA cert. This can be the self-signed CA
132 // or CA cert provided by AAF
133 caCert, err := ioutil.ReadFile(cfg.CAFile)
135 log.Fatalf("Error while reading CA file %v ", err)
138 caCertPool := x509.NewCertPool()
139 caCertPool.AppendCertsFromPEM(caCert)
142 Support Client certificates once we have auto generated certs
143 Load the client certificate files
144 cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
146 log.Fatalf("Error while loading key pair %v ", err)
150 transport.TLSClientConfig = &tls.Config{
151 MinVersion: tls.VersionTLS12,
153 //Enable once we have proper client certificates
154 //Certificates: []tls.Certificate{cert},
158 client := &http.Client{
159 Transport: &transport,
162 duration, _ := time.ParseDuration(cfg.TimeOut)
163 ticker := time.NewTicker(duration)
165 for _ = range ticker.C {
167 //URL and Port is configured in config file
168 response, err := client.Get(cfg.BackEndURL + "/v1/sms/quorum/status")
169 if smslogger.CheckError(err, "Connect to SMS") != nil {
174 Seal bool `json:"sealstatus"`
176 err = json.NewDecoder(response.Body).Decode(&data)
179 // Unseal the vault if sealed
181 //Register with SMS if not already done so
182 if !registrationDone {
183 body := strings.NewReader(`{"pgpkey":"` + pbkey + `","quorumid":"` + myID + `"}`)
184 res, err := client.Post(cfg.BackEndURL+"/v1/sms/quorum/register", "application/json", body)
185 if smslogger.CheckError(err, "Register with SMS") != nil {
188 registrationDone = true
190 Shard string `json:"shard"`
192 json.NewDecoder(res.Body).Decode(&data)
194 smsauth.WriteToFile(myShard, shardPath)
197 decShard, err := smsauth.DecryptPGPString(myShard, prkey)
198 body := strings.NewReader(`{"unsealshard":"` + decShard + `"}`)
199 //URL and PORT is configured via config file
200 response, err = client.Post(cfg.BackEndURL+"/v1/sms/quorum/unseal", "application/json", body)
201 if smslogger.CheckError(err, "Unsealing Vault") != nil {