Skip to content

Commit

Permalink
update for signing a pre-generated csr.
Browse files Browse the repository at this point in the history
  • Loading branch information
joevanwanzeeleKF committed Apr 21, 2022
1 parent fb305de commit a44be48
Show file tree
Hide file tree
Showing 8 changed files with 521 additions and 923 deletions.
344 changes: 1 addition & 343 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,96 +66,6 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
// Store certificates by serial number
type backend struct {
*framework.Backend
//storage logical.Storage
crlLifetime time.Duration
//tidyCASGuard *uint32
//store map[string][]byte
}

// func (b *backend) paths() []*framework.Path {
// return []*framework.Path{
// {
// Pattern: "issue/" + framework.GenericNameRegex("role"),

// Fields: addIssueAndSignCommonFields(map[string]*framework.FieldSchema{
// "role": {
// Type: framework.TypeLowerCaseString,
// Description: "Name of the role",
// Required: true,
// },
// }),

// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.UpdateOperation: b.handleWrite,
// logical.CreateOperation: b.handleWrite,
// },

// ExistenceCheck: b.handleExistenceCheck,
// },
// {
// Pattern: "revoke/",
// Fields: map[string]*framework.FieldSchema{
// "serial_number": {
// Type: framework.TypeString,
// Description: "The serial number of the certificate to revoke",
// Required: true,
// },
// },
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.UpdateOperation: b.handleRevoke,
// },
// },
// {
// Pattern: "sign/",
// Fields: addIssueAndSignCommonFields(map[string]*framework.FieldSchema{}),
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.UpdateOperation: b.handleSign,
// },
// },
// {
// Pattern: "ca/",
// Fields: addIssueAndSignCommonFields(map[string]*framework.FieldSchema{}),
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.ReadOperation: b.getCACert,
// },
// },
// {
// Pattern: "ca_chain/",
// Fields: addIssueAndSignCommonFields(map[string]*framework.FieldSchema{}),
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.ReadOperation: b.getCertChain,
// },
// },
// {
// Pattern: "certs/?$",
// Fields: addIssueAndSignCommonFields(map[string]*framework.FieldSchema{}),
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.ListOperation: b.handleList,
// },
// },
// {
// Pattern: `cert/(?P<serial>[0-9A-Fa-f-:]+)`,
// Fields: map[string]*framework.FieldSchema{
// "serial_number": {
// Type: framework.TypeString,
// Description: "The serial number of the certificate to read",
// Required: true,
// },
// },
// Callbacks: map[logical.Operation]framework.OperationFunc{
// logical.ReadOperation: b.handleRead,
// },
// },
// }
// }

func (b *backend) handleExistenceCheck(ctx context.Context, req *logical.Request, data *framework.FieldData) (bool, error) {
out, err := req.Storage.Get(ctx, req.Path)
if err != nil {
return false, errwrap.Wrapf("existence check failed: {{err}}", err)
}

return out != nil, nil
}

// Generate keypair and CSR
Expand Down Expand Up @@ -249,13 +159,7 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
}
serial := inner["SerialNumber"].(string)
kfId := inner["KeyfactorID"].(float64)
//caId := inner["CertificateAuthorityId"].(float64)
//b.Logger().Debug("CertificateAuthorityId = %d", caId)
// b.Logger().Debug("Serial number: ", serial)
// b.Logger().Debug("Keyfactor Id: ", kfId)
//b.store[serial] = []byte(certs[0])
// Retain the issuer cert for calls to "vault read keyfactor/cert/ca" - TODO Get via Keyfactor API
//b.save(ctx, req, serial, []byte(certs[0]))

if err != nil {
b.Logger().Error("unable to parse ca_chain response", err)
}
Expand Down Expand Up @@ -287,255 +191,9 @@ func (b *backend) submitCSR(ctx context.Context, req *logical.Request, csr strin
return nil, "", errwrap.Wrapf("unable to store the keyfactor ID for the certificate locally: {{err}}", err)
}

// caIdEntry, err := logical.StorageEntryJSON("caId", caId)
// if err != nil {
// return nil, "", err
// }

