Opened 3 years ago
Closed 23 months ago
#18655 closed enhancement (fixed)
Make meek-server easy to use with Let's Encrypt
Reported by: | dcf | Owned by: | dcf |
---|---|---|---|
Priority: | Medium | Milestone: | |
Component: | Obfuscation/meek | Version: | |
Severity: | Normal | Keywords: | |
Cc: | Actual Points: | ||
Parent ID: | Points: | ||
Reviewer: | Sponsor: |
Description
Currently it's not trivial to get certificates for meek-server using Let's Encrypt. The --webroot
option, for example, wants to write a token to the filesystem so the web server can serve it, but meek-server doesn't serve files from the filesystem.
Ideally this works in a way that certificates can be renewed (e.g. in a cron job) without restarting tor or meek-server.
Child Tickets
Change History (12)
comment:1 Changed 3 years ago by
comment:2 Changed 3 years ago by
Here are some other links I found.
Use on non-web servers? This question is more about using Let's Encrypt for e.g. IMAP or SMTP, but it's related in the sense that it's about a server type for which there is no ACME plugin.
allow ports other than 443. Somewhat related, says that the HTTPS authenticators can only run on port 443. (If we were not running meek-server on 443, then it would be pretty easy to run Let's Encrypt in standalone mode, which binds to 443. But we are running meek-server on 443.)
comment:3 Changed 3 years ago by
https://github.com/xenolf/lego
There's an acme library written in Go that will likely simplify this. Though to be honest the only annoying part is reloading the cert.
comment:4 follow-up: 5 Changed 3 years ago by
I stand corrected, as long as you use Go 1.6 or later, reloading the cert is trivial (See tls.Config.GetCertificate
). dcf, is "you must build with Go 1.6 or later" a reasonable requirement for meek-server
?
Not having to tear down the Listener on certificate reload is rather convenient. I can probably do the code for this in an evening if so...
comment:5 follow-up: 6 Changed 3 years ago by
Replying to yawning:
I stand corrected, as long as you use Go 1.6 or later, reloading the cert is trivial (See
tls.Config.GetCertificate
). dcf, is "you must build with Go 1.6 or later" a reasonable requirement formeek-server
?
Good idea. Requiring Go 1.6 is fine.
comment:6 follow-up: 7 Changed 3 years ago by
Replying to dcf:
Replying to yawning:
I stand corrected, as long as you use Go 1.6 or later, reloading the cert is trivial (See
tls.Config.GetCertificate
). dcf, is "you must build with Go 1.6 or later" a reasonable requirement formeek-server
?
Good idea. Requiring Go 1.6 is fine.
https://git.schwanenlied.me/yawning/meek/commit/b749b5846115d10ba5cc409f5f150362bb4dae57
Untested, should work, if there's something wrong it should be trivial. Behavior is:
- If the cert/key fail to
stat()
or load when creating the listener, fail hard. - On each incoming connection, if the mtime of either the cert or key has changed, reload the certificate, otherwise use the cached cert.
- Once the listener is created, failures to
stat()
or reload the certificate result in an error message logged at most once every 60s, and the existing cached certificate to be used.
Note that stat()
-ing 2 files every incoming connection may be a tad expensive (though that's outside the mutex). Performance can be improved by only reloading once in a while (gettimeofday()
is vDSO-ed on sensible systems), and by using a RWLock
instead of a Mutex
. Both to me are pre-mature optimizations since TLS handshake crypto blows both 2 syscalls and slight lock contention out of the water overhead wise.
I think the other half of this is easiest to implement via an --acme-webroot
that lets meek-server serve files over port 80 like you mentioned in your tor-dev@ e-mail.
comment:7 follow-up: 10 Changed 3 years ago by
Replying to yawning:
https://git.schwanenlied.me/yawning/meek/commit/b749b5846115d10ba5cc409f5f150362bb4dae57
Untested, should work, if there's something wrong it should be trivial. Behavior is:
- If the cert/key fail to
stat()
or load when creating the listener, fail hard.- On each incoming connection, if the mtime of either the cert or key has changed, reload the certificate, otherwise use the cached cert.
- Once the listener is created, failures to
stat()
or reload the certificate result in an error message logged at most once every 60s, and the existing cached certificate to be used.Note that
stat()
-ing 2 files every incoming connection may be a tad expensive (though that's outside the mutex). Performance can be improved by only reloading once in a while (gettimeofday()
is vDSO-ed on sensible systems), and by using aRWLock
instead of aMutex
. Both to me are pre-mature optimizations since TLS handshake crypto blows both 2 syscalls and slight lock contention out of the water overhead wise.
I think the other half of this is easiest to implement via an
--acme-webroot
that lets meek-server serve files over port 80 like you mentioned in your tor-dev@ e-mail.
Thanks. That's beautiful. I'll write tests for certContext
.
It occurs to me that an --acme-webroot
option will only work if meek-server is listening with TLS on port 443 or with non-TLS on port 80. So I think I'll check that one of those is true, and if not, throw an error at startup if the user tried to use --acme-webroot
. If they have something else running on 80 or 443, then they'll have to work out their own thing, for example running letsencrypt in standalone mode.
comment:8 follow-up: 9 Changed 3 years ago by
Here's my branch using Russ Cox's LE client library. The mechanism is the same as Yawning's (replacing GetCertificate). It's a new dependency, but only about 50 lines of code in meek-server itself: http://github.com/gtank/meek/tree/letsencrypt
The example torrc works fine on a Debian machine and starts bridging without noticeable delay on first fetch. The biggest caveats I can think of are
1) It manages the keys and certificates in a cache file on disk. There are good reasons for this (https://godoc.org/rsc.io/letsencrypt#hdr-Persistent_Storage) but it isn't strictly necessary.
2) If meek-server isn't listening on 443, this starts a new listener there since Let's Encrypt doesn't give you another option.
comment:9 Changed 2 years ago by
Replying to gtank:
Here's my branch using Russ Cox's LE client library. The mechanism is the same as Yawning's (replacing GetCertificate). It's a new dependency, but only about 50 lines of code in meek-server itself: http://github.com/gtank/meek/tree/letsencrypt
I want you to know that I used this idea in the WebSocket server plugin for Snowflake: comment:6:ticket:18654.
https://gitweb.torproject.org/user/dcf/snowflake.git/diff/?h=letsencrypt&id=1f8be86a01&id2=af70d49e96
It's not deployed yet but I'm happy with how it works (review on #18654 is welcome too).
comment:10 Changed 2 years ago by
Replying to dcf:
Thanks. That's beautiful. I'll write tests for
certContext
.
I merged certContext
in f7f792f28e and wrote tests in fe142d4ee3.
comment:11 Changed 23 months ago by
Status: | new → needs_review |
---|
Here is a branch for review.
https://gitweb.torproject.org/pluggable-transports/meek.git/log/?h=bug18655
https://gitweb.torproject.org/pluggable-transports/meek.git/diff/?h=bug18655&id=1fcae3cad2&id2=93dd339f7
Let's Encrypt support is activated by the --acme-hostnames
option, which specifies the set of hostnames for which the server will ask for certificates. For example,
ServerTransportPlugin meek exec /usr/local/bin/meek-server --acme-hostnames meek.bamsoftware.com --acme-email david@bamsoftware.com
I have this running right now on https://meek.bamsoftware.com/ (which is the bridge maenad).
comment:12 Changed 23 months ago by
Resolution: | → fixed |
---|---|
Status: | needs_review → closed |
Merged in e1fe61923fa2e22257615b65c5abfbcbc90dbceb.
I posted a request for ideas to tor-dev.
https://lists.torproject.org/pipermail/tor-dev/2016-March/010645.html