107 Commits

Author SHA1 Message Date
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
Lukas Schauer
4a811759dc version 0.5.0 2018-01-13 20:08:12 +01:00
Lukas Schauer
2adc57791c Add optional user and group configuration (fixes #434) 2017-12-18 00:35:26 +01:00
Lukas Schauer
f35aed6ae6 replace backticks with escaped dollarbracethingy (fixes #438) 2017-12-18 00:01:47 +01:00
Lukas Schauer
b6b56d0df7 export certificate alias to be used in hook scripts 2017-12-17 23:54:19 +01:00
Lukas Schauer
13c853d43b also reset configvars after domains loop 2017-12-17 23:51:23 +01:00
Lukas Schauer
c62f3d91fc implement certificate aliases as suggested by typingArtist (fixes #396) 2017-12-17 23:50:46 +01:00
typingArtist
eb1c4ac41d make certdir a parameter to sign_domain 2017-12-17 22:55:12 +01:00
Daniel Molkentin
3ec54e7e0f Add man page 2017-12-14 00:35:54 +01:00
sirrkitt
88267db7e2 Update wellknown.md
add Hiawatha to list
2017-12-14 00:33:11 +01:00
Lukas Schauer
eb4aaefda1 also inform about still-valid ocsp stapling files (fixes #457) 2017-12-14 00:11:40 +01:00
Lukas Schauer
3d97799d6a always revalidate challenges if --force is set (fixes #370) 2017-11-07 14:43:41 +01:00
Exagone313
742c0ad176 fix ocsp.der symlink 2017-10-22 16:31:25 +02:00
Andreas Loibl
7f410e9bff fix account command
backup file path generation should split the filename on the last dot instead of the first
2017-10-17 16:39:46 +02:00
Lukas Schauer
da3428a84a use nullglob, disable warning on empty CONFIG_D directory 2017-09-21 18:10:01 +02:00
Lukas Schauer
b5e178ea75 allow for spaces when extracting commonName from csr (fixes #423) 2017-09-20 15:44:05 +02:00
Lukas Schauer
bc20ec79f3 also show freebsd version 2017-09-20 15:31:38 +02:00
Marcin Gryszkalis
ce9b42d8ad fix issue #426 - version info on FreeBSD 2017-09-20 15:28:57 +02:00
Lukas Schauer
f838d93f40 stop verification loop after invalid challenge (fixes #431) 2017-09-20 15:17:30 +02:00
typingArtist
0be0ab083f replace ${CERTDIR}/${domain} with ${certdir} everywhere
• improves readability
• allows ${certdir} to be changed independent from ${domain} more easily
2017-07-18 15:46:25 +02:00
Lukas Schauer
58647cab65 added OPENSSL variable to example config (#414) 2017-07-18 15:46:25 +02:00
Lukas Schauer
c57ad87e7c fixed error handling on non-2xx http status codes (#413) 2017-07-18 03:29:39 +02:00
Lukas Schauer
2687054d25 cut path from url for ocsp host 2017-07-13 00:53:32 +02:00
Lukas Schauer
2b76d038d3 ocsp fetching should now also work with older openssl versions 2017-07-12 16:00:25 +02:00
Lukas Schauer
e339b28159 add host header to ocsp request 2017-07-12 15:33:56 +02:00
Lukas Schauer
4f3bd3e956 fixed exit_hook 2017-07-11 10:06:42 +02:00
Lukas Schauer
f86290ea52 revocation: don't fail if certificate already has been revoked (fixes #236) 2017-07-11 01:30:30 +02:00
Lukas Schauer
f1bc2b14ba cleanup old ocsp response files 2017-07-11 00:50:05 +02:00
Lukas Schauer
367ef574f1 export altnames so it can be used in hook scripts (fixes #360) 2017-07-11 00:47:37 +02:00
Lukas Schauer
4e7fb80bcd support otherName SAN entries for domain verification (fixes #356) 2017-07-11 00:35:59 +02:00
Lukas Schauer
ee75c5dca7 Initial support for fetching OCSP status to be used for OCSP stapling (as suggested in #385) 2017-07-11 00:28:36 +02:00
Lukas Schauer
82ca3ffcd3 added giant donation section to readme (please send me all your money) 2017-07-10 22:48:50 +02:00
Lukas Schauer
bb99742aa7 load config for version information but disable verification 2017-07-10 21:36:32 +02:00
Lukas Schauer
60583d3ef9 added hook to run before cron command (fixes #371) 2017-07-10 21:36:10 +02:00
Lukas Schauer
cbb661ca17 specify openssl config location when converting CSR to DER (fixes #397) 2017-07-10 20:32:05 +02:00
Lukas Schauer
67cf20765c updated changelog 2017-07-10 20:28:38 +02:00
Lukas Schauer
89377a1004 git: ignore chains directory 2017-07-10 20:28:20 +02:00
Lukas Schauer
dc600e39b8 cleanup: also move .pem-revoked files (fixes #237) 2017-07-10 20:27:42 +02:00
Lukas Schauer
16e91b415b added auto-cleanup feature to changelog 2017-07-10 20:23:27 +02:00
Lukas Schauer
e6d6882c78 added option to automatically run cleanup routine (implements #389) 2017-07-10 20:21:30 +02:00
Herman van Rink
14a5f63077 Redirect additional errors to STDERR 2017-07-10 19:59:52 +02:00
Herman van Rink
5787cd6a47 Remove double output redirection, _exiterr already does >&2 2017-07-10 19:59:52 +02:00
ProBackup-nl
875c1f74e5 Add some formatting to improve human scannability (while reading) 2017-07-10 19:53:39 +02:00
Lukas Schauer
db18820991 made openssl binary configurable (closes #393, closes #379) 2017-07-10 19:13:52 +02:00
Lukas Schauer
2f775d0e2a remove duplicate -a short option from --account (fixes #410) 2017-07-10 18:21:06 +02:00
Lukas Schauer
f2b589430c added version command 2017-07-10 17:21:22 +02:00
Lukas Schauer
533aa80129 replaced source url with dehydrated.de 2017-07-10 17:15:29 +02:00
Lukas Schauer
d1f215b652 fixed typos as suggested by @jwilk (closes #369) 2017-07-10 16:55:18 +02:00
Andreas Thienemann
bd57777c62 Ability to provide extra curl options
In some situations it might be necessary to pass extra commands to
the curl binary, e.g. proxy authentication credentials.

Adds the CURL_OPTS config option.
2017-07-10 16:44:39 +02:00
Haddon CD
ba31a505d2 Add MSYS support 2017-07-10 16:40:36 +02:00
Chase Bolt
0dcf94dd3d dont strip for docker containers that cwd is / 2017-07-10 16:36:14 +02:00
Andreas Thienemann
9ea75e7cfb Support older bash releases
Bash 3.0 and others seem to have serious issues running dehydrated.
https://github.com/lukas2511/dehydrated/issues/284 tracks the
problem but got closed as it seems too hard to support "stone age"
distributions.

Turns out it is actually only a three line change. ;-)
2017-07-10 15:29:30 +02:00
Ben Elliston
33c77e6daa Add some comments about IPv6. 2017-07-10 15:26:50 +02:00
Lukas Schauer
d685463673 implemented issuer-chain cache 2017-07-10 15:06:50 +02:00
Lukas Schauer
98ad01a110 allow using parts of dehydrated without running the main script (intended for testing parts of the script) 2017-07-10 14:51:55 +02:00
Lukas Schauer
8709d21ef2 updated usage instructions in readme (added --account) 2017-07-10 14:08:29 +02:00
Lukas Schauer
6ebaae416c removed build status from readme (test system seems to be broken...) 2017-07-10 14:07:28 +02:00
Lukas Schauer
7fc4040f47 updated changelog with account-update feature 2017-07-10 14:05:45 +02:00
Anton Avramov
ec1599e3b6 Added new feature Update registration contact #239 2017-07-10 14:02:21 +02:00
18 changed files with 1085 additions and 597 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@ hook.sh
certs/*
archive/*
accounts/*
chains/*

View File

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

View File

@@ -1,9 +1,32 @@
# Change Log
This file contains a log of major changes in dehydrated
## [x.x.x] - xxxx-xx-xx
## [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)
- OpenSSL binary path is now configurable (OPENSSL)
- Cleanup now also moves revoked certificates
## Added
- New feature for updating contact information (--account)
- Allow automatic cleanup on exit (AUTO_CLEANUP)
- Initial support for fetching OCSP status to be used for OCSP stapling (OCSP_FETCH)
- Certificates can now have aliases to create multiple certificates with identical set of domains (see --alias and domains.txt documentation)
- Allow dehydrated to run as specified user (/group)
## [0.4.0] - 2017-02-05
## Changed

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2017 Lukas Schauer
Copyright (c) 2015-2018 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,8 +1,9 @@
# dehydrated [![Build Status](https://travis-ci.org/lukas2511/dehydrated.svg?branch=master)](https://travis-ci.org/lukas2511/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)
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 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.
@@ -33,7 +34,7 @@ dehydrated is looking for a config file in a few different places, it will use t
- `/etc/dehydrated/config`
- `/usr/local/etc/dehydrated/config`
- The current working directory of your shell
- The directory from which dehydrated was ran
- The directory from which dehydrated was run
Have a look at [docs/examples/config](docs/examples/config) to get started, copy it to e.g. `/etc/dehydrated/config`
and edit it to fit your needs.
@@ -46,8 +47,10 @@ Usage: ./dehydrated [-h] [command [argument]] [parameter [argument]] [parameter
Default command: help
Commands:
--version (-v) Print version information
--register Register account key
--cron (-c) Sign/renew non-existant/changed/expiring certificates.
--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
--cleanup (-gc) Move unused certificate files to archive directory
@@ -60,6 +63,7 @@ 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!)
--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
--no-lock (-n) Don't use lockfile (potentially dangerous!)
@@ -72,3 +76,25 @@ Parameters:
--challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported
--algo (-a) rsa|prime256v1|secp384r1 Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
```
## Donate
I'm a student hacker with a few (unfortunately) quite expensive hobbies (self-hosting, virtualization clusters, routing,
high-speed networking, embedded hardware, etc.).
I'm really having fun playing around with hard- and software and I'm steadily learning new things.
Without those hobbies I probably would never have started working on dehydrated to begin with :)
I'd really appreciate if you could [donate a bit of money](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=23P9DSJBTY7C8)
so I can buy cool stuff (while still being able to afford food :D).
If you have hardware laying around that you think I'd enjoy playing with (e.g. decomissioned but still modern-ish servers,
10G networking hardware, enterprise grade routers or APs, interesting ARM/MIPS boards, etc.) and that you would be willing
to ship to me please contact me at `donations@dehydrated.de` or on Twitter [@lukas2511](https://twitter.com/lukas2511).
If you want your name to be added to the [donations list](https://dehydrated.de/donations.html) please add a note or send me an
email `donations@dehydrated.de`. I respect your privacy and won't publish your name without permission.
Other ways of donating:
- [My Amazon Wishlist](http://www.amazon.de/registry/wishlist/1TUCFJK35IO4Q)
- Monero: 4Kkf4tF4r9DakxLj37HDXLJgmpVfQoFhT7JLDvXwtUZZMTbsK9spsAPXivWPAFcDUj6jHhY8hJSHX8Cb8ndMhKeQHPSkBZZiK89Fx8NTHk
- Bitcoin: 12487bHxcrREffTGwUDnoxF1uYxCA7ztKK

1027
dehydrated

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,18 @@
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!
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, deploy_cert, invalid_challenge or request_failure) 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.
The hook script (indicated in the config file or the `--hook/-k` command line argument) gets four arguments:
$1 an operation name (`clean_challenge`, `deploy_challenge`, `deploy_cert`, `invalid_challenge` or `request_failure`) 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
@@ -13,10 +22,10 @@ _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 boxes to put "_acme-challenge.my" and the token value in, and a dropdown to choose the record type, TXT.
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 boxes 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.
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)

View File

@@ -7,7 +7,16 @@ The file should have the following format:
```text
example.com www.example.com
example.net www.example.net wiki.example.net
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.
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.
Certificates with a wildcard domain as first (or only) name require an alias to be set.
Aliases can't start with `*.`.

View File

@@ -10,6 +10,12 @@
# Default values of this config are in comments #
########################################################
# Which user should dehydrated run as? This will be implictly enforced when running as root
#DEHYDRATED_USER=
# Which group should dehydrated run as? This will be implictly enforced when running as root
#DEHYDRATED_GROUP=
# Resolve names to addresses of IP version only. (curl)
# supported values: 4, 6
# default: <unset>
@@ -18,11 +24,12 @@
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
#CA="https://acme-v01.api.letsencrypt.org/directory"
# 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 license agreement (default: <unset>)
#LICENSE=""
# 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: <unset>
#OLDCA=
# Which challenge should be used? Currently http-01 and dns-01 are supported
#CHALLENGETYPE="http-01"
@@ -54,6 +61,12 @@
# Path to openssl config file (default: <unset> - tries to figure out system default)
#OPENSSL_CNF=
# Path to OpenSSL binary (default: "openssl")
#OPENSSL="openssl"
# Extra options passed to the curl binary (default: <unset>)
#CURL_OPTS=
# Program or function called in certain situations
#
# After generating the challenge-response, or after failed challenge (in this case altname is empty)
@@ -89,3 +102,15 @@
# Option to add CSR-flag indicating OCSP stapling to be mandatory (default: no)
#OCSP_MUST_STAPLE="no"
# Fetch OCSP responses (default: no)
#OCSP_FETCH="no"
# Issuer chain cache directory (default: $BASEDIR/chains)
#CHAINCACHE="${BASEDIR}/chains"
# Automatic cleanup (default: no)
#AUTO_CLEANUP="no"
# ACME API version (default: auto)
#API=auto

View File

@@ -19,6 +19,9 @@ deploy_challenge() {
# 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() {
@@ -29,6 +32,9 @@ clean_challenge() {
# 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
}
deploy_cert() {
@@ -52,6 +58,10 @@ deploy_cert() {
# 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() {
@@ -86,12 +96,15 @@ invalid_challenge() {
# 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 a HTTP request fails (e.g., when the ACME
# 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.
@@ -103,16 +116,51 @@ request_failure() {
# The specified reason for the error.
# - REQTYPE
# The kind of request that was made (GET, POST...)
# 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 cerificate 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() {
# This hook is called before the cron command to do some initial tasks
# (e.g. starting a webserver).
:
}
exit_hook() {
# This hook is called at the end of a dehydrated command and can be used
# to do some final (cleanup or other) tasks.
# This hook is called at the end of the cron command and can be used to
# do some final (cleanup or other) tasks.
:
}
HANDLER="$1"; shift
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|exit_hook)$ ]]; then
if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|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!
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 42 KiB

155
docs/man/dehydrated.1 Normal file
View File

@@ -0,0 +1,155 @@
.TH DEHYDRATED 1 2018-01-13 "Dehydrated ACME Client"
.SH NAME
dehydrated \- ACME client implemented as a shell-script
.SH SYNOPSIS
.B dehydrated
[\fBcommand\fR [\fBargument\fR]]
[\fBargument\fR [\fBargument\fR]]
.IR ...
.SH DESCRIPTION
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
to acquire an account with the Certificate Authorities. Optionally, an email
address can be provided. It will be used to e.g. notify about expiring
certificates. You will usually need to accept the Terms of Service of the CA.
Dehydrated will notify if no account is configured. Run with \fB--register
--accept-terms\fR to create a new account.
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
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.
The \fIhttp-01\fR verification provides proof of ownership by providing a
challenge token. In order to do that, the directory referenced in the
\fIWELLKNOWN\fR config variable needs to be exposed at
\fIhttp://{domain}/.well-known/acme-challenge/\fR, where {domain} is every
domain name specified in \fIdomains.txt\fR. Dehydrated does not provide its
own challenge responder, but relies on an existing web server to provide the
challenge response. See \fIwellknown.md\fR for configuration examples of
popular web servers.
The \fIdns-01\fR verification works by providing a challenge token through DNS.
This is especially interesting for hosts that cannot be exposed to the public
Internet. Because adding records to DNS zones is oftentimes highly specific to
the software or the DNS provider at hand, there are many third party hooks
available for dehydrated. See \fIdns-verification.md\fR for hooks for popular
DNS servers and DNS hosters.
Finally, the certificates need to be requested and updated on a regular basis.
This can happen through a cron job or a timer. Initially, you may enforce this
by invoking \fIdehydrated -c\fR manually.
After a successful run, certificates are stored in
\fI/etc/dehydrated/certs/{domain}\fR, where {domain} is the domain name in the
first column of \fIdomains.txt\fR.
.SH OPTIONS
.BR Commands
.TP
.BR \-\-version ", " \-v
Print version information
.TP
.BR \-\-register
Register account key
.TP
.BR \-\-account
Update account contact information
.TP
.BR \-\-cron ", " \-c
Sign/renew non\-existent/changed/expiring certificates.
.TP
.BR \-\-signcsr ", " \-s " " \fIpath/to/csr.pem\fR
Sign a given CSR, output CRT on stdout (advanced usage)
.TP
.BR \-\-revoke ", " \-r " " \fIpath/to/cert.pem\fR
Revoke specified certificate
.TP
.BR \-\-cleanup ", " \-gc
Move unused certificate files to archive directory
.TP
.BR \-\-help ", " \-h
Show help text
.TP
.BR \-\-env ", " \-e
Output configuration variables for use in other scripts
.PP
.BR Parameters
.TP
.BR \-\-accept\-terms
Accept CAs terms of service
.TP
.BR \-\-full\-chain ", " \-fc
Print full chain when using \fB\-\-signcsr\fR
.TP
.BR \-\-ipv4 ", " \-4
Resolve names to IPv4 addresses only
.TP
.BR \-\-ipv6 ", " \-6
Resolve names to IPv6 addresses only
.TP
.BR \-\-domain ", " \-d " " \fIdomain.tld\fR
Use specified domain name(s) instead of domains.txt entry (one certificate!)
.TP
.BR \-\-keep\-going ", " \-g
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
.TP
.BR \-\-no\-lock ", " \-n
Don't use lockfile (potentially dangerous!)
.TP
.BR \-\-lock\-suffix " " \fIexample.com\fR
Suffix lockfile name with a string (useful for use with \-d)
.TP
.BR \-\-ocsp
Sets option in CSR indicating OCSP stapling to be mandatory
.TP
.BR \-\-privkey ", " \-p " " \fIpath/to/key.pem\fR
Use specified private key instead of account key (useful for revocation)
.TP
.BR \-\-config ", " \-f " " \fIpath/to/config\fR
Use specified config file
.TP
.BR \-\-hook ", " \-k " " \fIpath/to/hook.sh\fR
Use specified script for hooks
.TP
.BR \-\-out ", " \-o " " \fIcerts/directory\fR
Output certificates into the specified directory
.TP
.BR \-\-challenge ", " \-t " " \fI[http\-01|dns\-01]\fR
Which challenge should be used? Currently http\-01 and dns\-01 are supported
.TP
.BR \-\-algo ", " \-a " " \fI[rsa|prime256v1|secp384r1]\fR
Which public key algorithm should be used? Supported: rsa, prime256v1 and
secp384r1
.SH DIAGNOSTICS
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
.UE .
.SH AUTHOR
Dehydrated was written by Lukas Schauer. This man page was contributed by
Daniel Molkentin.
.SH COPYRIGHT
Copyright 20015-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
.UE .

View File

@@ -16,3 +16,10 @@ Currently supported options:
- WELLKNOWN
- OPENSSL_CNF
- RENEW_DAYS
## DOMAINS_D
If `DOMAINS_D` is set, dehydrated will use it for your per-certificate configurations.
Instead of `certs/example.org/config` it will look for a configuration under `DOMAINS_D/example.org`.
If an alias is set, it will be used instead of the primary domain name.

View File

@@ -9,5 +9,9 @@ To avoid this, please set the CA property to the Lets Encrypt staging server
```bash
CA="https://acme-staging.api.letsencrypt.org/directory"
CA_TERMS="https://acme-staging.api.letsencrypt.org/terms"
```
# ACMEv2 staging
You can use `CA="https://acme-staging-v02.api.letsencrypt.org/directory"` to test dehydrated with
the ACMEv2 staging endpoint.

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).
@@ -31,8 +25,8 @@ This also is an API limit from boulder, you are requesting to sign a certificate
There are a few factors that could result in invalid challenges.
If you are using http validation make sure that the path you have configured with WELLKNOWN is readable under your domain.
If you are using HTTP validation make sure that the path you have configured with WELLKNOWN is readable under your domain.
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`.
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 webserver configuration.
If you get any error you'll have to fix your web server configuration.

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;
}
[...]
@@ -65,3 +65,14 @@ alias.url += (
"/.well-known/acme-challenge/" => "/var/www/dehydrated/",
)
```
### Hiawatha example config
With Hiawatha just add an alias to your config file for each VirtualHost and it should work:
```hiawatha
VirtualHost {
Hostname = example.tld subdomain.mywebsite.tld
Alias = /.well-known/acme-challenge:/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