Initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.old/
|
||||
multisites/flatwhitedesign.pw
|
||||
multisites/greengingerdesign.pw
|
||||
multisites/greengingerdesign.space
|
||||
multisites/greengingermultisite.website
|
||||
|
||||
69
README.md
Normal file
69
README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# WIP
|
||||
|
||||
`acme-cpanel.sh` reads in a list of domains from one or more files. These files may only contain domains and empty lines (see `domains.txt` for example format).
|
||||
|
||||
"*www.*" subdomains will be added automatically (do not add them to the domains file list).
|
||||
|
||||
## Notes
|
||||
|
||||
The `--method webroot` may require the following additions to .htaccess so that challenges are not automatically redirected to https:
|
||||
|
||||
```text
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^\.well-known/.+ - [END]
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Command-line (Linux):
|
||||
|
||||
* Move script to user home directory on the server: `scp ./* username@ip:port:~`
|
||||
* Log in to server: `ssh user@ip -p port`
|
||||
* Make script executable: `chmod +x $HOME/acme-cpanel.sh`
|
||||
* Run script (ex. `$HOME/acme-cpanel.sh -s multisites`)
|
||||
* Follow prompts to enter credentials, issue certificates, and deploy them
|
||||
* Double-check that the acme cron job is enabled: `crontab -l`
|
||||
|
||||
cPanel:
|
||||
|
||||
* Use File Manager to upload files to the home directory (/home/username/)
|
||||
* You may need to make file executable in Terminal: `chmod +x $HOME/acme-cpanel.sh`
|
||||
* Use Terminal to run the script (ex. `$HOME/acme-cpanel.sh -s multisites`)
|
||||
* Follow prompts to enter credentials, issue certificates, and deploy them
|
||||
* Use Cron Jobs app to double-check that the acme cron job is present
|
||||
|
||||
## Usage
|
||||
|
||||
#### `./acme-cpanel.sh [OPTIONS] [FILES...]`
|
||||
|
||||
#### Options
|
||||
|
||||
```text
|
||||
--method, -m dns,webroot
|
||||
Choose the authentication method (default: dns)
|
||||
--email, -e EMAIL
|
||||
E-mail to be notified of certificate renewal failures
|
||||
--group-by-file, -g
|
||||
Issue multidomain certificates for all domains with the same webroot, grouped by input file
|
||||
The first domain in each file will be used to determine the shared webroot
|
||||
--sites-dir, -s DIR
|
||||
Load domain list files from this directory
|
||||
--force, -f
|
||||
Override default debug
|
||||
--debug, -d (default)
|
||||
Use --staging to issue certificates and do not deploy
|
||||
```
|
||||
|
||||
#### Examples
|
||||
|
||||
`./acme-cpanel.sh --force`
|
||||
|
||||
Load all sites from domains.txt, issue and deploy certificates using the webroot method
|
||||
|
||||
`./acme-cpanel.sh --method dns --force -s multisites`
|
||||
|
||||
Load sites from multisites directory, issue and deploy certificates using the dns method
|
||||
|
||||
`./acme-cpanel.sh --force -g multisites/site1.org multisites/site2.org`
|
||||
|
||||
Load sites from multisites directory, issue and deploy multidomain certificates with same webroot based on the grouping in the file using the webroot method
|
||||
225
acme-cpanel.sh
Normal file
225
acme-cpanel.sh
Normal file
@@ -0,0 +1,225 @@
|
||||
#!/usr/bin/env bash
|
||||
# This script uses acme.sh to issue and deploy SSL certificates from Let's Encrypt for a list of domains
|
||||
# using the webroot or dns methods
|
||||
#
|
||||
# See README.md for more details
|
||||
#
|
||||
# Copyright 2020 Bryan Roessler <bryanroessler@gmail.com>
|
||||
# MIT Licensed
|
||||
|
||||
unset SITES_DIR USEREMAIL DOMAIN_FILES DOMAIN_GROUPS DEPLOY_CMD_PREFIX ISSUE_CMD_PREFIX DEBUG GROUP
|
||||
|
||||
DEBUG="true" # quote this line to stop DEBUG mode and issue certificates for real, or use --force in user options
|
||||
METHOD="dns" # set the default method
|
||||
CONF="$HOME/.acme.sh/account.conf"
|
||||
ACME_SH="$HOME/.acme.sh/acme.sh"
|
||||
|
||||
parse_input() {
|
||||
|
||||
local input
|
||||
declare -g USEREMAIL
|
||||
declare -ag DOMAIN_FILES
|
||||
|
||||
if input=$(getopt -o +m:e:fgs:d -l method:,email:,force,group-by-file,sites-dir:,debug -- "$@"); then
|
||||
eval set -- "$input"
|
||||
while true; do
|
||||
case "$1" in
|
||||
--method|-m)
|
||||
shift
|
||||
METHOD="${1,,}"
|
||||
;;
|
||||
--force|-f)
|
||||
unset DEBUG
|
||||
;;
|
||||
--group-by-file|-g)
|
||||
GROUP="true"
|
||||
;;
|
||||
--sites-dir|-s)
|
||||
shift
|
||||
SITES_DIR="$1"
|
||||
;;
|
||||
--debug|-d)
|
||||
DEBUG="true"
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
else
|
||||
echo "Incorrect options provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load domain files from remaining arguments
|
||||
if [[ $# -lt 1 ]]; then
|
||||
[[ -v SITES_DIR && -d "$SITES_DIR" ]] && return 0
|
||||
if [[ -f "domains.txt" ]]; then
|
||||
echo "You have not supplied any domain files, using domains.txt by default"
|
||||
DOMAIN_FILES=("domains.txt")
|
||||
else
|
||||
echo "You must specify a domain list or use domains.txt by default"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
DOMAIN_FILES=("$@")
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
interactive_dns() {
|
||||
if [[ -f "$CONF" ]] && grep -q "CPANELDNS_AUTH_PASSWORD" "$CONF"; then
|
||||
echo "cPanel credentials already present, skipping configuration..."
|
||||
echo "To rerun the configuration, first run 'rm $CONF'"
|
||||
else
|
||||
read -rp 'Enter your cPanel username: ' CPANELDNS_AUTH_ID
|
||||
echo
|
||||
export CPANELDNS_AUTH_ID
|
||||
read -rp 'Enter your cPanel password: ' CPANELDNS_AUTH_PASSWORD
|
||||
echo
|
||||
export CPANELDNS_AUTH_PASSWORD
|
||||
read -rp 'Enter your cPanel address and port number (example: "https://www.example.com:2083/"): ' CPANELDNS_API
|
||||
echo
|
||||
export CPANELDNS_API
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
get_acme() {
|
||||
curl https://get.acme.sh | sh
|
||||
# shellcheck disable=SC1090
|
||||
source "$HOME/.bashrc"
|
||||
"$ACME_SH" --upgrade --auto-upgrade
|
||||
[[ "$METHOD" == "dns" ]] && \
|
||||
curl -o "$HOME/.acme.sh/dnsapi/dns_cpaneldns.sh" https://raw.githubusercontent.com/cryobry/dns_cpaneldns/master/dns_cpaneldns.sh
|
||||
}
|
||||
|
||||
|
||||
update_email() {
|
||||
if [[ ! -v USEREMAIL ]]; then
|
||||
if [[ -f "$CONF" ]] && line=$(grep -q "ACCOUNT_EMAIL" "$CONF"); then
|
||||
echo "Reusing existing contact e-mail: ${line#ACCOUNT_EMAIL=}"
|
||||
return 0
|
||||
fi
|
||||
read -rp 'Enter your contact e-mail (in case of renewal failures): ' USEREMAIL
|
||||
fi
|
||||
"$ACME_SH" --update-account --accountemail "${USEREMAIL}"
|
||||
}
|
||||
|
||||
|
||||
command_prefixes() {
|
||||
declare -ag ISSUE_CMD_PREFIX DEPLOY_CMD_PREFIX
|
||||
ISSUE_CMD_PREFIX=("$ACME_SH" "--issue")
|
||||
[[ "$METHOD" == "dns" ]] && ISSUE_CMD_PREFIX=("${ISSUE_CMD_PREFIX[@]}" "--dns" "dns_cpaneldns")
|
||||
[[ -v DEBUG ]] && ISSUE_CMD_PREFIX=("${ISSUE_CMD_PREFIX[@]}" "--staging") || ISSUE_CMD_PREFIX=("${ISSUE_CMD_PREFIX[@]}" "--force")
|
||||
DEPLOY_CMD_PREFIX=("$ACME_SH" "--deploy" "--deploy-hook" "cpanel_uapi")
|
||||
}
|
||||
|
||||
|
||||
get_webroot() {
|
||||
local webroot
|
||||
if ! webroot=$(uapi DomainInfo single_domain_data domain="$1" | grep documentroot); then
|
||||
echo "UAPI call failed" >&2
|
||||
fi
|
||||
if [[ ! -v webroot || "$webroot" == "" ]]; then
|
||||
if [[ -v DEBUG ]]; then
|
||||
webroot="/tmp" # set missing webroot in DEBUG mode for testing
|
||||
else
|
||||
echo "Could not find $1's webroot" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "$webroot"
|
||||
}
|
||||
|
||||
|
||||
# Either create a single array of all domains (DOMAINS) to issue one-by-one or create an array of array names to issue for a single webroot
|
||||
load_domains() {
|
||||
local domain_file
|
||||
declare -ag DOMAIN_GROUPS=()
|
||||
|
||||
if [[ -v SITES_DIR ]]; then
|
||||
for domain_file in "$SITES_DIR"/*; do
|
||||
DOMAIN_GROUPS+=("$(<"$domain_file")")
|
||||
done
|
||||
fi
|
||||
|
||||
for domain_file in "${DOMAIN_FILES[@]}"; do
|
||||
# Load list of domains as space-delimited strings in elements of the DOMAINS array
|
||||
# We can keep these separate or combine them later
|
||||
DOMAIN_GROUPS+=("$(<"$domain_file")")
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
issue_and_deploy_certs() {
|
||||
|
||||
local group_root domain_root domain domain_group
|
||||
|
||||
for domain_group in "${DOMAIN_GROUPS[@]}"; do
|
||||
local -a issue_cmd=("${ISSUE_CMD_PREFIX[@]}")
|
||||
local -a deploy_cmd=("${DEPLOY_CMD_PREFIX[@]}")
|
||||
local i="set"
|
||||
# Issue certificates
|
||||
for domain in $domain_group; do # we want to split on whitespace
|
||||
[[ "$domain" == "" ]] && continue
|
||||
if [[ -v GROUP ]]; then
|
||||
if [[ "$METHOD" == "webroot" && -v i ]]; then
|
||||
group_root=$(get_webroot "$domain")
|
||||
issue_cmd+=("-w" "$group_root")
|
||||
unset i
|
||||
fi
|
||||
# Append domains to issue command that we will call after the loop
|
||||
issue_cmd+=("-d" "$domain" "-d" "www.$domain")
|
||||
# Issue certificate for single domain
|
||||
else
|
||||
local -a issue_cmd=("${ISSUE_CMD_PREFIX[@]}")
|
||||
domain_root=$(get_webroot "$domain")
|
||||
issue_cmd+=("-d" "$domain" "-d" "www.$domain")
|
||||
[[ "$METHOD" == "webroot" ]] && issue_cmd+=("-w" "$domain_root")
|
||||
echo "Running:" "${issue_cmd[@]}"
|
||||
if ! "${issue_cmd[@]}"; then
|
||||
echo "Failed to issue certificate for domain: $domain"
|
||||
err=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Issue certificate for group of domains
|
||||
if [[ -v GROUP ]]; then
|
||||
echo "Running:" "${issue_cmd[@]}"
|
||||
if ! "${issue_cmd[@]}"; then
|
||||
echo "Failed to issue certificate for domain group: $domain_group"
|
||||
err=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Deploy certificates one domain at a time
|
||||
for domain in $domain_group; do
|
||||
deploy_cmd=("${DEPLOY_CMD_PREFIX[@]}" "-d" "$domain") # only deploy to the domain, not subdomains
|
||||
echo "Running:" "${deploy_cmd[@]}"
|
||||
if ! "${deploy_cmd[@]}"; then
|
||||
echo "Failed to deploy certificate for $domain"
|
||||
err=1
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
parse_input "$@"
|
||||
get_acme
|
||||
update_email
|
||||
command_prefixes
|
||||
load_domains
|
||||
[[ "$METHOD" == "dns" ]] && interactive_dns
|
||||
sanity_check
|
||||
issue_and_deploy_certs
|
||||
}
|
||||
|
||||
|
||||
main "$@"
|
||||
exit "${err:-0}"
|
||||
2
domains.txt
Normal file
2
domains.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
multisite1.org
|
||||
multisite2.org
|
||||
15
multisites/multisite1.org
Normal file
15
multisites/multisite1.org
Normal file
@@ -0,0 +1,15 @@
|
||||
example1.com
|
||||
testsite.net
|
||||
demo-website.org
|
||||
myproject.io
|
||||
sampleapp.dev
|
||||
placeholder-domain.com
|
||||
fictional-business.co
|
||||
random-site.online
|
||||
test-company.tech
|
||||
mock-website.app
|
||||
example-store.shop
|
||||
demo-blog.info
|
||||
sample-portal.xyz
|
||||
fake-domain.club
|
||||
test-platform.digital
|
||||
20
multisites/multisite2.org
Normal file
20
multisites/multisite2.org
Normal file
@@ -0,0 +1,20 @@
|
||||
example1.com
|
||||
testsite.net
|
||||
mydomain.org
|
||||
samplewebsite.io
|
||||
demopage.co
|
||||
fictionalsite.dev
|
||||
placeholder.app
|
||||
mockwebsite.online
|
||||
exampledomain.xyz
|
||||
testproject.com
|
||||
randomsite.net
|
||||
demobusiness.org
|
||||
fakecompany.io
|
||||
samplestore.shop
|
||||
placeholdersite.com
|
||||
myexample.net
|
||||
testingdomain.org
|
||||
dummysite.co
|
||||
mockbrand.com
|
||||
virtualsite.net
|
||||
Reference in New Issue
Block a user