Skip to content

Commit

Permalink
acme: send User-Agent and add Client.UserAgent
Browse files Browse the repository at this point in the history
This is useful to CAs, to identify and reach out to problematic clients.

Fixes golang/go#24496

Change-Id: I944fc8178c8fa8acaf3854e9c125d3af0364a4fb
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/183267
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
desdeel2d0m authored and FiloSottile committed Jun 21, 2019
1 parent 1e4ef05 commit 971ff6c
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 0 deletions.
7 changes: 7 additions & 0 deletions acme/acme.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ type Client struct {
// The jitter is a random value up to 1 second.
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration

// UserAgent is prepended to the User-Agent header sent to the ACME server,
// which by default is this package's name and version.
//
// Reusable libraries and tools in particular should set this value to be
// identifiable by the server, in case they are causing issues.
UserAgent string

dirMu sync.Mutex // guards writes to dir
dir *Directory // cached result of Client's Discover method

Expand Down
3 changes: 3 additions & 0 deletions acme/autocert/autocert.go
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,9 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
return nil, err
}
}
if client.UserAgent == "" {
client.UserAgent = "autocert"
}
var contact []string
if m.Email != "" {
contact = []string{"mailto:" + m.Email}
Expand Down
18 changes: 18 additions & 0 deletions acme/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string,

// doNoRetry issues a request req, replacing its context (if any) with ctx.
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
req.Header.Set("User-Agent", c.userAgent())
res, err := c.httpClient().Do(req.WithContext(ctx))
if err != nil {
select {
Expand All @@ -243,6 +244,23 @@ func (c *Client) httpClient() *http.Client {
return http.DefaultClient
}

// packageVersion is the version of the module that contains this package, for
// sending as part of the User-Agent header. It's set in version_go112.go.
var packageVersion string

// userAgent returns the User-Agent header value. It includes the package name,
// the module version (if available), and the c.UserAgent value (if set).
func (c *Client) userAgent() string {
ua := "golang.org/x/crypto/acme"
if packageVersion != "" {
ua += "@" + packageVersion
}
if c.UserAgent != "" {
ua = c.UserAgent + " " + ua
}
return ua
}

// isBadNonce reports whether err is an ACME "badnonce" error.
func isBadNonce(err error) bool {
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
Expand Down
27 changes: 27 additions & 0 deletions acme/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,30 @@ func TestRetryBackoffArgs(t *testing.T) {
t.Errorf("nretry = %d; want 3", nretry)
}
}

func TestUserAgent(t *testing.T) {
for _, custom := range []string{"", "CUSTOM_UA"} {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Log(r.UserAgent())
if s := "golang.org/x/crypto/acme"; !strings.Contains(r.UserAgent(), s) {
t.Errorf("expected User-Agent to contain %q, got %q", s, r.UserAgent())
}
if !strings.Contains(r.UserAgent(), custom) {
t.Errorf("expected User-Agent to contain %q, got %q", custom, r.UserAgent())
}

w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
}))
defer ts.Close()

client := &Client{
Key: testKey,
DirectoryURL: ts.URL,
UserAgent: custom,
}
if _, err := client.Discover(context.Background()); err != nil {
t.Errorf("client.Discover: %v", err)
}
}
}
27 changes: 27 additions & 0 deletions acme/version_go112.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build go1.12

package acme

import "runtime/debug"

func init() {
// Set packageVersion if the binary was built in modules mode and x/crypto
// was not replaced with a different module.
info, ok := debug.ReadBuildInfo()
if !ok {
return
}
for _, m := range info.Deps {
if m.Path != "golang.org/x/crypto" {
continue
}
if m.Replace == nil {
packageVersion = m.Version
}
break
}
}

0 comments on commit 971ff6c

Please sign in to comment.