// err = req.Storage.Put(ctx, caIdEntry)
// if err != nil {
// return nil, "", errwrap.Wrapf("unable to store the CA ID for the certificate locally: {{err}}", err)
// }

return certs, serial, nil
}

// func (b *backend) requestCert(ctx context.Context, req *logical.Request, data *framework.FieldData, role string) (*logical.Response, error) {
// arg, _ := json.Marshal(req.Data)
// b.Logger().Debug(string(arg))
// cn := ""
// var ip_sans []string
// var dns_sans []string

// // Get and validate subject info from Vault command
// if len(req.Data) == 0 {
// return nil, fmt.Errorf("common_name must be provided to issue certificate")
// }
// for k, v := range req.Data {
// if k == "common_name" {
// cn = v.(string)
// }
// if k == "ip_sans" { // TODO - type switch
// ip_sans = strings.Split(v.(string), ",")
// }
// if k == "dns_sans" { // TODO - type switch
// dns_sans = strings.Split(v.(string), ",")
// }
// }
// b.Logger().Debug("about to check role: " + role + " against domain " + cn)

// if !b.checkDomainAgainstRole(ctx, req, role, cn) { // <-- leaving off here.. fix this function
// return nil, fmt.Errorf("common name not allowed for provided role")
// }
// for u := range dns_sans {
// if !b.checkDomainAgainstRole(ctx, req, role, dns_sans[u]) {
// return nil, fmt.Errorf("Subject Alternative Name " + dns_sans[u] + " not allowed for provided role")
// }
// }

// // Generate and submit the CSR
// csr, key := b.generateCSR(cn, ip_sans, dns_sans)
// certs, serial, err := b.submitCSR(ctx, req, csr)
// if err != nil {
// return nil, fmt.Errorf("could not enroll certificate: %s/", err)
// }

// // Conform response to Vault PKI API
// response := &logical.Response{
// Data: map[string]interface{}{
// "certificate": certs[0],
// "issuing_ca": certs[1],
// "private_key": "-----BEGIN RSA PRIVATE KEY-----\n" + base64.StdEncoding.EncodeToString(key) + "\n-----END RSA PRIVATE KEY-----",
// "private_key_type": "rsa",
// "revocation_time": 0,
// "serial_number": serial,
// },
// }

// return response, nil
// }

// func (b *backend) getCACert(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// if len(issuer_chain) == 0 {
// return nil, fmt.Errorf("CA certificate unknown")
// }
// b.Logger().Debug("issuer: " + issuer_chain[0])
// response := &logical.Response{
// Data: map[string]interface{}{
// "certificate": issuer_chain[0],
// },
// }
// return response, nil
// }

// func (b *backend) getCertChain(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// chain := ""
// for c := range issuer_chain {
// chain += issuer_chain[c]
// }
// b.Logger().Debug("issuer chain: " + chain)
// response := &logical.Response{
// Data: map[string]interface{}{
// "certificate": chain,
// },
// }
// return response, nil
// }

// Check if a domain is allowed for a given role based on allowed domains and whether subdomains are allowed
func (b *backend) checkDomainAgainstRole(ctx context.Context, req *logical.Request, role string, domain string) bool {
b.Logger().Debug("checking role: " + role + " against domain " + domain)

roleEntry, err := b.getRole(ctx, req.Storage, role)
if err != nil {
b.Logger().Error("Error retrieving role " + role)
return false
}

if roleEntry.AllowedBaseDomain == domain {
return true
}

if strings.Contains(domain, roleEntry.AllowedBaseDomain) && roleEntry.AllowSubdomains {
return true
}

return false
}

// Add role or enroll certificate
// func (b *backend) handleWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// b.load(ctx, req)
// role := data.Get("role").(string)

// b.Logger().Debug("parsing role: " + role)

// // look up role then request certificate

// entry, err := req.Storage.Get(ctx, "role/"+role)
// if err != nil || entry == nil {
// return nil, fmt.Errorf("cannot find provided role")
// }

// return b.requestCert(ctx, req, data, role)
// }

// func (b *backend) handleRevoke(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// sn := data.Get("serial_number").(string)

