distributedHTTPChallengeSolver checks to see if this challenge request was initiated by this or another instance which uses the same storage as am does, and attempts to complete the challenge for it. It returns true if the request was handled; false otherwise.
(w http.ResponseWriter, r *http.Request)
| 69 | // same storage as am does, and attempts to complete the challenge for |
| 70 | // it. It returns true if the request was handled; false otherwise. |
| 71 | func (am *ACMEIssuer) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http.Request) bool { |
| 72 | if am == nil { |
| 73 | return false |
| 74 | } |
| 75 | host := hostOnly(r.Host) |
| 76 | chalInfo, distributed, err := am.config.getACMEChallengeInfo(r.Context(), host, !am.DisableDistributedSolvers) |
| 77 | if err != nil { |
| 78 | if am.DisableDistributedSolvers { |
| 79 | // Distributed solvers are disabled, so the only way an error can be returned is if |
| 80 | // this instance didn't initiate the challenge (or if the process exited after, but |
| 81 | // either way, we don't have the challenge info). Assuming this is a legitimate |
| 82 | // challenge request, we may still be able to solve it if we can present the correct |
| 83 | // account thumbprint with the token, since the token is given to us in the URL path. |
| 84 | // |
| 85 | // NOTE: About doing this, RFC 8555 section 8.3 says: |
| 86 | // |
| 87 | // Note that because the token appears both in the request sent by the |
| 88 | // ACME server and in the key authorization in the response, it is |
| 89 | // possible to build clients that copy the token from request to |
| 90 | // response. Clients should avoid this behavior because it can lead to |
| 91 | // cross-site scripting vulnerabilities; instead, clients should be |
| 92 | // explicitly configured on a per-challenge basis. A client that does |
| 93 | // copy tokens from requests to responses MUST validate that the token |
| 94 | // in the request matches the token syntax above (e.g., that it includes |
| 95 | // only characters from the base64url alphabet). |
| 96 | // |
| 97 | // Also, since we're just blindly solving a challenge, we're unable to mitigate DNS |
| 98 | // rebinding attacks, because we don't know what host to expect in the URL. So this |
| 99 | // is not ideal, but we do at least validate the copied token is in the base64url set. |
| 100 | if strings.HasPrefix(r.URL.Path, acmeHTTPChallengeBasePath) && |
| 101 | strings.Count(r.URL.Path, "/") == 3 && |
| 102 | r.Method == http.MethodGet { |
| 103 | tokenStart := strings.LastIndex(r.URL.Path, "/") + 1 |
| 104 | token := r.URL.Path[tokenStart:] |
| 105 | if allBase64URL(token) { |
| 106 | if err := am.solveHTTPChallengeBlindly(w, r); err != nil { |
| 107 | am.Logger.Error("solving http-01 challenge blindly", |
| 108 | zap.String("identifier", host), |
| 109 | zap.Error(err)) |
| 110 | } |
| 111 | return true |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // couldn't get challenge info even with distributed solver |
| 117 | am.Logger.Warn("looking up info for HTTP challenge", |
| 118 | zap.String("uri", r.RequestURI), |
| 119 | zap.String("identifier", host), |
| 120 | zap.String("remote_addr", r.RemoteAddr), |
| 121 | zap.String("user_agent", r.Header.Get("User-Agent")), |
| 122 | zap.Error(err)) |
| 123 | return false |
| 124 | } |
| 125 | |
| 126 | return solveHTTPChallenge(am.Logger, w, r, chalInfo.Challenge, distributed) |
| 127 | } |
| 128 |
no test coverage detected