|
#!/bin/bash |
|
# Run this script once to set up ACME.sh as a non-root user. After this script is done, it can be removed if you want. The script also can be run multiple times safely such that it will not create multiples of anything. |
|
|
|
#CFG_ACME_USERNAME='acme' # What do you want to call the user who will fetch certificates? |
|
#CFG_CERT_DOMAIN='tst-server.virtual.example.com' # What fully qualified domain name should the certificate be for? |
|
#CFG_ACME_SERVER='https://acme-v02.api.letsencrypt.org/directory' # What is the ACME server we should get certificates from? |
|
verbosity=2 # Start counting at 2 so that any increase to this will result in a minimum of file descriptor 3. You should leave this alone. |
|
maxverbosity=5 # The highest verbosity we use / allow to be displayed. Feel free to adjust. |
|
|
|
while getopts ":vr" opt; do |
|
case $opt in |
|
v) (( verbosity=verbosity+1 ));; |
|
r) run="true" ;; |
|
esac |
|
done |
|
|
|
# Use `script` utility to log output in case you want to review it later. |
|
[[ "$run" == "true" ]] || exec script --return --quiet --command "$0 -r $*" --append ${0}.log |
|
|
|
printf "%s %d. %s\n" "Verbosity level set to:" "$verbosity" "Increase with '-v[v]'." |
|
|
|
for v in $(seq 3 $verbosity) # Start counting from 3 since 1 and 2 are standards (stdout/stderr). |
|
do |
|
(( "$v" <= "$maxverbosity" )) && eval exec "$v>&2" # Don't change anything higher than the maximum verbosity allowed. |
|
done |
|
|
|
for v in $(seq $(( verbosity+1 )) $maxverbosity ) # From the verbosity level one higher than requested, through the maximum; |
|
do |
|
(( "$v" > "2" )) && eval exec "$v>/dev/null" # Redirect these to bitbucket, provided that they don't match stdout and stderr. |
|
done |
|
|
|
function prePrompts(){ |
|
printf '\tdebug: %s%s%s\n' "$(tput setab 0)" "$BASH_COMMAND" "$(tput sgr0)" >&3 |
|
} |
|
|
|
trap 'prePrompts' DEBUG |
|
|
|
printf '%s running at %s\n' "$(basename $0)" "$(date)" |
|
|
|
# Ensure a username has been chosen |
|
until [[ -n "$CFG_ACME_USERNAME" ]]; do read -r -p "What do you want to call the user who will fetch certificates?: " CFG_ACME_USERNAME; done; |
|
|
|
# Create a system user account and group for acme.sh to run as |
|
sudo useradd --create-home --shell $(which nologin) "$CFG_ACME_USERNAME" 2>&4 |
|
|
|
# Temporarily allow shell use |
|
shell_was=$(getent passwd "$CFG_ACME_USERNAME" | awk 'BEGIN{FS=":"} {print $NF}') |
|
sudo usermod --shell /bin/bash "$CFG_ACME_USERNAME" 2>&4 |
|
|
|
# Permanently allow crontab use |
|
if ! sudo grep -E "^${CFG_ACME_USERNAME}$" /etc/cron.allow >&4; then |
|
printf 'Allowing %s to use crontab...\n' "$CFG_ACME_USERNAME" >&3 |
|
echo "$CFG_ACME_USERNAME" | sudo tee -a /etc/cron.allow |
|
else |
|
printf '%s already allowed to use crontab...\n' "$CFG_ACME_USERNAME" >&3 |
|
fi |
|
|
|
# Permanently allow sudo use to restart the web server (to detect new certificates) |
|
my_systemctl=$(which systemctl) |
|
printf '%s ALL = NOPASSWD: %s restart httpd.service\n' "$CFG_ACME_USERNAME" "$my_systemctl" | sudo tee /etc/sudoers.d/99_acme >&3 |
|
|
|
# Permanently allow the user to write to /var/www/html/.well-known/acme-challenge/ |
|
sudo mkdir --verbose --parents /var/www/html/.well-known/acme-challenge 2>&3 |
|
sudo chgrp --verbose "$CFG_ACME_USERNAME" /var/www/html/.well-known/acme-challenge/ 1>&3 |
|
sudo chmod --verbose g+rwx /var/www/html/.well-known/acme-challenge/ 1>&3 |
|
|
|
# Permanently make location for our user to store key and certificates |
|
for file in /etc/pki/tls/certs/acme.crt /etc/pki/tls/private/acme.key /etc/pki/tls/certs/acme-chain.crt; do |
|
sudo touch $file; |
|
sudo chown --verbose "$CFG_ACME_USERNAME" $file 1>&3; |
|
done |
|
sudo chmod --verbose o-rwx /etc/pki/tls/private/acme.key 1>&3 |
|
|
|
# Permanently expose /var/www/html/.well-known/acme-challenge/ for HTTP |
|
sudo cat <<-EOF | sudo dd of=/etc/httpd/conf.d/wellknown-acmechallenge.conf 2>&4 |
|
<Directory "/var/www/html/.well-known/acme-challenge/" > |
|
#Options Indexes FollowSymLinks |
|
Require all granted |
|
</Directory> |
|
EOF |
|
sudo systemctl restart httpd.service |
|
|
|
# Download acme.sh as the user |
|
sudo --user="$CFG_ACME_USERNAME" --set-home bash -c "cd ~; curl https://get.acme.sh | sh" 2>&3 |
|
|
|
# Issue certificate as the user |
|
until [[ -n "$CFG_CERT_DOMAIN" ]]; do read -r -p "What domain should the certificate be for?: " CFG_CERT_DOMAIN; done; |
|
until [[ -n "$CFG_ACME_SERVER" ]]; do read -r -p "What is the ACME server we should get certificates from?: " CFG_ACME_SERVER; done; |
|
sudo CFG_CERT_DOMAIN="$CFG_CERT_DOMAIN" CFG_ACME_SERVER="$CFG_ACME_SERVER" --user="$CFG_ACME_USERNAME" --set-home bash -c 'cd ~; .acme.sh/acme.sh --force --issue --domain "$CFG_CERT_DOMAIN" --server $CFG_ACME_SERVER --webroot /var/www/html' 2>&3 |
|
|
|
# Put the certificate where the web server can use it |
|
sudo CFG_CERT_DOMAIN="$CFG_CERT_DOMAIN" --user="$CFG_ACME_USERNAME" --set-home bash -c 'cd ~; .acme.sh/acme.sh --force --install-cert --domain "$CFG_CERT_DOMAIN" --cert-file /etc/pki/tls/certs/acme.crt --key-file /etc/pki/tls/private/acme.key --fullchain-file /etc/pki/tls/certs/acme-chain.crt --reloadcmd "sudo systemctl restart httpd.service"' 2>&3 |
|
|
|
# Permanently configure web server to use the new certificate |
|
sudo sed --in-place --regexp-extended 's|^SSLCertificateFile .+|SSLCertificateFile /etc/pki/tls/certs/acme.crt|' /etc/httpd/conf.d/ssl.conf |
|
sudo sed --in-place --regexp-extended 's|^SSLCertificateKeyFile .+|SSLCertificateKeyFile /etc/pki/tls/private/acme.key|' /etc/httpd/conf.d/ssl.conf |
|
sudo sed --in-place --regexp-extended 's|^#?SSLCertificateChainFile .+|SSLCertificateChainFile /etc/pki/tls/certs/acme-chain.crt|' /etc/httpd/conf.d/ssl.conf |
|
sudo systemctl restart httpd.service |
|
|
|
# Re-disable shell use |
|
sudo usermod --shell "$shell_was" "$CFG_ACME_USERNAME" 2>&3 |