mirror of
https://github.com/dehydrated-io/dehydrated.git
synced 2026-04-23 16:58:28 +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
|
## [x.x.x] - xxxx-xx-xx
|
||||||
## Changed
|
## Changed
|
||||||
|
- dehydrated now asks you to read and accept the CAs terms of service before creating an account
|
||||||
- Skip challenges for already validated domains
|
- Skip challenges for already validated domains
|
||||||
- Removed need for some special commands (BusyBox compatibility)
|
- Removed need for some special commands (BusyBox compatibility)
|
||||||
- Exported a few more variables for use in hook-scripts
|
- 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
|
||||||
- Added private-key rollover functionality
|
- Added private-key rollover functionality
|
||||||
@@ -13,6 +15,7 @@ This file contains a log of major changes in dehydrated
|
|||||||
- Added `invalid_challenge` hook
|
- Added `invalid_challenge` hook
|
||||||
- Added `request_failure` hook
|
- Added `request_failure` hook
|
||||||
- Added `exit_hook` hook
|
- Added `exit_hook` hook
|
||||||
|
- Added standalone `register` command
|
||||||
|
|
||||||
## [0.3.1] - 2016-09-13
|
## [0.3.1] - 2016-09-13
|
||||||
## Changed
|
## Changed
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ Usage: ./dehydrated [-h] [command [argument]] [parameter [argument]] [parameter
|
|||||||
Default command: help
|
Default command: help
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|
--register Register account key
|
||||||
--cron (-c) Sign/renew non-existant/changed/expiring certificates.
|
--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)
|
--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
|
--revoke (-r) path/to/cert.pem Revoke specified certificate
|
||||||
@@ -54,6 +55,7 @@ Commands:
|
|||||||
--env (-e) Output configuration variables for use in other scripts
|
--env (-e) Output configuration variables for use in other scripts
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
--accept-terms Accept CAs terms of service
|
||||||
--full-chain (-fc) Print full chain when using --signcsr
|
--full-chain (-fc) Print full chain when using --signcsr
|
||||||
--ipv4 (-4) Resolve names to IPv4 addresses only
|
--ipv4 (-4) Resolve names to IPv4 addresses only
|
||||||
--ipv6 (-6) Resolve names to IPv6 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
|
--keep-going (-g) Keep going after encountering an error while creating/renewing multiple certificates in cron mode
|
||||||
--force (-x) Force renew of certificate even if it is longer valid than value in RENEW_DAYS
|
--force (-x) Force renew of certificate even if it is longer valid than value in RENEW_DAYS
|
||||||
--no-lock (-n) Don't use lockfile (potentially dangerous!)
|
--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
|
--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)
|
--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
|
--config (-f) path/to/config Use specified config file
|
||||||
|
|||||||
125
dehydrated
125
dehydrated
@@ -105,7 +105,8 @@ load_config() {
|
|||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
CA="https://acme-v01.api.letsencrypt.org/directory"
|
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=
|
CERTDIR=
|
||||||
ACCOUNTDIR=
|
ACCOUNTDIR=
|
||||||
CHALLENGETYPE="http-01"
|
CHALLENGETYPE="http-01"
|
||||||
@@ -233,6 +234,24 @@ init_system() {
|
|||||||
else
|
else
|
||||||
# Check if private account key exists, if it doesn't exist yet generate a new one (rsa key)
|
# Check if private account key exists, if it doesn't exist yet generate a new one (rsa key)
|
||||||
if [[ ! -e "${ACCOUNT_KEY}" ]]; then
|
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..."
|
echo "+ Generating account key..."
|
||||||
_openssl genrsa -out "${ACCOUNT_KEY}" "${KEYSIZE}"
|
_openssl genrsa -out "${ACCOUNT_KEY}" "${KEYSIZE}"
|
||||||
register_new_key="yes"
|
register_new_key="yes"
|
||||||
@@ -360,29 +379,31 @@ http_request() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! "${statuscode:0:1}" = "2" ]]; then
|
if [[ ! "${statuscode:0:1}" = "2" ]]; then
|
||||||
echo " + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
|
if [[ ! "${2}" = "${CA_TERMS}" ]] || [[ ! "${statuscode:0:1}" = "3" ]]; then
|
||||||
echo >&2
|
echo " + ERROR: An error occurred while sending ${1}-request to ${2} (Status ${statuscode})" >&2
|
||||||
echo "Details:" >&2
|
echo >&2
|
||||||
cat "${tempcont}" >&2
|
echo "Details:" >&2
|
||||||
echo >&2
|
cat "${tempcont}" >&2
|
||||||
echo >&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)
|
# 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
|
if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
|
||||||
errtxt=`cat ${tempcont}`
|
errtxt=`cat ${tempcont}`
|
||||||
"${HOOK}" "request_failure" "${statuscode}" "${errtxt}" "${1}"
|
"${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
|
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
|
fi
|
||||||
|
|
||||||
cat "${tempcont}"
|
cat "${tempcont}"
|
||||||
@@ -600,6 +621,39 @@ sign_csr() {
|
|||||||
echo " + Done!"
|
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)
|
# Create certificate for domain(s)
|
||||||
sign_domain() {
|
sign_domain() {
|
||||||
domain="${1}"
|
domain="${1}"
|
||||||
@@ -672,14 +726,7 @@ sign_domain() {
|
|||||||
# Create fullchain.pem
|
# Create fullchain.pem
|
||||||
echo " + Creating fullchain.pem..."
|
echo " + Creating fullchain.pem..."
|
||||||
cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
cat "${crt_path}" > "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
||||||
tmpchain="$(_mktemp)"
|
walk_chain "${crt_path}" > "${CERTDIR}/${domain}/chain-${timestamp}.pem"
|
||||||
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
|
|
||||||
cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
cat "${CERTDIR}/${domain}/chain-${timestamp}.pem" >> "${CERTDIR}/${domain}/fullchain-${timestamp}.pem"
|
||||||
|
|
||||||
# Update symlinks
|
# Update symlinks
|
||||||
@@ -697,6 +744,13 @@ sign_domain() {
|
|||||||
echo " + Done!"
|
echo " + Done!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Usage: --register
|
||||||
|
# Description: Register account key
|
||||||
|
command_register() {
|
||||||
|
init_system
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
# Usage: --cron (-c)
|
# Usage: --cron (-c)
|
||||||
# Description: Sign/renew non-existant/changed/expiring certificates.
|
# Description: Sign/renew non-existant/changed/expiring certificates.
|
||||||
command_sign_domains() {
|
command_sign_domains() {
|
||||||
@@ -1024,6 +1078,16 @@ main() {
|
|||||||
set_command sign_domains
|
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)
|
--signcsr|-s)
|
||||||
shift 1
|
shift 1
|
||||||
set_command sign_csr
|
set_command sign_csr
|
||||||
@@ -1166,6 +1230,7 @@ main() {
|
|||||||
case "${COMMAND}" in
|
case "${COMMAND}" in
|
||||||
env) command_env;;
|
env) command_env;;
|
||||||
sign_domains) command_sign_domains;;
|
sign_domains) command_sign_domains;;
|
||||||
|
register) command_register;;
|
||||||
sign_csr) command_sign_csr "${PARAM_CSR}";;
|
sign_csr) command_sign_csr "${PARAM_CSR}";;
|
||||||
revoke) command_revoke "${PARAM_REVOKECERT}";;
|
revoke) command_revoke "${PARAM_REVOKECERT}";;
|
||||||
cleanup) command_cleanup;;
|
cleanup) command_cleanup;;
|
||||||
|
|||||||
@@ -18,8 +18,11 @@
|
|||||||
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
|
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
|
||||||
#CA="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)
|
# Path to certificate authority license terms redirect (default: https://acme-v01.api.letsencrypt.org/terms)
|
||||||
#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
|
#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
|
# Which challenge should be used? Currently http-01 and dns-01 are supported
|
||||||
#CHALLENGETYPE="http-01"
|
#CHALLENGETYPE="http-01"
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ To avoid this, please set the CA property to the Let’s Encrypt staging server
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
CA="https://acme-staging.api.letsencrypt.org/directory"
|
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
|
# Generate config and create empty domains.txt
|
||||||
echo 'CA="https://testca.kurz.pw/directory"' > config
|
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 'WELLKNOWN=".acme-challenges/.well-known/acme-challenge"' >> config
|
||||||
echo 'RENEW_DAYS="14"' >> config
|
echo 'RENEW_DAYS="14"' >> config
|
||||||
touch domains.txt
|
touch domains.txt
|
||||||
@@ -110,6 +110,23 @@ _CHECK_LOG "--help (-h)"
|
|||||||
_CHECK_LOG "--domain (-d) domain.tld"
|
_CHECK_LOG "--domain (-d) domain.tld"
|
||||||
_CHECK_ERRORLOG
|
_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)
|
# 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"
|
_TEST "First run in cron mode, checking if private key is generated and registered"
|
||||||
./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
|
./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
|
# 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"
|
_TEST "Try signing using temporary config location and with domain as command line parameter"
|
||||||
mv config tmp_config
|
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_NOT_LOG "Checking domain name(s) of existing cert"
|
||||||
_CHECK_LOG "Generating private key"
|
_CHECK_LOG "Generating private key"
|
||||||
_CHECK_LOG "Requesting challenge for ${TMP_URL}"
|
_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 ${TMP_URL}"
|
||||||
_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
|
_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
|
||||||
_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
|
_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
|
||||||
_CHECK_LOG "Challenge is valid!"
|
_CHECK_LOG "Already validated!"
|
||||||
_CHECK_LOG "Creating fullchain.pem"
|
_CHECK_LOG "Creating fullchain.pem"
|
||||||
_CHECK_LOG "Done!"
|
_CHECK_LOG "Done!"
|
||||||
_CHECK_ERRORLOG
|
_CHECK_ERRORLOG
|
||||||
|
|||||||
Reference in New Issue
Block a user