mirror of
https://github.com/outbackdingo/certificates.git
synced 2026-01-27 10:18:34 +00:00
Merge pull request #2444 from smallstep/herman/acme-order-backdate
Fix backdate support for ACME provisioner
This commit is contained in:
@@ -272,10 +272,15 @@ func NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||
if o.NotAfter.IsZero() {
|
||||
o.NotAfter = o.NotBefore.Add(prov.DefaultTLSCertDuration())
|
||||
}
|
||||
// If request NotBefore was empty then backdate the order.NotBefore (now)
|
||||
|
||||
// if request NotBefore was empty, then backdate the order.NotBefore (now)
|
||||
// to avoid timing issues.
|
||||
if nor.NotBefore.IsZero() {
|
||||
o.NotBefore = o.NotBefore.Add(-defaultOrderBackdate)
|
||||
backdate := defaultOrderBackdate
|
||||
if bd := ca.GetBackdate(); bd != nil {
|
||||
backdate = *bd
|
||||
}
|
||||
o.NotBefore = o.NotBefore.Add(-backdate)
|
||||
}
|
||||
|
||||
if err := db.CreateOrder(ctx, o); err != nil {
|
||||
|
||||
@@ -1897,6 +1897,107 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/naf-nbf-from-ca": func(t *testing.T) test {
|
||||
now := clock.Now()
|
||||
expNaf := now.Add(15 * time.Minute)
|
||||
acc := &acme.Account{ID: "accID"}
|
||||
nor := &NewOrderRequest{
|
||||
Identifiers: []acme.Identifier{
|
||||
{Type: "dns", Value: "zap.internal"},
|
||||
},
|
||||
NotAfter: expNaf,
|
||||
}
|
||||
b, err := json.Marshal(nor)
|
||||
assert.FatalError(t, err)
|
||||
ctx := acme.NewProvisionerContext(context.Background(), prov)
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||
var (
|
||||
ch1, ch2, ch3 **acme.Challenge
|
||||
az1ID *string
|
||||
count = 0
|
||||
)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 201,
|
||||
nor: nor,
|
||||
ca: &mockCA{
|
||||
MockGetBackdate: func() *time.Duration {
|
||||
d, err := time.ParseDuration("1h")
|
||||
require.NoError(t, err)
|
||||
return &d
|
||||
},
|
||||
},
|
||||
db: &acme.MockDB{
|
||||
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
|
||||
switch count {
|
||||
case 0:
|
||||
ch.ID = "dns"
|
||||
assert.Equals(t, ch.Type, acme.DNS01)
|
||||
ch1 = &ch
|
||||
case 1:
|
||||
ch.ID = "http"
|
||||
assert.Equals(t, ch.Type, acme.HTTP01)
|
||||
ch2 = &ch
|
||||
case 2:
|
||||
ch.ID = "tls"
|
||||
assert.Equals(t, ch.Type, acme.TLSALPN01)
|
||||
ch3 = &ch
|
||||
default:
|
||||
assert.FatalError(t, errors.New("test logic error"))
|
||||
return errors.New("force")
|
||||
}
|
||||
count++
|
||||
assert.Equals(t, ch.AccountID, "accID")
|
||||
assert.NotEquals(t, ch.Token, "")
|
||||
assert.Equals(t, ch.Status, acme.StatusPending)
|
||||
assert.Equals(t, ch.Value, "zap.internal")
|
||||
return nil
|
||||
},
|
||||
MockCreateAuthorization: func(ctx context.Context, az *acme.Authorization) error {
|
||||
az.ID = "az1ID"
|
||||
az1ID = &az.ID
|
||||
assert.Equals(t, az.AccountID, "accID")
|
||||
assert.NotEquals(t, az.Token, "")
|
||||
assert.Equals(t, az.Status, acme.StatusPending)
|
||||
assert.Equals(t, az.Identifier, nor.Identifiers[0])
|
||||
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1, *ch2, *ch3})
|
||||
assert.Equals(t, az.Wildcard, false)
|
||||
return nil
|
||||
},
|
||||
MockCreateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||
o.ID = "ordID"
|
||||
assert.Equals(t, o.AccountID, "accID")
|
||||
assert.Equals(t, o.ProvisionerID, prov.GetID())
|
||||
assert.Equals(t, o.Status, acme.StatusPending)
|
||||
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
||||
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||
return nil
|
||||
},
|
||||
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, prov.GetID(), provisionerID)
|
||||
assert.Equals(t, "accID", accountID)
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
vr: func(t *testing.T, o *acme.Order) {
|
||||
testBufferDur := 5 * time.Second
|
||||
orderExpiry := now.Add(defaultOrderExpiry)
|
||||
expNbf := now.Add(-1 * time.Hour)
|
||||
|
||||
assert.Equals(t, o.ID, "ordID")
|
||||
assert.Equals(t, o.Status, acme.StatusPending)
|
||||
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
||||
assert.Equals(t, o.AuthorizationURLs, []string{fmt.Sprintf("%s/acme/%s/authz/az1ID", baseURL.String(), escProvName)})
|
||||
assert.True(t, o.NotBefore.Add(-testBufferDur).Before(expNbf))
|
||||
assert.True(t, o.NotBefore.Add(testBufferDur).After(expNbf))
|
||||
assert.True(t, o.NotAfter.Add(-testBufferDur).Before(expNaf))
|
||||
assert.True(t, o.NotAfter.Add(testBufferDur).After(expNaf))
|
||||
assert.True(t, o.ExpiresAt.Add(-testBufferDur).Before(orderExpiry))
|
||||
assert.True(t, o.ExpiresAt.Add(testBufferDur).After(orderExpiry))
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/default-naf-nbf-wireapp": func(t *testing.T) test {
|
||||
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
|
||||
Wire: &wire.Options{
|
||||
|
||||
@@ -279,6 +279,7 @@ type mockCA struct {
|
||||
MockIsRevoked func(sn string) (bool, error)
|
||||
MockRevoke func(ctx context.Context, opts *authority.RevokeOptions) error
|
||||
MockAreSANsallowed func(ctx context.Context, sans []string) error
|
||||
MockGetBackdate func() *time.Duration
|
||||
}
|
||||
|
||||
func (m *mockCA) SignWithContext(context.Context, *x509.CertificateRequest, provisioner.SignOptions, ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||
@@ -310,6 +311,13 @@ func (m *mockCA) LoadProvisionerByName(string) (provisioner.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockCA) GetBackdate() *time.Duration {
|
||||
if m.MockGetBackdate != nil {
|
||||
return m.MockGetBackdate()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_validateReasonCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -613,3 +613,7 @@ func (m *mockCASigner) Revoke(ctx context.Context, opts *authority.RevokeOptions
|
||||
func (m *mockCASigner) LoadProvisionerByName(string) (provisioner.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockCASigner) GetBackdate() *time.Duration {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ type CertificateAuthority interface {
|
||||
IsRevoked(sn string) (bool, error)
|
||||
Revoke(context.Context, *authority.RevokeOptions) error
|
||||
LoadProvisionerByName(string) (provisioner.Interface, error)
|
||||
GetBackdate() *time.Duration
|
||||
}
|
||||
|
||||
// NewContext adds the given acme components to the context.
|
||||
|
||||
@@ -311,6 +311,10 @@ func (m *mockSignAuth) Revoke(context.Context, *authority.RevokeOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockSignAuth) GetBackdate() *time.Duration {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestOrder_Finalize(t *testing.T) {
|
||||
mustSigner := func(kty, crv string, size int) crypto.Signer {
|
||||
s, err := keyutil.GenerateSigner(kty, crv, size)
|
||||
|
||||
@@ -882,6 +882,17 @@ func (a *Authority) GetConfig() *config.Config {
|
||||
return a.config
|
||||
}
|
||||
|
||||
// GetBackdate returns the [time.Duration] representing the
|
||||
// amount of time that is to be subtracted from the current
|
||||
// time when issuing a new certificate.
|
||||
func (a *Authority) GetBackdate() *time.Duration {
|
||||
if a.config == nil || a.config.AuthorityConfig == nil || a.config.AuthorityConfig.Backdate == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &a.config.AuthorityConfig.Backdate.Duration
|
||||
}
|
||||
|
||||
// GetInfo returns information about the authority.
|
||||
func (a *Authority) GetInfo() Info {
|
||||
ai := Info{
|
||||
|
||||
Reference in New Issue
Block a user