Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

va: Support IP address identifiers #8020

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4aa8275
va: Use Identifier in PerformValidationRequest & support IP address i…
jprenken Feb 21, 2025
aee999e
Merge branch 'main' of github.com:letsencrypt/boulder into va-identif…
jprenken Feb 21, 2025
a96107f
Fix tests: ASN.1 tag for IPs, err var name, bare IP redir
jprenken Feb 21, 2025
9bd2763
Update TODOs, clarify var names
jprenken Feb 21, 2025
d8e90bc
Fix lint, bring bad port test closer to original
jprenken Feb 21, 2025
b8aed44
Fix initialURL composition; update test strings to expect explicit po…
jprenken Feb 21, 2025
76d40b7
Fix redirect loop test for the new era of explicit port numbers
jprenken Feb 21, 2025
8a96db7
Revert explicit port number; hardcode port in processHTTPValidation i…
jprenken Feb 22, 2025
1423162
Remove superfluous Port from TestFetchHTTP struct
jprenken Feb 22, 2025
c7ae932
Fix TestHTTP failure
jprenken Feb 22, 2025
e330358
Move tlsConfig generation inside getChallengeCert; fix SNI hostname f…
jprenken Feb 22, 2025
84848b6
Tiny name & comment updates
jprenken Feb 22, 2025
f76c8ac
Regenerate PBs with comment
jprenken Feb 22, 2025
9df1046
Add more test cases
jprenken Feb 22, 2025
0e52b95
Add test cases & correctly handle bare IPv6 in redirects
jprenken Feb 23, 2025
ad6b6b2
Fix RFC 8738 Sec. 3 compliance; add IPv6 httptest ability; fix bare I…
jprenken Feb 23, 2025
e0692b8
Add backstop test for RFC 8738, Section 6
jprenken Feb 23, 2025
0197920
Future-proof for IPv7
jprenken Feb 23, 2025
b26365f
Add test cases; remove dnsi test function
jprenken Feb 23, 2025
1131332
Introduce identifier.FromProtoWithDefault
jprenken Feb 24, 2025
0d43377
Combine struct composition
jprenken Feb 24, 2025
8f57ef6
Merge branch 'main' of github.com:letsencrypt/boulder into va-identif…
jprenken Feb 25, 2025
4d19c25
Address feedback
jprenken Feb 25, 2025
d05232b
Address feedback: refactor checkExpectedSAN
jprenken Feb 25, 2025
67b0090
Address feedback: refactor host/port parsing in extractRequestTarget
jprenken Feb 25, 2025
7c57e1f
Merge branch 'main' of github.com:letsencrypt/boulder into va-identif…
jprenken Feb 25, 2025
dffcc99
Address feedback: clarify FromProtoOrName
jprenken Feb 25, 2025
6e38276
Change FromProtoOrName to FromProtoWithDefault, improve call sites
jprenken Feb 25, 2025
d281836
Address feedback
jprenken Feb 25, 2025
1699a1c
Address feedback: clean up extractRequestTarget changes & improve com…
jprenken Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bdns/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ func (mock *MockClient) LookupTXT(_ context.Context, hostname string) ([]string,
// expected token + test account jwk thumbprint
return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
}
if hostname == "_acme-challenge.good-dns02.com" {
// base64(sha256("LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
// + "." + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"))
// expected token + test account jwk thumbprint
return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
}
if hostname == "_acme-challenge.wrong-dns01.com" {
return []string{"a"}, ResolverAddrs{"MockClient"}, nil
}
Expand Down
2 changes: 2 additions & 0 deletions core/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ type ValidationRecord struct {
URL string `json:"url,omitempty"`

// Shared
//
// TODO(#7311): Replace DnsName with Identifier.
DnsName string `json:"hostname,omitempty"`
Port string `json:"port,omitempty"`
AddressesResolved []net.IP `json:"addressesResolved,omitempty"`
Expand Down
23 changes: 21 additions & 2 deletions identifier/identifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ func (i ACMEIdentifier) AsProto() *corepb.Identifier {
}
}

