@@ -15,32 +15,43 @@ import (
1515 "log"
1616 "net/http"
1717 "os"
18+ "path"
19+ "syscall"
1820)
1921
2022func 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.
134152func 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
149183func 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