Skip to content

Commit 31b9647

Browse files
added "ca" and "template" as parameters to sign and issue reqs.
1 parent 40b90e7 commit 31b9647

File tree

4 files changed

+102
-103
lines changed

4 files changed

+102
-103
lines changed

backend.go

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ var config map[string]string
2828

2929
// Factory configures and returns backend
3030
func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
31-
confPath := os.Getenv("KF_CONF_PATH")
32-
33-
file, _ := ioutil.ReadFile(confPath)
34-
config = make(map[string]string)
35-
jsonutil.DecodeJSON(file, &config)
3631

3732
var b backend
3833

@@ -50,15 +45,14 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
5045
pathIssue(&b),
5146
pathSign(&b),
5247
},
48+
InitializeFunc: b.initialize,
5349
}
5450

5551
if conf == nil {
5652
return nil, fmt.Errorf("configuration passed into backend is nil")
5753
}
5854

5955
b.Backend.Setup(ctx, conf)
60-
b.Logger().Debug("KF_CONF_PATH = " + confPath)
61-
b.Logger().Debug("config file contents = ", config)
6256
return b, nil
6357
}
6458

@@ -67,6 +61,22 @@ type backend struct {
6761
*framework.Backend
6862
}
6963

64+
func (b *backend) initialize(ctx context.Context, req *logical.InitializationRequest) error {
65+
err := req.Storage.Delete(ctx, "/ca")
66+
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
78+
}
79+
7080
// Generate keypair and CSR
7181
func (b *backend) generateCSR(cn string, ip_sans []string, dns_sans []string) (string, []byte) {
7282
keyBytes, _ := rsa.GenerateKey(rand.Reader, 2048)
@@ -93,12 +103,20 @@ func (b *backend) generateCSR(cn string, ip_sans []string, dns_sans []string) (s
93103
}
94104

95105
// Handle interface with Keyfactor API to enroll a certificate with given content
96-
func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr string) ([]string, string, error) {
106+
func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr string, caName string, templateName string) ([]string, string, error) {
97107
host := config["host"]
98108
template := config["template"]
99109
ca := config["CA"]
100110
creds := config["creds"]
101111

112+
if caName != "" {
113+
ca = caName
114+
}
115+
116+
if templateName != "" {
117+
template = templateName
118+
}
119+
102120
location, _ := time.LoadLocation("UTC")
103121
t := time.Now().In(location)
104122
time := t.Format("2006-01-02T15:04:05")
@@ -126,12 +144,14 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
126144
b.Logger().Debug("About to connect to " + config["host"] + "for csr submission")
127145
res, err := http.DefaultClient.Do(httpReq)
128146
if err != nil {
129-
b.Logger().Info("Enrollment failed: {{err}}", err)
147+
b.Logger().Info("CSR Enrollment failed: {{err}}", err)
130148
return nil, "", err
131149
}
132150
if res.StatusCode != 200 {
133-
b.Logger().Error("Enrollment failed: server returned" + fmt.Sprint(res.StatusCode))
134-
b.Logger().Error("Error response = " + fmt.Sprint(res.Body))
151+
b.Logger().Error("CSR Enrollment failed: server returned" + fmt.Sprint(res.StatusCode))
152+
defer res.Body.Close()
153+
body, _ := ioutil.ReadAll(res.Body)
154+
b.Logger().Error("Error response: " + fmt.Sprint(body))
135155
return nil, "", fmt.Errorf("enrollment failed: server returned %d\n ", res.StatusCode)
136156
}
137157

cert_util.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func fetchCAInfo(ctx context.Context, req *logical.Request, b *backend) (respons
6161
if caEntry != nil {
6262
var r map[string]interface{}
6363
json.Unmarshal(caEntry.Value, &r)
64-
b.Logger().Debug("caEntry.Value = ", r)
64+
b.Logger().Debug("stored ca = ", r)
6565

6666
resp := &logical.Response{
6767
Data: r,
@@ -268,6 +268,7 @@ func fetchCertFromKeyfactor(ctx context.Context, req *logical.Request, b *backen
268268

269269
// Read response and return certificate and key
270270
defer res.Body.Close()
271+
271272
body, err := ioutil.ReadAll(res.Body)
272273
if err != nil {
273274
b.Logger().Info("Error reading response: {{err}}", err)

fields.go

Lines changed: 14 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,83 +2,26 @@ package keyfactor
22

33
import "github.com/hashicorp/vault/sdk/framework"
44

5-
// addIssueAndSignCommonFields adds fields common to both CA and non-CA issuing
6-
// and signing
7-
func addIssueAndSignCommonFields(fields map[string]*framework.FieldSchema) map[string]*framework.FieldSchema {
8-
fields["exclude_cn_from_sans"] = &framework.FieldSchema{
9-
Type: framework.TypeBool,
10-
Default: false,
11-
Description: `If true, the Common Name will not be
12-
included in DNS or Email Subject Alternate Names.
13-
Defaults to false (CN is included).`,
14-
DisplayAttrs: &framework.DisplayAttributes{
15-
Name: "Exclude Common Name from Subject Alternative Names (SANs)",
16-
},
17-
}
5+
// addNonCACommonFields adds fields with help text specific to non-CA
6+
// certificate issuing and signing
7+
func addNonCACommonFields(fields map[string]*framework.FieldSchema) map[string]*framework.FieldSchema {
188

19-
fields["format"] = &framework.FieldSchema{
20-
Type: framework.TypeString,
21-
Default: "pem",
22-
Description: `Format for returned data. Can be "pem", "der",
23-
or "pem_bundle". If "pem_bundle" any private
24-
key and issuing cert will be appended to the
25-
certificate pem. Defaults to "pem".`,
26-
AllowedValues: []interface{}{"pem", "der", "pem_bundle"},
27-
DisplayAttrs: &framework.DisplayAttributes{
28-
Value: "pem",
29-
},
9+
fields["ca"] = &framework.FieldSchema{
10+
Type: framework.TypeString,
11+
Description: `Specify the CA to use for the request in the format "<host\\logical>". If blank, will use the default from configuration.`,
3012
}
3113

32-
fields["private_key_format"] = &framework.FieldSchema{
33-
Type: framework.TypeString,
34-
Default: "der",
35-
Description: `Format for the returned private key.
36-
Generally the default will be controlled by the "format"
37-
parameter as either base64-encoded DER or PEM-encoded DER.
38-
However, this can be set to "pkcs8" to have the returned
39-
private key contain base64-encoded pkcs8 or PEM-encoded
40-
pkcs8 instead. Defaults to "der".`,
41-
AllowedValues: []interface{}{"", "der", "pem", "pkcs8"},
42-
DisplayAttrs: &framework.DisplayAttributes{
43-
Value: "der",
44-
},
14+
fields["template"] = &framework.FieldSchema{
15+
Type: framework.TypeString,
16+
Description: `Specify the name of the certificate template to use for the request. If blank, will use the default from configuration.`,
4517
}
4618

47-
fields["ip_sans"] = &framework.FieldSchema{
48-
Type: framework.TypeCommaStringSlice,
49-
Description: `The requested IP SANs, if any, in a
50-
comma-delimited list`,
51-
DisplayAttrs: &framework.DisplayAttributes{
52-
Name: "IP Subject Alternative Names (SANs)",
53-
},
19+
fields["dns_sans"] = &framework.FieldSchema{
20+
Type: framework.TypeString,
21+
Description: `Comma seperated list of DNS Subject Alternative Names`,
22+
Required: true,
5423
}
5524

56-
fields["uri_sans"] = &framework.FieldSchema{
57-
Type: framework.TypeCommaStringSlice,
58-
Description: `The requested URI SANs, if any, in a
59-
comma-delimited list.`,
60-
DisplayAttrs: &framework.DisplayAttributes{
61-
Name: "URI Subject Alternative Names (SANs)",
62-
},
63-
}
64-
65-
fields["other_sans"] = &framework.FieldSchema{
66-
Type: framework.TypeCommaStringSlice,
67-
Description: `Requested other SANs, in an array with the format
68-
<oid>;UTF8:<utf8 string value> for each entry.`,
69-
DisplayAttrs: &framework.DisplayAttributes{
70-
Name: "Other SANs",
71-
},
72-
}
73-
74-
return fields
75-
}
76-
77-
// addNonCACommonFields adds fields with help text specific to non-CA
78-
// certificate issuing and signing
79-
func addNonCACommonFields(fields map[string]*framework.FieldSchema) map[string]*framework.FieldSchema {
80-
fields = addIssueAndSignCommonFields(fields)
81-
8225
fields["role"] = &framework.FieldSchema{
8326
Type: framework.TypeString,
8427
Description: `The desired role with configuration for this
@@ -91,6 +34,7 @@ request`,
9134
one, specify the alternative names in the
9235
alt_names map. If email protection is enabled
9336
in the role, this may be an email address.`,
37+
Required: true,
9438
}
9539

9640
fields["alt_names"] = &framework.FieldSchema{

path_issue_sign.go

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,13 @@ func (b *backend) pathSign(ctx context.Context, req *logical.Request, data *fram
8787
return logical.ErrorResponse(fmt.Sprintf("unknown role: %s", roleName)), nil
8888
}
8989

90-
certs, serial, errr := b.submitCSR(ctx, req, csr)
90+
caName := data.Get("ca").(string)
91+
templateName := data.Get("template").(string)
92+
93+
b.Logger().Debug("CA Name parameter = " + caName)
94+
b.Logger().Debug("Template name parameter = " + templateName)
95+
96+
certs, serial, errr := b.submitCSR(ctx, req, csr, caName, templateName)
9197

9298
if errr != nil {
9399
return nil, fmt.Errorf("could not sign csr: %s", errr)
@@ -109,36 +115,63 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d
109115
return nil, logical.ErrReadOnly
110116
}
111117

112-
arg, _ := json.Marshal(req.Data)
113-
b.Logger().Debug(string(arg))
114-
cn := ""
115118
var ip_sans []string
116119
var dns_sans []string
117120

118-
// Get and validate subject info from Vault command
119-
for k, v := range req.Data {
120-
if k == "common_name" {
121-
cn = v.(string)
122-
}
123-
if k == "ip_sans" { // TODO - type switch
124-
ip_sans = strings.Split(v.(string), ",")
125-
}
126-
if k == "dns_sans" { // TODO - type switch
127-
dns_sans = strings.Split(v.(string), ",")
128-
}
121+
arg, _ := json.Marshal(req.Data)
122+
b.Logger().Debug(string(arg))
123+
124+
// get common name
125+
cn, ok := data.GetOk("common_name")
126+
127+
if !ok {
128+
return nil, fmt.Errorf("common_name must be provided to issue certificate")
129129
}
130130

131+
cn = cn.(string)
132+
131133
if cn == "" {
132134
return nil, fmt.Errorf("common_name must be provided to issue certificate")
133135
}
134136

135-
var err_resp error
137+
// get dns sans (required)
138+
dns_sans_string, ok := data.GetOk("dns_sans")
136139

137-
if strings.Contains(cn, role.AllowedBaseDomain) && !role.AllowSubdomains {
138-
err_resp = fmt.Errorf("sub-domains not allowed for role")
140+
if !ok {
141+
return nil, fmt.Errorf("dns_sans must be provided to issue certificate")
139142
}
140143

141-
if role.AllowedBaseDomain == cn {
144+
dns_sans_string = dns_sans_string.(string)
145+
146+
if dns_sans_string == "" {
147+
return nil, fmt.Errorf("dns_sans must be provided to issue certificate")
148+
}
149+
150+
dns_sans = strings.Split(dns_sans_string.(string), ",")
151+
152+
if len(dns_sans) == 0 {
153+
return nil, fmt.Errorf("dns_sans must be provided to issue certificate")
154+
}
155+
156+
// get ip sans (optional)
157+
ip_sans_string, ok := data.GetOk("ip_sans")
158+
if ok {
159+
ip_sans = strings.Split(ip_sans_string.(string), ",")
160+
}
161+
162+
caName := data.Get("ca").(string)
163+
164+
templateName := data.Get("template").(string)
165+
166+
b.Logger().Debug("CA Name parameter = " + caName)
167+
b.Logger().Debug("Template name parameter = " + templateName)
168+
169+
//check role permissions
170+
var err_resp error
171+
if strings.Contains(cn.(string), role.AllowedBaseDomain) && !role.AllowSubdomains {
172+
err_resp = fmt.Errorf("sub-domains not allowed for role")
173+
}
174+
if role.AllowedBaseDomain == cn.(string) {
142175
err_resp = fmt.Errorf("common name not allowed for provided role")
143176
}
144177

@@ -152,8 +185,9 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d
152185
}
153186
}
154187

155-
csr, key := b.generateCSR(cn, ip_sans, dns_sans)
156-
certs, serial, errr := b.submitCSR(ctx, req, csr)
188+
//generate and submit CSR
189+
csr, key := b.generateCSR(cn.(string), ip_sans, dns_sans)
190+
certs, serial, errr := b.submitCSR(ctx, req, csr, caName, templateName)
157191

158192
if errr != nil {
159193
return nil, fmt.Errorf("could not enroll certificate: %s", errr)

0 commit comments

Comments
 (0)