// Test that the challenge is reachable at the given hostname. If a hostname // was not provided, this test is skipped. func (s *httpResponder) selfTest() error { if s.rcfg.Hostname == "" { return nil } u := url.URL{ Scheme: "http", Host: s.rcfg.Hostname, Path: "/.well-known/acme-challenge/" + s.rcfg.Token, } trans := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DisableKeepAlives: true, } client := &http.Client{ Transport: trans, Timeout: selfTestTimeout, } res, err := client.Get(u.String()) if err != nil { return err } defer res.Body.Close() if res.StatusCode != 200 { return fmt.Errorf("non-200 status code when doing self-test") } b, err := ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { return err } b = bytes.TrimSpace(b) if !bytes.Equal(b, s.ka) { return fmt.Errorf("got 200 response when doing self-test, but with the wrong data") } // If we detected a request, we support notifications, otherwise we don't. select { case <-s.requestDetectedChan: default: s.notifySupported = false } // Drain the notification channel in case we somehow made several requests. L: for { select { case <-s.requestDetectedChan: default: break L } } return nil }
// Checks OCSP for a certificate. The immediate issuer must be specified. If // the certificate does not support OCSP, (nil, nil) is returned. Uses HTTP // GET rather than POST. The response is verified. The caller must check the // response status. The raw OCSP response is also returned, even if parsing // failed and err is non-nil. func (c *Client) CheckOCSPRaw(crt, issuer *x509.Certificate, ctx context.Context) (parsedResponse *ocsp.Response, rawResponse []byte, err error) { if len(crt.OCSPServer) == 0 { return } b, err := ocsp.CreateRequest(crt, issuer, nil) if err != nil { return } b64 := base64.StdEncoding.EncodeToString(b) path := crt.OCSPServer[0] + "/" + b64 req, err := http.NewRequest("GET", path, nil) if err != nil { return } req.Header.Set("Accept", "application/ocsp-response") res, err := c.doReqActual(req, ctx) if err != nil { return } defer res.Body.Close() if res.StatusCode != 200 { err = fmt.Errorf("OCSP response has status %#v", res.Status) return } if res.Header.Get("Content-Type") != "application/ocsp-response" { err = fmt.Errorf("response to OCSP request had unexpected content type") return } // Read response, limiting response to 1MiB. rawResponse, err = ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { return } parsedResponse, err = ocsp.ParseResponse(rawResponse, issuer) return }
func (c *Client) loadExtraCertificates(crt *Certificate, res *http.Response, ctx context.Context) error { crt.ExtraCertificates = nil for { var err error lg := link.ParseResponse(res) up, ok := lg["up"] if !ok { return nil } crtURI, _ := url.Parse(crt.URI) upURI, _ := url.Parse(up.URI) if crtURI == nil || upURI == nil { return fmt.Errorf("invalid URI") } upURI = crtURI.ResolveReference(upURI) res, err = c.doReq("GET", upURI.String(), nil, nil, ctx) if err != nil { return err } defer res.Body.Close() ct := res.Header.Get("Content-Type") if ct != "application/pkix-cert" { return fmt.Errorf("unexpected certificate type: %v", ct) } der, err := ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { return err } res.Body.Close() crt.ExtraCertificates = append(crt.ExtraCertificates, der) } }
func (c *Client) loadCertificate(crt *Certificate, res *http.Response, ctx context.Context) error { defer res.Body.Close() ct := res.Header.Get("Content-Type") if ct == "application/pkix-cert" { der, err := ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { return err } crt.Certificate = der err = c.loadExtraCertificates(crt, res, ctx) if err != nil { return err } } else if res.StatusCode == 200 { return fmt.Errorf("Certificate returned with unexpected type: %v", ct) } crt.retryAt = retryAtDefault(res.Header, 10*time.Second) return nil }
func TestRedirector(t *testing.T) { dir, err := ioutil.TempDir("", "acme-redirector-test") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) r, err := New(Config{ Bind: ":9847", ChallengePath: dir, }) if err != nil { t.Fatal(err) } err = r.Start() if err != nil { t.Fatal(err) } defer r.Stop() req, err := http.NewRequest("FROBNICATE", "http://127.0.0.1:9847/foo/bar?alpha=beta", nil) if err != nil { t.Fatal(err) } res, err := http.DefaultTransport.RoundTrip(req) if err != nil { t.Fatal(err) } defer res.Body.Close() loc := res.Header.Get("Location") if loc != "https://127.0.0.1:9847/foo/bar?alpha=beta" { t.Fatalf("wrong Location: %v", loc) } err = ioutil.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0644) if err != nil { t.Fatal(err) } req, err = http.NewRequest("GET", "http://127.0.0.1:9847/.well-known/acme-challenge/foo", nil) if err != nil { t.Fatal(err) } res, err = http.DefaultTransport.RoundTrip(req) if err != nil { t.Fatal(err) } defer res.Body.Close() b, err := ioutil.ReadAll(denet.LimitReader(res.Body, 1*1024*1024)) if err != nil { t.Fatal(err) } if string(b) != "bar" { t.Fatal("wrong response") } }