Skip to content

Commit 47452d6

Browse files
cpujsha
authored andcommitted
Prefer IPv6 addresses, fall back to IPv4. (letsencrypt#2715)
This PR introduces a new feature flag "IPv6First". When the "IPv6First" feature is enabled the VA's HTTP dialer and TLS SNI (01 and 02) certificate fetch requests will attempt to automatically retry when the initial connection was to IPv6 and there is an IPv4 address available to retry with. This resolves letsencrypt#2623
1 parent 101da45 commit 47452d6

File tree

11 files changed

+424
-69
lines changed

11 files changed

+424
-69
lines changed

bdns/mocks.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ func (mock *MockDNSResolver) LookupHost(_ context.Context, hostname string) ([]n
7373
Err: errors.New("some net error"),
7474
}, -1}
7575
}
76+
// dual-homed host with an IPv6 and an IPv4 address
77+
if hostname == "ipv4.and.ipv6.localhost" {
78+
return []net.IP{
79+
net.ParseIP("::1"),
80+
net.ParseIP("127.0.0.1"),
81+
}, nil
82+
}
7683
ip := net.ParseIP("127.0.0.1")
7784
return []net.IP{ip}, nil
7885
}

core/objects.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,21 @@ type ValidationRecord struct {
182182
Port string `json:"port"`
183183
AddressesResolved []net.IP `json:"addressesResolved"`
184184
AddressUsed net.IP `json:"addressUsed"`
185+
// AddressesTried contains a list of addresses tried before the `AddressUsed`.
186+
// Presently this will only ever be one IP from `AddressesResolved` since the
187+
// only retry is in the case of a v6 failure with one v4 fallback. E.g. if
188+
// a record with `AddressesResolved: { 127.0.0.1, ::1 }` were processed for
189+
// a challenge validation with the IPv6 first flag on and the ::1 address
190+
// failed but the 127.0.0.1 retry succeeded then the record would end up
191+
// being:
192+
// {
193+
// ...
194+
// AddressesResolved: [ 127.0.0.1, ::1 ],
195+
// AddressUsed: 127.0.0.1
196+
// AddressesTried: [ ::1 ],
197+
// ...
198+
// }
199+
AddressesTried []net.IP `json:"addressesTried"`
185200
}
186201

187202
func looksLikeKeyAuthorization(str string) error {

core/proto/core.pb.go

Lines changed: 47 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/proto/core.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ message ValidationRecord {
2222

2323
repeated string authorities = 5;
2424
optional string url = 6;
25+
// A list of addresses tried before the address used (see
26+
// core/objects.go and the comment on the ValidationRecord structure
27+
// definition for more information.
28+
repeated bytes addressesTried = 7; // net.IP.MarshalText()
2529
}
2630

2731
message ProblemDetails {

features/featureflag_string.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

features/features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
AllowTLS02Challenges
2222
GenerateOCSPEarly
2323
CountCertificatesExact
24+
IPv6First
2425
)
2526

2627
// List of features and their default value, protected by fMu
@@ -35,6 +36,7 @@ var features = map[FeatureFlag]bool{
3536
AllowTLS02Challenges: false,
3637
GenerateOCSPEarly: false,
3738
CountCertificatesExact: false,
39+
IPv6First: false,
3840
}
3941

4042
var fMu = new(sync.RWMutex)

grpc/pb-marshalling.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,14 @@ func pbToChallenge(in *corepb.Challenge) (challenge core.Challenge, err error) {
146146

147147
func validationRecordToPB(record core.ValidationRecord) (*corepb.ValidationRecord, error) {
148148
addrs := make([][]byte, len(record.AddressesResolved))
149+
addrsTried := make([][]byte, len(record.AddressesTried))
149150
var err error
150151
for i, v := range record.AddressesResolved {
151152
addrs[i] = []byte(v)
152153
}
154+
for i, v := range record.AddressesTried {
155+
addrsTried[i] = []byte(v)
156+
}
153157
addrUsed, err := record.AddressUsed.MarshalText()
154158
if err != nil {
155159
return nil, err
@@ -161,6 +165,7 @@ func validationRecordToPB(record core.ValidationRecord) (*corepb.ValidationRecor
161165
AddressUsed: addrUsed,
162166
Authorities: record.Authorities,
163167
Url: &record.URL,
168+
AddressesTried: addrsTried,
164169
}, nil
165170
}
166171

@@ -175,6 +180,10 @@ func pbToValidationRecord(in *corepb.ValidationRecord) (record core.ValidationRe
175180
for i, v := range in.AddressesResolved {
176181
addrs[i] = net.IP(v)
177182
}
183+
addrsTried := make([]net.IP, len(in.AddressesTried))
184+
for i, v := range in.AddressesTried {
185+
addrsTried[i] = net.IP(v)
186+
}
178187
var addrUsed net.IP
179188
err = addrUsed.UnmarshalText(in.AddressUsed)
180189
if err != nil {
@@ -187,6 +196,7 @@ func pbToValidationRecord(in *corepb.ValidationRecord) (record core.ValidationRe
187196
AddressUsed: addrUsed,
188197
Authorities: in.Authorities,
189198
URL: *in.Url,
199+
AddressesTried: addrsTried,
190200
}, nil
191201
}
192202

grpc/pb-marshalling_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ func TestChallenge(t *testing.T) {
123123
AddressUsed: ip,
124124
URL: "url",
125125
Authorities: []string{"auth"},
126+
AddressesTried: []net.IP{ip},
126127
},
127128
}
128129
chall.Error = &probs.ProblemDetails{Type: probs.TLSProblem, Detail: "asd", HTTPStatus: 200}
@@ -151,6 +152,7 @@ func TestValidationRecord(t *testing.T) {
151152
AddressUsed: ip,
152153
URL: "url",
153154
Authorities: []string{"auth"},
155+
AddressesTried: []net.IP{ip},
154156
}
155157

156158
pb, err := validationRecordToPB(vr)
@@ -171,6 +173,7 @@ func TestValidationResult(t *testing.T) {
171173
AddressUsed: ip,
172174
URL: "urlA",
173175
Authorities: []string{"authA"},
176+
AddressesTried: []net.IP{ip},
174177
}
175178
vrB := core.ValidationRecord{
176179
Hostname: "hostB",
@@ -179,6 +182,7 @@ func TestValidationResult(t *testing.T) {
179182
AddressUsed: ip,
180183
URL: "urlB",
181184
Authorities: []string{"authB"},
185+
AddressesTried: []net.IP{ip},
182186
}
183187
result := []core.ValidationRecord{vrA, vrB}
184188
prob := &probs.ProblemDetails{Type: probs.TLSProblem, Detail: "asd", HTTPStatus: 200}

test/config-next/va.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"ServerURL": "http://boulder:6000"
2929
},
3030
"features": {
31-
"GoogleSafeBrowsingV4": true
31+
"GoogleSafeBrowsingV4": true,
32+
"IPv6First": true
3233
}
3334
},
3435

0 commit comments

Comments
 (0)