1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
/*
* 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
}
}
}
}
|