Closed flashmob closed 5 years ago
Note that currently working on configuration file reloading feature, which has the ability to reload TLS config without restarting the server. This means that after each command run, the server can send a SIG_HUP to itself if it running, and TLS certificates will be reloaded.
I think the configuration file should include the whitelisted hosts/domains and the certificates should be served/fetched/verified automatically much like the net/http implementation
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.com", "example2.com"),
Cache: autocert.DirCache("/root/letsencrypt"),
}
s := &http.Server{
Addr: ":443",
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
}
panic(s.ListenAndServeTLS("", ""))
I think /guerrillad certificate <server-interface> new
and ./guerrillad certificate renew
approach sucks because it's manual and the whole point of letsencrypt is to automate the certificate management.
Thanks for feedback.
Well then, perhaps it can be automatically setup if all conditions are met:
For storage of Let's encrypt certs: would a dot folder be ok? For renewals, check if the cert is about to expire when we get a SIGHUP, then renew if it matches above conditions.
There are likely a couple caveats. I think LE only permits requests for certs from servers listening on 'web ports', eg. :443, so the A Name record for the domain should be pointed at the IP address that the mail server is running on. This cert could then be used by the mail server, I imagine, without an difficulties.
I imagine the safest, and least breaking, way to do this would be adding options in the config file for ACME configuration(eg. Email address, agreeToTOS, ACME url etc), the user would then leave ServerConfig.PrivateKeyFile
and ServerConfig.PublicKeyFile
line empty or filled with a placeholder, perhaps "acme"
for each host requiring ACME certs. When starting the server, calls to the autocert package will run after parsing the config; obtaining a cert pair for each requiring certs(unless they exist in file already), saving the cert pairs to file, and adding that absolute path to the ServerConfig.PrivateKeyFile
and ServerConfig.PublicKeyFile
for that host.
I tried to implement a simple example within your package (a couple functions in config.go); not sure why but anything I try to print or log is simply ignored or suppressed. I imagine this has to do with the cobra cli app, but I am not sure how.
Lemme know, if you know why my functions seem to be ignored.
Schmorrison
Hi, thanks for doing some research.
For logging, have you tried setting the log_file to "stderr" or "stdout" in the config? Also, try starting with the -v flag.
FYI, as a convention, config values should be treated as read only, and only can be modified via the config file. That is because the config could be re-loaded at any time, there could be a race condition. To modify a config value, one must modify the file first and then either send a SIGHUP or call readConfig func, and then emit the config change events. See serve.go to see how it's done.
As for placeholders, would prefer not to use placeholders. The server should try to get the certs automatically as much as possible, as long as the above conditions are met. https://github.com/flashmob/go-guerrilla/issues/29#issuecomment-274945367
Email and 'agree to tos' are needed for account creation? Yes, these would need to be added to the main config, if an account is required.
So it looks like the problem is with how to do "Identifier Validation Challenges". https://github.com/letsencrypt/acme-spec/blob/master/draft-barnes-acme.md#identifier-validation-challenges
Yes, For HTTP based, it looks like it must bind to :443 or :80, which would not be possible, since almost certainly, there would be other servers bound to that port.
So, the alternative is to use DNS based?
We can model on how Caddy does it, for DNS https://caddyserver.com/docs/automatic-https After having a first look, it seems it has a few plugins which can talk to some of the most common DNS providers. It also loads in the API credentials from the environment.
Maybe sometime in the future, it may be good to propose an SMTP based "Identifier Validation Challenge" to the Lets Encrypt people? For MTAs, the DNS validation is already half-there in the form of an MX records, all they need to do is send an email with the challenge. They don't need to register an account, an email address can be synonymous with an account. Say if they send it to postmaster@example.com then our MTA would parse it. That would make it just as easy to get certificates for MTAs as it is for HTTP servers
Another alternative would be to use a tool called certbot from https://certbot.eff.org/ and writing a plugin: https://certbot.eff.org/docs/contributing.html#dev-plugin The plugin would update the go-guerrilla config & send a sighup to the server so that the certificate can reload. https://certbot.eff.org/docs/contributing.html#dev-plugin - they have some plugin examples. It's written in Python.
Thanks flashmob.
I was using -v
flag but it wasn't revealing any additional info. I will try modifying the log_file line in the config tonight.
Also, duly noted on the config conventions.
So it sounds like the preferred way would be writing a wrapper package for certbot. Using it to request certs manually, then update the config and send a SIGHUP. Then on server startup, with the certificates correctly mapped in the config file, check if the certs are about to expire, and if they are then renew them and send a SIGHUP.
I am sure you are aware, but this will still require having port :443 available for the certbot --standalone
, or mapping to the correct webroot directory of the host web server that is occupying that port. I am sure having both options would be advantageous, and I imagine having a separate json config for the certbot cli flags would also make sense.
Thanks for the info. It is now becoming clearer that bunding an Acme client in go-guerrilla may be too much (at least for now). It may bring in some technical debt, especially to support all the various DNS providers, which probably change all the time.
So it seems like certbot does a lot of the heavy lifting. Also, it conforms to the Unix philosophy of "Make each program do one thing well".
With certbot, there is no need to check if the certs are about to expire. Just put certbot renew
on cron to run once per day, and then send a sighup to go-guerrilla. Certbot does the renewals automatically.
Cerbot supports the following challenges: tls-sni-01 (port 443), http (port 80), and dns. So yes, unless DNS based challenge is used, certbot must be able to have access to the webroot for that web server.
Btw, certbot is used for go-guerrilla deployment on Guerrillamail.com but we don't have a plugin for it, so some things are manual.
Here is how we deploy it with Nginx. In /etc/nginx/sites-available/default we add the following to the default server block:
location ~ /.well-known {
allow all;
}
}
Then run this command:
certbot certonly --webroot -w /home/www/public -d example.com -d www.example.com
where /home/www/public
is the webroot. Certificate gets dropped to /etc/letsencrypt/live/grr.la/fullchain.pem
and then we enter that to the config for go-guerrilla manually to install it.
In summary, a solution using certbot would be acceptable to claim the bounty, just needs to be a bit less manual : -)
So deliverables to claim the bounty would be:
What do you think? (edit: grammar)
Well I will keep it in mind, but unfortunately I have not used Python nearly enough to feel confident writing a plugin.
I'm all up for claiming a bounty, but after this discussion I do not believe this is the one. At least the issue has been clarified for someone with more experience in Python.
Having a look at Python, it has a lot of similarity to Go, so if you know Go it may be easy to learn Python.
That said, the above are not absolute requirements. In the end, the contributor for this feature can decide on what to implement and how to do it. It should have been clarified that the above specifies the minim that would be accepted.
In summary,
Although we have not fully discussed the scope of a DNS based challenge. Perhaps the minimal solution that could work here is you could start go-guerrilla with --agree option which would then prompt the user to set an A/AAA record for their domain, press any key to continue. Then a goroutine could try the challenge and update the config file once the challenge passed. In that case, there would be a manual step involved. Others, such as Caddy server are able to remove the manual step by using external APsi, however, for us, this requirement may be too much to do on first version. Therefore, if a DNS change requires a manual step then that would be acceptable.
Also, what do you think if we drafted a proposal to the Let's Encrypt people to support an new MX record based challenge? It seems like LE is HTTPS centric, an MX record based challenge would be great for SMTP and possibly even POP. Maybe @ginkoob has some thoughts on this?
Been thinking about this, and while I'm not able to spend enough time to learn Python to write a plugin. I am able to continue thinking about the best way to implement it in Go. I'll take a close look at Caddy and how they manage the DNS challenges. In lieu of automated DNS challenges, knowing you are amenable to a manual step for the time being, I will think about how to display the txt record that needs to be changed for letsencrypt.
For example, if they know they are doing a DNS challenge; then first, they need to see the challenge value; then update the DNS record manually at their name server; then continue the challenge. It is not immediately clear to me how to execute this outside the main routine as you suggested.
It occurs to me that it could be significantly easier to write, if the user knows which type of challenge they are doing for certificates, and can configure that. If they want to do the "golang.org/x/crypto/acme/autocert" then:
If they have configured a webroot of a running server:
In all challenge types, probably:
In the case of a "acme/autocert" or webroot, after informing go-guerilla about the desired challenge, it certainly would be possible to execute in a separate goroutine, but if the challenge fails; what is the best way to inform the user?
I'm sure there are a number of incoherent thoughts in there. I want to spend some time this weekend maybe working on this.
schmorrison
Yes, it all boils down to how the user will do the challenge. Perhaps we can have this set in the config. In that case, please see cmd/guerrillad/serve.go - there is a CmdConfig struct defined. This struct adds additional options to the server config when starting from the command line.
You can add extra config options to CmdConfig, eg, what challenge method to use, where is the web-root, folder to store certificates, LE account email, accept to the LE TOS, etc. Somewhere in the serve function, after the config has loaded, you would need to add a call to your processing function, where these config settings and evaluated determine what steps to take. eg. perhaps configureAcme(c *CmdConfig)
Your configureAcme should update the config with new settings. If the configureAcme function changed the config file, i.e it installed a certificate, then you would need to reload the config. ie. err := readConfig(configPath, pidFile, &cmdConfig)
(doing it before config is used by the guerrilla package). Maybe it would be acceptable to prompt the user (y/n) to acknowledge if they want to do a step manually if starting from a command line. Timeout after x seconds if no response given.
The tricky part may be to get it to work with auto-config reloading (see sigHandler
func. In that case, we can do only if challenges are fully automated. The server can already detect if a certificate changed by comparing the timestamp, and re-configure automatically when a SIGHUP signal is caught.
That said, the code in serve.go is about change after huge refactor - but if you keep the changes around in serve.go and acme processing stuff in a separate file, that should minimise merge conflicts. See the new branch that's about to get merged (pending review) here https://github.com/flashmob/go-guerrilla/pull/71
Duly noted flashmob. I'll let you know if I have any questions.
Ok, sorry a bit in a hurry now and made a few mistakes and omitted a few details - made some edits above. Cheers.
@flashmob is the bounty on this issue still active?
@rahulgi Yes! There's been no activity on this for a while - so you're welcome to take over.
Why do need a plugin for certbot? I used deploy hook and I hope that everything will be fine)
Going to shelve this feature from the roadmap.
Rationale:
It seems certbot
works fairly well for getting LE certificates, and having them enabled in go-guerrilla is just a question of pointing the config to them.
If this feature is re-visited in the future, perhaps it can use the "DNS-01 challenge" using this library https://github.com/go-acme/lego
@flashmob what is the status on Lets Encrypt? Free certificate is always good, better if built in, no need to use any other scripts to do some hacks.
I think Lego is being used by Traefik, and it seems like a solid choice, if there are any vendor changes, just bump lego version and keep on going.
It doesn't need to be built in, since there are other tools that do the job of handling "lets encrypt" certificates , namely the Cerbot ACME client. Therefore, that issue was cancelled.
On Fri, 16 Oct 2020, 09:57 Alexandre Vicenzi, notifications@github.com wrote:
@flashmob https://github.com/flashmob what is the status on Lets Encrypt? Free certificate is always good, better if built in, no need to use any other scripts to do some hacks.
I think Lego is being used by Traefik, and it seems like a solid choice, if there are any vendor changes, just bump lego version and keep on going.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/flashmob/go-guerrilla/issues/29#issuecomment-709667073, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE6MP36HOTX3ZBAVJVPCADSK6K7PANCNFSM4C2KDKLQ .
Btw, to further explain, this project's goal is to be useful more as a library than stand-alone software. Therefore, it's best not to introduce any new dependencies or features that are already available outside. If say, LE support was added directly to go-guerrilla then we would need to import the go-acme library. It would be better to start an external project, import both go-guerrilla and go-acme and then put the two together. (Although it would probably be easier to just use the stand-alone Certbot client, of course).
On Fri, 16 Oct 2020 at 10:30, Adam M flashmob@gmail.com wrote:
It doesn't need to be built in, since there are other tools that do the job of handling "lets encrypt" certificates , namely the Cerbot ACME client. Therefore, that issue was cancelled.
On Fri, 16 Oct 2020, 09:57 Alexandre Vicenzi, notifications@github.com wrote:
@flashmob https://github.com/flashmob what is the status on Lets Encrypt? Free certificate is always good, better if built in, no need to use any other scripts to do some hacks.
I think Lego is being used by Traefik, and it seems like a solid choice, if there are any vendor changes, just bump lego version and keep on going.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/flashmob/go-guerrilla/issues/29#issuecomment-709667073, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAE6MP36HOTX3ZBAVJVPCADSK6K7PANCNFSM4C2KDKLQ .
I have been using Caddy for my routing, and as a bonus it handles SSL certs via LE automatically if required.
Let’s Encrypt and the golang.org/x/crypto/acme/autocert package’s GetCertificate function.
Requirements:
./guerrillad certificate <server-interface> new
to issue a new cert & add to config. (using the primary_mail_host as as the FQDN./guerrillad certificate <server-interface> renew
to renew./guerrillad certificate renew
to renew all certificates