Skip to content

Commit 9883659

Browse files
updated CSR enrollment to use Keyfactor Client SDK
1 parent e4d9b5b commit 9883659

5 files changed

Lines changed: 84 additions & 102 deletions

File tree

backend.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"strings"
1717
"sync"
1818

19+
"github.com/Keyfactor/keyfactor-go-client-sdk/v24"
1920
"github.com/hashicorp/vault/sdk/framework"
2021
"github.com/hashicorp/vault/sdk/logical"
2122
)
@@ -39,7 +40,7 @@ type keyfactorBackend struct {
3940
*framework.Backend
4041
configLock sync.RWMutex
4142
cachedConfig *keyfactorConfig
42-
client *keyfactorClient
43+
client *keyfactor.APIClient
4344
}
4445

4546
// keyfactorBackend defines the target API keyfactorBackend
@@ -100,13 +101,12 @@ func (b *keyfactorBackend) invalidate(ctx context.Context, key string) {
100101

101102
// getClient locks the backend as it configures and creates a
102103
// a new client for the target API
103-
func (b *keyfactorBackend) getClient(ctx context.Context, s logical.Storage) (*keyfactorClient, error) {
104+
func (b *keyfactorBackend) getClient(ctx context.Context, s logical.Storage) (*keyfactor.APIClient, error) {
104105
b.configLock.RLock()
105106
defer b.configLock.RUnlock()
106107

107108
if b.client != nil {
108109
b.Logger().Debug("closing idle connections before returning existing client")
109-
b.client.httpClient.CloseIdleConnections()
110110
return b.client, nil
111111
}
112112

cert_util.go

Lines changed: 51 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"strings"
2929
"time"
3030

31+
v1 "github.com/Keyfactor/keyfactor-go-client-sdk/v24/api/keyfactor/v1"
3132
"github.com/hashicorp/errwrap"
3233
"github.com/hashicorp/vault/sdk/helper/errutil"
3334
"github.com/hashicorp/vault/sdk/logical"
@@ -70,116 +71,83 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
7071
}
7172

7273
location, _ := time.LoadLocation("UTC")
73-
t := time.Now().In(location)
74-
time := t.Format("2006-01-02T15:04:05")
74+
time := time.Now().In(location)
7575

7676
// get client
7777
client, err := b.getClient(ctx, req.Storage)
7878
if err != nil {
7979
return nil, "", fmt.Errorf("error getting client: %w", err)
8080
}
8181

82-
b.Logger().Debug("Closing idle connections")
83-
client.httpClient.CloseIdleConnections()
84-
8582
// build request parameter structure
83+
var metadataMap map[string]interface{}
8684

87-
// build dns_sans payload string
88-
dns_sans_payload_string := ""
85+
err = json.Unmarshal([]byte(metaDataJson), &metadataMap)
8986

90-
for _, d := range dns_sans {
91-
if d != dns_sans[0] {
92-
dns_sans_payload_string += "," // pre-pend a comma before next entry if not the first entry
93-
}
94-
dns_sans_payload_string = dns_sans_payload_string + fmt.Sprintf("\"%s\"", d)
87+
if err != nil {
88+
return nil, "", fmt.Errorf("there was an error parsing the Metadata as JSON: %w", err)
9589
}
96-
b.Logger().Debug("dns_sans payload string = %s", dns_sans_payload_string)
9790

98-
ip_sans_payload_string := ""
91+
inclChain := true
9992

100-
for _, i := range ip_sans {
101-
if i != ip_sans[0] {
102-
ip_sans_payload_string += ","
103-
}
104-
ip_sans_payload_string = ip_sans_payload_string + fmt.Sprintf("\"%s\"", i)
93+
enrollmentRequest := v1.EnrollmentCSREnrollmentRequest{
94+
CSR: csr,
95+
CertificateAuthority: *v1.NewNullableString(&caName),
96+
IncludeChain: &inclChain,
97+
Metadata: metadataMap,
98+
Timestamp: &time,
99+
Template: *v1.NewNullableString(&templateName),
100+
//SANs: map[string][]string{},
105101
}
106-
b.Logger().Debug("ip_sans payload string = %s", ip_sans_payload_string)
107102

108-
url := config.KeyfactorUrl + "/" + config.CommandAPIPath + "/Enrollment/CSR"
109-
b.Logger().Debug("url: " + url)
110-
bodyContent := "{\"CSR\": \"" + csr + "\", \"CertificateAuthority\":\"" + caName + "\", \"IncludeChain\": true, \"Metadata\": " + metaDataJson + ", \"Timestamp\": \"" + time + "\",\"Template\": \"" + templateName + "\""
103+
// SANs parameter
104+
b.Logger().Debug("ip_sans = %s", ip_sans)
105+
b.Logger().Debug("dns_sans = %s", dns_sans)
111106

112-
sans_payload := "\"SANs\": {"
107+
if len(ip_sans) > 0 {
108+
enrollmentRequest.SANs["ip"] = ip_sans
109+
}
113110

114-
if dns_sans_payload_string != "" || ip_sans_payload_string != "" {
115-
if dns_sans_payload_string != "" {
116-
sans_payload += "\"dns\": [" + dns_sans_payload_string + "]"
117-
}
118-
if ip_sans_payload_string != "" {
119-
sans_payload += ", \"ip\": [" + ip_sans_payload_string + "]"
120-
}
111+
if len(dns_sans) > 0 {
112+
enrollmentRequest.SANs["dns"] = dns_sans
121113
}
122-
sans_payload += "}"
123114

124-
b.Logger().Trace(fmt.Sprintf("sans_payload: %s", sans_payload))
125-
bodyContent += ", " + sans_payload + "}"
126-
payload := strings.NewReader(bodyContent)
115+
reqMap, _ := enrollmentRequest.ToMap()
127116

128-
b.Logger().Debug("request body: " + bodyContent)
129-
httpReq, err := http.NewRequest("POST", url, payload)
117+
b.Logger().Debug("request body: %s", reqMap)
130118

131-
if err != nil {
132-
b.Logger().Info("Error forming request: {{err}}", err)
133-
}
119+
// Send request and check status
134120

135-
httpReq.Header.Add("x-keyfactor-requested-with", "APIClient")
136-
httpReq.Header.Add("content-type", "application/json")
137-
httpReq.Header.Add("x-certificateformat", "PEM")
121+
b.Logger().Debug("about to connect to " + config.KeyfactorUrl + " with Keyfactor client for CSR submission")
138122

139-
// Send request and check status
123+
apiRequest := client.V1.EnrollmentApi.NewCreateEnrollmentCSRRequest(ctx)
124+
apiRequest.XCertificateformat("PEM")
125+
apiRequest.EnrollmentCSREnrollmentRequest(enrollmentRequest)
140126

141-
b.Logger().Debug("About to connect to " + config.KeyfactorUrl + "for csr submission")
142-
res, err := client.httpClient.Do(httpReq)
143-
if err != nil {
144-
b.Logger().Info("CSR Enrollment failed: {{err}}", err.Error())
127+
resData, httpRes, err := apiRequest.Execute()
128+
129+
if err != nil || httpRes.StatusCode != 200 {
130+
b.Logger().Error("there was an error performing CSR enrollment. HttpStatusCode: %d, error: %s", httpRes.StatusCode, err)
145131
return nil, "", err
146132
}
147-
if res.StatusCode != 200 {
148-
b.Logger().Error("CSR Enrollment failed: server returned" + fmt.Sprint(res.StatusCode))
149-
defer res.Body.Close()
150-
body, _ := io.ReadAll(res.Body)
151-
b.Logger().Error("Error response: " + string(body[:]))
152-
return nil, "", fmt.Errorf("CSR Enrollment request failed with status code %d and error: "+string(body[:]), res.StatusCode)
153-
}
154133

155-
// Read response and return certificate and key
134+
// Read certificates from response
135+
certs, ok := resData.CertificateInformation.GetCertificatesOk()
156136

157-
defer res.Body.Close()
158-
body, err := io.ReadAll(res.Body)
159-
if err != nil {
160-
b.Logger().Error("Error reading response: {{err}}", err)
137+
if !ok {
138+
b.Logger().Error("unable to read certificate response : %s", err)
161139
return nil, "", err
162140
}
163141

164-
// Parse response
165-
var r map[string]interface{}
166-
json.Unmarshal(body, &r)
167-
b.Logger().Debug("response = ", r)
142+
serial := resData.CertificateInformation.SerialNumber
143+
kfId := resData.CertificateInformation.KeyfactorID
168144

169-
inner := r["CertificateInformation"].(map[string]interface{})
170-
certI := inner["Certificates"].([]interface{})
171-
certs := make([]string, len(certI))
172-
for i, v := range certI {
173-
certs[i] = v.(string)
174-
start := strings.Index(certs[i], "-----BEGIN CERTIFICATE-----")
175-
certs[i] = certs[i][start:]
176-
}
177-
serial := inner["SerialNumber"].(string)
178-
kfId := inner["KeyfactorID"].(float64)
145+
resMap, _ := resData.ToMap()
146+
b.Logger().Debug("full response: %s", resMap)
179147

180-
b.Logger().Debug("parsed response: ", certI...)
148+
// store the ca chain
181149

182-
caEntry, err := logical.StorageEntryJSON("ca_chain/", certs[1:])
150+
caEntry, err := logical.StorageEntryJSON("ca_chain/", certs[1:]) // certs after the first one are the chain
183151
if err != nil {
184152
b.Logger().Error("error creating ca_chain entry", err)
185153
}
@@ -189,7 +157,11 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
189157
b.Logger().Error("error storing the ca_chain locally", err)
190158
}
191159

192-
key := "certs/" + normalizeSerial(serial)
160+
// store the certificate
161+
162+
normalizedSerial := *serial.Get()
163+
164+
key := "certs/" + normalizedSerial
193165

194166
entry := &logical.StorageEntry{
195167
Key: key,
@@ -203,7 +175,7 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
203175
return nil, "", errwrap.Wrapf("unable to store certificate locally: {{err}}", err)
204176
}
205177

206-
kfIdEntry, err := logical.StorageEntryJSON("kfId/"+normalizeSerial(serial), kfId)
178+
kfIdEntry, err := logical.StorageEntryJSON("kfId/"+normalizedSerial, kfId)
207179
if err != nil {
208180
return nil, "", err
209181
}
@@ -213,7 +185,7 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
213185
return nil, "", errwrap.Wrapf("unable to store the keyfactor ID for the certificate locally: {{err}}", err)
214186
}
215187

216-
return certs, serial, nil
188+
return certs, normalizedSerial, nil
217189
}
218190

219191
// fetch the CA info from keyfactor

client.go

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,13 @@ package kfbackend
1212
import (
1313
"errors"
1414
"fmt"
15-
"net/http"
1615

1716
"github.com/Keyfactor/keyfactor-auth-client-go/auth_providers"
17+
"github.com/Keyfactor/keyfactor-go-client-sdk/v24"
1818
)
1919

20-
type keyfactorClient struct {
21-
httpClient *http.Client
22-
}
23-
24-
func newClient(config *keyfactorConfig, b *keyfactorBackend) (*keyfactorClient, error) {
25-
client := new(keyfactorClient)
20+
func newClient(config *keyfactorConfig, b *keyfactorBackend) (*keyfactor.APIClient, error) {
21+
b.Logger().Trace("creating a new Keyfactor API client..")
2622

2723
if config == nil {
2824
return nil, errors.New("client configuration was nil")
@@ -40,16 +36,17 @@ func newClient(config *keyfactorConfig, b *keyfactorBackend) (*keyfactorClient,
4036
if !isBasicAuth && !isOAuth {
4137
return nil, errors.New(
4238
"invalid Keyfactor Command client configuration, " +
43-
"please provide a valid Basic auth or OAuth configuration",
39+
"please provide a valid Basic (username/password) or OAuth configuration",
4440
)
4541
}
4642

47-
oAuthConfig := &auth_providers.CommandConfigOauth{}
48-
basicAuthConfig := &auth_providers.CommandAuthConfigBasic{}
43+
var conf *auth_providers.Server
4944

5045
if isBasicAuth {
5146
b.Logger().Debug(fmt.Sprintf("using basic auth with username %s, domain %s and password (hidden)", config.Username, config.Domain))
5247

48+
basicAuthConfig := &auth_providers.CommandAuthConfigBasic{}
49+
5350
basicAuthConfig.WithCommandHostName(hostname).
5451
WithCommandAPIPath(config.CommandAPIPath).
5552
WithSkipVerify(config.SkipTLSVerify).
@@ -68,17 +65,21 @@ func newClient(config *keyfactorConfig, b *keyfactorBackend) (*keyfactorClient,
6865
b.Logger().Debug("successfully authenticated using basic auth")
6966
}
7067

71-
client.httpClient, bErr = basicAuthConfig.GetHttpClient()
72-
7368
if bErr != nil {
7469
errMsg := fmt.Sprintf("[ERROR] there was an error retreiving the basic auth http client: %s", bErr.Error())
7570
b.Logger().Error(errMsg)
7671
return nil, bErr
7772
}
7873

74+
conf = basicAuthConfig.GetServerConfig()
75+
7976
} else if isOAuth {
77+
oAuthConfig := &auth_providers.CommandConfigOauth{}
78+
8079
b.Logger().Debug(fmt.Sprintf("using oAuth authentication with client_id: %s, token_url %s and client_secret: (hidden)", config.ClientId, config.TokenUrl))
81-
_ = oAuthConfig.WithCommandHostName(hostname).
80+
81+
oAuthConfig.CommandAuthConfig.
82+
WithCommandHostName(hostname).
8283
WithCommandAPIPath(config.CommandAPIPath).
8384
WithSkipVerify(config.SkipTLSVerify).
8485
WithCommandCACert(config.CommandCertPath)
@@ -92,18 +93,22 @@ func newClient(config *keyfactorConfig, b *keyfactorBackend) (*keyfactorClient,
9293
WithAudience(config.Audience).
9394
Authenticate()
9495

96+
conf = oAuthConfig.GetServerConfig()
97+
9598
if oErr != nil {
9699
errMsg := fmt.Sprintf("[ERROR] unable to authenticate with provided oAuth credentials: %s", oErr.Error())
97100
b.Logger().Error(errMsg)
98101
return nil, oErr
99102
}
103+
}
100104

101-
client.httpClient, oErr = oAuthConfig.GetHttpClient()
102-
if oErr != nil {
103-
errMsg := fmt.Sprintf("[ERROR] there was an error retreiving the oAuth http client: %s", oErr.Error())
104-
b.Logger().Error(errMsg)
105-
return nil, oErr
106-
}
105+
c, err := keyfactor.NewAPIClient(conf)
106+
107+
if err != nil {
108+
errMsg := fmt.Sprintf("[ERROR] there was an error creating the Keyfactor client: %s", err.Error())
109+
b.Logger().Error(errMsg)
110+
return nil, err
107111
}
108-
return client, nil
112+
113+
return c, nil
109114
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ toolchain go1.23.3
66

77
require (
88
github.com/Keyfactor/keyfactor-auth-client-go v1.2.0
9+
github.com/Keyfactor/keyfactor-go-client-sdk/v24 v24.0.0
910
github.com/hashicorp/errwrap v1.0.0
1011
github.com/hashicorp/go-hclog v1.5.0
1112
github.com/hashicorp/vault/api v1.1.1

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
2121
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
2222
github.com/Keyfactor/keyfactor-auth-client-go v1.2.0 h1:uNSlyOW5Bqpi0nsOGZtOYQzN0vP/h4S4J38jtQes+OI=
2323
github.com/Keyfactor/keyfactor-auth-client-go v1.2.0/go.mod h1:7htRcBIWn+X4fI5jaYBALSYwP84H/djN7d8y3n0ZDQ0=
24+
github.com/Keyfactor/keyfactor-go-client-sdk v1.0.2 h1:caLlzFCz2L4Dth/9wh+VlypFATmOMmCSQkCPKOKMxw8=
25+
github.com/Keyfactor/keyfactor-go-client-sdk v1.0.2/go.mod h1:Z5pSk8YFGXHbKeQ1wTzVN8A4P/fZmtAwqu3NgBHbDOs=
26+
github.com/Keyfactor/keyfactor-go-client-sdk/v24 v24.0.0 h1:b7jYhcw4ENQC/VdFiikquI9lkUBt5C84TM9VnH3jzFI=
27+
github.com/Keyfactor/keyfactor-go-client-sdk/v24 v24.0.0/go.mod h1:xIbpgJ9eYfcYeSM0VzP5Q3ifgLCf3yGKKyZtWmqqOi8=
2428
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
2529
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
2630
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

0 commit comments

Comments
 (0)