diff --git a/acme/client.go b/acme/client.go index 902d7af5..39d1288a 100644 --- a/acme/client.go +++ b/acme/client.go @@ -35,7 +35,7 @@ type User interface { // Interface for all challenge solvers to implement. type solver interface { - CanSolve() bool + CanSolve(domain string) bool Solve(challenge challenge, domain string) error } @@ -150,7 +150,7 @@ func (c *Client) solveChallenges(challenges []*authorizationResource) error { // loop through the resources, basically through the domains. for _, authz := range challenges { // no solvers - no solving - if solvers := c.chooseSolvers(authz.Body); solvers != nil { + if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil { for i, solver := range solvers { // TODO: do not immediately fail if one domain fails to validate. err := solver.Solve(authz.Body.Challenges[i], authz.Domain) @@ -168,11 +168,11 @@ func (c *Client) solveChallenges(challenges []*authorizationResource) error { // Checks all combinations from the server and returns an array of // solvers which should get executed in series. -func (c *Client) chooseSolvers(auth authorization) map[int]solver { +func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver { for _, combination := range auth.Combinations { solvers := make(map[int]solver) for _, idx := range combination { - if solver, ok := c.Solvers[auth.Challenges[idx].Type]; ok { + if solver, ok := c.Solvers[auth.Challenges[idx].Type]; ok && solver.CanSolve(domain) { solvers[idx] = solver } else { logger().Printf("Could not find solver for: %s", auth.Challenges[idx].Type) diff --git a/acme/dvsni_challenge.go b/acme/dvsni_challenge.go index 1715554a..ae5bf77a 100644 --- a/acme/dvsni_challenge.go +++ b/acme/dvsni_challenge.go @@ -2,7 +2,7 @@ package acme type dvsniChallenge struct{} -func (s *dvsniChallenge) CanSolve() bool { +func (s *dvsniChallenge) CanSolve(domain string) bool { return false } diff --git a/acme/simple_http_challenge.go b/acme/simple_http_challenge.go index f02bd48b..0192d70c 100644 --- a/acme/simple_http_challenge.go +++ b/acme/simple_http_challenge.go @@ -10,6 +10,7 @@ import ( "encoding/pem" "errors" "fmt" + "io/ioutil" "math/big" "net" "net/http" @@ -21,8 +22,38 @@ type simpleHTTPChallenge struct { optPort string } -func (s *simpleHTTPChallenge) CanSolve() bool { - return true +// SimpleHTTPS checks for DNS, public IP and port bindings +func (s *simpleHTTPChallenge) CanSolve(domain string) bool { + // determine public ip + resp, err := http.Get("https://icanhazip.com/") + if err != nil { + logger().Printf("Could not get public IP -> %v", err) + return false + } + + ip, err := ioutil.ReadAll(resp.Body) + if err != nil { + logger().Printf("Could not get public IP -> %v", err) + return false + } + ipStr := string(ip) + + // resolve domain we should solve for + resolvedIPs, err := net.LookupHost(domain) + if err != nil { + logger().Printf("Could not lookup DNS A record for %s", domain) + return false + } + + // if the resolve does not resolve to our public ip, we can't solve. + for _, resolvedIP := range resolvedIPs { + if resolvedIP == ipStr { + return true + } + } + + logger().Printf("SimpleHTTPS: Domain %s does not resolve to the public ip of this server. Determined ip: %s", domain, ipStr) + return false } func (s *simpleHTTPChallenge) Solve(chlng challenge, domain string) error {