46 Commits

Author SHA1 Message Date
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
15 changed files with 538 additions and 555 deletions

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,6 +1,16 @@
# Change Log
This file contains a log of major changes in dehydrated
## [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-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
# 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.
@@ -78,31 +79,22 @@ Parameters:
## Donate
I'm having fun developing dehydrated, but it takes time, and time is money, at least that's what I've been told.
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 will definitively continue developing dehydrated for free, but if you want to support me you can do so using the following ways:
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).
### PayPal
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).
[![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) :)
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

File diff suppressed because it is too large Load Diff

View File

@@ -17,3 +17,6 @@ You can define an alias for your certificate which will (instead of the primary
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

@@ -24,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"
@@ -110,3 +111,6 @@
# 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,10 +96,13 @@ 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 an HTTP request fails (e.g., when the ACME
# server is busy, returns an error, etc). It will be called upon any
@@ -103,6 +116,34 @@ 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() {
@@ -120,6 +161,6 @@ exit_hook() {
}
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|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

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
@@ -145,7 +145,7 @@ Please report any bugs that you may encounter at the project web site
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 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

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).

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