mirror of
https://github.com/dehydrated-io/dehydrated.git
synced 2026-05-08 16:13:35 +02:00
ask user to read and accept license, added register-command, fullchain.pem is now actually the full chain
This commit is contained in:
@@ -3,9 +3,11 @@ This file contains a log of major changes in dehydrated
|
||||
|
||||
## [x.x.x] - xxxx-xx-xx
|
||||
## Changed
|
||||
- dehydrated now asks you to read and accept the CAs terms of service before creating an account
|
||||
- Skip challenges for already validated domains
|
||||
- Removed need for some special commands (BusyBox compatibility)
|
||||
- Exported a few more variables for use in hook-scripts
|
||||
- fullchain.pem now actually contains the full chain instead of just the certificate with an intermediate cert
|
||||
|
||||
## Added
|
||||
- Added private-key rollover functionality
|
||||
@@ -13,6 +15,7 @@ This file contains a log of major changes in dehydrated
|
||||
- Added `invalid_challenge` hook
|
||||
- Added `request_failure` hook
|
||||
- Added `exit_hook` hook
|
||||
- Added standalone `register` command
|
||||
|
||||
## [0.3.1] - 2016-09-13
|
||||
## Changed
|
||||
|
||||
@@ -46,6 +46,7 @@ Usage: ./dehydrated [-h] [command [argument]] [parameter [argument]] [parameter
|
||||
Default command: help
|
||||
|
||||
Commands:
|
||||
--register Register account key
|
||||
--cron (-c) Sign/renew non-existant/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
|
||||
@@ -54,6 +55,7 @@ Commands:
|
||||
--env (-e) Output configuration variables for use in other scripts
|
||||
|
||||
Parameters:
|
||||
--accept-terms Accept CAs terms of service
|
||||
--full-chain (-fc) Print full chain when using --signcsr
|
||||
--ipv4 (-4) Resolve names to IPv4 addresses only
|
||||
--ipv6 (-6) Resolve names to IPv6 addresses only
|
||||
@@ -61,6 +63,7 @@ Parameters:
|
||||
--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!)
|
||||
--lock-suffix example.com Suffix lockfile name with a string (useful for with -d)
|
||||
--ocsp Sets option in CSR indicating OCSP stapling to be mandatory
|
||||
--privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation)
|
||||
--config (-f) path/to/config Use specified config file
|
||||
|
||||
125
dehydrated
125
dehydrated
@@ -105,7 +105,8 @@ load_config() {
|
||||
|
||||
# Default values
|
||||
CA="https://acme-v01.api.letsencrypt.org/directory"
|
||||
LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
|
||||
CA_TERMS="https://acme-v01.api.letsencrypt.org/terms"
|
||||
LICENSE=
|
||||
CERTDIR=
|
||||
ACCOUNTDIR=
|
||||
CHALLENGETYPE="http-01"
|
||||
@@ -233,6 +234,24 @@ init_system() {
|
||||
else
|
||||
# Check if private account key exists, if it doesn't exist yet generate a new one (rsa key)
|
||||
if [[ ! -e "${ACCOUNT_KEY}" ]]; then
|
||||
REAL_LICENSE="$(http_request head "${CA_TERMS}" | (grep Location: || true) | awk -F ': ' '{print $2}' | tr -d '\n\r')"
|
||||
if [[ -z "${REAL_LICENSE}" ]]; then
|
||||
printf '\n'
|
||||
printf 'Error retrieving terms of service from certificate authority.\n'
|
||||
printf 'Please set LICENSE in config manually.\n'
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${LICENSE}" = "${REAL_LICENSE}" ]]; then
|
||||
if [[ "${PARAM_ACCEPT_TERMS:-}" = "yes" ]]; then
|
||||
LICENSE="${REAL_LICENSE}"
|
||||
else
|
||||
printf '\n'
|
||||
printf 'To use dehydrated with this certificate authority you have to agree to their terms of service which you can find here: %s\n\n' "${REAL_LICENSE}"
|
||||
printf 'To accept these terms of service run `%s --register --accept-terms`.\n' "${0}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "+ Generating account key..."
|
||||
_openssl genrsa -out "${ACCOUNT_KEY}" "${KEYSIZE}"
|
||||
register_new_key="yes"
|
||||
@@ -360,29 +379,31 @@ http_request() {
|
||||
fi
|
||||
|
||||
if [[ ! "${statuscode:0:1}" = "2" ]]; then
|
||||
echo " + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
|
||||
echo >&2
|
||||
echo "Details:" >&2
|
||||
cat "${tempcont}" >&2
|
||||
echo >&2
|
||||
echo >&2
|
||||
if [[ ! "${2}" = "${CA_TERMS}" ]] || [[ ! "${statuscode:0:1}" = "3" ]]; then
|
||||
echo " + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
|
||||
echo >&2
|
||||
echo "Details:" >&2
|
||||
cat "${tempcont}" >&2
|
||||
echo >&2
|
||||
echo >&2
|
||||
|
||||
# An exclusive hook for the {1}-request error might be useful (e.g., for sending an e-mail to admins)
|
||||
if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
|
||||
errtxt=`cat ${tempcont}`
|
||||
"${HOOK}" "request_failure" "${statuscode}" "${errtxt}" "${1}"
|
||||
# An exclusive hook for the {1}-request error might be useful (e.g., for sending an e-mail to admins)
|
||||
if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
|
||||
errtxt=`cat ${tempcont}`
|
||||
"${HOOK}" "request_failure" "${statuscode}" "${errtxt}" "${1}"
|
||||
fi
|
||||
|
||||
rm -f "${tempcont}"
|
||||
|
||||
# Wait for hook script to clean the challenge if used
|
||||
if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
|
||||
"${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
|
||||
fi
|
||||
|
||||
# remove temporary domains.txt file if used
|
||||
[[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "${tempcont}"
|
||||
|
||||
# Wait for hook script to clean the challenge if used
|
||||
if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token:+set}" ]]; then
|
||||
"${HOOK}" "clean_challenge" '' "${challenge_token}" "${keyauth}"
|
||||
fi
|
||||
|
||||
# remove temporary domains.txt file if used
|
||||
[[ -n "${PARAM_DOMAIN:-}" && -n "${DOMAINS_TXT:-}" ]] && rm "${DOMAINS_TXT}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat "${tempcont}"
|
||||
@@ -600,6 +621,39 @@ sign_csr() {
|
||||
echo " + Done!"
|
||||
}
|
||||
|
||||
walk_chain() {
|
||||
certificate="${1}"
|
||||
|
||||
# grep uri from certificate
|
||||
local issuer_cert_uri
|
||||
issuer_cert_uri="$(openssl x509 -in "${certificate}" -noout -text | (grep 'CA Issuers - URI:' | cut -d':' -f2-) || true)"
|
||||
if [[ -n "${issuer_cert_uri}" ]]; then
|
||||
# create temporary files
|
||||
local tmpcert
|
||||
local tmpcert_raw
|
||||
tmpcert_raw="$(_mktemp)"
|
||||
tmpcert="$(_mktemp)"
|
||||
|
||||
# download certificate
|
||||
http_request get "${issuer_cert_uri}" > "${tmpcert_raw}"
|
||||
|
||||
# PEM
|
||||
if grep -q "BEGIN CERTIFICATE" "${tmpcert_raw}"; then mv "${tmpcert_raw}" "${tmpcert}"
|
||||
# DER
|
||||
elif openssl x509 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM 2> /dev/null > /dev/null; then :
|
||||
# PKCS7
|
||||
elif openssl pkcs7 -in "${tmpcert_raw}" -inform DER -out "${tmpcert}" -outform PEM -print_certs 2> /dev/null > /dev/null; then :
|
||||
# Unknown certificate type
|
||||
else _exiterr "Unknown certificate type in chain"
|
||||
fi
|
||||
|
||||
printf "\n%s\n" "${issuer_cert_uri}"
|
||||
cat "${tmpcert}"
|
||||
walk_chain "${tmpcert}"
|
||||
rm -f "${tmpcert}" "${tmpcert_raw}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create certificate for domain(s)
|
||||
sign_domain() {
|
||||
domain="${1}"
|
||||
@@ -672,14 +726,7 @@ sign_domain() {
|
||||
# Create fullchain.pem
|
||||
echo " + Creating fullchain.pem..."
|
||||
cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
||||
tmpchain="$(_mktemp)"
|
||||
http_request get "$(openssl x509 -in "${CERTDIR}/${domain}/cert-${timestamp}.pem" -noout -text | grep 'CA Issuers - URI:' | cut -d':' -f2-)" > "${tmpchain}"
|
||||
if grep -q "BEGIN CERTIFICATE" "${tmpchain}"; then
|
||||
mv "${tmpchain}" "${CERTDIR}/${domain}/chain-${timestamp}.pem"
|
||||
else
|
||||
openssl x509 -in "${tmpchain}" -inform DER -out "${CERTDIR}/${domain}/chain-${timestamp}.pem" -outform PEM
|
||||
rm "${tmpchain}"
|
||||
fi
|
||||
walk_chain "${crt_path}" > "${CERTDIR}/${domain}/chain-${timestamp}.pem"
|
||||
cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
||||
|
||||
# Update symlinks
|
||||
@@ -697,6 +744,13 @@ sign_domain() {
|
||||
echo " + Done!"
|
||||
}
|
||||
|
||||
# Usage: --register
|
||||
# Description: Register account key
|
||||
command_register() {
|
||||
init_system
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Usage: --cron (-c)
|
||||
# Description: Sign/renew non-existant/changed/expiring certificates.
|
||||
command_sign_domains() {
|
||||
@@ -1024,6 +1078,16 @@ main() {
|
||||
set_command sign_domains
|
||||
;;
|
||||
|
||||
--register)
|
||||
set_command register
|
||||
;;
|
||||
|
||||
# PARAM_Usage: --accept-terms
|
||||
# PARAM_Description: Accept CAs terms of service
|
||||
--accept-terms)
|
||||
PARAM_ACCEPT_TERMS="yes"
|
||||
;;
|
||||
|
||||
--signcsr|-s)
|
||||
shift 1
|
||||
set_command sign_csr
|
||||
@@ -1166,6 +1230,7 @@ main() {
|
||||
case "${COMMAND}" in
|
||||
env) command_env;;
|
||||
sign_domains) command_sign_domains;;
|
||||
register) command_register;;
|
||||
sign_csr) command_sign_csr "${PARAM_CSR}";;
|
||||
revoke) command_revoke "${PARAM_REVOKECERT}";;
|
||||
cleanup) command_cleanup;;
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
|
||||
#CA="https://acme-v01.api.letsencrypt.org/directory"
|
||||
|
||||
# Path to license agreement (default: https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf)
|
||||
#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
|
||||
# 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=""
|
||||
|
||||
# Which challenge should be used? Currently http-01 and dns-01 are supported
|
||||
#CHALLENGETYPE="http-01"
|
||||
|
||||
@@ -9,4 +9,5 @@ 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"
|
||||
```
|
||||
|
||||
23
test.sh
23
test.sh
@@ -97,7 +97,7 @@ mkdir -p .acme-challenges/.well-known/acme-challenge
|
||||
|
||||
# Generate config and create empty domains.txt
|
||||
echo 'CA="https://testca.kurz.pw/directory"' > config
|
||||
echo 'LICENSE="https://testca.kurz.pw/terms/v1"' >> 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
|
||||
@@ -110,6 +110,23 @@ _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"
|
||||
@@ -120,7 +137,7 @@ _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}" -f tmp_config > tmplog 2> errorlog || _FAIL "Script execution failed"
|
||||
./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}"
|
||||
@@ -168,7 +185,7 @@ _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 "Challenge is valid!"
|
||||
_CHECK_LOG "Already validated!"
|
||||
_CHECK_LOG "Creating fullchain.pem"
|
||||
_CHECK_LOG "Done!"
|
||||
_CHECK_ERRORLOG
|
||||
|
||||
Reference in New Issue
Block a user