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 BackendServerName string `json:"servername"`
110 CAFile string `json:"cafile"`
111 ClientCert string `json:"clientcert"`
112 ClientKey string `json:"clientkey"`
113 TimeOut string `json:"timeout"`
114 DisableTLS bool `json:"disable_tls"`
117 //Load the config File for reading
118 vcf, err := os.Open("config.json")
120 log.Fatalf("Error reading config file %v", err)
124 err = json.NewDecoder(vcf).Decode(&cfg)
126 log.Fatalf("Error while parsing config file %v", err)
129 transport := http.Transport{}
131 if cfg.DisableTLS == false {
132 // Read the CA cert. This can be the self-signed CA
133 // or CA cert provided by AAF
134 caCert, err := ioutil.ReadFile(cfg.CAFile)
136 log.Fatalf("Error while reading CA file %v ", err)
139 caCertPool := x509.NewCertPool()
140 caCertPool.AppendCertsFromPEM(caCert)
143 Support Client certificates once we have auto generated certs
144 Load the client certificate files
145 cert, err := tls.LoadX509KeyPair(cfg.ClientCert, cfg.ClientKey)
147 log.Fatalf("Error while loading key pair %v ", err)
151 transport.TLSClientConfig = &tls.Config{
152 MinVersion: tls.VersionTLS12,
154 //Enable once we have proper client certificates
155 //Certificates: []tls.Certificate{cert},
159 // Allow https connection in k8s where servername does not match
160 // certificate server name
161 if cfg.BackendServerName != "" {
162 transport.TLSClientConfig.ServerName = cfg.BackendServerName
165 client := &http.Client{
166 Transport: &transport,
169 duration, _ := time.ParseDuration(cfg.TimeOut)
170 ticker := time.NewTicker(duration)
172 for _ = range ticker.C {
174 //URL and Port is configured in config file
175 response, err := client.Get(cfg.BackEndURL + "/v1/sms/quorum/status")
176 if smslogger.CheckError(err, "Connect to SMS") != nil {
181 Seal bool `json:"sealstatus"`
183 err = json.NewDecoder(response.Body).Decode(&data)
186 // Unseal the vault if sealed
188 //Register with SMS if not already done so
189 if !registrationDone {
190 body := strings.NewReader(`{"pgpkey":"` + pbkey + `","quorumid":"` + myID + `"}`)
191 res, err := client.Post(cfg.BackEndURL+"/v1/sms/quorum/register", "application/json", body)
192 if smslogger.CheckError(err, "Register with SMS") != nil {
195 registrationDone = true
197 Shard string `json:"shard"`
199 json.NewDecoder(res.Body).Decode(&data)
201 smsauth.WriteToFile(myShard, shardPath)
204 decShard, err := smsauth.DecryptPGPString(myShard, prkey)
205 body := strings.NewReader(`{"unsealshard":"` + decShard + `"}`)
206 //URL and PORT is configured via config file
207 response, err = client.Post(cfg.BackEndURL+"/v1/sms/quorum/unseal", "application/json", body)
208 if smslogger.CheckError(err, "Unsealing Vault") != nil {