11package keyfactor
22
33import (
4- "bytes"
54 "context"
6- "crypto/rand"
7- "crypto/rsa"
8- "crypto/x509"
9- "crypto/x509/pkix"
10- "encoding/asn1"
5+ b64 "encoding/base64"
116 "encoding/json"
12- "encoding/pem "
7+ "errors "
138 "fmt"
149 "io/ioutil"
15- "net"
1610 "net/http"
17- "os"
1811 "strings"
12+ "sync"
1913 "time"
2014
2115 "github.com/hashicorp/errwrap"
2216 "github.com/hashicorp/vault/sdk/framework"
23- "github.com/hashicorp/vault/sdk/helper/jsonutil"
2417 "github.com/hashicorp/vault/sdk/logical"
2518)
2619
27- var config map [string ]string
20+ // var config map[string]string
2821
2922// Factory configures and returns backend
3023func Factory (ctx context.Context , conf * logical.BackendConfig ) (logical.Backend , error ) {
3124
32- var b backend
33-
34- b .Backend = & framework.Backend {
35- Help : strings .TrimSpace (keyfactorHelp ),
36- BackendType : logical .TypeLogical ,
37- Paths : []* framework.Path {
38- pathListRoles (& b ),
39- pathRoles (& b ),
40- pathFetchCA (& b ),
41- pathFetchCAChain (& b ),
42- pathFetchValid (& b ),
43- pathFetchListCerts (& b ),
44- pathRevoke (& b ),
45- pathIssue (& b ),
46- pathSign (& b ),
47- },
48- InitializeFunc : b .initialize ,
49- }
50-
51- if conf == nil {
52- return nil , fmt .Errorf ("configuration passed into backend is nil" )
25+ b := backend ()
26+ if err := b .Setup (ctx , conf ); err != nil {
27+ return nil , err
5328 }
54-
55- b .Backend .Setup (ctx , conf )
5629 return b , nil
5730}
5831
59- // Store certificates by serial number
60- type backend struct {
32+ // // Store certificates by serial number
33+ type keyfactorBackend struct {
6134 * framework.Backend
35+ lock sync.RWMutex
36+ cachedConfig * keyfactorConfig
37+ client * keyfactorClient
6238}
6339
64- func (b * backend ) initialize (ctx context.Context , req * logical.InitializationRequest ) error {
65- err := req .Storage .Delete (ctx , "/ca" )
40+ // keyfactorBackend defines the target API keyfactorBackend
41+ // for Vault. It must include each path
42+ // and the secrets it will store.
43+ func backend () * keyfactorBackend {
44+ var b = keyfactorBackend {}
6645
67- if err != nil {
68- b .Logger ().Error ("Error removing previous stored ca values on init" )
69- return err
70- }
71- confPath := os .Getenv ("KF_CONF_PATH" )
72- file , _ := ioutil .ReadFile (confPath )
73- config = make (map [string ]string )
74- jsonutil .DecodeJSON (file , & config )
75- b .Logger ().Debug ("INITIALIZE: KF_CONF_PATH = " + confPath )
76- b .Logger ().Debug ("config file contents = " , config )
77- return nil
46+ b .Backend = & framework.Backend {
47+ Help : strings .TrimSpace (keyfactorHelp ),
48+ PathsSpecial : & logical.Paths {
49+ LocalStorage : []string {},
50+ SealWrapStorage : []string {
51+ "config" ,
52+ "role/*" ,
53+ },
54+ },
55+ Paths : framework .PathAppend (
56+ pathConfig (& b ),
57+ pathRoles (& b ),
58+ pathCA (& b ),
59+ pathCerts (& b ),
60+ ),
61+ Secrets : []* framework.Secret {},
62+ BackendType : logical .TypeLogical ,
63+ Invalidate : b .invalidate ,
64+ }
65+ return & b
7866}
7967
80- // Generate keypair and CSR
81- func (b * backend ) generateCSR (cn string , ip_sans []string , dns_sans []string ) (string , []byte ) {
82- keyBytes , _ := rsa .GenerateKey (rand .Reader , 2048 )
83- subj := pkix.Name {
84- CommonName : cn ,
85- }
86- rawSubj := subj .ToRDNSequence ()
87- asn1Subj , _ := asn1 .Marshal (rawSubj )
88- var netIPSans []net.IP
89- for i := range ip_sans {
90- netIPSans = append (netIPSans , net .ParseIP (ip_sans [i ]))
91- }
92-
93- csrtemplate := x509.CertificateRequest {
94- RawSubject : asn1Subj ,
95- SignatureAlgorithm : x509 .SHA256WithRSA ,
96- IPAddresses : netIPSans ,
97- DNSNames : dns_sans ,
98- }
99- csrBytes , _ := x509 .CreateCertificateRequest (rand .Reader , & csrtemplate , keyBytes )
100- csrBuf := new (bytes.Buffer )
101- pem .Encode (csrBuf , & pem.Block {Type : "CERTIFICATE REQUEST" , Bytes : csrBytes })
102- return csrBuf .String (), x509 .MarshalPKCS1PrivateKey (keyBytes )
68+ // reset clears any client configuration for a new
69+ // backend to be configured
70+ func (b * keyfactorBackend ) reset () {
71+ b .lock .Lock ()
72+ defer b .lock .Unlock ()
73+ b .client = nil
10374}
10475
105- // Handle interface with Keyfactor API to enroll a certificate with given content
106- func (b * backend ) submitCSR (ctx context.Context , req * logical.Request , csr string , caName string , templateName string ) ([]string , string , error ) {
107- host := config ["host" ]
108- template := config ["template" ]
109- ca := config ["CA" ]
110- creds := config ["creds" ]
76+ // invalidate clears an existing client configuration in
77+ // the backend
78+ func (b * keyfactorBackend ) invalidate (ctx context.Context , key string ) {
79+ if key == "config" {
80+ b .reset ()
81+ }
82+ }
83+
84+ // getClient locks the backend as it configures and creates a
85+ // a new client for the target API
86+ func (b * keyfactorBackend ) getClient (ctx context.Context , s logical.Storage ) (* keyfactorClient , error ) {
87+ b .lock .RLock ()
88+ unlockFunc := b .lock .RUnlock
89+ defer func () { unlockFunc () }()
11190
112- if caName != "" {
113- ca = caName
91+ if b . client != nil {
92+ return b . client , nil
11493 }
11594
116- if templateName != "" {
117- template = templateName
95+ b .lock .RUnlock ()
96+ b .lock .Lock ()
97+ unlockFunc = b .lock .Unlock
98+
99+ return nil , fmt .Errorf ("need to return client" )
100+ }
101+
102+ // Handle interface with Keyfactor API to enroll a certificate with given content
103+ func (b * keyfactorBackend ) submitCSR (ctx context.Context , req * logical.Request , csr string , caName string , templateName string ) ([]string , string , error ) {
104+ config , err := b .config (ctx , req .Storage )
105+ if err != nil {
106+ return nil , "" , err
118107 }
108+ if config == nil {
109+ return nil , "" , errors .New ("configuration is empty." )
110+ }
111+
112+ ca := config .CertAuthority
113+ template := config .CertTemplate
114+
115+ creds := config .Username + ":" + config .Password
116+ encCreds := b64 .StdEncoding .EncodeToString ([]byte (creds ))
119117
120118 location , _ := time .LoadLocation ("UTC" )
121119 t := time .Now ().In (location )
@@ -126,7 +124,7 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
126124 http .DefaultClient .CloseIdleConnections ()
127125
128126 // Build request
129- url := config [ "protocol" ] + "://" + host + "/KeyfactorAPI/Enrollment/CSR"
127+ url := config . KeyfactorUrl + "/KeyfactorAPI/Enrollment/CSR"
130128 b .Logger ().Debug ("url: " + url )
131129 bodyContent := "{\" CSR\" : \" " + csr + "\" ,\" CertificateAuthority\" :\" " + ca + "\" ,\" IncludeChain\" : true, \" Metadata\" : {}, \" Timestamp\" : \" " + time + "\" ,\" Template\" : \" " + template + "\" ,\" SANs\" : {}}"
132130 payload := strings .NewReader (bodyContent )
@@ -137,21 +135,21 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
137135 }
138136 httpReq .Header .Add ("x-keyfactor-requested-with" , "APIClient" )
139137 httpReq .Header .Add ("content-type" , "application/json" )
140- httpReq .Header .Add ("authorization" , "Basic " + creds )
138+ httpReq .Header .Add ("authorization" , "Basic " + encCreds )
141139 httpReq .Header .Add ("x-certificateformat" , "PEM" )
142140
143141 // Send request and check status
144- b .Logger ().Debug ("About to connect to " + config [ "host" ] + "for csr submission" )
142+ b .Logger ().Debug ("About to connect to " + config . KeyfactorUrl + "for csr submission" )
145143 res , err := http .DefaultClient .Do (httpReq )
146144 if err != nil {
147- b .Logger ().Info ("CSR Enrollment failed: {{err}}" , err )
145+ b .Logger ().Info ("CSR Enrollment failed: {{err}}" , err . Error () )
148146 return nil , "" , err
149147 }
150148 if res .StatusCode != 200 {
151149 b .Logger ().Error ("CSR Enrollment failed: server returned" + fmt .Sprint (res .StatusCode ))
152150 defer res .Body .Close ()
153151 body , _ := ioutil .ReadAll (res .Body )
154- b .Logger ().Error ("Error response: " + fmt . Sprint (body ))
152+ b .Logger ().Error ("Error response: " + string (body [:] ))
155153 return nil , "" , fmt .Errorf ("enrollment failed: server returned %d\n " , res .StatusCode )
156154 }
157155
@@ -180,7 +178,7 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
180178 kfId := inner ["KeyfactorID" ].(float64 )
181179
182180 if err != nil {
183- b .Logger ().Error ("unable to parse ca_chain response" , err )
181+ b .Logger ().Error ("unable to parse ca_chain response" , fmt . Sprint ( err ) )
184182 }
185183 caEntry , err := logical .StorageEntryJSON ("ca_chain/" , certs [1 :])
186184 if err != nil {
0 commit comments