diff --git a/api/ssh.go b/api/ssh.go index 7e6ca0b6..4e6e9c0b 100644 --- a/api/ssh.go +++ b/api/ssh.go @@ -489,7 +489,7 @@ type identityModifier struct { NotAfter time.Time } -func (m *identityModifier) Constrain(cert *x509.Certificate) error { +func (m *identityModifier) Enforce(cert *x509.Certificate) error { cert.NotBefore = m.NotBefore cert.NotAfter = m.NotAfter return nil diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index d9b8df34..074e4470 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -47,11 +47,11 @@ type ProfileModifier interface { Option(o Options) x509util.WithOption } -// CertificateConstrainModifier is the interface used to modify a certificate -// after validation. -type CertificateConstrainModifier interface { +// CertificateEnforcer is the interface used to modify a certificate after +// validation. +type CertificateEnforcer interface { SignOption - Constrain(cert *x509.Certificate) error + Enforce(cert *x509.Certificate) error } // profileWithOption is a wrapper against x509util.WithOption to conform the diff --git a/authority/tls.go b/authority/tls.go index c25f91b5..dbfbf96a 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -61,10 +61,10 @@ func withDefaultASN1DN(def *x509util.ASN1DN) x509util.WithOption { // Sign creates a signed certificate from a certificate signing request. func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Options, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) { var ( - opts = []interface{}{errs.WithKeyVal("csr", csr), errs.WithKeyVal("signOptions", signOpts)} - mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)} - certValidators = []provisioner.CertificateValidator{} - constrainModifiers = []provisioner.CertificateConstrainModifier{} + opts = []interface{}{errs.WithKeyVal("csr", csr), errs.WithKeyVal("signOptions", signOpts)} + mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)} + certValidators = []provisioner.CertificateValidator{} + forcedModifiers = []provisioner.CertificateEnforcer{} ) // Set backdate with the configured value @@ -80,8 +80,8 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti } case provisioner.ProfileModifier: mods = append(mods, k.Option(signOpts)) - case provisioner.CertificateConstrainModifier: - constrainModifiers = append(constrainModifiers, k) + case provisioner.CertificateEnforcer: + forcedModifiers = append(forcedModifiers, k) default: return nil, errs.InternalServer("authority.Sign; invalid extra option type %T", append([]interface{}{k}, opts...)...) } @@ -104,8 +104,8 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti } // Certificate modifier after validation - for _, m := range constrainModifiers { - if err := m.Constrain(leaf.Subject()); err != nil { + for _, m := range forcedModifiers { + if err := m.Enforce(leaf.Subject()); err != nil { return nil, errs.Wrap(http.StatusUnauthorized, err, "authority.Sign", opts...) } } diff --git a/authority/tls_test.go b/authority/tls_test.go index 722338d3..183c3083 100644 --- a/authority/tls_test.go +++ b/authority/tls_test.go @@ -41,6 +41,17 @@ type stepProvisionerASN1 struct { CredentialID []byte } +type certificateDurationEnforcer struct { + NotBefore time.Time + NotAfter time.Time +} + +func (m *certificateDurationEnforcer) Enforce(cert *x509.Certificate) error { + cert.NotBefore = m.NotBefore + cert.NotAfter = m.NotAfter + return nil +} + func withProvisionerOID(name, kid string) x509util.WithOption { return func(p x509util.Profile) error { crt := p.Subject() @@ -114,6 +125,8 @@ func TestAuthority_Sign(t *testing.T) { csr *x509.CertificateRequest signOpts provisioner.Options extraOpts []provisioner.SignOption + notBefore time.Time + notAfter time.Time err error code int } @@ -253,6 +266,31 @@ ZYtQ9Ot36qc= csr: csr, extraOpts: extraOpts, signOpts: signOpts, + notBefore: signOpts.NotBefore.Time().Truncate(time.Second), + notAfter: signOpts.NotAfter.Time().Truncate(time.Second), + } + }, + "ok with enforced modifier": func(t *testing.T) *signTest { + csr := getCSR(t, priv) + now := time.Now().UTC() + enforcedExtraOptions := append(extraOpts, &certificateDurationEnforcer{ + NotBefore: now, + NotAfter: now.Add(365 * 24 * time.Hour), + }) + _a := testAuthority(t) + _a.db = &db.MockAuthDB{ + MStoreCertificate: func(crt *x509.Certificate) error { + assert.Equals(t, crt.Subject.CommonName, "smallstep test") + return nil + }, + } + return &signTest{ + auth: a, + csr: csr, + extraOpts: enforcedExtraOptions, + signOpts: signOpts, + notBefore: now.Truncate(time.Second), + notAfter: now.Add(365 * 24 * time.Hour).Truncate(time.Second), } }, } @@ -279,8 +317,8 @@ ZYtQ9Ot36qc= leaf := certChain[0] intermediate := certChain[1] if assert.Nil(t, tc.err) { - assert.Equals(t, leaf.NotBefore, signOpts.NotBefore.Time().Truncate(time.Second)) - assert.Equals(t, leaf.NotAfter, signOpts.NotAfter.Time().Truncate(time.Second)) + assert.Equals(t, leaf.NotBefore, tc.notBefore) + assert.Equals(t, leaf.NotAfter, tc.notAfter) tmplt := a.config.AuthorityConfig.Template assert.Equals(t, fmt.Sprintf("%v", leaf.Subject), fmt.Sprintf("%v", &pkix.Name{