Me, elsewhere on the net
  • Mastodon
  • Telegram
  • Facebook
  • X
  • VK
  • Instagram

Paolo Redaelli

A civil engineer with a longlife fondness for Software Libero
  • Home
  • Contatti
  • Gnome Apps
  • Info
  • La conta dei biscotti

Dehydrated: a bash client for Let’s Encrypt – Antoine Aflalo

Paolo Redaelli2018-12-102018-12-10

Dehydrated: a bash client for Let’s Encrypt

Dehydrated was firstly known as letsencrypt.sh but because letsencrypt is a trademark, they decided to rename the project, but keep the excellent features.

Purpose

If you’re already familiar with let’s encrypt and the ACME protocol, you can go directly to the next section on how to use Dehydrated.

Let’s encrypt give you the possibility to automatize the creation and renewal of SSL certificate. Those certificates and keys can be used for anything that uses SSL protocol like to activate https on your website, TLS on your SMTP/IMAP server, etc … everywhere you can use an SSL cert; you can use let’s encrypt.

Let’s Encrypt is an authorised CA which mean all the certificate generated by it are considered verified and will be recognised as it by the different clients (browser, email client, etc …). This is one of the biggest advantages against self-signed certificates, and your users won’t have to bypass a security warning or add a security exception.

Not only the generated certificate are valid, but let’s encrypt is an entirely automatize protocol, there is no need of human interaction to create and renew a certificate, all you need is a client compatible with the ACME protocol, and that were Dehydrated enter.

Dehydrated

Install

Either you take one of the release packages, or you clone the repository. I advise creating a symlink after to /usr/local/sbin/dehydrated it’s easier like this for setting the cronjob after. You also need to have the OpenSSL package installed for dehydrated to work. You also need a web server, Apache or Nginx will do fine.

lukas2511 / dehydrated

letsencrypt/acme client implemented as a shell-script – just add water

https://dehydrated.io

4,139542

Configuration

First, create a configuration folder only accessible by root: /etc/dehydrated/ You also need to create /var/www/dehydrated that will contain the challenge files for website verification.

Conf File

Now create a configuration file named config using their template:

Configuration File
########################################################
# This is the main config file for dehydrated          #
#                                                      #
# This file is looked for in the following locations:  #
# $SCRIPTDIR/config (next to this script)              #
# /usr/local/etc/dehydrated/config                     #
# /etc/dehydrated/config                               #
# ${PWD}/config (in current working-directory)         #
#                                                      #
# Default values of this config are in comments        #
########################################################

# Resolve names to addresses of IP version only. (curl)
# supported values: 4, 6
# default: <unset>
#IP_VERSION=

# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
#CA="https://acme-v01.api.letsencrypt.org/directory"

# Path to license agreement (default: https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf)
#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"

# Which challenge should be used? Currently http-01 and dns-01 are supported
#CHALLENGETYPE="http-01"

# Path to a directory containing additional config files, allowing to override
# the defaults found in the main configuration file. Additional config files
# in this directory needs to be named with a '.sh' ending.
# default: <unset>
#CONFIG_D=

# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
#BASEDIR=$SCRIPTDIR

# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
#DOMAINS_TXT="${BASEDIR}/domains.txt"

# Output directory for generated certificates
#CERTDIR="${BASEDIR}/certs"

# Directory for account keys and registration information
#ACCOUNTDIR="${BASEDIR}/accounts"

# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: /var/www/dehydrated)
#WELLKNOWN="/var/www/dehydrated"

# Default keysize for private keys (default: 4096)
#KEYSIZE="4096"

# Path to openssl config file (default: <unset> - tries to figure out system default)
#OPENSSL_CNF=

# Program or function called in certain situations
#
# After generating the challenge-response, or after failed challenge (in this case altname is empty)
# Given arguments: clean_challenge|deploy_challenge altname token-filename token-content
#
# After successfully signing certificate
# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem path/to/fullchain.pem
#
# BASEDIR and WELLKNOWN variables are exported and can be used in an external program
# default: <unset>
#HOOK=

# Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no)
#HOOK_CHAIN="no"

# Minimum days before expiration to automatically renew certificate (default: 30)
#RENEW_DAYS="30"

# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
#PRIVATE_KEY_RENEW="yes"

# Create an extra private key for rollover (default: no)
#PRIVATE_KEY_ROLLOVER="no"

# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
#KEY_ALGO=rsa

# E-mail to use during the registration (default: <unset>)
#CONTACT_EMAIL=

# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
#LOCKFILE="${BASEDIR}/lock"

# Option to add CSR-flag indicating OCSP stapling to be mandatory (default: no)
#OCSP_MUST_STAPLE="no"

In the configuration file, you need absolutely to set the CONTACT_EMAIL to an email address you own. You should also set the HOOK  variable to HOOK=/etc/dehydrated/hook.sh. We’ll create the script just after; this hook script is called at each step of the certificate renewal/creation process.

Hook file

Now let’s create the hook.sh. I’m providing you with my hook file based on their template.  The idea is to reload your webserver when a new certificate gets deployed, in my case it’s Nginx, if you use any other one, replace Nginx by yours. I also added a line for my Dovecot and Postfix server only to restart when their certificate is generated. Fell free to use this file and modify it for your needs.

Hook File
  1. #!/usr/bin/env bash
  2. deploy_challenge() {
  3. local DOMAIN=“${1}” TOKEN_FILENAME=“${2}” TOKEN_VALUE=“${3}”
  4. # This hook is called once for every domain that needs to be
  5. # validated, including any alternative names you may have listed.
  6. #
  7. # Parameters:
  8. # – DOMAIN
  9. # The domain name (CN or subject alternative name) being
  10. # validated.
  11. # – TOKEN_FILENAME
  12. # The name of the file containing the token to be served for HTTP
  13. # validation. Should be served by your web server as
  14. # /.well-known/acme-challenge/${TOKEN_FILENAME}.
  15. # – TOKEN_VALUE
  16. # The token value that needs to be served for validation. For DNS
  17. # validation, this is what you want to put in the _acme-challenge
  18. # TXT record. For HTTP validation it is the value that is expected
  19. # be found in the $TOKEN_FILENAME file.
  20. }
  21. clean_challenge() {
  22. local DOMAIN=“${1}” TOKEN_FILENAME=“${2}” TOKEN_VALUE=“${3}”
  23. # This hook is called after attempting to validate each domain,
  24. # whether or not validation was successful. Here you can delete
  25. # files or DNS records that are no longer needed.
  26. #
  27. # The parameters are the same as for deploy_challenge.
  28. }
  29. deploy_cert() {
  30. local DOMAIN=“${1}” KEYFILE=“${2}” CERTFILE=“${3}” FULLCHAINFILE=“${4}” CHAINFILE=“${5}” TIMESTAMP=“${6}”
  31. # This hook is called once for each certificate that has been
  32. # produced. Here you might, for instance, copy your new certificates
  33. # to service-specific locations and reload the service.
  34. #
  35. # Parameters:
  36. # – DOMAIN
  37. # The primary domain name, i.e. the certificate common
  38. # name (CN).
  39. # – KEYFILE
  40. # The path of the file containing the private key.
  41. # – CERTFILE
  42. # The path of the file containing the signed certificate.
  43. # – FULLCHAINFILE
  44. # The path of the file containing the full certificate chain.
  45. # – CHAINFILE
  46. # The path of the file containing the intermediate certificate(s).
  47. # – TIMESTAMP
  48. # Timestamp when the specified certificate was created.
  49. #systemctl reload nginx
  50. #if [ “$DOMAIN” = “smtp.xxx.xxx” ]
  51. # then
  52. # systemctl restart postfix dovecot
  53. #fi
  54. }
  55. unchanged_cert() {
  56. local DOMAIN=“${1}” KEYFILE=“${2}” CERTFILE=“${3}” FULLCHAINFILE=“${4}” CHAINFILE=“${5}”
  57. # This hook is called once for each certificate that is still
  58. # valid and therefore wasn’t reissued.
  59. #
  60. # Parameters:
  61. # – DOMAIN
  62. # The primary domain name, i.e. the certificate common
  63. # name (CN).
  64. # – KEYFILE
  65. # The path of the file containing the private key.
  66. # – CERTFILE
  67. # The path of the file containing the signed certificate.
  68. # – FULLCHAINFILE
  69. # The path of the file containing the full certificate chain.
  70. # – CHAINFILE
  71. # The path of the file containing the intermediate certificate(s).
  72. }
  73. invalid_challenge() {
  74. local DOMAIN=“${1}” RESPONSE=“${2}”
  75. # This hook is called if the challenge response has failed, so domain
  76. # owners can be aware and act accordingly.
  77. #
  78. # Parameters:
  79. # – DOMAIN
  80. # The primary domain name, i.e. the certificate common
  81. # name (CN).
  82. # – RESPONSE
  83. # The response that the verification server returned
  84. }
  85. request_failure() {
  86. local STATUSCODE=“${1}” REASON=“${2}” REQTYPE=“${3}”
  87. # This hook is called when a HTTP request fails (e.g., when the ACME
  88. # server is busy, returns an error, etc). It will be called upon any
  89. # response code that does not start with ‘2’. Useful to alert admins
  90. # about problems with requests.
  91. #
  92. # Parameters:
  93. # – STATUSCODE
  94. # The HTML status code that originated the error.
  95. # – REASON
  96. # The specified reason for the error.
  97. # – REQTYPE
  98. # The kind of request that was made (GET, POST…)
  99. }
  100. exit_hook() {
  101. # This hook is called at the end of a dehydrated command and can be used
  102. # to do some final (cleanup or other) tasks.
  103. :
  104. }
  105. HANDLER=“$1”; shift
  106. if [[ “${HANDLER}” =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|exit_hook)$ ]]; then
  107. “$HANDLER” “$@”
  108. fi

Domain file

Now in the same directory, create a domains.txt file containing the list of domains you want a certificate for. You need to have already a web server setup with those domains.

You can set multiple domains on the same line; they will be then put on the same certificate. I advise you to use this to keep subdomain and domain together.

Domains.txt Example
# Create certificate for 'example.org' with an alternative name of
# 'www.example.org'. It will be stored in the directory ${CERT_DIR}/example.org
example.org www.example.org

# Create certificate for 'example.com' with alternative names of
# 'www.example.com' & 'wiki.example.com'. It will be stored in the directory
# ${CERT_DIR}/example.com
example.com www.example.com wiki.example.com

# Using the alias 'certalias' create certificate for 'example.net' with
# alternate name 'www.example.net' and store it in the directory
# ${CERTDIR}/certalias
example.net www.example.net > certalias

# Using the alias 'service_example_com' create a wildcard certificate for
# '*.service.example.com' and store it in the directory
# ${CERTDIR}/service_example_com
# NOTE: It is NOT a certificate for 'service.example.com'
*.service.example.com > service_example_com

# Using the alias 'star_service_example_org' create a wildcard certificate for
# '*.service.example.org' with an alternative name of `service.example.org'
# and store it in the directory ${CERTDIR}/star_service_example_org
# NOTE: It is a certificate for 'service.example.org'
*.service.example.org service.example.org  > star_service_example_org

# Create a certificate for 'service.example.net' with an alternative name of
# '*.service.example.net' (which is a wildcard domain) and store it in the
# directory ${CERTDIR}/service.example.net
service.example.net *.service.example.net

Webserver

WELLKNOWN

With http-01-type verification (default in this script, there is also support for DNS-based verification) Let’s Encrypt (or the ACME-protocol in general) is checking if you are in control of a domain by accessing a verification file on a URL similar tohttp://example.org/.well-known/acme-challenge/m4g1C-t0k3n.
It will do that for any (sub-)domain you want to sign a certificate for.

At the moment you’ll need to have that location available over standard HTTP on port 80 (redirect to HTTPS will work, but the starting point is always HTTP!).

Dehydrated has a config variable called WELLKNOWNwhich corresponds to the directory which should be served under /.well-known/acme-challenge on your domain. So in the above example, the token would have been saved as $WELLKNOWN/m4g1C-t0k3n.

If you only have one docroot on your server, you could easily do something like WELLKNOWN=/var/www/.well-known/acme-challenge for anything else look at the example below.

Example Usage

If you have more than one docroot (or you are using your server as a reverse proxy/load balancer), the simple configuration mentioned above won’t work, but with just a few lines of web server configuration, this can be solved.

An example would be to create a directory /var/www/dehydrated and set WELLKNOWN=/var/www/dehydrated in the scripts config.

You’ll need to configure aliases on your Webserver:

Nginx example config

With Nginx you’ll need to add this to any of your server/VHost config blocks:

  1. server {
  2. […]
  3. location /.well-known/acme-challenge {
  4. alias /var/www/dehydrated;
  5. }
  6. […]
  7. }

Apache example config

With Apache just add this to your config and it should work in any VHost:

  1. Alias /.well-known/acme-challenge /var/www/dehydrated
  2. <Directory /var/www/dehydrated>
  3. Options None
  4. AllowOverride None
  5. # Apache 2.x
  6. <IfModule !mod_authz_core.c>
  7. Order allow,deny
  8. Allow from all
  9. </IfModule>
  10. # Apache 2.4
  11. <IfModule mod_authz_core.c>
  12. Require all granted
  13. </IfModule>
  14. </Directory>

 Lighttpd example config

With Lighttpd just add this to your config, and it should work in any VHost:

  1. modules += “alias”
  2. alias.url += (
  3. “/.well-known/acme-challenge/” => “/var/www/dehydrated/”
  4. )

DNS Server

Dehydrated can also configure for you (using the hooks) the DNS records of your domain to use the dns-01 type verification.

Tutorial

I’ve covered in another blog post the DNS-01 validation in great details.

dns-01 challenge

This script also supports the new dns-01-type verification. This type of verification requires you to be able to create a specific TXT DNS record for each hostname included in the certificate.

You need a hook script that deploys the challenge to your DNS server!

The hook script (indicated in the config file or the –hook/-k command line argument) gets four arguments: an operation name (clean_challenge, deploy_challenge, or deploy_cert) and some operands for that. For deploy_challenge $2 is the domain name for which the certificate is required, $3 is a “challenge token” (which is not needed for dns-01), and $4 is a token which needs to be inserted in a TXT record for the domain.

Typically, you will need to split the subdomain name in two, the subdomain name and the domain name separately. For example, for “my.example.com”, you’ll need “my” and “example.com” separately. You then have to prefix “_acme-challenge.” before the subdomain name, as in “_acme-challenge.my” and set a TXT record for that on the domain (e.g. “example.com”) which has the value supplied in $4

_acme-challenge IN TXT $4
_acme-challenge.my IN TXT $4

That could be done manually (as most providers don’t have a DNS API), by having your hook script echo $1, $2 and $4 and then wait (read -s -r -e < /dev/tty) – give it a little time to get into their DNS system. Usually, providers give you a box to put “_acme-challenge.my” and the token value in, and a dropdown to choose the record type, TXT.

Or when you do have a DNS API, pass the details accordingly to achieve the same thing.

You can delete the TXT record when called with operation clean_challenge, when $2 is also the domain name.

Here are some examples: Examples for DNS-01 hooks

First Run

Now that you have configured everything, that you have a set your web server (and restarted/reloaded it after the configuration changes). You’re set to run the command for the first time; it will create your account, generate your private key and create all the certificate your needs with their keys.

dehydrated -f -c /etc/dehydrated/config

Certificates

Now you should find a folder /etc/dehydrated/certs/ with a folder for each of your domain set in your domains.txt file.

In each of those folders, you’ll find two important symbolic links that you need to use in all your application that rely on that certificate-key pair.

  1. fullchain.pem :  /etc/dehydrated/certs/example.com/fullchain.pem
  2. privkey.pem :  /etc/dehydrated/certs/example.com/privkey.pem

The first one is your certificate will the different root certificates prepended to it, in other words, the one you need to set for your service. The second one is the private key of the certificate.

Cron

Using your favourite cron editor, add a new cron to be run every week. By default dehydrated will renew the certificates 30 days before their expiration, you can change that in the configuration if you want, keep in mind a let’s encrypt cert has an expiration of 3 months. This cron run it every Sunday at 2:05 AM.

5 2 * * 6 /usr/local/sbin/dehydrated -c -f /etc/dehydrated/config

More info

lukas2511 / dehydrated

letsencrypt/acme client implemented as a shell-script – just add water

https://dehydrated.io

4,139542
  • Click to Press This! (Opens in new window) Press This
  • Click to share on Telegram (Opens in new window) Telegram
  • Pocket
  • Share on Tumblr
  • More
  • Click to share on WhatsApp (Opens in new window) WhatsApp
  • Tweet

Like this:

Like Loading...

Related

Pages: 1 2
Posted in Web

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related

  • Ævitanda
  • BSSG – Bash Static Site Generator
  • Firefox inizia finalmente a supportare le PWA – Zeus News
  • Alternatives to white

Post navigation

Sans Forgetica – RMIT
Search popups
Powered by WordPress | Theme: Jorvik by UXL Themes
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish.Accept Read More
Privacy & Cookies Policy

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Non-necessary
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
SAVE & ACCEPT
%d