{"action":"created","issue":{"url":"https://api.github.com/repos/letsencrypt/boulder/issues/2897","repository_url":"https://api.github.com/repos/letsencrypt/boulder","labels_url":"https://api.github.com/repos/letsencrypt/boulder/issues/2897/labels{/name}","comments_url":"https://api.github.com/repos/letsencrypt/boulder/issues/2897/comments","events_url":"https://api.github.com/repos/letsencrypt/boulder/issues/2897/events","html_url":"https://github.com/letsencrypt/boulder/issues/2897","id":245424224,"number":2897,"title":"IPv6First with a hanging ipv6 connection never tries the ipv4 address","user":{"login":"marktheunissen","id":453297,"avatar_url":"https://avatars0.githubusercontent.com/u/453297?v=4","gravatar_id":"","url":"https://api.github.com/users/marktheunissen","html_url":"https://github.com/marktheunissen","followers_url":"https://api.github.com/users/marktheunissen/followers","following_url":"https://api.github.com/users/marktheunissen/following{/other_user}","gists_url":"https://api.github.com/users/marktheunissen/gists{/gist_id}","starred_url":"https://api.github.com/users/marktheunissen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marktheunissen/subscriptions","organizations_url":"https://api.github.com/users/marktheunissen/orgs","repos_url":"https://api.github.com/users/marktheunissen/repos","events_url":"https://api.github.com/users/marktheunissen/events{/privacy}","received_events_url":"https://api.github.com/users/marktheunissen/received_events","type":"User","site_admin":false},"labels":[{"id":401753923,"url":"https://api.github.com/repos/letsencrypt/boulder/labels/area/va","name":"area/va","color":"4B7C8C","default":false},{"id":401754255,"url":"https://api.github.com/repos/letsencrypt/boulder/labels/kind/bug","name":"kind/bug","color":"7F5195","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2017-07-25T14:39:51Z","updated_at":"2017-07-25T15:36:45Z","closed_at":null,"body":"Steps to recreate:\r\n\r\n- Attempt to go through http-01 validation for `testboulderv6.ddns.net`\r\n- Validation fails, even though there is a valid A record. \r\n\r\nDetails:\r\n\r\nGiven a domain with both A and AAAA records, if the connection to the AAAA record hangs, we expect that the A record is tried after a 5 second timeout given by `singleDialTimeout`. \r\n\r\nInstead, the A record is never tried.\r\n\r\n```\r\n$ dig +short AAAA testboulderv6.ddns.net\r\n2001:4801:7901:0:aff8:a2d8:0:249\r\n$ dig +short A testboulderv6.ddns.net\r\n23.185.0.3\r\n```\r\n\r\n```\r\n$ curl -4 testboulderv6.ddns.net/.well-known/acme-challenge/abcdef\r\nabcdef.pubkey\r\n```\r\n```\r\n$ time curl -6 -v testboulderv6.ddns.net/.well-known/acme-challenge/abcdef\r\n*   Trying 2001:4801:7901:0:aff8:a2d8:0:249...\r\n* connect to 2001:4801:7901:0:aff8:a2d8:0:249 port 80 failed: Connection timed out\r\n* Failed to connect to testboulderv6.ddns.net port 80: Connection timed out\r\n* Closing connection 0\r\ncurl: (7) Failed to connect to testboulderv6.ddns.net port 80: Connection timed out\r\n\r\nreal\t2m7.196s\r\nuser\t0m0.002s\r\nsys\t0m0.008s\r\n```\r\n\r\nHere's what I see in the challenge:\r\n\r\n```json\r\n{\r\n  \"type\": \"http-01\",\r\n  \"status\": \"invalid\",\r\n  \"error\": {\r\n    \"type\": \"urn:acme:error:connection\",\r\n    \"detail\": \"Fetching http://testboulderv6.ddns.net/.well-known/acme-challenge/<redacted>: Timeout\",\r\n    \"status\": 400\r\n  },\r\n  \"uri\": \"<redacted>\",\r\n  \"token\": \"<redacted>\",\r\n  \"keyAuthorization\": \"<redacted>\",\r\n  \"validationRecord\": [\r\n    {\r\n      \"url\": \"http://testboulderv6.ddns.net/.well-known/acme-challenge/<redacted>\",\r\n      \"hostname\": \"testboulderv6.ddns.net\",\r\n      \"port\": \"80\",\r\n      \"addressesResolved\": [\r\n        \"23.185.0.3\",\r\n        \"2001:4801:7901:0:aff8:a2d8:0:249\"\r\n      ],\r\n      \"addressUsed\": \"2001:4801:7901:0:aff8:a2d8:0:249\",\r\n      \"addressesTried\": []\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n`http01Dialer.Dial()` only checks that it can CONNECT to the target socket on that IP, not that the server is actually alive, is an HTTP server, and/or is capable of responding to any request. https://github.com/letsencrypt/boulder/blob/master/va/va.go#L231-L236\r\n\r\nIdeally, it would be nice if the test was stricter so that we failover to the ipv4 if the v6 address is broken. How about performing a round-trip, and ensuring a status code or some valid HTTP headers are received?\r\n\r\nIpv6 is poorly supported (e.g. not available in Google Cloud), so it's quite a pain to pre-verify these domain records before requesting a cert is issued.\r\n\r\nIf this is not possible, a feature request might be to add a parameter so that the client can request whether ipv6 or v4 should be tried first.\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/letsencrypt/boulder/issues/comments/317778118","html_url":"https://github.com/letsencrypt/boulder/issues/2897#issuecomment-317778118","issue_url":"https://api.github.com/repos/letsencrypt/boulder/issues/2897","id":317778118,"user":{"login":"marktheunissen","id":453297,"avatar_url":"https://avatars0.githubusercontent.com/u/453297?v=4","gravatar_id":"","url":"https://api.github.com/users/marktheunissen","html_url":"https://github.com/marktheunissen","followers_url":"https://api.github.com/users/marktheunissen/followers","following_url":"https://api.github.com/users/marktheunissen/following{/other_user}","gists_url":"https://api.github.com/users/marktheunissen/gists{/gist_id}","starred_url":"https://api.github.com/users/marktheunissen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marktheunissen/subscriptions","organizations_url":"https://api.github.com/users/marktheunissen/orgs","repos_url":"https://api.github.com/users/marktheunissen/repos","events_url":"https://api.github.com/users/marktheunissen/events{/privacy}","received_events_url":"https://api.github.com/users/marktheunissen/received_events","type":"User","site_admin":false},"created_at":"2017-07-25T15:36:45Z","updated_at":"2017-07-25T15:36:45Z","body":"Hi @cpu, thanks for getting back so quickly, yeah doing an HTTP req/resp cycle, regardless of the code or headers returned, might be the easiest."}}
{"actor":{"display_login":"marktheunissen"}}