// if sn == "" {
// return nil, fmt.Errorf("must supply serial_number parameter to revoke")
// }

// return b.revoke(ctx, req, data, sn)
// }

func (b *backend) handleSign(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
role := data.Get("role").(string)
csr := data.Get("csr").(string)
if csr == "" {
return nil, fmt.Errorf("must supply csr parameter to sign")
}
return b.sign(ctx, req, csr, role)
}

func (b *backend) sign(ctx context.Context, req *logical.Request, csrString string, role string) (*logical.Response, error) {
// TODO - Get CSR to parse from CLI
// csr, err := x509.ParseCertificateRequest([]byte(csrString))
// if err != nil {
// return nil, fmt.Errorf("Could not parse CSR: {{err}}",err)
// }
// cn := csr.Subject.CommonName
// b.Logger().Debug("Got CSR with CN="+cn)
// if !b.checkDomainAgainstRole(role, cn) {
// return nil, fmt.Errorf("Common name {{cn}} is not allowed for provided role", cn)
// }
// TODO - check SANs

certs, serial, err := b.submitCSR(ctx, req, csrString)

if err != nil {
b.Logger().Info("Error signing certificate: {{err}}", err)
return nil, err
}

response := &logical.Response{
Data: map[string]interface{}{
"certificate": certs[0],
"issuing_ca": certs[1],
"ca_chain": certs[1:],
"serial_number": serial,
"revocation_time": 0,
},
}
// err = req.Storage.Put(ctx, &logical.StorageEntry{
// Key: "certs/" + normalizeSerial(serial),
// Value: certs[0],
// })
// if err != nil {
// return nil, errwrap.Wrapf("unable to store certificate locally: {{err}}", err)
// }
return response, nil

}

// Revoke certificate.
// func (b *backend) revoke(ctx context.Context, req *logical.Request, data *framework.FieldData, serial string) (*logical.Response, error) {
// serial = strings.ReplaceAll(serial, "-", "")
// serial = strings.ReplaceAll(serial, ":", "")
// fmt.Println("Revoking serial number " + serial)

// //b.Logger().Info(string(b.store[path]))

// // set up keyfactor api request
// url := config["protocol"] + "://" + config["host"] + "/CMSAPI/Certificates/3/Revoke"
// payload := `{"Lookup":{"Type":"Serial","SerialNumber":"` + serial + `","IssuerDN":"CN=jdk-CA1,DC=jdk,DC=cms"},"Details":{"Reason":4, "EffectiveDate": "2020-5-5", "Comment":"" }}`

// httpReq, _ := http.NewRequest("POST", url, strings.NewReader(payload))

// httpReq.Header.Add("content-type", "application/json")
// httpReq.Header.Add("authorization", "Basic "+config["creds"])

// res, err := http.DefaultClient.Do(httpReq)
// if err != nil {
// b.Logger().Error("Revoke failed: {{err}}", err)
// }

// defer res.Body.Close()
// _, _ = ioutil.ReadAll(res.Body)

// // Remove entry for specified path
// //delete(b.store, path)
// certEntry, err := fetchCertBySerial(ctx, req, "certs/", serial)
// var revInfo revocationInfo
// revEntry, err := fetchCertBySerial(ctx, req, "revoked/", serial)
// if err != nil {
// switch err.(type) {
// case errutil.UserError:
// return logical.ErrorResponse(err.Error()), nil
// case errutil.InternalError:
// return nil, err
// }
// }

// currTime := time.Now()
// revInfo.CertificateBytes = certEntry.Value
// revInfo.RevocationTime = currTime.Unix()
// revInfo.RevocationTimeUTC = currTime.UTC()

// revEntry, err = logical.StorageEntryJSON("revoked/"+normalizeSerial(serial), revInfo)
// if err != nil {
// return nil, fmt.Errorf("error creating revocation entry")
// }

// err = req.Storage.Put(ctx, revEntry)
// if err != nil {
// return nil, fmt.Errorf("error saving revoked certificate to new location")
// }

// return nil, nil
// }

const keyfactorHelp = `
The Keyfactor backend is a pki service that issues and manages certificates.
`
Loading

0 comments on commit a44be48

Please sign in to comment.