letsencrypt, localhost and autocert

1,766 views
Skip to first unread message

Sankar

unread,
Jun 6, 2017, 1:07:16 PM6/6/17
to golang-nuts
Hi

I saw the tweet https://twitter.com/mholt6/status/848704744474292224 and decided to try out the code

log.Fatal(http.Serve(autocert.NewListener("mydomain.com"), handler))

but when I try to visit: https://localhost:443, I get an error on the server console as: server name component count invalid

Is there any detailed documentation on how to get letsencrypt working with golang ?

I am using go 1.8.3

Google gave me https://github.com/golang/go/issues/17053 , but I am not able to understand if the letsencrypt support is fully landed or not. Can anyone point me to docs and best practices for testing at localhost and deploying at production, with https and letsencrypt ? Thanks.

Axel Wagner

unread,
Jun 6, 2017, 1:22:28 PM6/6/17
to Sankar, golang-nuts
tl;dr: You need a) a publicly routed IP address (either IPv4 or IPv6 is fine), b) a publicly resolvable domain that points to that IP address and c) actually point your client (browser) to that domain.

Long explanation:

The HTTP client will use SNI to tell the server the domain it needs a cert for. The autocert package will then check that against the provided HostPolicy (in the case of NewListener, that means "is it one of the listed domains") and tell LetsEncrypt that it wants a certificate for that domain. LetsEncrypt will then verify, that you actually own that domain and the corresponding key (thus the need for a publicly resolvable Domain. LetsEncrypt can't verify that you are "localhost"). There are multiple challenges for that (I believe there is one that uses DNS and one that uses SNI?), autocert implements only one the latter (I think) and tells LetsEncrypt which. As it doesn't implement the DNS based challenge, LetsEncrypt needs to resolve the domain to an IP and make a connection to it (thus the need for a publicly routed IP address) to verify, that there actually is someone with the correct key sitting behind it. That'll be autocert. Finally, if all that works, LetsEncrypt issues a certificate for that domain and gives it to your server; again, the autocert package handles the receiving and caching of that cert. Once it has the cert, it will finish the TLS handshake with the HTTP client and you have a valid connection. Future connections will just reuse the cached certificate, given that the client sends the same domain via SNI.

Hope that helps. It's quite a bit of complexity behind that one line of code; but if you actually fulfill above requirements a, b and c, it will be a total breeze to get good, strong certificates for however many domains you need :)

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andy Balholm

unread,
Jun 6, 2017, 1:28:24 PM6/6/17
to Axel Wagner, Sankar, golang-nuts
What it boils down to is that your server tries to get a certificate for whatever name is in the URL. But Let’s Encrypt won’t issue a certificate for localhost, for various reasons—not the least of which is that a certificate for localhost makes as much sense as having a photo ID that says “Me” for the name.

Andy

winlin

unread,
Jun 6, 2017, 9:27:16 PM6/6/17
to golang-nuts
There is a library base on lego and letsencrypt project: https://github.com/ossrs/go-oryx-lib/blob/master/https/example_test.go#L33
And there is a server as an example for that library: https://github.com/ossrs/go-oryx/blob/develop/httpx-static/main.go#L174

Sankar P

unread,
Jun 7, 2017, 1:22:29 PM6/7/17
to Axel Wagner, golang-nuts

2017-06-06 22:52 GMT+05:30 Axel Wagner <axel.wa...@googlemail.com>:
tl;dr: You need a) a publicly routed IP address (either IPv4 or IPv6 is fine), b) a publicly resolvable domain that points to that IP address and c) actually point your client (browser) to that domain.

a) I created an AWS VM with a public-ip address. I verified that the machine is accesible by ssh-ing into it.
b) In my domain name provider (Gandi, if it matters), I added a web-forwarding rule to forward all incoming requests to http://api.mydomain.com to https://public-ip
c) I ran a go server with that magical line: log.Fatal(http.Serve(autocert.NewListener("mydomain.com"), handler))
in that public-ip

Now if I try to access http://api.mydomain.com then I am not able to reach this server, nor do I get any mail from letsencrypt about certificates. What should I be doing extra ?

Thank you everyone for the responses.


--

Jim Smart

unread,
Jun 7, 2017, 1:42:10 PM6/7/17
to golang-nuts, sankar.c...@gmail.com


