mirror of
https://github.com/outbackdingo/certificates.git
synced 2026-01-27 10:18:34 +00:00
Do strict DNS lookup on ACME
This commit changes the ACME challenges to perform a strict DNS lookup without taking into account the search list in a resolv.conf
This commit is contained in:
@@ -157,15 +157,40 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
|
||||
return nil
|
||||
}
|
||||
|
||||
// rootedName adds a trailing "." to a given domain name.
|
||||
func rootedName(name string) string {
|
||||
if name == "" || name[len(name)-1] != '.' {
|
||||
return name + "."
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// http01ChallengeHost checks if a Challenge value is an IPv6 address
|
||||
// and adds square brackets if that's the case, so that it can be used
|
||||
// as a hostname. Returns the original Challenge value as the host to
|
||||
// use in other cases.
|
||||
func http01ChallengeHost(value string) string {
|
||||
if ip := net.ParseIP(value); ip != nil && ip.To4() == nil {
|
||||
value = "[" + value + "]"
|
||||
if ip := net.ParseIP(value); ip != nil {
|
||||
if ip.To4() == nil {
|
||||
value = "[" + value + "]"
|
||||
}
|
||||
return value
|
||||
}
|
||||
return value
|
||||
return rootedName(value)
|
||||
}
|
||||
|
||||
// tlsAlpn01ChallengeHost returns the rooted DNS used on TLS-ALPN-01
|
||||
// validations.
|
||||
func tlsAlpn01ChallengeHost(name string) string {
|
||||
if ip := net.ParseIP(name); ip != nil {
|
||||
return name
|
||||
}
|
||||
return rootedName(name)
|
||||
}
|
||||
|
||||
// dns01ChallengeHost returns the TXT record used in DNS-01 validations.
|
||||
func dns01ChallengeHost(domain string) string {
|
||||
return "_acme-challenge." + rootedName(domain)
|
||||
}
|
||||
|
||||
func tlsAlert(err error) uint8 {
|
||||
@@ -190,13 +215,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||
InsecureSkipVerify: true, //nolint:gosec // we expect a self-signed challenge certificate
|
||||
}
|
||||
|
||||
var hostPort string
|
||||
|
||||
// Allow to change TLS port for testing purposes.
|
||||
hostPort := tlsAlpn01ChallengeHost(ch.Value)
|
||||
if port := InsecurePortTLSALPN01; port == 0 {
|
||||
hostPort = net.JoinHostPort(ch.Value, "443")
|
||||
hostPort = net.JoinHostPort(hostPort, "443")
|
||||
} else {
|
||||
hostPort = net.JoinHostPort(ch.Value, strconv.Itoa(port))
|
||||
hostPort = net.JoinHostPort(hostPort, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
@@ -211,7 +235,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||
"cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge"))
|
||||
}
|
||||
return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err,
|
||||
"error doing TLS dial for %s", hostPort))
|
||||
"error doing TLS dial for %s", ch.Value))
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@@ -307,7 +331,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
|
||||
domain := strings.TrimPrefix(ch.Value, "*.")
|
||||
|
||||
vc := MustClientFromContext(ctx)
|
||||
txtRecords, err := vc.LookupTxt("_acme-challenge." + domain)
|
||||
txtRecords, err := vc.LookupTxt(dns01ChallengeHost(domain))
|
||||
if err != nil {
|
||||
return storeError(ctx, db, ch, false, WrapError(ErrorDNSType, err,
|
||||
"error looking up TXT records for domain %s", domain))
|
||||
@@ -1058,14 +1082,10 @@ func doStepAttestationFormat(_ context.Context, prov Provisioner, ch *Challenge,
|
||||
// should be the ARPA address https://datatracker.ietf.org/doc/html/rfc8738#section-6.
|
||||
// It also references TLS Extensions [RFC6066].
|
||||
func serverName(ch *Challenge) string {
|
||||
var serverName string
|
||||
ip := net.ParseIP(ch.Value)
|
||||
if ip != nil {
|
||||
serverName = reverseAddr(ip)
|
||||
} else {
|
||||
serverName = ch.Value
|
||||
if ip := net.ParseIP(ch.Value); ip != nil {
|
||||
return reverseAddr(ip)
|
||||
}
|
||||
return serverName
|
||||
return ch.Value
|
||||
}
|
||||
|
||||
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
|
||||
|
||||
@@ -457,7 +457,7 @@ func TestChallenge_Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -494,7 +494,7 @@ func TestChallenge_Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -536,7 +536,7 @@ func TestChallenge_Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal:8080/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal.:8080/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -646,7 +646,7 @@ func TestChallenge_Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -926,7 +926,7 @@ func TestHTTP01Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -961,7 +961,7 @@ func TestHTTP01Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s: force", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -998,7 +998,7 @@ func TestHTTP01Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s with status code 400", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -1036,7 +1036,7 @@ func TestHTTP01Validate(t *testing.T) {
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
assert.Equal(t, StatusPending, updch.Status)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token)
|
||||
err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal./.well-known/acme-challenge/%s with status code 400", ch.Token)
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
assert.Equal(t, err.Detail, updch.Error.Detail)
|
||||
@@ -1065,7 +1065,7 @@ func TestHTTP01Validate(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
err: NewErrorISE("error reading response body for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token),
|
||||
err: NewErrorISE("error reading response body for url http://zap.internal./.well-known/acme-challenge/%s: force", ch.Token),
|
||||
}
|
||||
},
|
||||
"fail/key-auth-gen-error": func(t *testing.T) test {
|
||||
@@ -1723,7 +1723,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -1754,7 +1754,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: force", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: force", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -1786,7 +1786,7 @@ func TestTLSALPN01Validate(t *testing.T) {
|
||||
assert.Equal(t, ChallengeType("tls-alpn-01"), updch.Type)
|
||||
assert.Equal(t, "zap.internal", updch.Value)
|
||||
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v:443: context deadline exceeded", ch.Value)
|
||||
err := NewError(ErrorConnectionType, "error doing TLS dial for %v: context deadline exceeded", ch.Value)
|
||||
|
||||
assert.EqualError(t, updch.Error.Err, err.Err.Error())
|
||||
assert.Equal(t, err.Type, updch.Error.Type)
|
||||
@@ -2774,7 +2774,12 @@ func Test_http01ChallengeHost(t *testing.T) {
|
||||
{
|
||||
name: "dns",
|
||||
value: "www.example.com",
|
||||
want: "www.example.com",
|
||||
want: "www.example.com.",
|
||||
},
|
||||
{
|
||||
name: "rooted dns",
|
||||
value: "www.example.com.",
|
||||
want: "www.example.com.",
|
||||
},
|
||||
{
|
||||
name: "ipv4",
|
||||
@@ -4301,3 +4306,43 @@ func createSubjectAltNameExtension(dnsNames, emailAddresses x509util.MultiString
|
||||
Value: rawBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Test_tlsAlpn01ChallengeHost(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"dns", args{"smallstep.com"}, "smallstep.com."},
|
||||
{"rooted dns", args{"smallstep.com."}, "smallstep.com."},
|
||||
{"ipv4", args{"1.2.3.4"}, "1.2.3.4"},
|
||||
{"ipv6", args{"2607:f8b0:4023:1009::71"}, "2607:f8b0:4023:1009::71"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, tlsAlpn01ChallengeHost(tt.args.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_dns01ChallengeHost(t *testing.T) {
|
||||
type args struct {
|
||||
domain string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{"dns", args{"smallstep.com"}, "_acme-challenge.smallstep.com."},
|
||||
{"rooted dns", args{"smallstep.com."}, "_acme-challenge.smallstep.com."},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, dns01ChallengeHost(tt.args.domain))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user