72447202775692590bd413937ab713502d9441ba
[aaf/sms.git] / sms-service / src / quorumclient / quorumclient.go
1 /*
2 * Copyright 2018 TechMahindra
3 *
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
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15  */
16
17 package main
18
19 import (
20         "crypto/tls"
21         "crypto/x509"
22         "encoding/json"
23         uuid "github.com/hashicorp/go-uuid"
24         "io/ioutil"
25         "log"
26         "net/http"
27         "os"
28         smsauth "sms/auth"
29         smslogger "sms/log"
30         "strings"
31         "time"
32 )
33
34 func loadPGPKeys(prKeyPath string, pbKeyPath string) (string, string, error) {
35
36         var pbkey, prkey string
37         generated := false
38         prkey, err := smsauth.ReadFromFile(prKeyPath)
39         if err != nil {
40                 smslogger.WriteWarn("No Private Key found. Generating...")
41                 pbkey, prkey, _ = smsauth.GeneratePGPKeyPair()
42                 generated = true
43         } else {
44                 pbkey, err = smsauth.ReadFromFile(pbKeyPath)
45                 if err != nil {
46                         smslogger.WriteWarn("No Public Key found. Generating...")
47                         pbkey, prkey, _ = smsauth.GeneratePGPKeyPair()
48                         generated = true
49                 }
50         }
51
52         // Storing the keys to file to allow for recovery during restarts
53         if generated {
54                 smsauth.WriteToFile(prkey, prKeyPath)
55                 smsauth.WriteToFile(pbkey, pbKeyPath)
56         }
57
58         return pbkey, prkey, nil
59
60 }
61
62 //This application checks the backend status and
63 //calls necessary initialization endpoints on the
64 //SMS webservice
65 func main() {
66         idFilePath := "auth/myid"
67         pbKeyPath := "auth/pbkey"
68         prKeyPath := "auth/prkey"
69         shardPath := "auth/shard"
70
71         smslogger.Init("")
72         smslogger.WriteInfo("Starting Log for Quorum Client")
73
74         /*
75                 myID is used to uniquely identify the quorum client
76                 Using any other information such as hostname is not
77                 guaranteed to be unique.
78                 In Kubernetes, pod restarts will also change the hostname
79         */
80         myID, err := smsauth.ReadFromFile(idFilePath)
81         if err != nil {
82                 smslogger.WriteWarn("Unable to find an ID for this client. Generating...")
83                 myID, _ = uuid.GenerateUUID()
84                 smsauth.WriteToFile(myID, idFilePath)
85         }
86
87         /*
88                 readMyShard will read the shard from disk when this client
89                 instance restarts. It will return err when a shard is not found.
90                 This is the case for first startup
91         */
92         registrationDone := true
93         myShard, err := smsauth.ReadFromFile(shardPath)
94         if err != nil {
95                 smslogger.WriteWarn("Unable to find a shard file. Registering with SMS...")
96                 registrationDone = false
97         }
98
99         pbkey, prkey, _ := loadPGPKeys(prKeyPath, pbKeyPath)
100
101         //Struct to read json configuration file
102         type config struct {
103                 BackEndURL string `json:"url"`
104                 CAFile     string `json:"cafile"`
105                 ClientCert string `json:"clientcert"`
106                 ClientKey  string `json:"clientkey"`
107                 TimeOut    string `json:"timeout"`
108                 DisableTLS bool   `json:"disable_tls"`
109         }
110
111         //Load the config File for reading
112         vcf, err := os.Open("config.json")
113         if err != nil {
114                 log.Fatalf("Error reading config file %v", err)
115         }
116
117         cfg := config{}
118         err = json.NewDecoder(vcf).Decode(&cfg)
119         if err != nil {
120                 log.Fatalf("Error while parsing config file %v", err)
121         }
122
123         transport := http.Transport{}
124
125         if cfg.DisableTLS == false {
126                 // Read the CA cert. This can be the self-signed CA
127                 // or CA cert provided by AAF
128                 caCert, err := ioutil.ReadFile(cfg.CAFile)
129                 if err != nil {
130                         log.Fatalf("Error while reading CA file %v ", err)
131                 }
132
133                 caCertPool := x509.NewCertPool()
134                 caCertPool.AppendCertsFromPEM(caCert)
135
136                 // Load the client certificate files
137                 //cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
138                 //if err != nil {
139                 //      log.Fatalf("Error while loading key pair %v ", err)
140                 //}
141
142                 transport.TLSClientConfig = &tls.Config{
143                         MinVersion: tls.VersionTLS12,
144                         RootCAs:    caCertPool,
145                         //Enable once we have proper client certificates
146                         //Certificates: []tls.Certificate{cert},
147                 }
148         }
149
150         client := &http.Client{
151                 Transport: &transport,
152         }
153
154         duration, _ := time.ParseDuration(cfg.TimeOut)
155         ticker := time.NewTicker(duration)
156
157         for _ = range ticker.C {
158
159                 //URL and Port is configured in config file
160                 response, err := client.Get(cfg.BackEndURL + "/v1/sms/quorum/status")
161                 if err != nil {
162                         smslogger.WriteError("Unable to connect to SMS. Retrying...")
163                         continue
164                 }
165
166                 var data struct {
167                         Seal bool `json:"sealstatus"`
168                 }
169                 err = json.NewDecoder(response.Body).Decode(&data)
170
171                 sealed := data.Seal
172
173                 // Unseal the vault if sealed
174                 if sealed {
175                         //Register with SMS if not already done so
176                         if !registrationDone {
177                                 body := strings.NewReader(`{"pgpkey":"` + pbkey + `","quorumid":"` + myID + `"}`)
178                                 res, err := client.Post(cfg.BackEndURL+"/v1/sms/quorum/register", "application/json", body)
179                                 if err != nil {
180                                         smslogger.WriteError("Ran into error during registration. Retrying...")
181                                         continue
182                                 }
183                                 registrationDone = true
184                                 var data struct {
185                                         Shard string `json:"shard"`
186                                 }
187                                 json.NewDecoder(res.Body).Decode(&data)
188                                 myShard = data.Shard
189                                 smsauth.WriteToFile(myShard, shardPath)
190                         }
191
192                         decShard, err := smsauth.DecryptPGPString(myShard, prkey)
193                         body := strings.NewReader(`{"unsealshard":"` + decShard + `"}`)
194                         //URL and PORT is configured via config file
195                         response, err = client.Post(cfg.BackEndURL+"/v1/sms/quorum/unseal", "application/json", body)
196                         if err != nil {
197                                 smslogger.WriteError("Error unsealing vault. Retrying... " + err.Error())
198                         }
199                 }
200         }
201 }