Skip to content

Commit 832e316

Browse files
Merge pull request #8 from Keyfactor/configuration_update
Configuration update
2 parents 75a67c4 + cff388c commit 832e316

File tree

18 files changed

+1632
-2353
lines changed

18 files changed

+1632
-2353
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.vs/*
22
vaultSecretsEngine-update.zip
33
vault/plugins/*
4+
*.exe

.vscode/templates/go.lict

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Copyright %(CurrentYear) Keyfactor
2+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
3+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
5+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
6+
and limitations under the License.

.vscode/templates/sh.lict

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Copyright %(CurrentYear) Keyfactor
2+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
3+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
5+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
6+
and limitations under the License.

README.md

Lines changed: 112 additions & 110 deletions
Large diffs are not rendered by default.

backend.go

Lines changed: 86 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,119 @@
11
package keyfactor
22

33
import (
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
3023
func 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

Comments
 (0)