Skip to content

Add check for changed order identifier by ACME Server (in case it is misbehaving) #2519

Closed
@kiliant

Description

@kiliant

Welcome

  • Yes, I've searched similar issues on GitHub and didn't find any.

How do you use lego?

Through Traefik

Effective version of lego

v4.23.1

Detailed Description

Hello,

first of all, thank you very much for your effort and project. Lego in combination with Traefik is really great!

We recently faced a problem where we wanted to issue a wildcard certificate as part of SANs using a private ACME offering of a CAB member. Our Traefik config was like this:

entryPoints:
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: acmeResolver
          domains:
          - main: "test.example.com"
            sans:
              - "*.test.example.com"

We were wondering, why we would not get a wildcard certificate, even though Traefik would save the certificate with associated wildcard in acme.json. Also, no errors by Lego or Traefik were logged at all.

I then tried with certbot which worked as expected and directly with Lego, which showed the same behaviour as Traefik. To debug this, I added dumps in the do() function is sender.go (https://github.com/kiliant/lego/blob/debug-wildcard/acme/api/internal/sender/sender.go#L88).
It became evident, that the ACME server truncated the wildcard from the respective (actually immutable) identifier (which they must not do, according to RFC 8555 Section 7.1.3). As Lego unmarshals the response from the ACME server into its acme.Order struct, the Identifiers given by the ACME server are used for CSR creation.

Therefore, identifiers [{Type:dns Value:test.example.com} {Type:dns Value:*.test.example.com}] became [{Type:dns Value:test.example.com} {Type:dns Value:test.example.com}] and the certificate was issued without wildcard.

RFC 8555 Section 7.1.3 states:

The elements of the "authorizations" and "identifiers" arrays are
immutable once set. The server MUST NOT change the contents of
either array after they are created. If a client observes a change
in the contents of either array, then it SHOULD consider the order
invalid.

Maybe it would be appropriate to add a check in acme/api/order.go for this in order to detect a misbehaving ACME Server and propagate an error to the (potentially) calling application? E. g. like this:

// Check if the Identifiers changed, which they must not.
// The elements of the "authorizations" and "identifiers" arrays are immutable once set. The server MUST NOT
// change the contents of either array after they are created. If a client observes a change in the contents of
// either array, then it SHOULD consider the order invalid.
// https://www.rfc-editor.org/rfc/rfc8555#section-7.1.3
// Identifier order might be different, thus we need a helper function.
if !IdentifiersMatch(order.Identifiers, orderReq.Identifiers) {
	return acme.ExtendedOrder{},
		fmt.Errorf("order: Order Identifiers do not match: %+v != %+v (ACME Server != ACME Client). "+
		"The order is considered invalid as per RFC 8555 section 7.1.3",
			order.Identifiers, identifiers)
}

https://github.com/kiliant/lego/blob/7fdc90b92b2f674503e4ebfbb559d985351b0fa4/acme/api/order.go#L88-L99

Example Lego output with identifier validation:

./lego.sh
2025/04/28 10:44:44 [INFO] [test.example.com, *.test.example.com] acme: Obtaining bundled SAN certificate
2025/04/28 10:44:44 Could not obtain certificates: order: Order Identifiers do not match: [{Type:dns Value:test.example.com} {Type:dns Value:test.example.com}] != [{Type:dns Value:test.example.com} {Type:dns Value:*.test.example.com}] (ACME Server != ACME Client). The order is considered invalid as per RFC 8555 section 7.1.3

Best regards and thanks again
Thomas

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions