Skip to content

Commit b2c7a25

Browse files
authored
Merge pull request #39 from Keyfactor/68509_including_scopes_and_audience_for_auth
go client SDK update, scopes and audience parameters
2 parents 3c68be1 + e4d9b5b commit b2c7a25

13 files changed

+789
-563
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
- 1.4.1
2+
- Updated CA and CA chain retreival to work for CA's hosted outside of Command (EJBCA)
3+
- Updated Keyfactor Client library to 1.2.0
4+
- Now passing scopes and audience along with oAuth token request.
5+
- including dns_sans, ip_sans and metadata along with pre-generated csr sign requests
6+
17
- 1.4.0
28
- Added support for oAuth2 authentication to Keyfactor Command.
39
- Included the ability to specify CA and Template via command parameters

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,13 @@ any of the paths below, use the help command with any route matching
332332
the path pattern. Note that depending on the policy of your auth token,
333333
you may or may not be able to access certain paths.
334334
335-
^ca(/pem)?$
335+
^ca
336336
Fetch a CA, CRL, CA Chain, or non-revoked certificate.
337+
pass "ca=<ca name>" to retrieve them for a CA other than the one set in the configuration.
337338
338-
^ca_chain(/pem)?$
339+
^ca_chain
339340
Fetch a CA, CRL, CA Chain, or non-revoked certificate.
341+
pass "ca=<ca name>" to retrieve them for a CA other than the one set in the configuration.
340342
341343
^certs/?$
342344
Use with the "list" command to display the list of certificate serial numbers for certificates managed by this secrets engine.
@@ -396,7 +398,7 @@ Here is a table of the available configuration paramaters
396398
| **token_url** | string | no[^3] | | oAuth authentication: Endpoint for retreiving the authentication token |
397399
| **access_token** | string | no | | oAuth access token, if retrieved outside the context of the plugin |
398400
| **scopes** | []string (comma separated list) | no | | the defined scopes to apply to the retreived token in the oAuth authorization flow. If not provided, all available scopes for the service account will be assigned to the token upon authentication |
399-
| **audience** | []string (comma seperated list) | no | | the OpenID Connect v1.0 or oAuth v2.0 token audience |
401+
| **audience** | string | no | | the OpenID Connect v1.0 or oAuth v2.0 token audience |
400402
| **skip_verify** | bool | no | _false_ | set this to true to skip checking the CRL list of the HTTPS endpoint |
401403
| **command_cert_path** | string | no | | set this value to the local path of the CA cert if it is untrusted by the client and skip_verify is false
402404

@@ -617,10 +619,10 @@ instance of the plugin is named "keyfactor".
617619

618620
### Read CA cert
619621

620-
`vault read keyfactor/ca`
622+
`vault read keyfactor/ca ca=<ca name>`
621623

622624
### Read CA chain
623625

624-
`vault read keyfactor/ca_chain`
626+
`vault read keyfactor/ca_chain ca=<ca name>`
625627

626628

backend.go

Lines changed: 2 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,11 @@ package kfbackend
1111

