Skip to content

Commit fc27b2b

Browse files
committed
Feature enhancements and completed integration.
Fix: - Added PEM encoding to CSR prior to shipping to Pollendina Break: - Moved to HTTP from HTTPS until the service uses HTTPS - Client side certificate validation was commented out -- The current configuration has a difficult time parsing the asn1 data due to some mismatch. Needs investigation. Added: - Specifying a CA certificate will create a custom trust pool - Omitting a CA certificate will use the default trust pool - If the key location does not exist, it will be created - A tmpfs mount will be created at the key location (with CAP_SYS_ADMIN) Signed-off-by: Jeff Nickoloff <jeff@allingeek.com>
1 parent dab485a commit fc27b2b

1 file changed

Lines changed: 75 additions & 41 deletions

File tree

client/native/src/client.go

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,43 @@ import (
1515
"log"
1616
"net/http"
1717
"os"
18+
"path"
19+
"syscall"
1820
)
1921

2022
func main() {
2123

2224
var token = flag.String("token", "400", "The Pollendina provisioning token.")
2325
var endpoint = flag.String("host", ":33004", "Default port for Pollendina CA.")
2426
var cn = flag.String("cn", "Tommy", "Default common name.")
25-
var caFile = flag.String("cacert", "./cacert.pem", "The relevant CA certificate.")
26-
var keyFileOut = flag.String("keyout", "/etc/certs/client-key.pem", "The locattion where the client private key will be written.")
27-
var crtFileOut = flag.String("certout", "/etc/certs/client-cert.pem", "The locattion where the client certificate will be written.")
28-
flag.Parse()
27+
var caFile = flag.String("cacert", "", "File path for a custom CA certificate.")
28+
var keyFileOut = flag.String("keyout", "/etc/secret/client-key.pem", "The locattion where the client private key will be written.")
29+
var crtFileOut = flag.String("certout", "/etc/secret/client-cert.pem", "The locattion where the client certificate will be written.")
30+
var keysize = flag.Int("size", 4096, "The size of the private key e.g. 1024, 2048, 4096 (default).")
31+
flag.Parse()
2932

3033
// Load the CA certificate from the specified file
31-
cacert, err := ioutil.ReadFile(*caFile)
32-
if err != nil {
33-
log.Fatal(err)
34+
var pool *x509.CertPool
35+
if len(*caFile) > 0 {
36+
cacert, err := ioutil.ReadFile(*caFile)
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
pool = x509.NewCertPool()
41+
pool.AppendCertsFromPEM(cacert)
3442
}
35-
pool := x509.NewCertPool()
36-
pool.AppendCertsFromPEM(cacert)
3743

3844
// Generate the private key
39-
key, err := rsa.GenerateKey(rand.Reader, 4096)
45+
key, err := rsa.GenerateKey(rand.Reader, *keysize)
4046
if err != nil {
4147
log.Fatal("Unable to generate a private key.", err)
4248
os.Exit(1)
4349
}
50+
err = installKey(key, *keyFileOut)
51+
if err != nil {
52+
log.Fatal("Unable to install the private key.", err)
53+
os.Exit(4)
54+
}
4455

4556
// Generate a CSR
4657
t := x509.CertificateRequest{
@@ -56,26 +67,24 @@ func main() {
5667
os.Exit(2)
5768
}
5869

70+
// PEM encode the CSR
71+
pemCSR := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr})
72+
5973
// Fetch a signed certificate
60-
crt, err := shipCSR(&csr, token, endpoint, pool)
74+
crt, err := shipCSR(pemCSR, token, endpoint, pool)
6175
if err != nil {
6276
log.Fatal("Unable to retrieve a signed CRT.", err)
6377
os.Exit(3)
6478
}
6579

66-
err = installKey(key, *keyFileOut)
67-
if err != nil {
68-
log.Fatal("Unable to install the private key.", err)
69-
os.Exit(4)
70-
}
7180
err = installCrt(crt, *crtFileOut)
7281
if err != nil {
7382
log.Fatal("Unable to install the CRT.", err)
7483
os.Exit(5)
7584
}
7685
}
7786

