logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

My-lets-encrypt-setup.md (4009B)


  1. ---
  2. date: 2018-06-27
  3. title: A quick review of my Let's Encrypt setup
  4. layout: post
  5. tags: [encryption]
  6. ---
  7. Let's Encrypt makes TLS much easier for pretty much everyone, but can still
  8. be annoying to use. It took me a while to smooth over the cracks in my Let's
  9. Encrypt configuration across my (large) fleet of different TLS-enabled services.
  10. I wanted to take a quick moment to share setup with you.
  11. 2020-01-02 update: acme-client is unmaintained and caught the BSD disease
  12. anyway. I use [uacme](https://github.com/ndilieto/uacme) and my current
  13. procedure is documented on my [new server checklist](/new-server.html). It might
  14. not be exactly applicable to your circumstances, YMMV.
  15. The main components are:
  16. - [acme-client](https://kristaps.bsd.lv/acme-client/)
  17. - nginx
  18. - cron
  19. nginx and cron need no introduction, but acme-client deserves a closer look. The
  20. acme client blessed by Let's Encrypt is [certbot](https://certbot.eff.org/), but
  21. BOY is it complicated. It's a big ol' pile of Python and I've found it fragile,
  22. complicated, and annoying. The goal of maintaining your nginx and apache configs
  23. for you is well intentioned but ultimately useless for advanced users. The
  24. complexity of certbot is through the roof, and complicated software breaks.
  25. I bounced between alternatives for a while but when I found acme-client, it
  26. totally clicked. This one is written in C with minimal dependencies (LibreSSL
  27. and libcurl, no brainers IMO). I bring a statically linked acme-client binary
  28. with me to new servers and setup time approaches zero as a result.
  29. I use nginx to answer challenges (and for some services, to use the final
  30. certificates for HTTPS - did you know you can use Let's Encrypt for more
  31. protocols than just HTTPS?). I quickly `mkdir -p
  32. /var/www/acme/.well-known/acme-challenge`, make sure nginx can read it, and add
  33. the following rules to nginx to handle challenges:
  34. ```nginx
  35. server {
  36. listen 80;
  37. listen [::]:80;
  38. server_name example.org;
  39. location ^~ /.well-known/acme-challenge {
  40. alias /var/www/acme;
  41. }
  42. }
  43. ```
  44. If I'm not using the certificates for HTTPS, this is all I need. But assuming I
  45. have some kind of website going, the full configuration usually looks more like
  46. this:
  47. ```nginx
  48. server {
  49. listen 80;
  50. listen [::]:80;
  51. server_name example.org;
  52. location / {
  53. return 302 https://$server_name$request_uri;
  54. }
  55. location ^~ /.well-known/acme-challenge {
  56. alias /var/www/acme;
  57. }
  58. }
  59. server {
  60. listen 443 ssl;
  61. listen [::]:443 ssl;
  62. server_name example.org;
  63. ssl_certificate /etc/ssl/acme/$server_name/fullchain.pem;
  64. ssl_certificate_key /etc/ssl/acme/$server_name/privkey.pem;
  65. location ^~ /.well-known/acme-challenge {
  66. alias /var/www/acme;
  67. }
  68. # ...application specific rules...
  69. }
  70. ```
  71. This covers the nginx side of things. To actually do certificate negotiation, I
  72. have a simple script I carry around:
  73. ```sh
  74. exec >>/var/log/acme 2>&1
  75. date
  76. acme() {
  77. site=$1
  78. shift
  79. acme-client -vNn \
  80. -c /etc/ssl/acme/$site/ \
  81. -k /etc/ssl/acme/$site/privkey.pem \
  82. $site $*
  83. }
  84. acme example.org subd1.example.org subd2.example.org
  85. nginx -s reload
  86. ```
  87. The first two lines set up a log file in `/var/log/acme` I can use to debug any
  88. issues that arise. Then I have a little helper function that wires up
  89. acme-client the way I like it, and I can call it for each domain I need certs
  90. for on this server. The last line changes if I'm doing something other than
  91. HTTPS with the certs (for example, `postfix reload`).
  92. One gotcha is that acme-client will bail out if the directories don't exist when
  93. you run it, so a quick `mkdir -p /etc/ssl/acme/example.org` when adding new
  94. sites is necessary
  95. The final step is a simple cron entry that runs the script daily:
  96. ```cron
  97. 0 0 * * * /usr/local/bin/acme-update-certs
  98. ```
  99. It's that easy. It took me a while to get a Let's Encrypt setup that was simple
  100. and satisfactory, but I believe I've settled on this one. I hope you find it
  101. useful!