Add ability to configure the NotBefore property of certificates in role api (#5325)
* Add ability to configure the NotBefore property of certificates in role api * Update index.html.md * converting field to time.Duration * setting default back to 30s * renaming the parameter not_before_duration to differentiate between the NotBefore datetime on the cert * Update description
This commit is contained in:
parent
a105664141
commit
0fab335eec
|
@ -424,14 +424,6 @@ func checkCertsAndPrivateKey(keyType string, key crypto.Signer, usage x509.KeyUs
|
|||
}
|
||||
}
|
||||
|
||||
// 40 seconds since we add 30 second slack for clock skew
|
||||
if math.Abs(float64(time.Now().Unix()-cert.NotBefore.Unix())) > 40 {
|
||||
return nil, fmt.Errorf("validity period starts out of range")
|
||||
}
|
||||
if !cert.NotBefore.Before(time.Now().Add(-10 * time.Second)) {
|
||||
return nil, fmt.Errorf("validity period not far enough in the past")
|
||||
}
|
||||
|
||||
if math.Abs(float64(time.Now().Add(validity).Unix()-cert.NotAfter.Unix())) > 20 {
|
||||
return nil, fmt.Errorf("certificate validity end: %s; expected within 20 seconds of %s", cert.NotAfter.Format(time.RFC3339), time.Now().Add(validity).Format(time.RFC3339))
|
||||
}
|
||||
|
@ -976,6 +968,29 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
|||
}
|
||||
}
|
||||
|
||||
getNotBeforeCheck := func(role roleEntry) logicaltest.TestCheckFunc {
|
||||
var certBundle certutil.CertBundle
|
||||
return func(resp *logical.Response) error {
|
||||
err := mapstructure.Decode(resp.Data, &certBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsedCertBundle, err := certBundle.ToParsedCertBundle()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking generated certificate: %s", err)
|
||||
}
|
||||
cert := parsedCertBundle.Certificate
|
||||
|
||||
actualDiff := time.Now().Sub(cert.NotBefore)
|
||||
certRoleDiff := role.NotBeforeDuration - actualDiff
|
||||
// These times get truncated, so give a 1 second buffer on each side
|
||||
if certRoleDiff > -1*time.Second && certRoleDiff < 1*time.Second {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("validity period out of range diff: %v", certRoleDiff)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a TestCheckFunc that performs various validity checks on the
|
||||
// returned certificate information, mostly within checkCertsAndPrivateKey
|
||||
getCnCheck := func(name string, role roleEntry, key crypto.Signer, usage x509.KeyUsage, extUsage x509.ExtKeyUsage, validity time.Duration) logicaltest.TestCheckFunc {
|
||||
|
@ -1324,6 +1339,16 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep {
|
|||
roleVals.PostalCode = []string{"f00", "b4r"}
|
||||
addTests(getPostalCodeCheck(roleVals))
|
||||
}
|
||||
// NotBefore tests
|
||||
{
|
||||
roleVals.NotBeforeDuration = 10 * time.Second
|
||||
addTests(getNotBeforeCheck(roleVals))
|
||||
|
||||
roleVals.NotBeforeDuration = 30 * time.Second
|
||||
addTests(getNotBeforeCheck(roleVals))
|
||||
|
||||
roleVals.NotBeforeDuration = 0
|
||||
}
|
||||
// IP SAN tests
|
||||
{
|
||||
roleVals.UseCSRSANs = true
|
||||
|
@ -1961,8 +1986,8 @@ func TestBackend_SignSelfIssued(t *testing.T) {
|
|||
Subject: pkix.Name{
|
||||
CommonName: "foo.bar.com",
|
||||
},
|
||||
SerialNumber: big.NewInt(1234),
|
||||
IsCA: false,
|
||||
SerialNumber: big.NewInt(1234),
|
||||
IsCA: false,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
|
@ -1992,8 +2017,8 @@ func TestBackend_SignSelfIssued(t *testing.T) {
|
|||
Subject: pkix.Name{
|
||||
CommonName: "bar.foo.com",
|
||||
},
|
||||
SerialNumber: big.NewInt(2345),
|
||||
IsCA: true,
|
||||
SerialNumber: big.NewInt(2345),
|
||||
IsCA: true,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
ss, ssCert := getSelfSigned(template, issuer)
|
||||
|
@ -2573,10 +2598,9 @@ func setCerts() {
|
|||
DNSNames: []string{"root.localhost"},
|
||||
KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign),
|
||||
SerialNumber: big.NewInt(mathrand.Int63()),
|
||||
NotBefore: time.Now().Add(-30 * time.Second),
|
||||
NotAfter: time.Now().Add(262980 * time.Hour),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
IsCA: true,
|
||||
}
|
||||
caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, cak.Public(), cak)
|
||||
if err != nil {
|
||||
|
|
|
@ -79,7 +79,6 @@ func TestBackend_CA_Steps(t *testing.T) {
|
|||
DNSNames: []string{"root.localhost"},
|
||||
KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign),
|
||||
SerialNumber: big.NewInt(mathrand.Int63()),
|
||||
NotBefore: time.Now().Add(-30 * time.Second),
|
||||
NotAfter: time.Now().Add(262980 * time.Hour),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
|
|
|
@ -87,6 +87,9 @@ type creationParameters struct {
|
|||
|
||||
// The maximum path length to encode
|
||||
MaxPathLength int
|
||||
|
||||
// The duration the certificate will use NotBefore
|
||||
NotBeforeDuration time.Duration
|
||||
}
|
||||
|
||||
type caInfoBundle struct {
|
||||
|
@ -1019,6 +1022,7 @@ func generateCreationBundle(b *backend, data *dataBundle) error {
|
|||
ExtKeyUsageOIDs: data.role.ExtKeyUsageOIDs,
|
||||
PolicyIdentifiers: data.role.PolicyIdentifiers,
|
||||
BasicConstraintsValidForNonCA: data.role.BasicConstraintsValidForNonCA,
|
||||
NotBeforeDuration: data.role.NotBeforeDuration,
|
||||
}
|
||||
|
||||
// Don't deal with URLs or max path length if it's self-signed, as these
|
||||
|
@ -1165,7 +1169,6 @@ func createCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) {
|
|||
|
||||
certTemplate := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: time.Now().Add(-30 * time.Second),
|
||||
NotAfter: data.params.NotAfter,
|
||||
IsCA: false,
|
||||
SubjectKeyId: subjKeyID,
|
||||
|
@ -1175,6 +1178,9 @@ func createCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) {
|
|||
IPAddresses: data.params.IPAddresses,
|
||||
URIs: data.params.URIs,
|
||||
}
|
||||
if data.params.NotBeforeDuration > 0 {
|
||||
certTemplate.NotBefore = time.Now().Add(-1 * data.params.NotBeforeDuration)
|
||||
}
|
||||
|
||||
if err := handleOtherSANs(certTemplate, data.params.OtherSANs); err != nil {
|
||||
return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
|
||||
|
@ -1365,11 +1371,13 @@ func signCertificate(data *dataBundle) (*certutil.ParsedCertBundle, error) {
|
|||
certTemplate := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: data.params.Subject,
|
||||
NotBefore: time.Now().Add(-30 * time.Second),
|
||||
NotAfter: data.params.NotAfter,
|
||||
SubjectKeyId: subjKeyID[:],
|
||||
AuthorityKeyId: caCert.SubjectKeyId,
|
||||
}
|
||||
if data.params.NotBeforeDuration > 0 {
|
||||
certTemplate.NotBefore = time.Now().Add(-1 * data.params.NotBeforeDuration)
|
||||
}
|
||||
|
||||
switch data.signingBundle.PrivateKeyType {
|
||||
case certutil.RSAPrivateKey:
|
||||
|
|
|
@ -286,6 +286,11 @@ for "generate_lease".`,
|
|||
Type: framework.TypeBool,
|
||||
Description: `Mark Basic Constraints valid when issuing non-CA certificates.`,
|
||||
},
|
||||
"not_before_duration": &framework.FieldSchema{
|
||||
Type: framework.TypeDurationSecond,
|
||||
Default: 30,
|
||||
Description: `The duration before now the cert needs to be created / signed.`,
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -493,6 +498,7 @@ func (b *backend) pathRoleCreate(ctx context.Context, req *logical.Request, data
|
|||
AllowedSerialNumbers: data.Get("allowed_serial_numbers").([]string),
|
||||
PolicyIdentifiers: data.Get("policy_identifiers").([]string),
|
||||
BasicConstraintsValidForNonCA: data.Get("basic_constraints_valid_for_non_ca").(bool),
|
||||
NotBeforeDuration: time.Duration(data.Get("not_before_duration").(int)) * time.Second,
|
||||
}
|
||||
|
||||
otherSANs := data.Get("allowed_other_sans").([]string)
|
||||
|
@ -683,6 +689,7 @@ type roleEntry struct {
|
|||
PolicyIdentifiers []string `json:"policy_identifiers" mapstructure:"policy_identifiers"`
|
||||
ExtKeyUsageOIDs []string `json:"ext_key_usage_oids" mapstructure:"ext_key_usage_oids"`
|
||||
BasicConstraintsValidForNonCA bool `json:"basic_constraints_valid_for_non_ca" mapstructure:"basic_constraints_valid_for_non_ca"`
|
||||
NotBeforeDuration time.Duration `json:"not_before_duration" mapstructure:"not_before_duration"`
|
||||
|
||||
// Used internally for signing intermediates
|
||||
AllowExpirationPastCA bool
|
||||
|
@ -726,6 +733,7 @@ func (r *roleEntry) ToResponseData() map[string]interface{} {
|
|||
"require_cn": r.RequireCN,
|
||||
"policy_identifiers": r.PolicyIdentifiers,
|
||||
"basic_constraints_valid_for_non_ca": r.BasicConstraintsValidForNonCA,
|
||||
"not_before_duration": int64(r.NotBeforeDuration.Seconds()),
|
||||
}
|
||||
if r.MaxPathLength != nil {
|
||||
responseData["max_path_length"] = r.MaxPathLength
|
||||
|
|
|
@ -104,6 +104,11 @@ export default DS.Model.extend({
|
|||
basicConstraintsValidForNonCA: attr('boolean', {
|
||||
label: 'Mark Basic Constraints valid when issuing non-CA certificates.',
|
||||
}),
|
||||
notBeforeDuration: attr({
|
||||
label: 'Not Before Duration',
|
||||
editType: 'ttl',
|
||||
defaultValue: '30s',
|
||||
}),
|
||||
|
||||
updatePath: lazyCapabilities(apiPath`${'backend'}/roles/${'id'}`, 'backend', 'id'),
|
||||
canDelete: alias('updatePath.canDelete'),
|
||||
|
@ -137,6 +142,7 @@ export default DS.Model.extend({
|
|||
'organization',
|
||||
'keyUsage',
|
||||
'allowedOtherSans',
|
||||
'notBeforeDuration',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -883,11 +883,13 @@ request is denied.
|
|||
optional while generating a certificate.
|
||||
|
||||
- `policy_identifiers` `(list: [])` – A comma-separated string or list of policy
|
||||
oids.
|
||||
OIDs.
|
||||
|
||||
- `basic_constraints_valid_for_non_ca` `(bool: false)` - Mark Basic Constraints
|
||||
valid when issuing non-CA certificates.
|
||||
|
||||
- `not_before_duration` `(duration: "30s")` – Specifies the duration by which to backdate the NotBefore property.
|
||||
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
|
Loading…
Reference in a new issue