#!/usr/bin/env bash # +-------------------------------------------------------------------------+ # | LEMPer CLI - Virtual Host (Site) Manager | # +-------------------------------------------------------------------------+ # | Copyright (c) 2014-2024 MasEDI.Net (https://masedi.net/lemper) | # +-------------------------------------------------------------------------+ # | This source file is subject to the GNU General Public License | # | that is bundled with this package in the file LICENSE.md. | # | | # | If you did not receive a copy of the license and are unable to | # | obtain it through the world-wide-web, please send an email | # | to license@lemper.cloud so we can send you a copy immediately. | # +-------------------------------------------------------------------------+ # | Authors: Edi Septriyanto | # +-------------------------------------------------------------------------+ # Version control. CMD_PARENT="${PROG_NAME}" CMD_NAME="manage" # Make sure only root can access and not direct access. if [[ "$(type -t requires_root)" != "function" ]]; then echo "Direct access to this script is not permitted." exit 1 fi ## # Main Functions ## ## # Show usage # output to STDERR. ## function show_usage() { cat <<- EOL ${CMD_PARENT} ${CMD_NAME} ${PROG_VERSION} LEMPer Stack virtual host (vhost) manager, enable, disable, remove Nginx vhost on Debian/Ubuntu server. Requirements: * LEMP stack setup uses [LEMPer](https://github.com/joglomedia/LEMPer) Usage: ${CMD_PARENT} ${CMD_NAME} [OPTION]... Options: -b, --enable-brotli Enable Brotli compression. -c, --enable-fastcgi-cache Enable FastCGI cache. --disable-fastcgi-cache Disable FastCHI cache. -d, --disable Disable virtual host. -e, --enable Enable virtual host. -f, --enable-fail2ban Enable fail2ban jail. --disable-fail2ban Disable fail2ban jail. -g, --enable-gzip Enable Gzip compression. --disable-compression Disable Gzip/Brotli compression. -r, --remove Remove virtual host configuration. -s, --enable-ssl Enable HTTP over SSL with Let's Encrypt. -w, --enforce-non-www Redirect www to non www host. --disable-ssl Disable HTTP over SSL. --remove-ssl Remove SSL certificate. --renew-ssl Renew SSL certificate. -h, --help Print this message and exit. -v, --version Output version information and exit. Example: ${CMD_PARENT} ${CMD_NAME} --remove example.com For more informations visit https://masedi.net/lemper Mail bug reports and suggestions to EOL } ## # Enable vhost. ## function enable_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} verify_vhost "${DOMAIN}" echo "Enabling virtual host: ${DOMAIN}..." # Enable Nginx's vhost config. if [[ ! -f "/etc/nginx/sites-enabled/${DOMAIN}.conf" && -f "/etc/nginx/sites-available/${DOMAIN}.conf" ]]; then run ln -s "/etc/nginx/sites-available/${DOMAIN}.conf" "/etc/nginx/sites-enabled/${DOMAIN}.conf" success "Your virtual host ${DOMAIN} has been enabled..." reload_nginx else fail "${DOMAIN} couldn't be enabled. Probably, it has been enabled or not created yet." exit 1 fi } ## # Disable vhost. ## function disable_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} verify_vhost "${DOMAIN}" echo "Disabling virtual host: ${DOMAIN}..." # Disable Nginx's vhost config. if [[ -f "/etc/nginx/sites-enabled/${DOMAIN}.conf" ]]; then run unlink "/etc/nginx/sites-enabled/${DOMAIN}.conf" success "Your virtual host ${DOMAIN} has been disabled..." reload_nginx else fail "${DOMAIN} couldn't be disabled. Probably, it has been disabled or removed." exit 1 fi } ## # Remove vhost. ## function remove_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} verify_vhost "${DOMAIN}" echo "Removing virtual host is not reversible." read -t 30 -rp "Press [Enter] to continue..." "/etc/fail2ban/jail.d/${DOMAIN}.conf" < HTTPS redirection block. cat >> "/etc/nginx/sites-available/${DOMAIN}.conf" < /dev/null 2>&1; then service nginx reload -s > /dev/null 2>&1 else error "Configuration couldn't be validated. Please correct the error below:"; nginx -t exit 1 fi # Nginx service dead? Try to start it. else if [[ -n $(command -v nginx) ]]; then if nginx -t 2>/dev/null > /dev/null; then service nginx restart > /dev/null 2>&1 else error "Configuration couldn't be validated. Please correct the error below:"; nginx -t exit 1 fi else info "Something went wrong with your LEMP stack installation." exit 1 fi fi if [[ $(pgrep -c nginx) -gt 0 ]]; then success "Your change has been successfully applied." exit 0 else fail "An error occurred when updating configuration."; fi } ## # Generate sel-signed certificate. ## function generate_selfsigned_ssl() { # Verify user input hostname (domain name). local DOMAIN=${1} local SERVER_IP=${2:-$(get_ip_public)} verify_vhost "${DOMAIN}" if [ ! -d "/etc/lemper/ssl/${DOMAIN}" ]; then run mkdir -p "/etc/lemper/ssl/${DOMAIN}" fi run sed -i "s|^CN\ =\ .*|CN\ =\ ${DOMAIN}|g" /etc/lemper/ssl/ca.conf && \ run sed -i "s|^CN\ =\ .*|CN\ =\ ${DOMAIN}|g" /etc/lemper/ssl/csr.conf && \ run sed -i "s|^DNS\.1\ =\ .*|DNS\.1\ =\ ${DOMAIN}|g" /etc/lemper/ssl/csr.conf && \ run sed -i "s|^DNS\.2\ =\ .*|DNS\.2\ =\ www\.${DOMAIN}|g" /etc/lemper/ssl/csr.conf && \ run sed -r -i "s|^IP.1\ =\ (\b[0-9]{1,3}\.){3}[0-9]{1,3}\b$|IP.1\ =\ ${SERVER_IP}|g" /etc/lemper/ssl/csr.conf && \ run sed -r -i "s|^IP.2\ =\ (\b[0-9]{1,3}\.){3}[0-9]{1,3}\b$|IP.2\ =\ ${SERVER_IP}|g" /etc/lemper/ssl/csr.conf && \ run sed -i "s|^DNS\.1\ =\ .*|DNS\.1\ =\ ${DOMAIN}|g" /etc/lemper/ssl/cert.conf # Create Certificate Authority (CA). run openssl req -x509 -sha256 -days 365000 -nodes -newkey rsa:2048 \ -keyout "/etc/lemper/ssl/${DOMAIN}/ca.key" -out "/etc/lemper/ssl/${DOMAIN}/ca.crt" \ -config /etc/lemper/ssl/ca.conf CA_KEY_FILE="/etc/lemper/ssl/${DOMAIN}/ca.key" CA_CRT_FILE="/etc/lemper/ssl/${DOMAIN}/ca.crt" # Create Server Private Key. run openssl genrsa -out "/etc/lemper/ssl/${DOMAIN}/privkey.pem" 2048 && \ # Generate Certificate Signing Request (CSR) using Server Private Key. run openssl req -new -key "/etc/lemper/ssl/${DOMAIN}/privkey.pem" \ -out "/etc/lemper/ssl/${DOMAIN}/csr.csr" -config /etc/lemper/ssl/csr.conf # Generate SSL certificate With self signed CA. run openssl x509 -req -sha256 -days 365000 -CAcreateserial \ -CA "${CA_CRT_FILE}" -CAkey "${CA_KEY_FILE}" \ -in "/etc/lemper/ssl/${DOMAIN}/csr.csr" -out "/etc/lemper/ssl/${DOMAIN}/cert.pem" \ -extfile /etc/lemper/ssl/cert.conf # Create chain file. run cat "/etc/lemper/ssl/${DOMAIN}/cert.pem" "${CA_CRT_FILE}" >> \ "/etc/lemper/ssl/${DOMAIN}/fullchain.pem" if [ -f "/etc/lemper/ssl/${DOMAIN}/cert.pem" ]; then success "Self-signed SSL certificate has been successfully generated." else fail "An error occurred while generating self-signed SSL certificate." fi } ## # Get server private IP Address. ## function get_ip_private() { local SERVER_IP_PRIVATE && \ SERVER_IP_PRIVATE=$(ip addr | grep 'inet' | grep -v inet6 | \ grep -vE '127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | \ grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | head -1) echo "${SERVER_IP_PRIVATE}" } ## # Get server public IP Address. ## function get_ip_public() { local SERVER_IP_PRIVATE && SERVER_IP_PRIVATE=$(get_ip_private) local SERVER_IP_PUBLIC && \ SERVER_IP_PUBLIC=$(curl -sk --connect-timeout 10 --retry 3 --retry-delay 0 http://ipecho.net/plain) # Ugly hack to detect aws-lightsail public IP address. if [[ "${SERVER_IP_PRIVATE}" == "${SERVER_IP_PUBLIC}" ]]; then echo "${SERVER_IP_PRIVATE}" else echo "${SERVER_IP_PUBLIC}" fi } ## # Main Manage CLI Wrapper ## function init_lemper_manage() { OPTS=$(getopt -o c:d:e:f:r:s:bghv \ -l enable:,disable:,remove:,enable-fail2ban:,disable-fail2ban:,enable-fastcgi-cache:,disable-fastcgi-cache: \ -l enable-ssl:,disable-ssl:,remove-ssl:,renew-ssl:,enable-brotli:,enable-gzip:,disable-compression:,help,version \ -n "${PROG_NAME}" -- "$@") eval set -- "${OPTS}" while true do case "${1}" in -e | --enable) enable_vhost "${2}" shift 2 exit 0 ;; -d | --disable) disable_vhost "${2}" shift 2 exit 0 ;; -r | --remove) remove_vhost "${2}" shift 2 exit 0 ;; -c | --enable-fastcgi-cache) enable_fastcgi_cache "${2}" shift 2 exit 0 ;; --disable-fastcgi-cache) disable_fastcgi_cache "${2}" shift 2 exit 0 ;; -f | --enable-fail2ban) enable_fail2ban "${2}" shift 2 exit 0 ;; --disable-fail2ban) disable_fail2ban "${2}" shift 2 exit 0 ;; -s | --enable-ssl) enable_ssl "${2}" shift 2 exit 0 ;; --disable-ssl) disable_ssl "${2}" shift 2 exit 0 ;; --remove-ssl) remove_ssl "${2}" shift 2 exit 0 ;; --renew-ssl) renew_ssl "${2}" shift 2 exit 0 ;; -b | --enable-brotli) enable_brotli "${2}" shift 2 exit 0 ;; -g | --enable-gzip) enable_gzip "${2}" shift 2 exit 0 ;; --disable-compression) disable_compression "${2}" shift 2 exit 0 ;; -h | --help) show_usage shift 2 exit 0 ;; -v | --version) echo "${PROG_NAME} version ${PROG_VERSION}" shift 2 exit 0 ;; --) # End of all options, shift to the next (non getopt) argument as $1. shift break ;; *) fail "Invalid argument: ${1}" exit 1 ;; esac done echo "${CMD_PARENT} ${CMD_NAME}: missing required argument" echo "See '${CMD_PARENT} ${CMD_NAME} --help' for more information." } # Start running things from a call at the end so if this script is executed # after a partial download it doesn't do anything. init_lemper_manage "$@"