VA: Add integration test for HTTP timeouts. (#4050)
Also update `TestHTTPTimeout` to test with the `SimplifiedVAHTTP` feature flag enabled.
This commit is contained in:
parent
c37355b40b
commit
1c0be52e53
|
|
@ -4,7 +4,7 @@ services:
|
|||
# To minimize fetching this should be the same version used below
|
||||
image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.11.5}:2019-02-11
|
||||
environment:
|
||||
FAKE_DNS: 127.0.0.1
|
||||
FAKE_DNS: 10.77.77.77
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
BOULDER_CONFIG_DIR: test/config
|
||||
volumes:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"vaService": {
|
||||
"serverAddress": "va.boulder:9092",
|
||||
"timeout": "20s"
|
||||
"timeout": "2s"
|
||||
},
|
||||
"caService": {
|
||||
"serverAddress": "ca.boulder:9093",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"vaService": {
|
||||
"serverAddress": "va.boulder:9092",
|
||||
"timeout": "20s"
|
||||
"timeout": "2s"
|
||||
},
|
||||
"caService": {
|
||||
"serverAddress": "ca.boulder:9093",
|
||||
|
|
|
|||
|
|
@ -306,6 +306,65 @@ def test_http_challenge_https_redirect():
|
|||
elif r['ServerName'] != d:
|
||||
raise Exception("Expected all redirected requests to have ServerName {0} got \"{1}\"".format(d, r['ServerName']))
|
||||
|
||||
import threading
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
class SlowHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
try:
|
||||
# Sleeptime needs to be larger than the RA->VA timeout (2s at the
|
||||
# time of writing)
|
||||
sleeptime = 5
|
||||
print("SlowHTTPRequestHandler: sleeping for {0}s\n".format(sleeptime))
|
||||
time.sleep(sleeptime)
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'this is not an ACME key authorization')
|
||||
except:
|
||||
pass
|
||||
|
||||
def test_http_challenge_timeout():
|
||||
"""
|
||||
test_http_challenge_timeout tests that the VA times out challenge requests
|
||||
to a slow HTTP server appropriately.
|
||||
"""
|
||||
# Start a simple python HTTP server on port 5002 in its own thread.
|
||||
# NOTE(@cpu): The pebble-challtestsrv binds 10.77.77.77:5002 for HTTP-01
|
||||
# challenges so we must use the 10.88.88.88 address for the throw away
|
||||
# server for this test and add a mock DNS entry that directs the VA to it.
|
||||
httpd = HTTPServer(('10.88.88.88', 5002), SlowHTTPRequestHandler)
|
||||
thread = threading.Thread(target = httpd.serve_forever)
|
||||
thread.daemon = False
|
||||
thread.start()
|
||||
|
||||
# Pick a random domains
|
||||
hostname = random_domain()
|
||||
|
||||
# Add A record for the domains to ensure the VA's requests are directed
|
||||
# to the interface that we bound the HTTPServer to.
|
||||
challSrv.add_a_record(hostname, ["10.88.88.88"])
|
||||
|
||||
start = datetime.datetime.utcnow()
|
||||
end = 0
|
||||
|
||||
try:
|
||||
# We expect a connection timeout error to occur
|
||||
chisel.expect_problem("urn:acme:error:connection",
|
||||
lambda: auth_and_issue([hostname], chall_type="http-01"))
|
||||
end = datetime.datetime.utcnow()
|
||||
finally:
|
||||
# Shut down the HTTP server gracefully and join on its thread.
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
thread.join()
|
||||
|
||||
delta = end - start
|
||||
# Expected duration should be the RA->VA timeout plus some padding (At
|
||||
# present the timeout is 2s so adding 4s of padding = 6s)
|
||||
expectedDuration = 6
|
||||
if delta.total_seconds() == 0 or delta.total_seconds() > expectedDuration:
|
||||
raise Exception("expected timeout to occur in under {0} seconds. Took {1}".format(expectedDuration, delta.total_seconds()))
|
||||
|
||||
def test_tls_alpn_challenge():
|
||||
# Pick two random domains
|
||||
domains = [random_domain(), random_domain()]
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ def start(race_detection, fakeclock=None, account_uri=None):
|
|||
# interface and TLS-ALPN-01 responses on 5001 for another interface. The
|
||||
# choice of which is used is controlled by mock DNS data added by the
|
||||
# relevant integration tests.
|
||||
[8053, 'pebble-challtestsrv --defaultIPv4 %s --defaultIPv6 "" --dns01 :8053,:8054 --management :8055 --http01 :5002 -https01 10.77.77.77:5001 --tlsalpn01 10.88.88.88:5001' % os.environ.get("FAKE_DNS")],
|
||||
[8053, 'pebble-challtestsrv --defaultIPv4 %s --defaultIPv6 "" --dns01 :8053,:8054 --management :8055 --http01 10.77.77.77:5002 -https01 10.77.77.77:5001 --tlsalpn01 10.88.88.88:5001' % os.environ.get("FAKE_DNS")],
|
||||
[8004, './bin/boulder-va --config %s --addr va1.boulder:9092 --debug-addr :8004' % os.path.join(default_config_dir, "va.json")],
|
||||
[8104, './bin/boulder-va --config %s --addr va2.boulder:9092 --debug-addr :8104' % os.path.join(default_config_dir, "va.json")],
|
||||
[8001, './bin/boulder-ca --config %s --ca-addr ca1.boulder:9093 --ocsp-addr ca1.boulder:9096 --debug-addr :8001' % os.path.join(default_config_dir, "ca-a.json")],
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/bdns"
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/features"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/metrics"
|
||||
"github.com/letsencrypt/boulder/metrics/mock_metrics"
|
||||
|
|
@ -378,33 +379,58 @@ func TestHTTPTimeout(t *testing.T) {
|
|||
// TODO(#1989): close hs
|
||||
|
||||
va, _ := setup(hs, 0)
|
||||
|
||||
setChallengeToken(&chall, pathWaitLong)
|
||||
started := time.Now()
|
||||
|
||||
timeout := 50 * time.Millisecond
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
testCases := []struct {
|
||||
Name string
|
||||
SimplifiedVAHTTP bool
|
||||
}{
|
||||
{"Legacy VA HTTP", false},
|
||||
{"Simplified VA HTTP", true},
|
||||
}
|
||||
|
||||
_, prob := va.validateHTTP01(ctx, dnsi("localhost"), chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Connection should've timed out")
|
||||
}
|
||||
took := time.Since(started)
|
||||
// Check that the HTTP connection doesn't return before a timeout, and times
|
||||
// out after the expected time
|
||||
if took < timeout {
|
||||
t.Fatalf("HTTP timed out before %s: %s with %s", timeout, took, prob)
|
||||
}
|
||||
if took > 2*timeout {
|
||||
t.Fatalf("HTTP connection didn't timeout after %s", timeout)
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.ConnectionProblem)
|
||||
expectMatch := regexp.MustCompile(
|
||||
"Fetching http://localhost:\\d+/.well-known/acme-challenge/wait-long: Timeout after connect")
|
||||
if !expectMatch.MatchString(prob.Detail) {
|
||||
t.Errorf("Problem details incorrect. Got %q, expected to match %q",
|
||||
prob.Detail, expectMatch)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
var expectMatch *regexp.Regexp
|
||||
expectMatch = regexp.MustCompile(
|
||||
"Fetching http://localhost:\\d+/.well-known/acme-challenge/wait-long: Timeout after connect")
|
||||
|
||||
if tc.SimplifiedVAHTTP {
|
||||
err := features.Set(map[string]bool{"SimplifiedVAHTTP": true})
|
||||
test.AssertNotError(t, err, "Failed to set SimplifiedVAHTTP feature flag")
|
||||
defer features.Reset()
|
||||
// Simplified VA HTTP error messages don't include the port number when
|
||||
// it is equal to the va http port since it is implied by the `http://`
|
||||
// protocol prefix.
|
||||
expectMatch = regexp.MustCompile(
|
||||
"Fetching http://localhost/.well-known/acme-challenge/wait-long: Timeout after connect")
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
timeout := 50 * time.Millisecond
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
_, prob := va.validateHTTP01(ctx, dnsi("localhost"), chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Connection should've timed out")
|
||||
}
|
||||
|
||||
took := time.Since(started)
|
||||
// Check that the HTTP connection doesn't return before a timeout, and times
|
||||
// out after the expected time
|
||||
if took < timeout {
|
||||
t.Fatalf("HTTP timed out before %s: %s with %s", timeout, took, prob)
|
||||
}
|
||||
if took > 2*timeout {
|
||||
t.Fatalf("HTTP connection didn't timeout after %s", timeout)
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.ConnectionProblem)
|
||||
|
||||
if !expectMatch.MatchString(prob.Detail) {
|
||||
t.Errorf("Problem details incorrect. Got %q, expected to match %q",
|
||||
prob.Detail, expectMatch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue