Skip to content

Commit

Permalink
Add preliminary support for TLS-ALPN-01 challenge for IP identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
hslatman committed May 28, 2021
1 parent 848b520 commit 3e36522
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 14 deletions.
7 changes: 3 additions & 4 deletions acme/api/order.go
Expand Up @@ -275,15 +275,14 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) {
func challengeTypes(az *acme.Authorization) []string {
chTypes := []string{}

// DNS challenge can not be used for identifiers with type IP
if az.Identifier.Type != "ip" {
// DNS challenge can only be used for identifiers with type dns
if az.Identifier.Type == "dns" {
chTypes = append(chTypes, "dns-01") // TODO: make these types consts/enum?
}

// HTTP and TLS challenges can only be used for identifiers without wildcards.
if !az.Wildcard {
//chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...)
chTypes = append(chTypes, []string{"http-01"}...) // TODO: fix tls-alpn-01
chTypes = append(chTypes, []string{"http-01", "tls-alpn-01"}...)
}

return chTypes
Expand Down
24 changes: 21 additions & 3 deletions acme/challenge.go
Expand Up @@ -119,8 +119,14 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON

hostPort := net.JoinHostPort(ch.Value, "443")

fmt.Println(hostPort)
fmt.Println(fmt.Sprintf("%#+v", config))

time.Sleep(5 * time.Second) // TODO: remove this; client seems to take a while to start serving; the server does not seem to retry the conn

conn, err := vo.TLSDial("tcp", hostPort, config)
if err != nil {
fmt.Println(err)
return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err,
"error doing TLS dial for %s", hostPort))
}
Expand All @@ -129,6 +135,9 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
cs := conn.ConnectionState()
certs := cs.PeerCertificates

fmt.Println(fmt.Sprintf("%#+v", cs))
fmt.Println(fmt.Sprintf("%#+v", certs))

if len(certs) == 0 {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"%s challenge for %s resulted in no certificates", ch.Type, ch.Value))
Expand All @@ -140,10 +149,19 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
}

leafCert := certs[0]
fmt.Println(fmt.Sprintf("%#+v", leafCert))

if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value))
// if no DNS names present, look for IP address and verify that exactly one exists
if len(leafCert.DNSNames) == 0 {
if len(leafCert.IPAddresses) != 1 || !strings.EqualFold(leafCert.IPAddresses[0].String(), ch.Value) {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single IP address, %v", ch.Value))
}
} else {
if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value))
}
}

idPeAcmeIdentifier := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
Expand Down
9 changes: 2 additions & 7 deletions acme/order.go
Expand Up @@ -207,17 +207,12 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
orderNames = uniqueSortedLowerNames(orderNames)
orderIPs = uniqueSortedIPs(orderIPs)

// TODO: check whether this order was requested with identifier-type IP,
// if so, handle it as an IP order; not as a DNSName order, so the logic
// for verifying the contents MAY not be necessary.
// TODO: limit what IP addresses can be used? Only private? Only certain ranges
// based on configuration? Public vs. private range? That logic should be configurable somewhere.
// TODO: how to handler orders that have DNSNames AND IPs? I guess it could
// happen in cases where there are multiple "identifiers" to order a cert for
// and http or tls-alpn-1 is used (NOT DNS, because that can't be used for IPs).
// TODO: ensure that DNSNames indeed MUST NEVER have an IP
// TODO: only allow IP based identifier based on configuration?
// TODO: validation of the input (if IP; should be valid IPv4/v6)
// TODO: validation of the input (if IP; should be valid IPv4/v6; Incoming request should have Host header set / ALPN IN-ADDR.ARPA)
// TODO: limit the IP address identifier to a single IP address? RFC _can_ be read like that, but there can be multiple identifiers, of course

// Determine if DNS names or IPs should be processed.
// At this time, orders in which DNS names and IPs are mixed are not supported. // TODO: ensure that's OK and/or should we support more, RFC-wise
Expand Down

0 comments on commit 3e36522

Please sign in to comment.