78-
func shipCSR(csr *[]byte, token *string, endpoint *string, pool *x509.CertPool) ([]byte, error) {
87+
func shipCSR(csr []byte, token *string, endpoint *string, pool *x509.CertPool) ([]byte, error) {
7988
if csr == nil {
8089
return nil, errors.New("csr is nil")
8190
}
@@ -85,19 +94,25 @@ func shipCSR(csr *[]byte, token *string, endpoint *string, pool *x509.CertPool)
8594
if endpoint == nil {
8695
return nil, errors.New("endpoint is nil")
8796
}
88-
if pool == nil {
89-
return nil, errors.New("endpoint is nil")
90-
}
9197

92-
// Create an HTTPS client, and execute the PUT request
93-
tr := &http.Transport{
94-
TLSClientConfig: &tls.Config{RootCAs: pool},
95-
DisableCompression: true,
96-
}
97-
client := &http.Client{
98-
Transport: tr,
99-
}
100-
req, err := http.NewRequest("PUT", fmt.Sprintf("https://%s/v1/sign/%s", *endpoint, *token), bytes.NewReader(*csr))
98+
// Create an HTTPS client. If the pool has not been set, then use the default
99+
// CA trust store, otherwise add the configured pool into the tls config.
100+
var client *http.Client
101+
if pool != nil {
102+
tr := &http.Transport{
103+
TLSClientConfig: &tls.Config{RootCAs: pool},
104+
DisableCompression: true,
105+
}
106+
client = &http.Client{
107+
Transport: tr,
108+
}
109+
} else {
110+
client = &http.Client{}
111+
}
112+
113+
// Execute a PUT request to upload the provided CSR
114+
//req, err := http.NewRequest("PUT", fmt.Sprintf("https://%s/v1/sign/%s", *endpoint, *token), bytes.NewReader(csr))
115+
req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s/v1/sign/%s", *endpoint, *token), bytes.NewReader(csr))
101116
if err != nil {
102117
log.Fatal("Unable to form the HTTPS request.", err)
103118
return nil, err
@@ -112,28 +127,47 @@ func shipCSR(csr *[]byte, token *string, endpoint *string, pool *x509.CertPool)
112127

113128
// read the response body and get it into certificate form
114129
var rawCert []byte
115-
rawCert = make([]byte, req.ContentLength, req.ContentLength)
130+
rawCert = make([]byte, req.ContentLength, req.ContentLength)
116131
_, err = res.Body.Read(rawCert)
117132
if err != nil {
118133
log.Fatal("A problem occurred while reading the certificate from the Pollendina CA.", err)
119134
return nil, err
120135
}
121136
// TODO: validate that the number of bytes read matches the reported content length
122-
defer res.Body.Close()
123137

124138
// parse the CRT and validate form
125-
_, err = x509.ParseCertificate(rawCert)
126-
// TODO: assign the cert to a variable and actually validate some of the fields
127-
if err != nil {
128-
log.Fatal("A problem occurred while validating the generated certificate.", err)
129-
return nil, err
130-
}
139+
//_, err = x509.ParseCertificate(rawCert)
140+
// TODO: assign the cert to a variable and actually validate some of the fields
141+
//if err != nil {
142+
// log.Fatal("A problem occurred while validating the generated certificate.", err)
143+
// return nil, err
144+
//}
145+
defer res.Body.Close()
131146
return rawCert, nil
132147
}
133148

149+
// TODO: install functions should be defined on an interface. Composed implementations
150+
// would persist to various stores. This implementation will use mounted tmpfs, but others
151+
// might include some vault.
134152
func installKey(key *rsa.PrivateKey, location string) error {
135153

136-
log.Println("Writing file.")
154+
dir := path.Dir(location)
155+
// Create destination directory
156+
if err := syscall.Mkdir(dir, 0600); err != nil {
157+
if err != syscall.EEXIST {
158+
return err
159+
}
160+
// The directory already exists
161+
log.Printf("The key destination directory already exists.")
162+
}
163+
164+
// with CAP_SYS_ADMIN we could create a tmpfs mount
165+
log.Println("Creating tmpfs mount")
166+
if err := syscall.Mount("tmpfs", dir, "tmpfs", 0600, "size=1M"); err != nil {
167+
log.Printf("Unable to create tmpfs mount. Do you have CAP_SYS_ADMIN? Error: %s", err)
168+
}
169+
170+
log.Printf("Writing key: %s\n", location)
137171
// Write out PEM encoded private key file
138172
keyOut, err := os.OpenFile(location, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
139173
defer keyOut.Close()
@@ -143,12 +177,12 @@ func installKey(key *rsa.PrivateKey, location string) error {
143177
return nil
144178
}
145179
pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
146-
return nil
180+
return nil
147181
}
148182

149183
func installCrt(crt []byte, location string) error {
150184

151-
log.Println("Writing file.")
185+
log.Printf("Writing certificate: %s\n", location)
152186
return ioutil.WriteFile(location, crt, 0600)
153-
187+
154188
}

0 commit comments

Comments
 (0)