On Tuesday, June 6, 2017 at 6:22:28 PM UTC+1, Axel Wagner wrote:
tl;dr: You need a) a publicly routed IP address (either IPv4 or IPv6 is fine), b) a publicly resolvable domain that points to that IP address and c) actually point your client (browser) to that domain.



I've not done it specifically for Go stuff, but I expect this will work (it's a simple trick, works in other scenarios)

Consider temporarily adding mydomain.com to your OS's 'hosts' file (on your development system), so that you can then point a browser at mydomain.com and it will resolve to your localhost — then your Go server will get the mydomain headers it needs, and should find your cert fine from there.


Axel Wagner

unread,
Jun 7, 2017, 2:04:28 PM6/7/17
to Sankar P, golang-nuts
On Wed, Jun 7, 2017 at 7:22 PM, Sankar P <sankar.c...@gmail.com> wrote:

2017-06-06 22:52 GMT+05:30 Axel Wagner <axel.wa...@googlemail.com>:
tl;dr: You need a) a publicly routed IP address (either IPv4 or IPv6 is fine), b) a publicly resolvable domain that points to that IP address and c) actually point your client (browser) to that domain.

a) I created an AWS VM with a public-ip address. I verified that the machine is accesible by ssh-ing into it.
b) In my domain name provider (Gandi, if it matters), I added a web-forwarding rule to forward all incoming requests to http://api.mydomain.com to https://public-ip

This doesn't sound right. It seems that this would imply a) that your DNS-provider actually does HTTP proxying, which is definitely *not* what you want, you want to terminate the connection yourself and b) that your server still doesn't get an HTTP handshake for the Domain, as your client doesn't do the HTTP handshake with your server, but with the server of your DNS provider.

You want to set up an A/AAAA record for api.mydomain.com to point to your public IP.

For testing, what Jim suggested below (entering the IP address into your host-file, or the local DNS cache of your router, for example) would also work. But you need to actually set up DNS to point to your server.
 
c) I ran a go server with that magical line: log.Fatal(http.Serve(autocert.NewListener("mydomain.com"), handler))
in that public-ip

Note, that "api.mydomain.com" and "mydomain.com" are different domains. You need to list the same domains as arguments to NewListener as you are creating records for.

If you want, feel free to send me your actual domain name off-list and I could verify, that you set it up correctly.

BTW, note that none of these problems is specific to LetsEncrypt or the autocert package; you'd also need a correct DNS setup and everything if you'd use any other SSL certificate provider.

Sankar P

unread,
Jun 9, 2017, 4:07:15 AM6/9/17
to Axel Wagner, golang-nuts
Thank you so much Axel Wagner. I was able to get everything working, once I added the A record. Everything worked so magically together correctly :)

Axel Wagner

unread,
Jun 9, 2017, 4:12:04 AM6/9/17
to Sankar P, golang-nuts
No worries :) Glad to help increase security on the web by adding another Site with good TLS :)

Brad Fitzpatrick

unread,
Jun 10, 2017, 9:57:38 PM6/10/17
to Axel Wagner, Sankar, golang-nuts
Perhaps the autocert package should special case localhost and just serve a self-signed cert in that case. That'd be useful for testing and consistency.

I filed https://github.com/golang/go/issues/20640 to think about that.

Lucio

unread,
Jun 11, 2017, 1:41:39 AM6/11/17
to golang-nuts, axel.wa...@googlemail.com, sankar.c...@gmail.com
On Sunday, 11 June 2017 03:57:38 UTC+2, bradfitz wrote:
Perhaps the autocert package should special case localhost and just serve a self-signed cert in that case. That'd be useful for testing and consistency.

I filed https://github.com/golang/go/issues/20640 to think about that.

Maybe I should comment there, but, simply put, the OP accidentally triggered a problem for himself by unwittingly using localhost, we don't want to make this hard to figure out if it happens to anyone else. Documentation would address part of this concern, but not adequately. Other than a very clear warning at run time, I can't think how one would alert a developer that he's using autocert in a test-only mode.

Then again, despite three cups of coffee, I still can't quite explain my reservations properly, either, so maybe someone else can pick up that ball.

Lucio.

Reply all
Reply to author
Forward
0 new messages