func FromProto(ident *corepb.Identifier) ACMEIdentifier {
return ACMEIdentifier{
Type: IdentifierType(ident.Type),
Value: ident.Value,
}
}

// FromProtoWithDefault can be removed after DnsNames are no longer used in
// RPCs. TODO(#7311)
func FromProtoWithDefault(ident *corepb.Identifier, name string) ACMEIdentifier {
if ident == nil {
return NewDNS(name)
}
return FromProto(ident)
}

// NewDNS is a convenience function for creating an ACMEIdentifier with Type
// "dns" for a given domain name.
func NewDNS(domain string) ACMEIdentifier {
Expand All @@ -52,7 +68,10 @@ func NewDNS(domain string) ACMEIdentifier {
// for a given IP address.
func NewIP(ip netip.Addr) ACMEIdentifier {
return ACMEIdentifier{
Type: TypeIP,
Value: ip.StringExpanded(),
Type: TypeIP,
// Section 3 of RFC 8738: The identifier value MUST contain the textual
// form of the address as defined in Section 2.1 of RFC1123 for IPv4 and
// in Section 4 of RFC5952 for IPv6.
Value: ip.String(),
}
}
2 changes: 1 addition & 1 deletion va/caa.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
// TODO(#7061) Plumb req.Authz.Id as "AuthzID:" through from the RA to
// correlate which authz triggered this request.
Requester: req.AccountURIID,
Identifier: req.Domain,
Identifier: identifier.NewDNS(req.Domain),
}

challType := core.AcmeChallenge(req.ValidationMethod)
Expand Down
6 changes: 3 additions & 3 deletions va/caa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1226,18 +1226,18 @@ func TestMultiCAARechecking(t *testing.T) {
}

func TestCAAFailure(t *testing.T) {
hs := httpSrv(t, expectedToken)
hs := httpSrv(t, expectedToken, false)
defer hs.Close()

va, _ := setup(hs, "", nil, caaMockDNS{})

err := va.checkCAA(ctx, dnsi("reserved.com"), &caaParams{1, core.ChallengeTypeHTTP01})
err := va.checkCAA(ctx, identifier.NewDNS("reserved.com"), &caaParams{1, core.ChallengeTypeHTTP01})
if err == nil {
t.Fatalf("Expected CAA rejection for reserved.com, got success")
}
test.AssertErrorIs(t, err, berrors.CAA)

err = va.checkCAA(ctx, dnsi("example.gonetld"), &caaParams{1, core.ChallengeTypeHTTP01})
err = va.checkCAA(ctx, identifier.NewDNS("example.gonetld"), &caaParams{1, core.ChallengeTypeHTTP01})
if err == nil {
t.Fatalf("Expected CAA rejection for gonetld, got success")
}
Expand Down
2 changes: 1 addition & 1 deletion va/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func availableAddresses(allAddrs []net.IP) (v4 []net.IP, v6 []net.IP) {
func (va *ValidationAuthorityImpl) validateDNS01(ctx context.Context, ident identifier.ACMEIdentifier, keyAuthorization string) ([]core.ValidationRecord, error) {
if ident.Type != identifier.TypeDNS {
va.log.Infof("Identifier type for DNS challenge was not DNS: %s", ident)
return nil, berrors.MalformedError("Identifier type for DNS was not itself DNS")
return nil, berrors.MalformedError("Identifier type for DNS challenge was not DNS")
}

// Compute the digest of the key authorization file
Expand Down
28 changes: 19 additions & 9 deletions va/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"testing"
"time"

Expand All @@ -23,7 +24,7 @@ func TestDNSValidationEmpty(t *testing.T) {

// This test calls PerformValidation directly, because that is where the
// metrics checked below are incremented.
req := createValidationRequest("empty-txts.com", core.ChallengeTypeDNS01)
req := createValidationRequest(identifier.NewDNS("empty-txts.com"), core.ChallengeTypeDNS01)
res, _ := va.PerformValidation(context.Background(), req)
test.AssertEquals(t, res.Problem.ProblemType, "unauthorized")
test.AssertEquals(t, res.Problem.Detail, "No TXT record found at _acme-challenge.empty-txts.com")
Expand All @@ -39,7 +40,7 @@ func TestDNSValidationEmpty(t *testing.T) {

func TestDNSValidationWrong(t *testing.T) {
va, _ := setup(nil, "", nil, nil)
_, err := va.validateDNS01(context.Background(), dnsi("wrong-dns01.com"), expectedKeyAuthorization)
_, err := va.validateDNS01(context.Background(), identifier.NewDNS("wrong-dns01.com"), expectedKeyAuthorization)
if err == nil {
t.Fatalf("Successful DNS validation with wrong TXT record")
}
Expand All @@ -50,7 +51,7 @@ func TestDNSValidationWrong(t *testing.T) {
func TestDNSValidationWrongMany(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, err := va.validateDNS01(context.Background(), dnsi("wrong-many-dns01.com"), expectedKeyAuthorization)
_, err := va.validateDNS01(context.Background(), identifier.NewDNS("wrong-many-dns01.com"), expectedKeyAuthorization)
if err == nil {
t.Fatalf("Successful DNS validation with wrong TXT record")
}
Expand All @@ -61,7 +62,7 @@ func TestDNSValidationWrongMany(t *testing.T) {
func TestDNSValidationWrongLong(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, err := va.validateDNS01(context.Background(), dnsi("long-dns01.com"), expectedKeyAuthorization)
_, err := va.validateDNS01(context.Background(), identifier.NewDNS("long-dns01.com"), expectedKeyAuthorization)
if err == nil {
t.Fatalf("Successful DNS validation with wrong TXT record")
}
Expand All @@ -72,12 +73,21 @@ func TestDNSValidationWrongLong(t *testing.T) {
func TestDNSValidationFailure(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, err := va.validateDNS01(ctx, dnsi("localhost"), expectedKeyAuthorization)
_, err := va.validateDNS01(ctx, identifier.NewDNS("localhost"), expectedKeyAuthorization)
prob := detailedError(err)

test.AssertEquals(t, prob.Type, probs.UnauthorizedProblem)
}

func TestDNSValidationIP(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, err := va.validateDNS01(ctx, identifier.NewIP(netip.MustParseAddr("127.0.0.1")), expectedKeyAuthorization)
prob := detailedError(err)

test.AssertEquals(t, prob.Type, probs.MalformedProblem)
}

func TestDNSValidationInvalid(t *testing.T) {
var notDNS = identifier.ACMEIdentifier{
Type: identifier.IdentifierType("iris"),
Expand All @@ -95,7 +105,7 @@ func TestDNSValidationInvalid(t *testing.T) {
func TestDNSValidationServFail(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, err := va.validateDNS01(ctx, dnsi("servfail.com"), expectedKeyAuthorization)
_, err := va.validateDNS01(ctx, identifier.NewDNS("servfail.com"), expectedKeyAuthorization)

prob := detailedError(err)
test.AssertEquals(t, prob.Type, probs.DNSProblem)
Expand All @@ -115,23 +125,23 @@ func TestDNSValidationNoServer(t *testing.T) {
log,
nil)

_, err = va.validateDNS01(ctx, dnsi("localhost"), expectedKeyAuthorization)
_, err = va.validateDNS01(ctx, identifier.NewDNS("localhost"), expectedKeyAuthorization)
prob := detailedError(err)
test.AssertEquals(t, prob.Type, probs.DNSProblem)
}

func TestDNSValidationOK(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, prob := va.validateDNS01(ctx, dnsi("good-dns01.com"), expectedKeyAuthorization)
_, prob := va.validateDNS01(ctx, identifier.NewDNS("good-dns01.com"), expectedKeyAuthorization)

test.Assert(t, prob == nil, "Should be valid.")
}

func TestDNSValidationNoAuthorityOK(t *testing.T) {
va, _ := setup(nil, "", nil, nil)

_, prob := va.validateDNS01(ctx, dnsi("no-authority-dns01.com"), expectedKeyAuthorization)
_, prob := va.validateDNS01(ctx, identifier.NewDNS("no-authority-dns01.com"), expectedKeyAuthorization)

test.Assert(t, prob == nil, "Should be valid.")
}
Expand Down
Loading
Loading