mirror of
https://github.com/dehydrated-io/dehydrated.git
synced 2026-03-13 05:35:16 +01:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70d261a729 | ||
|
|
947dbb9e29 | ||
|
|
8a414e55bc | ||
|
|
fd3fc8af62 | ||
|
|
6e802ddc19 | ||
|
|
0211d24577 | ||
|
|
68274646bb | ||
|
|
c0bcf91410 | ||
|
|
a91074b707 | ||
|
|
a6a07779ad | ||
|
|
a6e6aa7445 | ||
|
|
dcdb2940fb | ||
|
|
0ade30cc74 | ||
|
|
bc34f3aa86 | ||
|
|
5940c55e18 | ||
|
|
2eedd69ee9 | ||
|
|
be252c7db9 | ||
|
|
9ebab3e026 | ||
|
|
ad291207d0 | ||
|
|
a7b2af2b92 | ||
|
|
082ed17a0a | ||
|
|
dec5ad5840 | ||
|
|
da67297288 | ||
|
|
83bf2664b0 | ||
|
|
63854b752b | ||
|
|
901f9f76e2 | ||
|
|
b5de2e26eb | ||
|
|
73a116e879 | ||
|
|
9c35fce61e | ||
|
|
87194f6596 | ||
|
|
62d37c9b3d | ||
|
|
b53cb6643b | ||
|
|
fb41783885 | ||
|
|
0bc0bd13d6 | ||
|
|
6d02bfdb42 | ||
|
|
727443483d | ||
|
|
7a0e71c6c2 | ||
|
|
45f5c17260 | ||
|
|
61083cf522 | ||
|
|
afba7c694c | ||
|
|
471899b4d8 | ||
|
|
ec5dbcc816 | ||
|
|
0f69481e2b | ||
|
|
6f3fed496d | ||
|
|
5fd93ea874 | ||
|
|
656af8cadc | ||
|
|
3e521e1c01 | ||
|
|
68cb1e0661 | ||
|
|
35a9f31643 | ||
|
|
4a811759dc | ||
|
|
2adc57791c | ||
|
|
f35aed6ae6 | ||
|
|
b6b56d0df7 | ||
|
|
13c853d43b | ||
|
|
c62f3d91fc | ||
|
|
eb1c4ac41d | ||
|
|
3ec54e7e0f | ||
|
|
88267db7e2 | ||
|
|
eb4aaefda1 | ||
|
|
3d97799d6a | ||
|
|
742c0ad176 | ||
|
|
7f410e9bff | ||
|
|
da3428a84a | ||
|
|
b5e178ea75 | ||
|
|
bc20ec79f3 | ||
|
|
ce9b42d8ad | ||
|
|
f838d93f40 | ||
|
|
0be0ab083f | ||
|
|
58647cab65 | ||
|
|
c57ad87e7c | ||
|
|
2687054d25 | ||
|
|
2b76d038d3 | ||
|
|
e339b28159 | ||
|
|
4f3bd3e956 | ||
|
|
f86290ea52 | ||
|
|
f1bc2b14ba | ||
|
|
367ef574f1 | ||
|
|
4e7fb80bcd | ||
|
|
ee75c5dca7 | ||
|
|
82ca3ffcd3 | ||
|
|
bb99742aa7 | ||
|
|
60583d3ef9 | ||
|
|
cbb661ca17 | ||
|
|
67cf20765c | ||
|
|
89377a1004 | ||
|
|
dc600e39b8 | ||
|
|
16e91b415b | ||
|
|
e6d6882c78 | ||
|
|
14a5f63077 | ||
|
|
5787cd6a47 | ||
|
|
875c1f74e5 | ||
|
|
db18820991 | ||
|
|
2f775d0e2a | ||
|
|
f2b589430c | ||
|
|
533aa80129 | ||
|
|
d1f215b652 | ||
|
|
bd57777c62 | ||
|
|
ba31a505d2 | ||
|
|
0dcf94dd3d | ||
|
|
9ea75e7cfb | ||
|
|
33c77e6daa | ||
|
|
d685463673 | ||
|
|
98ad01a110 | ||
|
|
8709d21ef2 | ||
|
|
6ebaae416c | ||
|
|
7fc4040f47 | ||
|
|
ec1599e3b6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ hook.sh
|
||||
certs/*
|
||||
archive/*
|
||||
accounts/*
|
||||
chains/*
|
||||
|
||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
||||
sudo: false
|
||||
language: shell
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ngrok
|
||||
|
||||
script:
|
||||
- export CI="true"
|
||||
- ./test.sh
|
||||
27
CHANGELOG
27
CHANGELOG
@@ -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
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -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
|
||||
|
||||
32
README.md
32
README.md
@@ -1,8 +1,9 @@
|
||||
# dehydrated [](https://travis-ci.org/lukas2511/dehydrated)
|
||||
# dehydrated [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=23P9DSJBTY7C8)
|
||||
|
||||

|
||||
|
||||
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
1027
dehydrated
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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 `*.`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
```
|
||||
|
||||
|
||||
BIN
docs/logo.jpg
BIN
docs/logo.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 42 KiB |
155
docs/man/dehydrated.1
Normal file
155
docs/man/dehydrated.1
Normal 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 .
|
||||
@@ -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.
|
||||
|
||||
@@ -9,5 +9,9 @@ To avoid this, please set the CA property to the Let’s 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
266
test.sh
@@ -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
|
||||
Reference in New Issue
Block a user