Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/acme/autocert: acme: identifier authorization failed #17263

Closed
stapelberg opened this issue Sep 28, 2016 · 9 comments
Closed

x/crypto/acme/autocert: acme: identifier authorization failed #17263

stapelberg opened this issue Sep 28, 2016 · 9 comments

Comments

@stapelberg
Copy link
Contributor

stapelberg commented Sep 28, 2016

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.7.1 linux/amd64

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/michael/gocode"
GORACE=""
GOROOT="/home/michael/go"
GOTOOLDIR="/home/michael/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build701460639=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"

What did you do?

https://play.golang.org/p/Nas2lT_XeY

What did you expect to see?

I expected acme/autocert to acquire a certificate and serve my HTTPS request.

What did you see instead?

$ wget -O- https://bleh.zekjur.net:60667/
--2016-09-28 17:04:08--  https://bleh.zekjur.net:60667/
Resolving bleh.zekjur.net (bleh.zekjur.net)... 2a02:168:4a00:0:…
Connecting to bleh.zekjur.net (bleh.zekjur.net)|2a02:168:4a00:0:…|:60667... connected.
GnuTLS: A TLS fatal alert has been received.
GnuTLS: received alert [80]: Internal error
Unable to establish SSL connection.

The program output is:

2016/09/28 17:04:12 http: TLS handshake error from [2a02:…]:36790: acme: identifier authorization failed

It might be note-worthy that the hostname in question is reachable via IPv6 only. That’s not a problem for LetsEncrypt, at least not for the well-known URL verification method. I’m not sure whether this is the problem for the particular issue I’m seeing, and I’m also not sure how to further debug this. Any pointers welcome.

@rogpeppe
Copy link
Contributor

Are you serving your web server at bleh.zekjur.net:443 as well as 60667?
If not, it won't work.

@x1ddos
Copy link

x1ddos commented Sep 28, 2016

I think it should be possible to validate tls-sni challenge on a non standard port. acme/autocert currently ignores the port and let's encrypt assumes the default, 443, I believe.

Will do some experiments tomorrow and then create a CL.
Thanks for reporting this.

@stapelberg
Copy link
Contributor Author

Thanks for clarifying. I indeed only serve on 60667 and intend to keep it that way. I’d be happy to test a change to verify the problem is addressed.

@quentinmit quentinmit modified the milestone: Unreleased Oct 4, 2016
@stapelberg
Copy link
Contributor Author

@x1ddos Any updates on this issue? Thanks.

@x1ddos
Copy link

x1ddos commented Oct 10, 2016

@stapelberg I've realized tls/Config.GetCertificate only takes ClientHelloInfo which doesn't have port info, only host name.

Have you tried doing it manually? Not sure it'll work, but something like this:

m := autocert.Manager{
  Prompt: autocert.AcceptTOS,
  HostPolicy: autocert.HostWhitelist("example.org"),
}
getCert := func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
  return m.GetCertificate(...)
}
s := &http.Server{
    Addr: ":60667",
    TLSConfig: &tls.Config{GetCertificate: getCert},
}
s.ListenAndServeTLS("", "")

Otherwise, it'll have to wait until a Listener is implemented, as discussed in #17053.

@x1ddos
Copy link

x1ddos commented Oct 10, 2016

Oh wait, neither of my suggestions would work. Let's Encrypt (and the spec, for that matter, it seems) supports only port 443 for tls-sni-x challenges, which is what autocert uses.

You'd have to obtain the certs in some other way (use dns-01 with acme.Client or run autocert on another host listening on port 443) and then pass it to your server running on 60667.

See https://community.letsencrypt.org/t/letsencrypt-doesnt-work-for-different-ports/17519 and https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419

@bradfitz I think this issue can be closed now.

@stapelberg
Copy link
Contributor Author

Thanks for looking into this.

Let's Encrypt (and the spec, for that matter, it seems) supports only port 443 for tls-sni-x challenges, which is what autocert uses.

My reading of https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3 (which outlines the tls-sni-02 challenge) differs. Step 2 of the described verification procedure says “Open a TLS connection to the domain name being validated on the requested port”.

So, is this a Let’s Encrypt limitation or an ACME limitation? If the latter, I think we should update the spec to clearly state that (and its intention). If the former, we should file a tracking bug at LetsEncrypt to lift that limitation.

You'd have to obtain the certs in some other way (use dns-01 with acme.Client or run autocert on another host listening on port 443) and then pass it to your server running on 60667.

Is there by any chance a code example explaining how to use the dns-01 challenge with acme.Client?

@x1ddos
Copy link

x1ddos commented Oct 12, 2016

“Open a TLS connection to the domain name being validated on the requested port”
I think we should update the spec

There's no way to specify the port, AFAIK.
Forgot to link acme issue: ietf-wg-acme/acme#4 is what you want, I believe.

Is there by any chance a code example explaining how to use the dns-01 challenge with acme.Client?

import "golang.org/x/crypto/acme"

key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
  log.Fatal(err)
}
client := &acme.Client{Key: key}

ctx := context.Background()
a, err := client.Authorize(ctx, "example.com")
if err != nil {
  log.Fatal(err)
}
if a.Status == acme.StatusValid {
  // Client.Key is already authorized for example.com.
  // Skip DNS record provisioning and go to client.CreateCert
}

// Find dns-01 challenge in a.Challenges.
// Let's assume the var name is challenge.
tok, err := client.DNS01ChallengeRecord(challenge.Token)
if err != nil {
  log.Fatal(err)
}
// Provision tok value under _acme-challenge.example.com as a TXT record.
// Remember to defer unprovision().
// Once provisioned and propagated in DNS:
if _, err := client.Accept(ctx, challenge); err != nil {
  log.Fatal(err)
}
a, err = client.WaitAuthorization(ctx, a.URI)
if err != nil {
  log.Fatal(err)
}
if a.Status != acme.StatusValid {
  log.Fatal("domain authorization failed")
}

// Create the certificate.
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
  log.Fatal(err)
}
req := &x509.CertificateRequest{DNSNames: []string{"example.com"}} // populate other fields
csr, err := x509.CreateCertificateRequest(rand.Reader, req, priv)
if err != nil {
  log.Fatal(err)
}
pub, _, err := client.CreateCert(ctx, csr, 0, true)
if err != nil {
  log.Fatal(err)
}

// priv is now the private part of the cert.
// pub is the public part (DER format), including the chain.

@stapelberg
Copy link
Contributor Author

Thanks for the details! Given that DNS validation requires integrating with my DNS provider, I’ll probably use lego instead of crypto/acme, given that lego already has cloudflare integration.

I’m closing this issue as there’s nothing we can do right now about LetsEncrypt/the ACME spec not supporting arbitrary ports, and you’ve referenced the appropriate tracking bugs on their end.

@golang golang locked and limited conversation to collaborators Oct 24, 2017
@rsc rsc unassigned x1ddos Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants