예제 #1
0
파일: http.go 프로젝트: hlandau/acme
// 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
}
예제 #2
0
파일: ocsp.go 프로젝트: hlandau/acme
// 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
}
예제 #3
0
파일: api.go 프로젝트: hlandau/acme
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)
	}
}
예제 #4
0
파일: api.go 프로젝트: hlandau/acme
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
}
예제 #5
0
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")
	}
}