private_key.pem
private_key.json
domains.txt
-config.sh
+config
hook.sh
certs/*
archive/*
## [x.x.x] - xxxx-xx-xx
## Changed
+- Config is now named `config` instead of `config.sh`!
- Location of domains.txt is now configurable via DOMAINS_TXT config variable
## [0.2.0] - 2016-05-22
--domain (-d) domain.tld Use specified domain name(s) instead of domains.txt entry (one certificate!)
--force (-x) Force renew of certificate even if it is longer valid than value in RENEW_DAYS
--privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation)
- --config (-f) path/to/config.sh Use specified config file
+ --config (-f) path/to/config Use specified config file
--hook (-k) path/to/hook.sh Use specified script for hooks
--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
You need a hook script that deploys the challenge to your DNS server!
-The hook script (indicated in the config.sh file or the --hook/-k command line argument) gets four arguments: an operation name (clean_challenge, deploy_challenge, or deploy_cert) 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: an operation name (clean_challenge, deploy_challenge, or deploy_cert) 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
--- /dev/null
+########################################################
+# This is the main config file for letsencrypt.sh #
+# #
+# This file is looked for in the following locations: #
+# $SCRIPTDIR/config (next to this script) #
+# /usr/local/etc/letsencrypt.sh/config #
+# /etc/letsencrypt.sh/config #
+# ${PWD}/config (in current working-directory) #
+# #
+# Default values of this config are in comments #
+########################################################
+
+# 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.0.1-July-27-2015.pdf)
+#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
+
+# Which challenge should be used? Currently http-01 and dns-01 are supported
+#CHALLENGETYPE="http-01"
+
+# Path to a directory containing additional config files, allowing to override
+# the defaults found in the main configuration file. Additional config files
+# in this directory needs to be named with a '.sh' ending.
+# default: <unset>
+#CONFIG_D=
+
+# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
+#BASEDIR=$SCRIPTDIR
+
+# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
+#DOMAINS_TXT="${BASEDIR}/domains.txt"
+
+# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges)
+#WELLKNOWN="${BASEDIR}/.acme-challenges"
+
+# Location of private account key (default: $BASEDIR/private_key.pem)
+#ACCOUNT_KEY="${BASEDIR}/private_key.pem"
+
+# Location of private account registration information (default: $BASEDIR/private_key.json)
+#ACCOUNT_KEY_JSON="${BASEDIR}/private_key.json"
+
+# Default keysize for private keys (default: 4096)
+#KEYSIZE="4096"
+
+# Path to openssl config file (default: <unset> - tries to figure out system default)
+#OPENSSL_CNF=
+
+# Program or function called in certain situations
+#
+# After generating the challenge-response, or after failed challenge (in this case altname is empty)
+# Given arguments: clean_challenge|deploy_challenge altname token-filename token-content
+#
+# After successfully signing certificate
+# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem path/to/fullchain.pem
+#
+# BASEDIR and WELLKNOWN variables are exported and can be used in an external program
+# default: <unset>
+#HOOK=
+
+# Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no)
+#HOOK_CHAIN="no"
+
+# Minimum days before expiration to automatically renew certificate (default: 30)
+#RENEW_DAYS="30"
+
+# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
+#PRIVATE_KEY_RENEW="yes"
+
+# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
+#KEY_ALGO=rsa
+
+# E-mail to use during the registration (default: <unset>)
+#CONTACT_EMAIL=
+
+# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
+#LOCKFILE="${BASEDIR}/lock"
+++ /dev/null
-########################################################
-# This is the main config file for letsencrypt.sh #
-# #
-# This file is looked for in the following locations: #
-# $SCRIPTDIR/config.sh (next to this script) #
-# /usr/local/etc/letsencrypt.sh/config.sh #
-# /etc/letsencrypt.sh/config.sh #
-# ${PWD}/config.sh (in current working-directory) #
-# #
-# Default values of this config are in comments #
-########################################################
-
-# 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.0.1-July-27-2015.pdf)
-#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
-
-# Which challenge should be used? Currently http-01 and dns-01 are supported
-#CHALLENGETYPE="http-01"
-
-# Path to a directory containing additional config files, allowing to override
-# the defaults found in the main configuration file. Additional config files
-# in this directory needs to be named with a '.sh' ending.
-# default: <unset>
-#CONFIG_D=
-
-# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
-#BASEDIR=$SCRIPTDIR
-
-# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
-#DOMAINS_TXT="${BASEDIR}/domains.txt"
-
-# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges)
-#WELLKNOWN="${BASEDIR}/.acme-challenges"
-
-# Location of private account key (default: $BASEDIR/private_key.pem)
-#ACCOUNT_KEY="${BASEDIR}/private_key.pem"
-
-# Location of private account registration information (default: $BASEDIR/private_key.json)
-#ACCOUNT_KEY_JSON="${BASEDIR}/private_key.json"
-
-# Default keysize for private keys (default: 4096)
-#KEYSIZE="4096"
-
-# Path to openssl config file (default: <unset> - tries to figure out system default)
-#OPENSSL_CNF=
-
-# Program or function called in certain situations
-#
-# After generating the challenge-response, or after failed challenge (in this case altname is empty)
-# Given arguments: clean_challenge|deploy_challenge altname token-filename token-content
-#
-# After successfully signing certificate
-# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem path/to/fullchain.pem
-#
-# BASEDIR and WELLKNOWN variables are exported and can be used in an external program
-# default: <unset>
-#HOOK=
-
-# Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no)
-#HOOK_CHAIN="no"
-
-# Minimum days before expiration to automatically renew certificate (default: 30)
-#RENEW_DAYS="30"
-
-# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
-#PRIVATE_KEY_RENEW="yes"
-
-# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
-#KEY_ALGO=rsa
-
-# E-mail to use during the registration (default: <unset>)
-#CONTACT_EMAIL=
-
-# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
-#LOCKFILE="${BASEDIR}/lock"
--- /dev/null
+example.org www.example.org
+example.com www.example.com wiki.example.com
+++ /dev/null
-example.org www.example.org
-example.com www.example.com wiki.example.com
--- /dev/null
+#!/usr/bin/env bash
+
+function deploy_challenge {
+ local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
+
+ # This hook is called once for every domain that needs to be
+ # validated, including any alternative names you may have listed.
+ #
+ # Parameters:
+ # - DOMAIN
+ # The domain name (CN or subject alternative name) being
+ # validated.
+ # - TOKEN_FILENAME
+ # The name of the file containing the token to be served for HTTP
+ # validation. Should be served by your web server as
+ # /.well-known/acme-challenge/${TOKEN_FILENAME}.
+ # - TOKEN_VALUE
+ # The token value that needs to be served for validation. For DNS
+ # 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.
+}
+
+function clean_challenge {
+ local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
+
+ # This hook is called after attempting to validate each domain,
+ # whether or not validation was successful. Here you can delete
+ # files or DNS records that are no longer needed.
+ #
+ # The parameters are the same as for deploy_challenge.
+}
+
+function deploy_cert {
+ local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
+
+ # This hook is called once for each certificate that has been
+ # produced. Here you might, for instance, copy your new certificates
+ # to service-specific locations and reload the service.
+ #
+ # Parameters:
+ # - DOMAIN
+ # The primary domain name, i.e. the certificate common
+ # name (CN).
+ # - KEYFILE
+ # The path of the file containing the private key.
+ # - CERTFILE
+ # The path of the file containing the signed certificate.
+ # - FULLCHAINFILE
+ # The path of the file containing the full certificate chain.
+ # - CHAINFILE
+ # The path of the file containing the intermediate certificate(s).
+ # - TIMESTAMP
+ # Timestamp when the specified certificate was created.
+}
+
+function unchanged_cert {
+ local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
+
+ # This hook is called once for each certificate that is still
+ # valid and therefore wasn't reissued.
+ #
+ # Parameters:
+ # - DOMAIN
+ # The primary domain name, i.e. the certificate common
+ # name (CN).
+ # - KEYFILE
+ # The path of the file containing the private key.
+ # - CERTFILE
+ # The path of the file containing the signed certificate.
+ # - FULLCHAINFILE
+ # The path of the file containing the full certificate chain.
+ # - CHAINFILE
+ # The path of the file containing the intermediate certificate(s).
+}
+
+HANDLER=$1; shift; $HANDLER $@
+++ /dev/null
-#!/usr/bin/env bash
-
-function deploy_challenge {
- local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
-
- # This hook is called once for every domain that needs to be
- # validated, including any alternative names you may have listed.
- #
- # Parameters:
- # - DOMAIN
- # The domain name (CN or subject alternative name) being
- # validated.
- # - TOKEN_FILENAME
- # The name of the file containing the token to be served for HTTP
- # validation. Should be served by your web server as
- # /.well-known/acme-challenge/${TOKEN_FILENAME}.
- # - TOKEN_VALUE
- # The token value that needs to be served for validation. For DNS
- # 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.
-}
-
-function clean_challenge {
- local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}"
-
- # This hook is called after attempting to validate each domain,
- # whether or not validation was successful. Here you can delete
- # files or DNS records that are no longer needed.
- #
- # The parameters are the same as for deploy_challenge.
-}
-
-function deploy_cert {
- local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"
-
- # This hook is called once for each certificate that has been
- # produced. Here you might, for instance, copy your new certificates
- # to service-specific locations and reload the service.
- #
- # Parameters:
- # - DOMAIN
- # The primary domain name, i.e. the certificate common
- # name (CN).
- # - KEYFILE
- # The path of the file containing the private key.
- # - CERTFILE
- # The path of the file containing the signed certificate.
- # - FULLCHAINFILE
- # The path of the file containing the full certificate chain.
- # - CHAINFILE
- # The path of the file containing the intermediate certificate(s).
- # - TIMESTAMP
- # Timestamp when the specified certificate was created.
-}
-
-function unchanged_cert {
- local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
-
- # This hook is called once for each certificate that is still
- # valid and therefore wasn't reissued.
- #
- # Parameters:
- # - DOMAIN
- # The primary domain name, i.e. the certificate common
- # name (CN).
- # - KEYFILE
- # The path of the file containing the private key.
- # - CERTFILE
- # The path of the file containing the signed certificate.
- # - FULLCHAINFILE
- # The path of the file containing the full certificate chain.
- # - CHAINFILE
- # The path of the file containing the intermediate certificate(s).
-}
-
-HANDLER=$1; shift; $HANDLER $@
### HOOK_CHAIN="no" (default behaviour)
```
-# INFO: Using main config file /etc/letsencrypt.sh/config.sh
+# INFO: Using main config file /etc/letsencrypt.sh/config
Processing lukas.im with alternative names: www.lukas.im
+ Checking domain name(s) of existing cert... unchanged.
+ Checking expire date of existing cert...
### HOOK_CHAIN="yes"
```
-# INFO: Using main config file /etc/letsencrypt.sh/config.sh
+# INFO: Using main config file /etc/letsencrypt.sh/config
Processing lukas.im with alternative names: www.lukas.im
+ Checking domain name(s) of existing cert... unchanged.
+ Checking expire date of existing cert...
If you start testing using the production endpoint (which is the default),
you will quickly hit these limits and find yourself locked out.
-To avoid this, please set the CA property to the Let’s Encrypt staging server URL in your `config.sh` file:
+To avoid this, please set the CA property to the Let’s Encrypt staging server URL in your config file:
```bash
CA="https://acme-staging.api.letsencrypt.org/directory"
# Check for config in various locations
if [[ -z "${CONFIG:-}" ]]; then
for check_config in "/etc/letsencrypt.sh" "/usr/local/etc/letsencrypt.sh" "${PWD}" "${SCRIPTDIR}"; do
- if [[ -e "${check_config}/config.sh" ]]; then
+ if [[ -e "${check_config}/config" ]]; then
BASEDIR="${check_config}"
- CONFIG="${check_config}/config.sh"
+ CONFIG="${check_config}/config"
break
fi
done
PARAM_ACCOUNT_KEY="${1}"
;;
- # PARAM_Usage: --config (-f) path/to/config.sh
+ # PARAM_Usage: --config (-f) path/to/config
# PARAM_Description: Use specified config file
--config|-f)
shift 1
) &
# Generate config and create empty domains.txt
-echo 'CA="https://testca.kurz.pw/directory"' > config.sh
-echo 'LICENSE="https://testca.kurz.pw/terms/v1"' >> config.sh
-echo 'WELLKNOWN=".acme-challenges/.well-known/acme-challenge"' >> config.sh
-echo 'RENEW_DAYS="14"' >> config.sh
+echo 'CA="https://testca.kurz.pw/directory"' > config
+echo 'LICENSE="https://testca.kurz.pw/terms/v1"' >> config
+echo 'WELLKNOWN=".acme-challenges/.well-known/acme-challenge"' >> config
+echo 'RENEW_DAYS="14"' >> config
touch domains.txt
# Check if help command is working
# 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.sh tmp_config.sh
-./letsencrypt.sh --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" -f tmp_config.sh > tmplog 2> errorlog || _FAIL "Script execution failed"
+mv config tmp_config
+./letsencrypt.sh --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" -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 "Creating fullchain.pem"
_CHECK_LOG "Done!"
_CHECK_ERRORLOG
-mv tmp_config.sh config.sh
+mv tmp_config config
# Move private key and add new location to config
mv private_key.pem account_key.pem
-echo 'PRIVATE_KEY="./account_key.pem"' >> config.sh
+echo 'PRIVATE_KEY="./account_key.pem"' >> config
# Add third domain to command-lime, should force renewal.
_TEST "Run in cron mode again, this time adding third domain, should force renewal."
_CHECK_ERRORLOG
# Disable private key renew
-echo 'PRIVATE_KEY_RENEW="no"' >> config.sh
+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"
# Check if renewal works
_TEST "Run in cron mode again, to check if renewal works"
-echo 'RENEW_DAYS="300"' >> config.sh
+echo 'RENEW_DAYS="300"' >> config
./letsencrypt.sh --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
_CHECK_LOG "Renewing!"