1212
import (
1313
"context"
14-
"encoding/json"
1514
"errors"
1615
"fmt"
17-
"io"
18-
"net/http"
1916
"strings"
2017
"sync"
21-
"time"
2218

23-
"github.com/hashicorp/errwrap"
2419
"github.com/hashicorp/vault/sdk/framework"
2520
"github.com/hashicorp/vault/sdk/logical"
2621
)
@@ -110,6 +105,8 @@ func (b *keyfactorBackend) getClient(ctx context.Context, s logical.Storage) (*k
110105
defer b.configLock.RUnlock()
111106

112107
if b.client != nil {
108+
b.Logger().Debug("closing idle connections before returning existing client")
109+
b.client.httpClient.CloseIdleConnections()
113110
return b.client, nil
114111
}
115112

@@ -129,141 +126,6 @@ func (b *keyfactorBackend) getClient(ctx context.Context, s logical.Storage) (*k
129126
return b.client, nil
130127
}
131128

132-
// Handle interface with Keyfactor API to enroll a certificate with given content
133-
func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request, csr string, caName string, templateName string, metaDataJson string) ([]string, string, error) {
134-
config, err := b.fetchConfig(ctx, req.Storage)
135-
if err != nil {
136-
return nil, "", err
137-
}
138-
if config == nil {
139-
return nil, "", errors.New("configuration is empty")
140-
}
141-
142-
location, _ := time.LoadLocation("UTC")
143-
t := time.Now().In(location)
144-
time := t.Format("2006-01-02T15:04:05")
145-
146-
// get client
147-
client, err := b.getClient(ctx, req.Storage)
148-
if err != nil {
149-
return nil, "", fmt.Errorf("error getting client: %w", err)
150-
}
151-
152-
b.Logger().Debug("Closing idle connections")
153-
client.httpClient.CloseIdleConnections()
154-
155-
// build request parameter structure
156-
157-
url := config.KeyfactorUrl + "/" + config.CommandAPIPath + "/Enrollment/CSR"
158-
b.Logger().Debug("url: " + url)
159-
bodyContent := "{\"CSR\": \"" + csr + "\",\"CertificateAuthority\":\"" + caName + "\",\"IncludeChain\": true, \"Metadata\": " + metaDataJson + ", \"Timestamp\": \"" + time + "\",\"Template\": \"" + templateName + "\",\"SANs\": {}}"
160-
payload := strings.NewReader(bodyContent)
161-
b.Logger().Debug("body: " + bodyContent)
162-
httpReq, err := http.NewRequest("POST", url, payload)
163-
164-
if err != nil {
165-
b.Logger().Info("Error forming request: {{err}}", err)
166-
}
167-
168-
httpReq.Header.Add("x-keyfactor-requested-with", "APIClient")
169-
httpReq.Header.Add("content-type", "application/json")
170-
httpReq.Header.Add("x-certificateformat", "PEM")
171-
172-
// Send request and check status
173-
174-
b.Logger().Debug("About to connect to " + config.KeyfactorUrl + "for csr submission")
175-
res, err := client.httpClient.Do(httpReq)
176-
if err != nil {
177-
b.Logger().Info("CSR Enrollment failed: {{err}}", err.Error())
178-
return nil, "", err
179-
}
180-
if res.StatusCode != 200 {
181-
b.Logger().Error("CSR Enrollment failed: server returned" + fmt.Sprint(res.StatusCode))
182-
defer res.Body.Close()
183-
body, _ := io.ReadAll(res.Body)
184-
b.Logger().Error("Error response: " + string(body[:]))
185-
return nil, "", fmt.Errorf("CSR Enrollment request failed with status code %d and error: "+string(body[:]), res.StatusCode)
186-
}
187-
188-
// Read response and return certificate and key
189-
190-
defer res.Body.Close()
191-
body, err := io.ReadAll(res.Body)
192-
if err != nil {
193-
b.Logger().Error("Error reading response: {{err}}", err)
194-
return nil, "", err
195-
}
196-
197-
// Parse response
198-
var r map[string]interface{}
199-
json.Unmarshal(body, &r)
200-
b.Logger().Debug("response = ", r)
201-
202-
inner := r["CertificateInformation"].(map[string]interface{})
203-
certI := inner["Certificates"].([]interface{})
204-
certs := make([]string, len(certI))
205-
for i, v := range certI {
206-
certs[i] = v.(string)
207-
start := strings.Index(certs[i], "-----BEGIN CERTIFICATE-----")
208-
certs[i] = certs[i][start:]
209-
}
210-
serial := inner["SerialNumber"].(string)
211-
kfId := inner["KeyfactorID"].(float64)
212-
213-
b.Logger().Debug("parsed response: ", certI...)
214-
215-
if err != nil {
216-
b.Logger().Error("unable to parse ca_chain response", fmt.Sprint(err))
217-
}
218-
caEntry, err := logical.StorageEntryJSON("ca_chain/", certs[1:])
219-
if err != nil {
220-
b.Logger().Error("error creating ca_chain entry", err)
221-
}
222-
223-
err = req.Storage.Put(ctx, caEntry)
224-
if err != nil {
225-
b.Logger().Error("error storing the ca_chain locally", err)
226-
}
227-
228-
key := "certs/" + normalizeSerial(serial)
229-
230-
entry := &logical.StorageEntry{
231-
Key: key,
232-
Value: []byte(certs[0]),
233-
}
234-
235-
b.Logger().Debug("cert entry.Value = ", string(entry.Value))
236-
237-
err = req.Storage.Put(ctx, entry)
238-
if err != nil {
239-
return nil, "", errwrap.Wrapf("unable to store certificate locally: {{err}}", err)
240-
}
241-
242-
kfIdEntry, err := logical.StorageEntryJSON("kfId/"+normalizeSerial(serial), kfId)
243-
if err != nil {
244-
return nil, "", err
245-
}
246-
247-
err = req.Storage.Put(ctx, kfIdEntry)
248-
if err != nil {
249-
return nil, "", errwrap.Wrapf("unable to store the keyfactor ID for the certificate locally: {{err}}", err)
250-
}
251-
252-
return certs, serial, nil
253-
}
254-
255129
const keyfactorHelp = `
256130
The Keyfactor backend is a pki service that issues and manages certificates.
257131
`
258-
259-
func (b *keyfactorBackend) isValidJSON(str string) bool {
260-
var js json.RawMessage
261-
err := json.Unmarshal([]byte(str), &js)
262-
if err != nil {
263-
b.Logger().Debug(err.Error())
264-
return false
265-
} else {
266-
b.Logger().Debug("the metadata was able to be parsed as valid JSON")
267-
return true
268-
}
269-
}

0 commit comments

Comments
 (0)