aio-libs / aiosmtpd

A reimplementation of the Python stdlib smtpd.py based on asyncio.
https://aiosmtpd.aio-libs.org
Apache License 2.0
323 stars 96 forks source link

Add option to reload cert files periodically (letsencrypt) #253

Open FrederikLauber opened 3 years ago

FrederikLauber commented 3 years ago

Hi,

as far as I can tell, at the moment, certs are only read on context creation. This poses a problem for lets encrypt setups as the certs will regularly change on disk but an always running aiosmtpd controller will always use the context at startup. Would it be possible to either add an option to reload the certs after some time?

waynew commented 3 years ago

I believe it should be possible. I'm not sure if the capability to hot-swap the controller(?) exists, but I certainly like the idea. Obviously the current workaround would be to drain the existing connections/and or terminate and then just restart. But that feels wrong, if nothing else :upside_down_face:

pepoluan commented 3 years ago

For SMTPS, likely difficult. I'm not familiar with the innards of create_server; the Controller likely needs to be restarted.

For STARTTLS ... probably doable? Because the TLS Context is not pressed into service until SMTP receives the STARTTLS command.

However, the current implementation uses TLS Context as passed into __init__ during SMTP instantiation. Changing the mechanism will likely break things.

That said, the SMTP.tls_context attribute is accessible. Even if SMTP is started by Controller, one can access the attribute via Controller.smtpd.tls_context. If you use Controller via your own program, you can implement a "watcher" of sorts; upon detecting the cert & key change, create a new TLS Context and 'inject' it via Controller.smtpd.tls_context.

If we're talking about the CLI program, I think the best way is to not make it even more complex than it already is, but use certbot's --post-hook option to restart the CLI program.

Edit: Come to think of it, if you're not using the CLI program but use the Controller class directly, your program can also watch, say, for SIGHUP. When SIGHUP is received, your program would then create a new TLS Context and inject it into Controller.smtpd.tls_context

pepoluan commented 3 years ago

@waynew I believe you created the current test certs in f414dcd ?

Can you create an additional pair of cert+key? I'm a bit rusty with OpenSSL incantation.. 😅

I want to test if we can replace the cert+key on the fly. If so, I'll add a test to ensure this behavior.

(Don't replace the current server.crt and server.key files; just add a pair of new ones into the tests/certs dir)

waynew commented 3 years ago

I'm pretty sure, based on the days, that I used this incantation:

openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout server.key -out server.crt

And then just filled out the appropriate fields. Since I created it I used:

Issuer: C = US, ST = AR, L = Greenwood, O = Aiosmtpd, OU = DevTeam, CN = aes, emailAddress = wayne@waynewerner.com

Since that's where I was living at the time :joy:. I went ahead and created some here: https://github.com/aio-libs/aiosmtpd/compare/master...waynew:alt-certs?expand=1