#!/usr/bin/env bash LPATH=( /usr/lib/le "$HOME/.local/lib/le" "lib/le" "lib" ) for l in "${LPATH[@]}"; do [[ -f "$l/util" ]] && source "$l/util" done gen_san_string() { declare d declare -a argv argv=( "$@" ) printf '[SAN]\nsubjectAltName=' for d in "${argv[@]}"; do printf 'DNS:%s' "$d" if ! [[ "$d" == "${argv[-1]}" ]]; then printf ',' fi done } gen_renew_config() { declare d printf 'domains=(\n' for d in "$@"; do printf " %s\n" "$d" done printf ')\n' } usage() { while read -r line; do printf '%s\n' "$line"; done <<- EOF Usage: le [options] [domain ...] Options: -c # Configuration directory. Default: \$HOME/.acme -a # A dir to put challenges in. Default: /var/www/acme -h # Show this message. EOF } main() { declare cfg_dir declare -a domains while (( $# )); do case $1 in -c) cfg_dir=$2 shift;; -a) challenge_dir=$2 shift;; -h) usage return 0;; --) shift break;; *) break;; esac shift done domains=( "$@" ) (( ${#domains[@]} )) || { usage; return 1 } set_default cfg_dir "$HOME/.acme" set_default challenge_dir '/var/www/acme' if ! [[ -d "$cfg_dir" ]]; then err "Config dir ($cfg_dir) not found, creating." if ! mkdir -m700 "$cfg_dir"; then err "Can't create config dir: $cfg_dir" return 1 fi fi if ! [[ -f "$cfg_dir/account.key" ]]; then err "Account key not found, generating a new one." openssl genrsa 4096 > "$cfg_dir/account.key" fi if ! [[ -d "$cfg_dir/domains/${domains[0]}" ]]; then mkdir -p "$cfg_dir/domains/${domains[0]}" fi if ! [[ -f "$cfg_dir/domains/${domains[0]}/privkey.pem" ]]; then err "Generating a new key for ${domains[0]}." openssl genrsa 4096 > "$cfg_dir/domains/${domains[0]}/privkey.pem" fi if ! [[ -d "$challenge_dir" ]]; then err "Challenge dir does not exist: $challenge_dir" return 1 fi err "Generating a certificate request for: ${domains[@]}." if (( ${#domains[@]} > 1 )); then openssl req -new -sha256 \ -key "$cfg_dir/domains/${domains[0]}/privkey.pem" \ -subj "/" \ -reqexts SAN \ -config <(cat /etc/ssl/openssl.cnf <(gen_san_string "${domains[@]}")) > "$cfg_dir/domains/${domains[0]}/request.csr" else openssl req -new -sha256 \ -key "$cfg_dir/domains/${domains[0]}/privkey.pem" \ -subj "/CN=${domains[0]}" > "$cfg_dir/domains/${domains[0]}/request.csr" fi err "Getting a signed certificate." acme-tiny \ --account-key "$cfg_dir/account.key" \ --csr "$cfg_dir/domains/${domains[0]}/request.csr" \ --acme-dir "$challenge_dir" | grep -vE '^$' > "$cfg_dir/domains/${domains[0]}/certificate.pem" if (( $? )); then return 1 fi gen_renew_config "${domains[@]}" > "$cfg_dir/domains/${domains[0]}/renew.cfg" ln -fs "$cfg_dir/domains/${domains[0]}/certificate.pem" "$cfg_dir/domains/${domains[0]}/fullchain.pem" err "Your certificate is available at $cfg_dir/domains/${domains[0]}/fullchain.pem" } main "$@"