Skip to content
Snippets Groups Projects
Commit d33d5aba authored by ale's avatar ale
Browse files

Switch to order-based workflow for RFC 8555

parent be165244
No related branches found
No related tags found
No related merge requests found
......@@ -133,7 +133,7 @@ func (a *ACME) acmeClient(ctx context.Context) (*acme.Client, error) {
// account is already registered we get a StatusConflict,
// which we can ignore.
_, err = client.Register(ctx, ac, func(_ string) bool { return true })
if ae, ok := err.(*acme.Error); err == nil || ok && ae.StatusCode == http.StatusConflict {
if ae, ok := err.(*acme.Error); err == nil || err == acme.ErrAccountAlreadyExists || (ok && ae.StatusCode == http.StatusConflict) {
a.client = client
err = nil
}
......@@ -149,7 +149,8 @@ func (a *ACME) GetCertificate(ctx context.Context, key crypto.Signer, c *certCon
return nil, nil, err
}
if err = a.verifyAll(ctx, client, c); err != nil {
o, err := a.verifyAll(ctx, client, c)
if err != nil {
return nil, nil, err
}
......@@ -157,7 +158,7 @@ func (a *ACME) GetCertificate(ctx context.Context, key crypto.Signer, c *certCon
if err != nil {
return nil, nil, err
}
der, _, err = client.CreateCert(ctx, csr, 0, true)
der, _, err = client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
......@@ -168,56 +169,66 @@ func (a *ACME) GetCertificate(ctx context.Context, key crypto.Signer, c *certCon
return der, leaf, nil
}
func (a *ACME) verifyAll(ctx context.Context, client *acme.Client, c *certConfig) error {
for _, domain := range c.Names {
if err := a.verify(ctx, client, c, domain); err != nil {
return err
}
}
return nil
}
func (a *ACME) verify(ctx context.Context, client *acme.Client, c *certConfig, domain string) error {
func (a *ACME) verifyAll(ctx context.Context, client *acme.Client, c *certConfig) (*acme.Order, error) {
// Make an authorization request to the ACME server, and
// verify that it returns a valid response with challenges.
authz, err := client.Authorize(ctx, domain)
o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(c.Names...))
if err != nil {
return err
}
switch authz.Status {
case acme.StatusValid:
return nil // already authorized
case acme.StatusInvalid:
return fmt.Errorf("invalid authorization %q", authz.URI)
return nil, fmt.Errorf("AuthorizeOrder failed: %v", err)
}
// Pick a challenge that matches our preferences and the
// available validators. The validator fulfills the challenge,
// and returns a cleanup function that we're going to call
// before we return. All steps are sequential and idempotent.
chal := a.pickChallenge(authz.Challenges, c)
if chal == nil {
return fmt.Errorf("unable to authorize %q", domain)
}
v, ok := a.validators[chal.Type]
if !ok {
return fmt.Errorf("challenge type '%s' is not available", chal.Type)
switch o.Status {
case acme.StatusReady:
return o, nil // already authorized
case acme.StatusPending:
default:
return nil, fmt.Errorf("invalid new order status %q", o.Status)
}
cleanup, err := v.Fulfill(ctx, client, domain, chal)
if err != nil {
return err
for _, zurl := range o.AuthzURLs {
z, err := client.GetAuthorization(ctx, zurl)
if err != nil {
return nil, fmt.Errorf("GetAuthorization(%s) failed: %v", zurl, err)
}
if z.Status != acme.StatusPending {
continue
}
// Pick a challenge that matches our preferences and the
// available validators. The validator fulfills the challenge,
// and returns a cleanup function that we're going to call
// before we return. All steps are sequential and idempotent.
chal := a.pickChallenge(z.Challenges, c)
if chal == nil {
return nil, fmt.Errorf("unable to authorize %q", c.Names)
}
v, ok := a.validators[chal.Type]
if !ok {
return nil, fmt.Errorf("challenge type '%s' is not available", chal.Type)
}
for _, domain := range c.Names {
cleanup, err := v.Fulfill(ctx, client, domain, chal)
if err != nil {
return nil, fmt.Errorf("fulfillment failed: %v", err)
}
defer cleanup()
}
if _, err := client.Accept(ctx, chal); err != nil {
return nil, fmt.Errorf("challenge accept failed: %v", err)
}
if _, err := client.WaitAuthorization(ctx, z.URI); err != nil {
return nil, fmt.Errorf("WaitAuthorization(%s) failed: %v", z.URI, err)
}
}
defer cleanup()
// Tell the ACME server that we've accepted the challenge, and
// then wait, possibly for some time, until there is an
// authorization response (either successful or not) from the
// server.
if _, err = client.Accept(ctx, chal); err != nil {
return err
// Authorizations are satisfied, wait for the CA
// to update the order status.
if _, err = client.WaitOrder(ctx, o.URI); err != nil {
return nil, err
}
_, err = client.WaitAuthorization(ctx, authz.URI)
return err
return o, nil
}
// Pick a challenge with the right type from the Challenge response
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment