231 Commits

Author SHA1 Message Date
Lukas Schauer
1dbbc64ce9 implement workaround for openssl regression (fixes #981)
The introduction of the `-multi` option to the x509 subcommand
introduced a regression to the `-checkend` behaviour, preventing
openssl to correctly indicate the certificate expiry status via
its exit code.

This commit introduces a (maybe temporary) workaround by instead
checking the output string.
2025-10-24 09:22:31 +02:00
Lukas Schauer
12877bb238 throw error with information about OCSP deprecation if certificate doesn't indicate OCSP support 2025-07-05 11:13:45 +02:00
Lukas Schauer
ad43e250b2 allow KEEP_GOING to also skip over ocsp stapling errors, update ocsp error message with a hint about deprecation on some CAs 2025-07-05 10:55:33 +02:00
Lukas Schauer
8e9e5ef9c7 also allow setting KEEP_GOING as a config option 2025-07-05 10:54:29 +02:00
Lukas Schauer
a7deeaedbc set empty subject for ip-certificates
as suggested by @candlerb in #783
2025-07-05 10:28:13 +02:00
Victor Coss
3d95f18000 Don't allow CDN's to send cached responses
A lot of CA's use a CDN service to protect and speed up their ACME service. These CDN services can sometimes miss-behave and send cached results. For example DigiCert's ACME service uses the Imperva CDN. It will send cached results on the DNS validation, challenge endpoint, resulting in it being stuck in the processing status, thus dehydrated is hung and never gets the certificate.
2025-06-17 19:52:29 +02:00
Lukas Schauer
ce9eb300e2 implemented domain validation timeout 2025-06-17 19:51:27 +02:00
Lukas Schauer
9cfcd66f15 small addition to 0.7.2 changelog 2025-05-18 02:28:57 +02:00
Lukas Schauer
73bb54a4b2 updated changelog 2025-05-18 02:16:14 +02:00
Lukas Schauer
3a71a7ad94 only validate existance of wellknown directory or hook script when actually necessary (fixes #965) 2025-05-18 02:07:04 +02:00
Lukas Schauer
0290338853 post-v0.7.2-release 2025-05-18 01:36:16 +02:00
Lukas Schauer
fcca67b53c release v0.7.2 2025-05-18 01:34:32 +02:00
Lukas Schauer
cf9e6a33fd Allow for automatic deletion of old files 2025-05-02 15:00:48 +02:00
Lukas Schauer
bec154f070 Added a configuration parameter to allow for timeouts during order processing (fixes #955) 2025-05-02 14:42:57 +02:00
Lukas Schauer
0141d86267 Update README (closes #964) 2025-05-02 14:38:45 +02:00
Lukas Schauer
a86a176805 use temporary csr file instead of stdin (keeps compatibility to older openssl versions) 2025-04-23 11:24:42 +02:00
Lukas Schauer
200cd68e7e updated changelog 2025-04-14 19:49:31 +02:00
Christian Kujau
e973cb2d8a Disable warning when reading CSRs from stdin.
Coming across the same warning that was reported in
[PR#929](https://github.com/dehydrated-io/dehydrated/pull/929 "Suppress
openssl warning about reading from stdin") this is my attempt to disable
this warning. Instead of discarding stderr in total (this can still be
useful), we just use the "-in" parameter as hinted in the warning:

 $ foo=$(cat req.csr)
 $ <<<${foo} openssl req -noout -verify > /dev/null; echo $?
 Warning: Will read cert request from stdin since no -in option is given
 0

 $ <<<${foo} openssl req -in - -noout -verify > /dev/null; echo $?
 0
2025-04-14 19:42:15 +02:00
Lukas Schauer
7c438c484f added google ca to example config and added documentation link to error message 2025-04-14 19:12:59 +02:00
hshh
a94f451014 Add support for Google Trust Services.
Official Documentation: https://cloud.google.com/certificate-manager/docs/public-ca-tutorial
The first registration requires obtaining EAB_KID and EAB_HMAC_KEY according to the document, and setting CONTACT_EMAIL, EAB_HMAC_KEY, EAB_KID in the configuration file.
2025-04-14 18:59:59 +02:00
Bob Idle
a615a55ad6 Update dehydrated repo urls in man page 2025-04-14 18:57:00 +02:00
Lukas Schauer
f6d82e2715 fix small issue with certificate profile selection (use key instead of value) 2025-04-14 18:49:44 +02:00
Lukas Schauer
1a1cb94a61 added changelog + default config entries for certificate profile selection 2025-04-14 18:41:38 +02:00
Youfu Zhang
5ab8c3806d implemented certificate profile selection (draft-aaron-acme-profiles-00)
https://letsencrypt.org/2025/01/09/acme-profiles/
https://datatracker.ietf.org/doc/html/draft-aaron-acme-profiles-00

Signed-off-by: Youfu Zhang <zhangyoufu@gmail.com>
2025-04-14 18:35:10 +02:00
Lukas Schauer
4ea5081640 renew certificates with 32 days remaining (instead of 30) to avoid issues with monthly cronjobs (fixes #963) 2025-04-11 10:33:07 +02:00
Wilfried Teiken
4fd777e87e Ignore output of 'openssl req -verify'.
Newer versions of openssl seem to send the verify outout to stdout instead of
stderr in the past. Ignore that output when retrieving altnames.
2023-12-05 02:36:40 +01:00
Lukas Schauer
e3ef43c816 fix zsh compatibility (fixes #896) 2023-01-16 22:41:05 +01:00
Alexander Sulfrian
67b111a7b0 Replace all escaped slashes in json strings (closes #866)
${var/pattern/string} will only replace the first occurence. We should
use ${var//pattern/string} to replace all escaped slashes.
2022-10-31 16:27:16 +01:00
Daniel Molkentin
fa68ad8b23 improve man page based on feedback from debian-l10n-english (fixes #873, closes #875)
Also propagate changes to dehydrated help and README.md
2022-10-31 16:22:04 +01:00
Lukas Schauer
5c4adf6baa added note about dehydrated irc channel 2022-10-31 15:46:28 +01:00
Lukas Schauer
35bfea55b6 increase dehydrated version for git master use 2022-10-31 15:46:07 +01:00
Lukas Schauer
ea84199863 release 0.7.1 (it finally happened!) 2022-10-31 15:12:38 +01:00
Krayon
6091ba4bc2 Add missing checks and fix hexdump output (closes #878) 2022-10-31 15:12:04 +01:00
Lukas Schauer
6fb8eba56a implemented workaround for retrying on badNonce errors 2022-09-07 15:09:57 +02:00
Simon Deziel
19c7fbbf47 egrep is deprecated
egrep has been deprecated since 2007 and warns it's obsolete since:
https://git.savannah.gnu.org/cgit/grep.git/commit/?id=a9515624709865d480e3142fd959bccd1c9372d1

Signed-off-by: Simon Deziel <simon@sdeziel.info>
2022-04-07 21:49:56 +02:00
Lukas Schauer
7128e6b63c rfc8738: fix CN on certs with mixed ip+dns 2022-04-07 01:34:21 +02:00
Lukas Schauer
861f4c733d rfc8738: only replace ip with reverse dns thingy if tls-alpn-01 is used 2022-04-07 01:33:48 +02:00
Lukas Schauer
ad3f08084c implemented rfc 8738 support 2022-04-06 22:23:43 +02:00
Lukas Schauer
784fb806c8 really reverted regression in somehow broken array expansion from e963438c.. 2021-11-02 09:05:19 +01:00
Lukas Schauer
b2574b16d1 reverted regression in somehow broken array expansion from e963438c (fixes #850) 2021-11-02 09:01:00 +01:00
Lukas Schauer
da641588ce removed old logo 2021-11-01 19:25:17 +01:00
Lukas Schauer
8e6ddf6286 readme and (temporary) logo update 2021-11-01 19:22:50 +01:00
Lukas Schauer
8e5977890a fix regression from e963438c (fixes #849) 2021-11-01 18:57:57 +01:00
Lukas Schauer
3bcf0c7f5a use noglob helpers for domains.txt.d parsing 2021-11-01 18:57:49 +01:00
Lukas Schauer
b347bc9086 added some changes to changelog 2021-10-31 22:58:06 +01:00
Lukas Schauer
08477170e9 Exit with error if somebody is trying to use EC account keys with ACME v1 2021-10-31 22:36:40 +01:00
Lukas Schauer
f4cf92bae5 extend ec algorithms with secp521r1 (not yet supported by LetsEncrypt, but maybe by other CAs) 2021-10-31 22:31:09 +01:00
Lukas Schauer
93573cda3c experimental support for ec account keys (fixes #827) 2021-10-31 22:29:44 +01:00
Lukas Schauer
607a6088d3 Avoid writing ec-parameters to private-key file (fixes #830) 2021-10-31 20:20:40 +01:00
Lukas Schauer
880c99aa63 Better solution for issue #845 2021-10-31 20:11:31 +01:00
Lukas Schauer
7ac25358ef Show error if chain is configured for a CA which doesn't offer alternate chains (fixes #845) 2021-10-31 20:06:50 +01:00
Lukas Schauer
5733863b93 added warning about possible behaviour-change with new domains.txt.d feature 2021-10-31 19:55:41 +01:00
Marc Schütz
f6a84a88fa Support reading domains from drop-in snippets in domains.txt.d 2021-10-31 19:48:28 +01:00
Lukas Schauer
e963438c5a make shellcheck happy again 2021-10-31 19:33:03 +01:00
Stefaan Ghysels
095165ee96 Only check existing certs when necessary 2021-10-31 19:29:00 +01:00
Simon Deziel
199cd59774 Remove debug echo in command_cleanup()
Signed-off-by: Simon Deziel <simon@sdeziel.info>
2021-10-31 19:23:01 +01:00
Elan Ruusamäe
e17456778f Use consistent indent in hook.sh 2021-10-31 19:21:26 +01:00
Brian Bennett
71f6bc617e Better handling around grep/awk 2021-10-31 19:17:49 +01:00
Joao Morais
6ee4ae508e fix command_version on Darwin/macOS 11
Current output of `uname` on Darwin/macOS 11 is only `Darwin`, which
breaks the premisse used in `command_version()`. This update adds
`Darwin` alongside `BSD`.
2021-10-31 19:08:52 +01:00
27o
91cccc0c23 ensure newline before new section in openssl.cnf
openssl.cnf may not end with a newline. The section [SAN] will then not be found as it is added to the last line of openssl.cnf.
2021-04-23 02:03:23 +02:00
Lukas Schauer
ab016803dd expand documentation on using letsencrypt staging ca 2021-04-18 03:48:47 +02:00
Nick
7d8573af12 Update staging.md to use ACMEv2 server (closes #812)
letsencrypt is phasing out the v1 server:

```
  + ERROR: An error occurred while sending get-request to https://acme-staging.api.letsencrypt.org/directory (Status 403)

Details:
HTTP/2 403
server: nginx
date: Thu, 01 Apr 2021 20:48:17 GMT
content-type: application/problem+json
content-length: 189
etag: "600b3710-bd"

{
  "type": "urn:acme:error:serverInternal",
  "detail": "ACMEv1 Brownout in Progress. ACMEv1 will fully turn off on June 1, 2021. Check https://letsencrypt.status.io/ for more details."
}

```
2021-04-18 03:44:41 +02:00
Lukas Schauer
fb06530097 command_sign_csr: redirect fds after init_system (fixes #816) 2021-04-16 14:32:05 +02:00
Lukas Schauer
5c1551e946 remove some dots :) 2021-03-29 20:20:52 +02:00
Marcus Rückert
20c27b291c Add more examples to show case how to create certs
e.g. with different key algorithms
2021-03-29 20:19:41 +02:00
Lukas Schauer
24f66a3473 generic support for weird curl versions with lower-case headers and no whitespace 2021-03-21 20:51:10 +01:00
joele89
21bff55b7c Updating nonce handler for newer versions of F5 2021-03-21 20:46:31 +01:00
Glenn Strauss
374fce0249 document using -t tls-alpn-01 with lighttpd 2021-03-21 20:42:23 +01:00
Glenn Strauss
00941472b2 add -t tls-alpn-01 to command line help 2021-02-18 16:56:05 +01:00
Michel Lespinasse
527933db24 Per-certificate config fixes
- Ensure that all per-certificate settings are saved and restored in
  store_configvars() and reset_configvars() - that's what makes them
  per-certificate in the first place...

- Add OCSP_FETCH and OCSP_DAYS in the documented list of supported
  per-certificate configs, since the code does allow these.
2021-02-18 16:51:14 +01:00
Nathan Felton
33a421f1e4 Support for LibreSSL version of openssl on macOS 2021-02-18 16:47:24 +01:00
Lukas Schauer
dd0bbd2405 update copyright year 2021-02-18 16:47:23 +01:00
Lukas Schauer
26660e11c7 Fixed small unassigned variable issue 2020-12-12 03:12:13 +01:00
Lukas Schauer
316054ad1c Do not revalidate authorizations on forced renewal
This commit introduces a new cli argument `--force-validation` which,
when used in combination with `--force` ignores valid domain
authorizations and forces a revalidation.

This has been implemented since at least LE seems to have changed some
behavior on valid authorizations. Only the previously validated
authorization-type is reusable, causing dehydrated to error out when
changing from recently validated authorization types while still trying
to force-renew certificates for whatever reason (e.g. changing algorithms).
2020-12-12 03:01:59 +01:00
Lukas Schauer
29b67962ac fix CN extraction for older openssl versions 2020-12-11 18:02:51 +01:00
Lukas Schauer
3a7795589b bump changelog for new draft releases 2020-12-10 16:56:13 +01:00
Lukas Schauer
082da2527c preparing for release 0.7.0 2020-12-10 16:54:26 +01:00
Lukas Schauer
e784ba3853 use normal error behaviour for failing http requests (fixes #782) 2020-12-10 16:32:26 +01:00
Lukas Schauer
abd369d062 allow to set domains.txt as cli argument (fixes #678) 2020-12-10 16:07:28 +01:00
Lukas Schauer
cb7fb82beb use secp384r1 as default (instead of rsa, fixes #651) 2020-12-10 16:01:54 +01:00
Lukas Schauer
174616becd use secp384r1 as default (instead of rsa, fixes #651) 2020-12-10 16:01:08 +01:00
Raphael Hoegger
27fd41d75f adding new CLI Command (--cleanupdelete / -gcd) to cleanup+delete (instead of just moving to /archive) (closes #587) 2020-12-10 14:58:14 +01:00
Lukas Schauer
ea106ef72e allow setting OCSP_FETCH and OCSP_DAYS per certificate config (closes #602, thx @bjacke) 2020-12-10 14:28:30 +01:00
Lukas Schauer
f2d6a6152e cleanup: also remove dangling symlinks 2020-12-10 14:15:07 +01:00
Arnout Vandecappelle (Essensium/Mind)
129ec851ed cleanup: also do cleanup if symlink is broken (closes #667)
The cleanup command skips filetypes for which the symlink is broken or
doesn't exist. However, if dehydrated fails, we may end up in exactly
the situation that the symlink doesn't exist (yet). If dehydrated fails
repeatedly, we may end up with a lot of old cert.csr, cert.pem and
privkey.pem files, so we really want to be able to clean them up.

Remove all files if the symlink is broken/missing, instead of skipping
those files.

Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
2020-12-10 14:14:35 +01:00
Georg Altmann
835963fa6e make alpn-validation certificates and keys group readable (closes #754, fixes #753) 2020-12-10 14:00:26 +01:00
Daniel Molkentin
829aaeff2d Fix OCSP_FETCH with libressl
libressl did not pick up the implicit host header patches
of OpenSSL 1.1 even in version 3 and thus exhibits the same
behavior as OpenSSL 1.0.

Patch by Chen, Chih-Chia <pigfoot@gmail.com>

Fixes #778
2020-12-10 13:52:47 +01:00
Lukas Schauer
481aba7d7b remove quotes from per-cert-config vars to allow for spaces (fixes #789, closes #791) 2020-12-09 20:41:17 +01:00
Lukas Schauer
fbcaac89f9 changed method for parsing issuer cn, fixing compatibility with some openssl versions 2020-12-09 19:38:27 +01:00
Lukas Schauer
589e9f30b3 show available options if preferred chain is not found 2020-11-21 16:19:08 +01:00
Lukas Schauer
f2103340f3 fix spaces in sudo arguments 2020-11-20 17:09:04 +01:00
Lukas Schauer
c670c18299 added display-terms to changelog+readme 2020-11-13 20:49:18 +01:00
Daniel Molkentin
7cc9e2d07f add --display-terms to display the URL for the current ToS
Implements #649
2020-11-13 20:47:49 +01:00
Lukas Schauer
7dfde364a3 added support for requesting preferred-chain instead of default chain 2020-11-13 20:36:51 +01:00
Lukas Schauer
7d3288f428 one more \s -> [[:space:]] replacement 2020-09-30 11:35:06 +02:00
Jarkko Oranen
e69df6521b Replace \s with [[:space:]] for compatibility 2020-09-30 11:32:23 +02:00
Daniel Molkentin
8ddead4854 Complain about deactivated accounts 2020-09-28 01:15:48 +02:00
Daniel Molkentin
308b3ec750 implement account deactivation through --deactivate parameter
This is an updated version of https://github.com/lukas2511/dehydrated/files/2641548/dehydrated_add_deactivate_command.diff.txt

Fixes #216
2020-09-28 01:15:47 +02:00
Daniel Molkentin
39e1068a87 Don't require sudo before we know we really need it
Fixes #665
2020-09-27 22:26:20 +02:00
Daniel Molkentin
6d9fcd2588 Do not fail silently with invalid sudo user/group 2020-09-27 22:26:18 +02:00
Daniel Molkentin
60cb678e3b add more CAs, now that support for CA presets is implemented
- letsencrypt-test (LE staging CA)
- buypass (verified to work with the new json parsing, see #653)
- buypass-test analogously
2020-09-27 20:41:34 +02:00
Daniel Molkentin
5f8cfa50ba fix OS name detection
before applying heuristics, use PRETTY_NAME from os-release(3),
which reliably exists on all common linux distributions.

keep the /etc/issue parsing as fallback.
2020-09-27 20:35:18 +02:00
Lukas Schauer
b3abc41dbe tmpfix: log error if acmev1 validation is denied + fix unbound variable 2020-09-15 17:27:24 +02:00
Lukas Schauer
b3b2fee496 eab: use hex key instead of binary (fixes issue with nullbytes) 2020-09-14 18:59:41 +02:00
Lukas Schauer
416fd0fd1b do not fail on challenge in "processing" state (fixes #759) 2020-09-14 18:31:24 +02:00
Lukas Schauer
142c69dd90 fixed bad typo.. 2020-09-14 18:28:05 +02:00
Lukas Schauer
74c136905b readme+changelog 2020-09-14 18:24:01 +02:00
Lukas Schauer
5fc1175aef EAB + ZeroSSL support 2020-09-14 18:22:36 +02:00
Lukas Schauer
4b91fcf498 read boolean values from json 2020-09-14 18:19:08 +02:00
Lukas Schauer
11323d0727 removed accidental shebang 2020-09-14 18:18:35 +02:00
Lukas Schauer
a9a64c9fd0 use presets for some CAs instead of requiring full urls 2020-09-14 16:37:16 +02:00
Jason Francis
42a0fc9a5e fix tls-alpn-01 configuration example 2020-07-05 22:30:38 +02:00
Lukas Schauer
e119d9136b fixed some typos (fixes #725, fixes #741, fixes #740) 2020-07-05 22:29:57 +02:00
j-ed
275fb40ab4 removed tmp file in 'generate_alpn_certificate' function
Made sure that the temp file will be removed at the end of the function.
2020-07-05 21:41:12 +02:00
Lukas Schauer
7e92850957 fixed zsh compatibility 2020-07-05 04:13:11 +02:00
Lukas Schauer
bb5a1473d1 merged temporary json.sh into dehydrated, fixed authorization "pending" loop 2020-07-04 21:51:32 +02:00
Lukas Schauer
7f970b527c experimental json.sh support 2020-07-04 21:36:23 +02:00
Krayon
dc552c602e Use existing curl version var
Signed-off-by: Krayon <krayon.git@qdnx.org>
2020-04-28 21:36:52 +02:00
Lukas Schauer
9827a411b3 removed instructions for importing from "official" client (certbot) as it probably doesn't work anymore and there isn't really much use for it anyway 2020-04-28 21:29:24 +02:00
Lukas Schauer
4a55f93896 fix link to wiki in documentation (fixes #690) 2020-04-28 21:27:49 +02:00
Lukas Schauer
a07c8d14f6 reworked dependency check and moved it up a bit in code (fixes #715, resolves #717 again...) 2020-04-28 21:25:08 +02:00
Lukas Schauer
42047fdf11 added changelog 2020-04-28 21:10:22 +02:00
Lukas Schauer
76d7e31981 added note about newline encoded in accounts directory hashes (resolves #730) 2020-04-28 21:03:55 +02:00
Lukas Schauer
4fd4d4d3c2 temporarily store raw curl version output to fix check (fixes #717) 2020-04-28 20:58:46 +02:00
Lukas Schauer
229f7186a6 store errorcode while using KEEP_GOING (fixes #659) 2020-04-28 20:39:04 +02:00
Lukas Schauer
4b7a1e4ce6 report issues with hook scripts instead of silently exiting (fixes #733, fixes #686) 2020-04-28 20:13:03 +02:00
Lukas Schauer
871efe653b skip exit_hook in _exiterr if KEEP_GOING is enabled (fixes #686) 2020-04-28 18:36:02 +02:00
Rogdham
dbb0ef1ce1 Move from account ID to account URL
We store the account URL on account creation in the account_id.json file.

When reading the file, if the attribute is missing, we retrieve the account URL
from the CA ( https://tools.ietf.org/html/rfc8555#section-7.3.1 ) and edit the
file.

Per https://tools.ietf.org/html/rfc8555#section-7.3
> The server returns this account object in a 201 (Created) response, with the
> account URL in a Location header field.  The account URL is used as the "kid"
> value in the JWS authenticating subsequent requests by this account (see
> Section 6.2).  The account URL is also used for requests for management
> actions on this account, as described below.
2020-04-03 09:16:30 +02:00
Lukas Schauer
fcfb077a95 redirect output of cert expiry check (fixes #713) 2020-04-02 12:44:40 +02:00
Lukas Schauer
bc9344392a fixed typo (closes #712) 2020-04-02 12:23:09 +02:00
Lukas Schauer
5b7c898b63 only show order processing/pending message when waiting 2020-04-02 12:15:17 +02:00
Rogdham
58bd926e30 Don't assume order status to be valid
Per https://tools.ietf.org/html/rfc8555#section-7.1.3

> status (required, string):  The status of this order.  Possible values are
> "pending", "ready", "processing", "valid", and "invalid".  See Section 7.1.6.
2020-04-02 12:09:27 +02:00
Rogdham
c8333f5a56 Fix challenge response POST body in ACMEv2
Per https://tools.ietf.org/html/rfc8555#section-7.5.1

> The client indicates to the server that it is ready for the challenge
> validation by sending an empty JSON body ("{}") carried in a POST
> request to the challenge URL (not the authorization URL).
2020-03-30 21:34:43 +02:00
Lukas Schauer
307eaadddf updated notice about move 2020-01-30 22:51:25 +01:00
Lukas Schauer
dfffb1b88b added note about moving the repository 2020-01-30 01:45:17 +01:00
Lukas Schauer
e2eeaf7ec6 added funding.yml 2019-11-11 22:46:13 +01:00
Lukas Schauer
946e5712ba fixed small logic bug 2019-10-09 02:05:54 +02:00
Lukas Schauer
018254974c Merge tag 'v0.6.5' 2019-07-06 19:25:32 +02:00
Lukas Schauer
05eda91a2f release 0.6.5 (fixed apiv1 compatibility...) 2019-06-26 12:33:35 +02:00
Lukas Schauer
f60f2f81e8 release 0.6.5 (fixed apiv1 compatibility...) 2019-06-26 12:29:39 +02:00
Lukas Schauer
4f358e22f4 release 0.6.4 (fixed account id handling, again) 2019-06-25 15:28:09 +02:00
Lukas Schauer
f9d0b1bd70 release 0.6.3 2019-06-25 12:50:45 +02:00
Lukas Schauer
be13dcd454 fixed fetching of account information (fixes #652, fixes #647, fixes #650, closes #648) 2019-06-25 12:19:20 +02:00
Lukas Schauer
74a536c161 added documentation about possible future removal of api version 1 2019-03-04 23:23:40 +01:00
Lukas Schauer
444cea4669 Revert "cleanup: removed api version 1 support (closes #510)"
Since a few CAs out there actually seem to (only) support ACME v1 I
decided to revert the removal and keep ACME v1 around, at least until
it eventually becomes a bigger inconvenience to maintain.

This reverts commit aadf7d5e64.
2019-03-04 23:11:07 +01:00
Lukas Schauer
ea93170959 BSD bugfixes for version command (closes #619) 2019-03-03 21:51:01 +01:00
Lukas Schauer
133e31de0b tiny documentation fix: per-certificate-config can override PRIVATE_KEY_ROLLOVER (closes #614) 2019-03-03 20:38:47 +01:00
Lukas Schauer
aadf7d5e64 cleanup: removed api version 1 support (closes #510) 2019-03-03 20:32:10 +01:00
Lukas Schauer
e4a32acbe2 new hook: sync_cert (closes #609) 2019-03-03 20:22:41 +01:00
Lukas Schauer
1c77730373 call exit_hook with error message (fixes #630) 2019-03-03 20:08:18 +01:00
Lukas Schauer
e623fcc024 implement POST-as-GET (closes #626) 2019-03-03 20:00:13 +01:00
Lukas Schauer
585ed5404b updated oid for tls-alpn verification token (fixes #624) 2019-01-18 13:25:57 +01:00
Lukas Schauer
92aa1ecd5a document DOMAINS_D parameter in example config (fixes #575, closes #582) 2018-10-20 13:05:20 +02:00
Lukas Schauer
5783a2dd45 fixed a bug that resulted in a deleted domains.txt when using incorrect parameters in combination with signcsr (fixes #597) 2018-10-20 12:27:23 +02:00
Lukas Schauer
fba49ba28e implemented initial support for tls-alpn-01 verification 2018-07-26 04:44:29 +02:00
Lukas Schauer
10d4b98e7f Only match Replace-Nonce header at beginning of line 2018-05-09 21:01:57 +02:00
Florent
e4e712c03a Fixes #559 : when HTTP/2 is used, header names are lower case. So adding ignore case option (-i) to grep's. 2018-05-09 21:00:05 +02:00
Lukas Schauer
2a8af8fda7 made ocsp refresh interval configurable 2018-05-07 03:31:43 +02:00
Lukas Schauer
9165cfdebf added dns-txt-foo to troubleshooting.md 2018-05-01 17:54:13 +02:00
Lukas Schauer
b5dddd7a2b prepare for next version 2018-04-27 13:08:44 +02:00
Lukas Schauer
ce3d658377 release 0.6.2 2018-04-25 23:22:40 +02:00
Lukas Schauer
89de83c994 add explanation on HEADERS parameter to request_failure hook (fixes #545) 2018-04-25 22:48:16 +02:00
Lukas Schauer
52c2c19994 added workaround for use with advanced filesystem ACLs (as originally suggested in #467) 2018-04-20 02:55:07 +02:00
Tobias Tiederle
53c458c318 fix parameter 2018-04-15 16:08:30 +02:00
Lukas Schauer
ba5928776f fix behaviour for fetching missing additional account information with unknown keys 2018-04-13 22:09:52 +02:00
Lukas Schauer
dff7d4ea35 allow registration using cli-specified private key (#534) 2018-04-13 21:34:11 +02:00
Lukas Schauer
0262997451 also call clean_challenge hook for http-01 challenges (fixes #536) 2018-04-13 20:59:27 +02:00
Lukas Schauer
8ba56a8048 renamed ocsp hook to deploy_ocsp, exported altnames, added example hook 2018-04-08 22:44:28 +02:00
Ike Johnson
2fca309e94 Add ocsp_update hook
In relation to issue #513
2018-04-08 22:31:56 +02:00
Daniel Molkentin
13b8a3f29f fix date in man page 2018-04-08 22:21:33 +02:00
Lukas Schauer
a67816468a also run request_failure and invalid_challenge when HOOK_CHAIN is defined (fixes #450) 2018-04-08 22:18:30 +02:00
Lukas Schauer
ea46aee44f fixed cleanup for invalid challenges 2018-04-08 22:18:16 +02:00
Lukas Schauer
537877a0e2 allow for widely-used non-standard CSR label (fixes #488) 2018-03-26 00:00:50 +02:00
Lukas Schauer
981179a770 hail hydra! 🐙 2018-03-24 16:18:01 +01:00
Lukas Schauer
ff18d39aa8 strip validationRecord from challenge before grepping for "url" string... (fixes #515) 2018-03-18 20:12:04 +01:00
John L. Villalovos
7c40c727a0 Improve documentation on wildcards
Improve the documentation on how to use wildcard domains.

Also give more examples in the docs/examples/domains.txt file.
2018-03-17 13:27:15 +01:00
Lukas Schauer
9f1ff67870 removed dual use of challenge_identifiers variable (fixes #511) 2018-03-17 01:40:19 +01:00
Lukas Schauer
b116e6bc2b close weird external file descriptors 2018-03-15 13:52:51 +01:00
Lukas Schauer
6083218501 removed some unused code 2018-03-15 13:52:15 +01:00
Lukas Schauer
2533931cf1 don't walk certificate chain for ACMEv2 (certificate contains chain by default) 2018-03-14 18:54:51 +01:00
Lukas Schauer
b93eac3893 fixed CA url in example config 2018-03-13 21:08:20 +01:00
Lukas Schauer
e374d21d45 prepare for next version 2018-03-13 20:59:20 +01:00
Lukas Schauer
70d261a729 release v0.6.1 2018-03-13 20:57:52 +01:00
Lukas Schauer
947dbb9e29 use new acme-v02 endpoint by default 2018-03-13 20:48:42 +01:00
Lukas Schauer
8a414e55bc prepare for next version 2018-03-11 20:22:38 +01:00
Lukas Schauer
fd3fc8af62 release 0.6.0 2018-03-11 20:19:25 +01:00
Lukas Schauer
6e802ddc19 include content-type in post requests (fixes #491) 2018-03-09 12:25:37 +01:00
Lukas Schauer
0211d24577 require a valid alias to be set for certain wildcard certificates (fixes #483) 2018-03-02 18:53:00 +01:00
Lukas Schauer
68274646bb curl: use custom user agent (temporarily using a bit of 1337) 2018-03-01 21:39:00 +01:00
Lukas Schauer
c0bcf91410 show error details on ocsp update failure 2018-02-22 22:32:39 +01:00
Lukas Schauer
a91074b707 fixed undefined-variable bug on early connection failure (while accessing ca directory) 2018-02-22 22:22:04 +01:00
Ewald Dieterich
a6a07779ad fixed "sed: invalid option -- 'E'" 2018-02-22 19:28:36 +01:00
Lukas Schauer
a6e6aa7445 fixed spurious return code from hook bricker... 2018-02-14 15:20:26 +01:00
Lukas Schauer
dcdb2940fb removed random prefix from hook bricker 2018-02-14 14:58:11 +01:00
Lukas Schauer
0ade30cc74 hook-bricker now also warns users on per-certificate-config-hooks 2018-02-13 21:45:30 +01:00
Lukas Schauer
bc34f3aa86 merged random hook with human-readable-message 2018-02-13 21:34:02 +01:00
Lukas Schauer
5940c55e18 prepared future migration to new acmev2 endpoint 2018-02-07 03:14:29 +01:00
Lukas Schauer
2eedd69ee9 request_failure hook: added http response headers as new parameter 2018-02-06 23:33:28 +01:00
Lukas Schauer
be252c7db9 updated changelog 2018-02-06 23:22:53 +01:00
Lukas Schauer
9ebab3e026 added call to random hook to make it clear to hook authors that unknown hooks should just be ignored 2018-02-06 23:16:28 +01:00
Lukas Schauer
ad291207d0 fetch account information if missing 2018-02-06 23:08:40 +01:00
Lukas Schauer
a7b2af2b92 http_request: make http headers available on fd 4 2018-02-06 23:08:16 +01:00
Lukas Schauer
082ed17a0a added acmev2 staging information to docs 2018-02-06 21:58:42 +01:00
Lukas Schauer
dec5ad5840 read url to terms of service from ca directory 2018-02-06 21:58:42 +01:00
Lukas Schauer
da67297288 only write csr file if renewal will be requested 2018-02-06 21:19:33 +01:00
Lukas Schauer
83bf2664b0 added a few simple example use cases to example hook script 2018-02-06 21:13:37 +01:00
Lukas Schauer
63854b752b New hook: generate_csr (see example hook script for more information, implements #475, replaces #377) 2018-02-06 20:57:33 +01:00
Lukas Schauer
901f9f76e2 pre-bump version 2018-02-06 20:53:09 +01:00
Lukas Schauer
b5de2e26eb sign_domain: Use existing CSR with matching timestamp 2018-02-06 20:41:26 +01:00
Lukas Schauer
73a116e879 Create required certificate and chaincache directories outside of sign_domain 2018-02-06 20:40:32 +01:00
Lukas Schauer
9c35fce61e Pre-generate timestamp outside of sign_domain 2018-02-06 20:39:23 +01:00
Lukas Schauer
87194f6596 Remove additional whitespace from extract_altnames 2018-02-06 20:38:02 +01:00
Mattia Rizzolo
62d37c9b3d Fix grammer error in the manpage (fixes #466)
"allows to" requires a subject (e.g. "allows one to"), without it's just
syntactically wrong.  Change the verb entirely to workaround the
problem.
2018-02-06 19:14:12 +01:00
Lukas Schauer
b53cb6643b moved manpage to docs directory 2018-02-06 18:53:21 +01:00
Lukas Schauer
fb41783885 automatic discovery of remote acme api version 2018-02-05 19:20:28 +01:00
Nick Muerdter
0bc0bd13d6 Fix globbing of CONFIG_D *.sh files.
With the globbing changes made in
61083cf522 to globally disable globbing by
default, this broke the ability to load the CONFIG_D `*.sh` files.

This re-enables globbing when reading these `*.sh` files and then disables it
again afterwards. Note that this also keeps globbing enabled inside the
loop, when sourcing the individual `*.sh` files for backwards
compatibility (so if the individual config scripts relied on the default
of globbing being enabled, there won't be any change in behavior).
2018-02-03 16:11:14 -07:00
Lukas Schauer
6d02bfdb42 shrink "logo" a bit 2018-02-03 22:14:43 +01:00
Lukas Schauer
727443483d added acmev2 information to readme 2018-02-03 22:12:32 +01:00
Lukas Schauer
7a0e71c6c2 follow location on http get-requests 2018-02-03 22:03:58 +01:00
Lukas Schauer
45f5c17260 fixed altname extraction of csr with wildcard domains, moved altname extraction from sign_csr to command_sign_csr 2018-02-02 23:47:29 +01:00
Lukas Schauer
61083cf522 disable globbing globally (only allow for cleanup routine) 2018-02-02 23:45:34 +01:00
Lukas Schauer
afba7c694c moved deploy_challenge to earlier loop so it works with multiple challenge tokens on the same identifier (important for wildcard certificate), fixed array-name, removed hook-chain warning 2018-01-28 19:48:25 +01:00
Pandark
471899b4d8 Add ^~ to nginx location block
To make sure it is not overridden.
> http://nginx.org/en/docs/http/ngx_http_core_module.html#location :
> If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.
2018-01-28 06:18:10 +01:00
Lukas Schauer
ec5dbcc816 updated changelog 2018-01-28 06:14:44 +01:00
Lukas Schauer
0f69481e2b rewrote challenge validation to iterate over authorizations instead of altnames (fixes some acmev2 validation edgecases), also removed broken test-script (for now) 2018-01-28 06:13:37 +01:00
Lukas Schauer
6f3fed496d rewrote donation section in readme 2018-01-28 06:13:01 +01:00
Lukas Schauer
5fd93ea874 be more verbose for acme v2 challenge handling 2018-01-27 22:51:39 +01:00
Lukas Schauer
656af8cadc don't fail on nested json array in challenge info 2018-01-13 23:10:31 +01:00
Lukas Schauer
3e521e1c01 fixed domains.txt parsing (theoretically compatible with wildcard domains) 2018-01-13 20:54:55 +01:00
Martin Strobel
68cb1e0661 ACME v02 Support 2018-01-13 20:17:25 +01:00
Lukas Schauer
35a9f31643 changelog template, year update 2018-01-13 20:10:32 +01:00
23 changed files with 2112 additions and 847 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: lukas2511
custom: ["https://paypal.me/lukas2511", "http://www.amazon.de/registry/wishlist/1TUCFJK35IO4Q"]

View File

@@ -1,14 +0,0 @@
sudo: false
language: shell
os:
- linux
- osx
cache:
directories:
- ngrok
script:
- export CI="true"
- ./test.sh

113
CHANGELOG
View File

@@ -1,6 +1,119 @@
# Change Log
This file contains a log of major changes in dehydrated
## [x.x.x] - xxxx-xx-xx
## Added
- Added a configuration parameter to allow for timeouts during domain validation processing (`VALIDATION_TIMEOUT`, defaults to 0 = no timeout)
## Changed
- Only validate existance of wellknown directory or hook script when actually needed
- Also allow setting `KEEP_GOING` in config file instead of relying on cli arguments
- Allow skipping over OCSP stapling errors, indicate that some CAs no longer support OCSP
- Throw error with information about OCSP deprecation if certificate doesn't indicate OCSP support
## [0.7.2] - 2025-05-18
## Added
- Implemented support for certificate profile selection
- Added a configuration parameter to allow for timeouts during order processing (`ORDER_TIMEOUT`, defaults to 0 = no timeout)
- Allowed for automatic deletion of old files (`AUTO_CLEANUP_DELETE`, disabled by default)
- Added CA presets for Google Trust Services (prod: google, test: google-test)
## Changed
- Renew certificates with 32 days remaining (instead of 30) to avoid issues with monthly cronjobs (`RENEW_DAYS=32`)
## Fixed
- Changed behaviour of `openssl req` stdin handling to fix compatibility with OpenSSL version 3.2+
## [0.7.1] - 2022-10-31
## Changed
- `--force` no longer forces domain name revalidation by default, a new argument `--force-validation` has been added for that
- Added support for EC secp521r1 algorithm (works with e.g. zerossl)
- `EC PARAMETERS` are no longer written to privkey.pem (didn't seem necessary and was causing issues with various software)
## Fixed
- Requests resulting in `badNonce` errors are now automatically retried (fixes operation with LE staging servers)
- Deprecated `egrep` usage has been removed
## Added
- Implemented EC for account keys
- Domain list now also read from domains.txt.d subdirectory (behaviour might change, see docs)
- Implemented RFC 8738 (validating/signing certificates for IP addresses instead of domain names) support (this will not work with most public CAs, if any!)
## [0.7.0] - 2020-12-10
## Added
- Support for external account bindings
- Special support for ZeroSSL
- Support presets for some CAs instead of requiring URLs
- Allow requesting preferred chain (`--preferred-chain`)
- Added method to show CAs current terms of service (`--display-terms`)
- Allow setting path to domains.txt using cli arguments (`--domains-txt`)
- Added new cli command `--cleanupdelete` which deletes old files instead of archiving them
## Fixed
- No more silent failures on broken hook-scripts
- Better error-handling with KEEP_GOING enabled
- Check actual order status instead of assuming it's valid
- Don't include keyAuthorization in challenge validation (RFC compliance)
## Changed
- Using EC secp384r1 as default certificate type
- Use JSON.sh to parse JSON
- Use account URL instead of account ID (RFC compliance)
- Dehydrated now has a new home: https://github.com/dehydrated-io/dehydrated
- Added `OCSP_FETCH` and `OCSP_DAYS` to per-certificate configurable options
- Cleanup now also removes dangling symlinks
## [0.6.5] - 2019-06-26
## Fixed
- Fixed broken APIv1 compatibility from last update
## [0.6.4] - 2019-06-25
## Changed
- Fetch account ID from Location header instead of account json
## [0.6.3] - 2019-06-25
## Changed
- OCSP refresh interval is now configurable
- Implemented POST-as-GET
- Call exit_hook on errors (with error-message as first parameter)
## Added
- Initial support for tls-alpn-01 validation
- New hook: sync_cert (for syncing certificate files to disk, see example hook description)
## Fixes
- Fetch account information after registration to avoid missing account id
## [0.6.2] - 2018-04-25
## Added
- New deploy_ocsp hook
- Allow account registration with custom key
## Changed
- Don't walk certificate chain for ACMEv2 (certificate contains chain by default)
- Improved documentation on wildcards
## Fixes
- Added workaround for compatibility with filesystem ACLs
- Close unwanted external file-descriptors
- Fixed JSON parsing on force-renewal
- Fixed cleanup of challenge files/dns-entries on validation errors
- A few more minor fixes
## [0.6.1] - 2018-03-13
## Changed
- Use new ACME v2 endpoint by default
## [0.6.0] - 2018-03-11
## Changed
- Challenge validation loop has been modified to loop over authorization identifiers instead of altnames (ACMEv2 + wildcard support)
- Removed LICENSE parameter from config (terms of service is now acquired directly from the CA directory)
## Added
- Support for ACME v02 (including wildcard certificates!)
- New hook: generate_csr (see example hook script for more information)
- Calling random hook on startup to make it clear to hook script authors that unknown hooks should just be ignored...
## [0.5.0] - 2018-01-13
## Changed
- Certificate chain is now cached (CHAINCACHE)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2017 Lukas Schauer
Copyright (c) 2015-2021 Lukas Schauer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,20 +1,23 @@
# dehydrated
# dehydrated [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=23P9DSJBTY7C8)
![](docs/logo.jpg)
![](docs/logo.png)
This is a client for signing certificates with an ACME-server (currently only provided by Let's Encrypt) implemented as a relatively simple bash-script.
Dehydrated is a client for signing certificates with an ACME-server (e.g. Let's Encrypt) implemented as a relatively simple (zsh-compatible) bash-script.
This client supports both ACME v1 and the new ACME v2 including support for wildcard certificates!
It uses the `openssl` utility for everything related to actually handling keys and certificates, so you need to have that installed.
Other dependencies are: cURL, sed, grep, mktemp (all found on almost any system, cURL being the only exception)
Other dependencies are: cURL, sed, grep, awk, mktemp (all found pre-installed on almost any system, cURL being the only exception).
Current features:
- Signing of a list of domains
- Signing of a CSR
- Renewal if a certificate is about to expire or SAN (subdomains) changed
- Signing of a list of domains (including wildcard domains!)
- Signing of a custom CSR (either standalone or completely automated using hooks!)
- Renewal if a certificate is about to expire or defined set of domains changed
- Certificate revocation
- and lots more..
Please keep in mind that this software and even the acme-protocol are relatively young and may still have some unresolved issues. Feel free to report any issues you find with this script or contribute by submitting a pull request.
Please keep in mind that this software, the ACME-protocol and all supported CA servers out there are relatively young and there might be a few issues. Feel free to report any issues you find with this script or contribute by submitting a pull request,
but please check for duplicates first (feel free to comment on those to get things rolling).
## Getting started
@@ -47,12 +50,15 @@ Default command: help
Commands:
--version (-v) Print version information
--display-terms Display current terms of service
--register Register account key
--account Update account contact information
--cron (-c) Sign/renew non-existent/changed/expiring certificates.
--signcsr (-s) path/to/csr.pem Sign a given CSR, output CRT on stdout (advanced usage)
--revoke (-r) path/to/cert.pem Revoke specified certificate
--deactivate Deactivate account
--cleanup (-gc) Move unused certificate files to archive directory
--cleanup-delete (-gcd) Deletes (!) unused certificate files
--help (-h) Show help text
--env (-e) Output configuration variables for use in other scripts
@@ -62,47 +68,30 @@ Parameters:
--ipv4 (-4) Resolve names to IPv4 addresses only
--ipv6 (-6) Resolve names to IPv6 addresses only
--domain (-d) domain.tld Use specified domain name(s) instead of domains.txt entry (one certificate!)
--ca url/preset Use specified CA URL or preset
--alias certalias Use specified name for certificate directory (and per-certificate config) instead of the primary domain (only used if --domain is specified)
--keep-going (-g) Keep going after encountering an error while creating/renewing multiple certificates in cron mode
--force (-x) Force renew of certificate even if it is longer valid than value in RENEW_DAYS
--force (-x) Force certificate renewal even if it is not due to expire within RENEW_DAYS
--force-validation Force revalidation of domain names (used in combination with --force)
--no-lock (-n) Don't use lockfile (potentially dangerous!)
--lock-suffix example.com Suffix lockfile name with a string (useful for with -d)
--ocsp Sets option in CSR indicating OCSP stapling to be mandatory
--privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation)
--domains-txt path/to/domains.txt Use specified domains.txt instead of default/configured one
--config (-f) path/to/config Use specified config file
--hook (-k) path/to/hook.sh Use specified script for hooks
--preferred-chain issuer-cn Use alternative certificate chain identified by issuer CN
--out (-o) certs/directory Output certificates into the specified directory
--challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported
--alpn alpn-certs/directory Output alpn verification certificates into the specified directory
--challenge (-t) http-01|dns-01|tls-alpn-01 Which challenge should be used? Currently http-01, dns-01, and tls-alpn-01 are supported
--algo (-a) rsa|prime256v1|secp384r1 Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
--acme-profile profile_name Use specified ACME profile
--order-timeout seconds Amount of seconds to wait for processing of order until erroring out
--validation-timeout seconds Amount of seconds to wait for processing of domain validations until erroring out
```
## Donate
## Chat
I'm having fun developing dehydrated, but it takes time, and time is money, at least that's what I've been told.
I will definitively continue developing dehydrated for free, but if you want to support me you can do so using the following ways:
### PayPal
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=23P9DSJBTY7C8)
### BitCoin
Send bitcoins to 12487bHxcrREffTGwUDnoxF1uYxCA7ztKK
### Server
I'm still planning on building a bigger testing-suite for dehydrated, it would be really cool to have a big(ish) server running
in a datacenter somewhere without having to pay for it... If you are a server provider and can offer me a (dedicated!) machine,
please contact me at `donations@dehydrated.de`.
### Other ways
I always like to play around with modern(ish) network and computer gear, 10G switches and stuff, modern ARM boards
(but please not that Raspberry Pi rubbish), tiny PCs for routing, etc.
If you have something that seems of value and that you don't need anymore feel free to contact me at
`donations@dehydrated.de`.
Also here is my [Amazon Wishlist](http://www.amazon.de/registry/wishlist/1TUCFJK35IO4Q) :)
Dehydrated has an official IRC-channel `#dehydrated` on libera.chat that can be used for general discussion and suggestions.
The channel can also be accessed with Matrix using the official libera.chat bridge at `#dehydrated:libera.chat`.

1827
dehydrated

File diff suppressed because it is too large Load Diff

19
docs/acme-v1.md Normal file
View File

@@ -0,0 +1,19 @@
## (Future) Removal of API version 1
The ACME API version 1 was never really standardized and was only supported by Let's Encrypt. Even though the protocol specification was public,
it wasn't really friendly to be integrated into existing CA systems so initial adoption was basically non-existant.
ACME version 2 is being designed to overcome these issues by becoming an official IETF standard and supporting a more traditional approach of account
and order management in the backend, making it friendlier to integrate into existing systems centered around those. It has since become a semi-stable IETF
standard draft which only ever got two breaking changes, Content-Type enforcement and `POST-as-GET`, the latter being announced in October 2018 to be enforced
by November 2019. See https://datatracker.ietf.org/wg/acme/documents/ for a better insight into the draft and its changes.
Next to backend changes that many users won't really care about ACME v2 has all of the features ACME v1 had, but also some additional new features like
e.g. support for [wildcard certificates](domains_txt.md#wildcards).
Since ACME v2 is basically to be considered stable and ACME v1 has no real benefits over v2, there doesn't seem to be much of a reason to keep the old
protocol around, but since there actually are a few Certificate Authorities and resellers that implemented the v1 protocol and didn't yet make the change
to v2, so dehydrated still supports the old protocol for now.
Please keep in mind that support for the old ACME protocol version 1 might get removed at any point of bigger inconvenience, e.g. on code changes that
would require a lot of work or ugly workarounds to keep both versions supported.

View File

@@ -28,4 +28,4 @@ Or when you do have a DNS API, pass the details accordingly to achieve the same
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](https://github.com/lukas2511/dehydrated/wiki/Examples-for-DNS-01-hooks)
Here are some examples: [Examples for DNS-01 hooks](https://github.com/dehydrated-io/dehydrated/wiki)

View File

@@ -1,19 +1,107 @@
### domains.txt
## domains.txt
dehydrated uses the file `domains.txt` as configuration for which certificates should be requested.
dehydrated uses the file `domains.txt` as configuration for which certificates
should be requested.
The file should have the following format:
```text
example.org
example.com www.example.com
example.net www.example.net wiki.example.net
```
This states that there are the following certificates:
* `example.org` without any *alternative names*
* `example.com` with an *alternative name* of `www.example.com`
* `example.net` with the *alternative names*: `www.example.net` and
`wiki.example.net`
### Aliases
You can define an *alias* for your certificate which will (instead of the
primary domain) be used as the directory name under your `CERTDIR` and for a
per-certificate lookup. This is done using the `>` character. This allows
multiple certificates with identical sets of domains but different
configuration to exist.
Here is an example of using an *alias* called `certalias` for creating the
certificate for `example.net` with *alternative names* `www.example.net` and
`wiki.example.net`. The certificate will be stored in the directory `certalias`
under your `CERTDIR`.
```text
example.net www.example.net wiki.example.net > certalias
```
This states that there should be two certificates `example.com` and `example.net`,
with the other domains in the corresponding line being their alternative names.
This allows to set per certificates options. The options you can change are
explained in [Per Certificate Config](per-certificate-config.md).
You can define an alias for your certificate which will (instead of the primary domain) be
used as directory name under your certdir and for a per-certificate lookup.
This allows multiple certificates with identical sets of domains but different configuration
to exist.
If you want to create different certificate types for the same domain
you can use:
```text
*.service.example.org service.example.org > star_service_example_org_rsa
*.service.example.org service.example.org > star_service_example_org_ecdsa
```
Then add a config file `certs/star_service_example_org_rsa/config` with
the value
```
KEY_ALGO="rsa"
```
or respectively
```
KEY_ALGO="ecdsa"
```
### Wildcards
Support for wildcards was added by the ACME v2 protocol.
Certificates with a wildcard domain as the first (or only) name require an
*alias* to be set. *Aliases* can't start with `*.`.
For example to create the wildcard for `*.service.example.com` your
`domains.txt` could use the *alias* method like this:
```text
*.service.example.com > star_service_example_com
```
This creates a wildcard certificate for only `*.service.example.com` and will
store it in the directory `star_service_example_com` under your `CERTDIR`. As a
note this certificate will **NOT** be valid for `service.example.com` but only
for `*.service.example.com`. So it would, for example, be valid for
`foo.service.example.com`.
Another way to create it is using *alternative names*. For example your
`domains.txt` could do this:
```text
service.example.com *.service.example.com
eggs.example.com *.ham.example.com
```
This creates two certificates one for `service.example.com` with an
*alternative name* of `*.service.example.com` and a second certificate for
`eggs.example.com` with an *alternative name* of `*.ham.example.com`.
**Note:** The first certificate is valid for both `service.example.com` and for
`*.service.example.com` which can be a useful way to create wildcard
certificates.
### Drop-in directory
If a directory named `domains.txt.d` exists in the same location as
`domains.txt`, the contents of `*.txt` files in that directory are appended to
the list of domains, in alphabetical order of the filenames. This is useful for
automation, as it doesn't require editing an existing file to add new domains.
Warning: Behaviour of this might change as the naming between `domains.txt.d`
and the `DOMAINS_D` config variable (which is used for per-certificate
configuration) is a bit confusing.

View File

@@ -10,10 +10,10 @@
# Default values of this config are in comments #
########################################################
# Which user should dehydrated run as? This will be implictly enforced when running as root
# Which user should dehydrated run as? This will be implicitly enforced when running as root
#DEHYDRATED_USER=
# Which group should dehydrated run as? This will be implictly enforced when running as root
# Which group should dehydrated run as? This will be implicitly enforced when running as root
#DEHYDRATED_GROUP=
# Resolve names to addresses of IP version only. (curl)
@@ -21,16 +21,19 @@
# default: <unset>
#IP_VERSION=
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
#CA="https://acme-v01.api.letsencrypt.org/directory"
# URL to certificate authority or internal preset
# Presets: letsencrypt, letsencrypt-test, zerossl, buypass, buypass-test, google, google-test
# default: letsencrypt
#CA="letsencrypt"
# Path to certificate authority license terms redirect (default: https://acme-v01.api.letsencrypt.org/terms)
#CA_TERMS="https://acme-v01.api.letsencrypt.org/terms"
# Path to old certificate authority
# Set this value to your old CA value when upgrading from ACMEv1 to ACMEv2 under a different endpoint.
# If dehydrated detects an account-key for the old CA it will automatically reuse that key
# instead of registering a new one.
# default: https://acme-v01.api.letsencrypt.org/directory
#OLDCA="https://acme-v01.api.letsencrypt.org/directory"
# Path to license agreement (default: <unset>)
#LICENSE=""
# Which challenge should be used? Currently http-01 and dns-01 are supported
# Which challenge should be used? Currently http-01, dns-01 and tls-alpn-01 are supported
#CHALLENGETYPE="http-01"
# Path to a directory containing additional config files, allowing to override
@@ -39,6 +42,11 @@
# default: <unset>
#CONFIG_D=
# Directory for per-domain configuration files.
# If not set, per-domain configurations are sourced from each certificates output directory.
# default: <unset>
#DOMAINS_D=
# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
#BASEDIR=$SCRIPTDIR
@@ -48,6 +56,9 @@
# Output directory for generated certificates
#CERTDIR="${BASEDIR}/certs"
# Output directory for alpn verification certificates
#ALPNCERTDIR="${BASEDIR}/alpn-certs"
# Directory for account keys and registration information
#ACCOUNTDIR="${BASEDIR}/accounts"
@@ -81,8 +92,8 @@
# 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"
# Minimum days before expiration to automatically renew certificate (default: 32)
#RENEW_DAYS="32"
# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
#PRIVATE_KEY_RENEW="yes"
@@ -91,7 +102,7 @@
#PRIVATE_KEY_ROLLOVER="no"
# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
#KEY_ALGO=rsa
#KEY_ALGO=secp384r1
# E-mail to use during the registration (default: <unset>)
#CONTACT_EMAIL=
@@ -105,8 +116,29 @@
# Fetch OCSP responses (default: no)
#OCSP_FETCH="no"
# OCSP refresh interval (default: 5 days)
#OCSP_DAYS=5
# Issuer chain cache directory (default: $BASEDIR/chains)
#CHAINCACHE="${BASEDIR}/chains"
# Automatic cleanup (default: no)
#AUTO_CLEANUP="no"
# Delete files during automatic cleanup instead of moving to archive (default: no)
#AUTO_CLEANUP_DELETE="no"
# ACME API version (default: auto)
#API=auto
# Preferred issuer chain (default: <unset> -> uses default chain)
#PREFERRED_CHAIN=
# Request certificate with specific profile (default: <unset>)
#ACME_PROFILE=
# Amount of seconds to wait for processing of order until erroring out (default: 0 => no timeout)
#ORDER_TIMEOUT=0
# Skip over errors during certificate orders and updating of OCSP stapling information (default: no)
#KEEP_GOING=no

View File

@@ -1,2 +1,39 @@
# 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
# Optionally you can also append the certificate algorithm here to create
# multiple certificate types for the same domain.
#
# This allows to set per certificates options. How to do this is
# explained in [domains.txt documentation](domains_txt.md).
#
*.service.example.org service.example.org > star_service_example_org_rsa
*.service.example.org service.example.org > star_service_example_org_ecdsa
# 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

View File

@@ -1,108 +1,199 @@
#!/usr/bin/env bash
deploy_challenge() {
local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
# This hook is called once for every domain that needs to be
# validated, including any alternative names you may have listed.
#
# Parameters:
# - DOMAIN
# The domain name (CN or subject alternative name) being
# validated.
# - TOKEN_FILENAME
# The name of the file containing the token to be served for HTTP
# validation. Should be served by your web server as
# /.well-known/acme-challenge/${TOKEN_FILENAME}.
# - TOKEN_VALUE
# The token value that needs to be served for validation. For DNS
# validation, this is what you want to put in the _acme-challenge
# TXT record. For HTTP validation it is the value that is expected
# be found in the $TOKEN_FILENAME file.
# This hook is called once for every domain that needs to be
# validated, including any alternative names you may have listed.
#
# Parameters:
# - DOMAIN
# The domain name (CN or subject alternative name) being
# validated.
# - TOKEN_FILENAME
# The name of the file containing the token to be served for HTTP
# validation. Should be served by your web server as
# /.well-known/acme-challenge/${TOKEN_FILENAME}.
# - TOKEN_VALUE
# The token value that needs to be served for validation. For DNS
# validation, this is what you want to put in the _acme-challenge
# TXT record. For HTTP validation it is the value that is expected
# be found in the $TOKEN_FILENAME file.
# Simple example: Use nsupdate with local named
# printf 'server 127.0.0.1\nupdate add _acme-challenge.%s 300 IN TXT "%s"\nsend\n' "${DOMAIN}" "${TOKEN_VALUE}" | nsupdate -k /var/run/named/session.key
}
clean_challenge() {
local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
# This hook is called after attempting to validate each domain,
# whether or not validation was successful. Here you can delete
# files or DNS records that are no longer needed.
#
# The parameters are the same as for deploy_challenge.
# This hook is called after attempting to validate each domain,
# whether or not validation was successful. Here you can delete
# files or DNS records that are no longer needed.
#
# The parameters are the same as for deploy_challenge.
# Simple example: Use nsupdate with local named
# printf 'server 127.0.0.1\nupdate delete _acme-challenge.%s TXT "%s"\nsend\n' "${DOMAIN}" "${TOKEN_VALUE}" | nsupdate -k /var/run/named/session.key
}
sync_cert() {
local KEYFILE="${1}" CERTFILE="${2}" FULLCHAINFILE="${3}" CHAINFILE="${4}" REQUESTFILE="${5}"
# This hook is called after the certificates have been created but before
# they are symlinked. This allows you to sync the files to disk to prevent
# creating a symlink to empty files on unexpected system crashes.
#
# This hook is not intended to be used for further processing of certificate
# files, see deploy_cert for that.
#
# Parameters:
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - REQUESTFILE
# The path of the file containing the certificate signing request.
# Simple example: sync the files before symlinking them
# sync "${KEYFILE}" "${CERTFILE}" "${FULLCHAINFILE}" "${CHAINFILE}" "${REQUESTFILE}"
}
deploy_cert() {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
# This hook is called once for each certificate that has been
# produced. Here you might, for instance, copy your new certificates
# to service-specific locations and reload the service.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - TIMESTAMP
# Timestamp when the specified certificate was created.
# This hook is called once for each certificate that has been
# produced. Here you might, for instance, copy your new certificates
# to service-specific locations and reload the service.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - TIMESTAMP
# Timestamp when the specified certificate was created.
# Simple example: Copy file to nginx config
# cp "${KEYFILE}" "${FULLCHAINFILE}" /etc/nginx/ssl/; chown -R nginx: /etc/nginx/ssl
# systemctl reload nginx
}
unchanged_cert() {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
deploy_ocsp() {
local DOMAIN="${1}" OCSPFILE="${2}" TIMESTAMP="${3}"
# This hook is called once for each certificate that is still
# valid and therefore wasn't reissued.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# This hook is called once for each updated ocsp stapling file that has
# been produced. Here you might, for instance, copy your new ocsp stapling
# files to service-specific locations and reload the service.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - OCSPFILE
# The path of the ocsp stapling file
# - TIMESTAMP
# Timestamp when the specified ocsp stapling file was created.
# Simple example: Copy file to nginx config
# cp "${OCSPFILE}" /etc/nginx/ssl/; chown -R nginx: /etc/nginx/ssl
# systemctl reload nginx
}
unchanged_cert() {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
# This hook is called once for each certificate that is still
# valid and therefore wasn't reissued.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
}
invalid_challenge() {
local DOMAIN="${1}" RESPONSE="${2}"
local DOMAIN="${1}" RESPONSE="${2}"
# This hook is called if the challenge response has failed, so domain
# owners can be aware and act accordingly.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - RESPONSE
# The response that the verification server returned
# This hook is called if the challenge response has failed, so domain
# owners can be aware and act accordingly.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - RESPONSE
# The response that the verification server returned
# Simple example: Send mail to root
# printf "Subject: Validation of ${DOMAIN} failed!\n\nOh noez!" | sendmail root
}
request_failure() {
local STATUSCODE="${1}" REASON="${2}" REQTYPE="${3}"
local STATUSCODE="${1}" REASON="${2}" REQTYPE="${3}" HEADERS="${4}"
# This hook is called when an HTTP request fails (e.g., when the ACME
# server is busy, returns an error, etc). It will be called upon any
# response code that does not start with '2'. Useful to alert admins
# about problems with requests.
#
# Parameters:
# - STATUSCODE
# The HTML status code that originated the error.
# - REASON
# The specified reason for the error.
# - REQTYPE
# The kind of request that was made (GET, POST...)
# This hook is called when an HTTP request fails (e.g., when the ACME
# server is busy, returns an error, etc). It will be called upon any
# response code that does not start with '2'. Useful to alert admins
# about problems with requests.
#
# Parameters:
# - STATUSCODE
# The HTML status code that originated the error.
# - REASON
# The specified reason for the error.
# - REQTYPE
# The kind of request that was made (GET, POST...)
# - HEADERS
# HTTP headers returned by the CA
# Simple example: Send mail to root
# printf "Subject: HTTP request failed failed!\n\nA http request failed with status ${STATUSCODE}!" | sendmail root
}
generate_csr() {
local DOMAIN="${1}" CERTDIR="${2}" ALTNAMES="${3}"
# This hook is called before any certificate signing operation takes place.
# It can be used to generate or fetch a certificate signing request with external
# tools.
# The output should be just the certificate signing request formatted as PEM.
#
# Parameters:
# - DOMAIN
# The primary domain as specified in domains.txt. This does not need to
# match with the domains in the CSR, it's basically just the directory name.
# - CERTDIR
# Certificate output directory for this particular certificate. Can be used
# for storing additional files.
# - ALTNAMES
# All domain names for the current certificate as specified in domains.txt.
# Again, this doesn't need to match with the CSR, it's just there for convenience.
# Simple example: Look for pre-generated CSRs
# if [ -e "${CERTDIR}/pre-generated.csr" ]; then
# cat "${CERTDIR}/pre-generated.csr"
# fi
}
startup_hook() {
@@ -113,13 +204,17 @@ startup_hook() {
}
exit_hook() {
local ERROR="${1:-}"
# This hook is called at the end of the cron command and can be used to
# do some final (cleanup or other) tasks.
:
#
# Parameters:
# - ERROR
# Contains error message if dehydrated exits with error
}
HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|startup_hook|exit_hook)$ ]]; then
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|sync_cert|deploy_cert|deploy_ocsp|unchanged_cert|invalid_challenge|request_failure|generate_csr|startup_hook|exit_hook)$ ]]; then
"$HANDLER" "$@"
fi

View File

@@ -60,4 +60,3 @@ HOOK: clean_challenge lukas.im blablabla blablabla.supersecure www.lukas.im blub
HOOK: deploy_cert lukas.im /etc/dehydrated/certs/lukas.im/privkey.pem /etc/dehydrated/certs/lukas.im/cert.pem /etc/dehydrated/certs/lukas.im/fullchain.pem /etc/dehydrated/certs/lukas.im/chain.pem 1460152408
+ Done!
```

View File

@@ -1,3 +0,0 @@
# Import
If you want to import existing keys from the official letsencrypt client have a look at [Import from official letsencrypt client](https://github.com/lukas2511/dehydrated/wiki/Import-from-official-letsencrypt-client).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -1,4 +1,4 @@
.TH DEHYDRATED 1 2017-09-20 "Dehydrated ACME Client"
.TH DEHYDRATED 1 2018-01-13 "Dehydrated ACME Client"
.SH NAME
dehydrated \- ACME client implemented as a shell-script
.SH SYNOPSIS
@@ -7,8 +7,8 @@ dehydrated \- ACME client implemented as a shell-script
[\fBargument\fR [\fBargument\fR]]
.IR ...
.SH DESCRIPTION
A client for ACME-based Certificate Authorities, such as LetsEncrypt. It
allows to request and obtain TLS certificates from an ACME-based
A client for ACME-based Certificate Authorities, such as LetsEncrypt. It can
be used to request and obtain TLS certificates from an ACME-based
certificate authority.
Before any certificates can be requested, Dehydrated needs
@@ -20,13 +20,13 @@ Dehydrated will notify if no account is configured. Run with \fB--register
Next, all domain names must be provided in domains.txt. The format is line
based: If the file contains two lines "example.com" and "example.net",
Dehydrated will request two certificate, one for "example.com" and the other
for "example.net". A single line while "example.com example.net" will request a
dehydrated will request two certificate, one for "example.com" and the other
for "example.net". A single line containing "example.com example.net" will request a
single certificate valid for both "example.net" and "example.com" through the \fISubject
Alternative Name\fR (SAN) field.
For the next step, one way of verifying domain name ownership needs to be
configured. Dehydrated implements \fIhttp-01\fR and \fIdns-01\fR verification.
configured. Dehydrated implements \fIhttp-01\fR and \fIdns-01\fR verification.
The \fIhttp-01\fR verification provides proof of ownership by providing a
challenge token. In order to do that, the directory referenced in the
@@ -106,7 +106,7 @@ Keep going after encountering an error while creating/renewing multiple
certificates in cron mode
.TP
.BR \-\-force ", " \-x
Force renew of certificate even if it is longer valid than value in RENEW_DAYS
Force certificate renewal even if it is not due to expire within RENEW_DAYS
.TP
.BR \-\-no\-lock ", " \-n
Don't use lockfile (potentially dangerous!)
@@ -139,17 +139,17 @@ secp384r1
The program exits 0 if everything was fine, 1 if an error occurred.
.SH BUGS
Please report any bugs that you may encounter at the project web site
.UR https://github.com/lukas2511/dehydrated/issues
.UR https://github.com/dehydrated-io/dehydrated/issues
.UE .
.SH AUTHOR
Dehydrated was written by Lukas Schauer. This man page was contributed by
Daniel Molkentin.
.SH COPYRIGHT
Copyright 20015-2017 by Lukas Schauer and the respective contributors.
Copyright 2015-2018 by Lukas Schauer and the respective contributors.
Provided under the MIT License. See the LICENSE file that accompanies the
distribution for licensing information.
.SH SEE ALSO
Full documentation along with configuration examples are provided in the \fIdocs\fR
directory of the distribution, or at
.UR https://github.com/lukas2511/dehydrated/tree/master/docs
.UR https://github.com/dehydrated-io/dehydrated/tree/master/docs
.UE .

View File

@@ -7,15 +7,19 @@ To use this feature create a `config` file in the certificates output directory
Currently supported options:
- PRIVATE_KEY_RENEW
- PRIVATE_KEY_ROLLOVER
- KEY_ALGO
- KEYSIZE
- OCSP_MUST_STAPLE
- OCSP_FETCH
- OCSP_DAYS
- CHALLENGETYPE
- HOOK
- HOOK_CHAIN
- WELLKNOWN
- OPENSSL_CNF
- RENEW_DAYS
- PREFERRED_CHAIN
## DOMAINS_D

View File

@@ -8,6 +8,7 @@ you will quickly hit these limits and find yourself locked out.
To avoid this, please set the CA property to the Lets Encrypt staging server URL in your config file:
```bash
CA="https://acme-staging.api.letsencrypt.org/directory"
CA_TERMS="https://acme-staging.api.letsencrypt.org/terms"
CA="https://acme-staging-v02.api.letsencrypt.org/directory"
```
Alternatively you can define the CA using the CLI argument `--ca letsencrypt-test` (`letsencrypt-test` is an integrated preset-CA corresponding to the URL above).

124
docs/tls-alpn.md Normal file
View File

@@ -0,0 +1,124 @@
# TLS-ALPN-01
With `tls-alpn-01`-type verification Let's Encrypt (or the ACME-protocol in general) is checking if you are in control of a domain by accessing
your webserver using a custom ALPN and expecting a specially crafted TLS certificate containing a verification token.
It will do that for any (sub-)domain you want to sign a certificate for.
Dehydrated generates the required verification certificates, but the delivery is out of its scope.
### Example lighttpd config
lighttpd can be configured to recognize ALPN `acme-tls/1` and to respond to such
requests using the specially crafted TLS certificates generated by dehydrated.
Configure lighttpd and dehydrated to use the same path for these certificates.
(Be sure to allow read access to the user account under which the lighttpd
server is running.) `mkdir -p /etc/dehydrated/alpn-certs`
lighttpd.conf:
```
ssl.acme-tls-1 = "/etc/dehydrated/alpn-certs"
```
When renewing certificates, specify `-t tls-alpn-01` and `--alpn /etc/dehydrated/alpn-certs` to dehydrated, e.g.
```
dehydrated -t tls-alpn-01 --alpn /etc/dehydrated/alpn-certs -c --out /etc/lighttpd/certs -d www.example.com
# gracefully reload lighttpd to use the new certificates by sending lighttpd pid SIGUSR1
systemctl reload lighttpd
```
### Example nginx config
On an nginx tcp load-balancer you can use the `ssl_preread` module to map a different port for acme-tls
requests than for e.g. HTTP/2 or HTTP/1.1 requests.
Your config should look something like this:
```nginx
stream {
map $ssl_preread_alpn_protocols $tls_port {
~\bacme-tls/1\b 10443;
default 443;
}
server {
listen 443;
listen [::]:443;
proxy_pass 10.13.37.42:$tls_port;
ssl_preread on;
}
}
```
That way https requests are forwarded to port 443 on the backend server, and acme-tls/1 requests are
forwarded to port 10443.
In the future nginx might support internal routing based on custom ALPNs, but for now you'll have to
use a custom responder for the alpn verification certificates (see below).
### Example responder
I hacked together a simple responder in Python, it might not be the best, but it works for me:
```python
#!/usr/bin/env python3
import ssl
import socketserver
import threading
import re
import os
ALPNDIR="/etc/dehydrated/alpn-certs"
PROXY_PROTOCOL=False
FALLBACK_CERTIFICATE="/etc/ssl/certs/ssl-cert-snakeoil.pem"
FALLBACK_KEY="/etc/ssl/private/ssl-cert-snakeoil.key"
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def create_context(self, certfile, keyfile, first=False):
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.set_ciphers('ECDHE+AESGCM')
ssl_context.set_alpn_protocols(["acme-tls/1"])
ssl_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
if first:
ssl_context.set_servername_callback(self.load_certificate)
ssl_context.load_cert_chain(certfile=certfile, keyfile=keyfile)
return ssl_context
def load_certificate(self, sslsocket, sni_name, sslcontext):
print("Got request for %s" % sni_name)
if not re.match(r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3})$', sni_name):
return
certfile = os.path.join(ALPNDIR, "%s.crt.pem" % sni_name)
keyfile = os.path.join(ALPNDIR, "%s.key.pem" % sni_name)
if not os.path.exists(certfile) or not os.path.exists(keyfile):
return
sslsocket.context = self.create_context(certfile, keyfile)
def handle(self):
if PROXY_PROTOCOL:
buf = b""
while b"\r\n" not in buf:
buf += self.request.recv(1)
ssl_context = self.create_context(FALLBACK_CERTIFICATE, FALLBACK_KEY, True)
newsock = ssl_context.wrap_socket(self.request, server_side=True)
if __name__ == "__main__":
HOST, PORT = "0.0.0.0", 10443
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler, bind_and_activate=False)
server.allow_reuse_address = True
try:
server.server_bind()
server.server_activate()
server.serve_forever()
except:
server.shutdown()
```

View File

@@ -11,12 +11,6 @@ the current workaround is to move `private_key.pem` (and, if you care, `private_
This will hopefully be fixed in the future.
## "Provided agreement URL [LICENSE1] does not match current agreement URL [LICENSE2]"
Set LICENSE in your config to the value in place of "LICENSE2".
LICENSE1 and LICENSE2 are just placeholders for the real values in this troubleshooting document!
## "Error creating new cert :: Too many certificates already issued for: [...]"
This is not an issue with dehydrated but an API limit with boulder (the ACME server).
@@ -36,3 +30,23 @@ If you are using HTTP validation make sure that the path you have configured wit
To test this create a file (e.g. `test.txt`) in that directory and try opening it with your browser: `http://example.org/.well-known/acme-challenge/test.txt`. Note that if you have an IPv6 address, the challenge connection will be on IPv6. Be sure that you test HTTP connections on both IPv4 and IPv6. Checking the test file in your browser is often not sufficient because the browser just fails over to IPv4.
If you get any error you'll have to fix your web server configuration.
## DNS invalid challenge since dehydrated 0.6.0 / Why are DNS challenges deployed first and verified later?
Since Let's Encrypt (and in general the ACMEv2 protocol) now supports wildcard domains there is a situation where DNS caching can become a problem.
If somebody wants to validate a certificate with `example.org` and `*.example.org` there are two tokens that have to be deployed on `_acme-challenge.example.org`.
If dehydrated would deploy and verify each token on its own the CA would cache the first token on `_acme-challenge.example.org` and the next challenge would simply fail.
Let's Encrypt uses your DNS TTL with a max limit of 5 minutes, but this doesn't seem to be part of the ACME protocol, just some LE specific configuration,
so with other CAs and certain DNS providers who don't allow low TTLs this could potentially take hours.
Since dehydrated now deploys all challenges first that no longer is a problem. The CA will query and cache both challenges, and both authorizations can be validated.
Some hook-scripts were written in a way that erases the old TXT record rather than adding a new entry, those should be (and many of them already have been) fixed.
There are certain DNS providers which really only allow one TXT record on a domain. This is really odd and you should probably contact your DNS provider and ask them
to fix this.
If for whatever reason you can't switch DNS providers and your DNS provider only supports one TXT record and doesn't want to fix that you could try splitting your
certificate into multiple certificates and add a sleep in the `deploy_cert` hook.
If you can't do that or really don't want to please leave a comment on https://github.com/lukas2511/dehydrated/issues/554,
if many people are having this unfixable problem I might try to implement a workaround.

View File

@@ -24,7 +24,7 @@ With Nginx you'll need to add this to any of your `server`/VHost config blocks:
```nginx
server {
[...]
location /.well-known/acme-challenge {
location ^~ /.well-known/acme-challenge {
alias /var/www/dehydrated;
}
[...]

266
test.sh
View File

@@ -1,266 +0,0 @@
#!/usr/bin/env bash
# Fail early
set -eu -o pipefail
# Check if running in CI environment
if [[ ! "${CI:-false}" == "true" ]]; then
echo "ERROR: Not running in CI environment!"
exit 1
fi
_TEST() {
echo
echo "${1} "
}
_SUBTEST() {
echo -n " + ${1} "
}
_PASS() {
echo -e "[\u001B[32mPASS\u001B[0m]"
}
_FAIL() {
echo -e "[\u001B[31mFAIL\u001B[0m]"
echo
echo "Problem: ${@}"
echo
echo "STDOUT:"
cat tmplog
echo
echo "STDERR:"
cat errorlog
exit 1
}
_CHECK_FILE() {
_SUBTEST "Checking if file '${1}' exists..."
if [[ -e "${1}" ]]; then
_PASS
else
_FAIL "Missing file: ${1}"
fi
}
_CHECK_LOG() {
_SUBTEST "Checking if log contains '${1}'..."
if grep -- "${1}" tmplog > /dev/null; then
_PASS
else
_FAIL "Missing in log: ${1}"
fi
}
_CHECK_NOT_LOG() {
_SUBTEST "Checking if log doesn't contain '${1}'..."
if grep -- "${1}" tmplog > /dev/null; then
_FAIL "Found in log: ${1}"
else
_PASS
fi
}
_CHECK_ERRORLOG() {
_SUBTEST "Checking if errorlog is empty..."
if [[ -z "$(cat errorlog)" ]]; then
_PASS
else
_FAIL "Non-empty errorlog"
fi
}
# If not found (should be cached in travis) download ngrok
if [[ ! -e "ngrok/ngrok" ]]; then
(
mkdir -p ngrok
cd ngrok
if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
wget -O ngrok.zip https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
wget -O ngrok.zip https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip
else
echo "No ngrok for ${TRAVIS_OS_NAME}"
exit 1
fi
unzip ngrok.zip ngrok
chmod +x ngrok
)
fi
# Run ngrok and grab temporary url from logfile
ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp.log &
ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp2.log &
ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp3.log &
sleep 2
TMP_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp.log | head -1 | cut -d':' -f2)"
TMP2_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp2.log | head -1 | cut -d':' -f2)"
TMP3_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp3.log | head -1 | cut -d':' -f2)"
if [[ -z "${TMP_URL}" ]] || [[ -z "${TMP2_URL}" ]] || [[ -z "${TMP3_URL}" ]]; then
echo "Couldn't get an url from ngrok, not a dehydrated bug, tests can't continue."
exit 1
fi
# Run python webserver in .acme-challenges directory to serve challenge responses
mkdir -p .acme-challenges/.well-known/acme-challenge
(
cd .acme-challenges
python -m SimpleHTTPServer 8080 > /dev/null 2> /dev/null
) &
# Generate config and create empty domains.txt
echo 'CA="https://testca.kurz.pw/directory"' > config
echo 'CA_TERMS="https://testca.kurz.pw/terms"' >> config
echo 'WELLKNOWN=".acme-challenges/.well-known/acme-challenge"' >> config
echo 'RENEW_DAYS="14"' >> config
touch domains.txt
# Check if help command is working
_TEST "Checking if help command is working..."
./dehydrated --help > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Default command: help"
_CHECK_LOG "--help (-h)"
_CHECK_LOG "--domain (-d) domain.tld"
_CHECK_ERRORLOG
# Register account key without LICENSE set
_TEST "Register account key without LICENSE set"
./dehydrated --register > tmplog 2> errorlog && _FAIL "Script execution failed"
_CHECK_LOG "To accept these terms"
_CHECK_ERRORLOG
# Register account key and agreeing to terms
_TEST "Register account key without LICENSE set"
./dehydrated --register --accept-terms > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Registering account key"
_CHECK_FILE accounts/*/account_key.pem
_CHECK_ERRORLOG
# Delete accounts and add LICENSE to config for normal operation
rm -rf accounts
echo 'LICENSE="https://testca.kurz.pw/terms/v1"' >> config
# Run in cron mode with empty domains.txt (should only generate private key and exit)
_TEST "First run in cron mode, checking if private key is generated and registered"
./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Registering account key"
_CHECK_FILE accounts/*/account_key.pem
_CHECK_ERRORLOG
# Temporarily move config out of the way and try signing certificate by using temporary config location
_TEST "Try signing using temporary config location and with domain as command line parameter"
mv config tmp_config
./dehydrated --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" --accept-terms -f tmp_config > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_NOT_LOG "Checking domain name(s) of existing cert"
_CHECK_LOG "Generating private key"
_CHECK_LOG "Requesting challenge for ${TMP_URL}"
_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
_CHECK_LOG "Challenge is valid!"
_CHECK_LOG "Creating fullchain.pem"
_CHECK_LOG "Done!"
_CHECK_ERRORLOG
mv tmp_config config
# Add third domain to command-lime, should force renewal.
_TEST "Run in cron mode again, this time adding third domain, should force renewal."
./dehydrated --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" --domain "${TMP3_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Domain name(s) are not matching!"
_CHECK_LOG "Forcing renew."
_CHECK_LOG "Generating private key"
_CHECK_LOG "Requesting challenge for ${TMP_URL}"
_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
_CHECK_LOG "Challenge is valid!"
_CHECK_LOG "Creating fullchain.pem"
_CHECK_LOG "Done!"
_CHECK_ERRORLOG
# Prepare domains.txt
# Modify TMP3_URL to be uppercase to check for upper-lower-case mismatch bugs
echo "${TMP_URL} ${TMP2_URL} $(tr 'a-z' 'A-Z' <<<"${TMP3_URL}")" >> domains.txt
# Run in cron mode again (should find a non-expiring certificate and do nothing)
_TEST "Run in cron mode again, this time with domain in domains.txt, should find non-expiring certificate"
./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
_CHECK_LOG "Skipping renew"
_CHECK_ERRORLOG
# Disable private key renew
echo 'PRIVATE_KEY_RENEW="no"' >> config
# Run in cron mode one last time, with domain in domains.txt and force-resign (should find certificate, resign anyway, and not generate private key)
_TEST "Run in cron mode one last time, with domain in domains.txt and force-resign"
./dehydrated --cron --force > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
_CHECK_LOG "Ignoring because renew was forced!"
_CHECK_NOT_LOG "Generating private key"
_CHECK_LOG "Requesting challenge for ${TMP_URL}"
_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
_CHECK_LOG "Already validated!"
_CHECK_LOG "Creating fullchain.pem"
_CHECK_LOG "Done!"
_CHECK_ERRORLOG
# Check if signcsr command is working
_TEST "Running signcsr command"
./dehydrated --signcsr certs/${TMP_URL}/cert.csr > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "BEGIN CERTIFICATE"
_CHECK_LOG "END CERTIFICATE"
_CHECK_NOT_LOG "ERROR"
# Check if renewal works
_TEST "Run in cron mode again, to check if renewal works"
echo 'RENEW_DAYS="300"' >> config
./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
_CHECK_LOG "Renewing!"
_CHECK_ERRORLOG
# Check if certificate is valid in various ways
_TEST "Verifying certificate..."
_SUBTEST "Verifying certificate on its own..."
openssl x509 -in "certs/${TMP_URL}/cert.pem" -noout -text > tmplog 2> errorlog && _PASS || _FAIL
_CHECK_LOG "CN=${TMP_URL}"
_CHECK_LOG "${TMP2_URL}"
_SUBTEST "Verifying file with full chain..."
openssl x509 -in "certs/${TMP_URL}/fullchain.pem" -noout -text > /dev/null 2>> errorlog && _PASS || _FAIL
_SUBTEST "Verifying certificate against CA certificate..."
curl -s https://testca.kurz.pw/acme/issuer-cert | openssl x509 -inform DER -outform PEM > ca.pem
(openssl verify -verbose -CAfile "ca.pem" -purpose sslserver "certs/${TMP_URL}/fullchain.pem" 2>&1 || true) | (grep -v ': OK$' || true) >> errorlog 2>> errorlog && _PASS || _FAIL
_CHECK_ERRORLOG
# Revoke certificate using certificate key
_TEST "Revoking certificate..."
./dehydrated --revoke "certs/${TMP_URL}/cert.pem" --privkey "certs/${TMP_URL}/privkey.pem" > tmplog 2> errorlog || _FAIL "Script execution failed"
REAL_CERT="$(readlink -n "certs/${TMP_URL}/cert.pem")"
_CHECK_LOG "Revoking certs/${TMP_URL}/${REAL_CERT}"
_CHECK_LOG "Done."
_CHECK_FILE "certs/${TMP_URL}/${REAL_CERT}-revoked"
_CHECK_ERRORLOG
# Enable private key renew
echo 'PRIVATE_KEY_RENEW="yes"' >> config
echo 'PRIVATE_KEY_ROLLOVER="yes"' >> config
# Check if Rolloverkey creation works
_TEST "Testing Rolloverkeys..."
_SUBTEST "First Run: Creating rolloverkey"
./dehydrated --cron --domain "${TMP2_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
CERT_ROLL_HASH=$(openssl rsa -in certs/${TMP2_URL}/privkey.roll.pem -outform DER -pubout 2>/dev/null | openssl sha -sha256)
_CHECK_LOG "Generating private key"
_CHECK_LOG "Generating private rollover key"
_SUBTEST "Second Run: Force Renew, Use rolloverkey"
./dehydrated --cron --force --domain "${TMP2_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
CERT_NEW_HASH=$(openssl rsa -in certs/${TMP2_URL}/privkey.pem -outform DER -pubout 2>/dev/null | openssl sha -sha256)
_CHECK_LOG "Generating private key"
_CHECK_LOG "Moving Rolloverkey into position"
_SUBTEST "Verifying Hash Rolloverkey and private key second run"
[[ "${CERT_ROLL_HASH}" = "${CERT_NEW_HASH}" ]] && _PASS || _FAIL
_CHECK_ERRORLOG
# Test cleanup command
_TEST "Cleaning up certificates"
./dehydrated --cleanup > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/cert-"
_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/chain-"
_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/fullchain-"
_CHECK_ERRORLOG
# All done
exit 0