diff --git a/.gitignore b/.gitignore index 6e1823d..2fa1a32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ data latest.yml config -lsws/conf \ No newline at end of file +lsws/conf +certs \ No newline at end of file diff --git a/README.md b/README.md index f4f783d..5daa42f 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,66 @@ Other parameters: * [`-V`, `--remove`]: Remove a domain. +### Using mkcert for Local Development SSL + +For local development domains (`.test`, `.local`, `.dev`, etc.), you can use `mkcert` to generate trusted SSL certificates without warnings. + +#### Installing mkcert + +First-time installation (Windows with Chocolatey): + +```bash +bash bin/mkcert.sh --install +``` + +This will: + +* Install `mkcert` via Chocolatey +* Create and install a local Certificate Authority (CA) in your system trust store + +> **Note**: For macOS or Linux users, please install mkcert manually: +> +> * macOS: `brew install mkcert nss` +> * Ubuntu: `sudo apt install mkcert libnss3-tools` +> * Fedora: `sudo dnf install mkcert nss-tools` + +#### Generating Local SSL Certificate + +After adding a domain to your environment, generate an SSL certificate: + +```bash +bash bin/mkcert.sh [-D, --domain] example.test +``` + +This will: + +1. Check if the domain exists in your configuration +2. Generate certificates for `example.test` and `www.example.test` +3. Create a `dockerLocal` template with SSL configuration +4. Copy certificates to the container +5. Move the domain from the standard template to the SSL-enabled template +6. Restart OpenLiteSpeed + +Your domain will now be accessible via HTTPS with a trusted certificate at `https://example.test` + +#### Removing Local SSL Certificate + +To remove the SSL certificate and revert to HTTP: + +```bash +bash bin/mkcert.sh [-R, --remove] [-D, --domain] example.test +``` + +This will: + +1. Remove the domain from the `dockerLocal` template +2. Move it back to the standard `docker` template +3. Delete certificate files from both host and container +4. Clean up empty templates if no other domains use SSL +5. Restart OpenLiteSpeed + +> **Important**: You must add the domain to your environment first using `bash bin/domain.sh --add example.test` before generating certificates. + ### Update Web Server To upgrade the web server to latest stable version, run the following: diff --git a/bin/mkcert.sh b/bin/mkcert.sh new file mode 100644 index 0000000..3b5124c --- /dev/null +++ b/bin/mkcert.sh @@ -0,0 +1,485 @@ +#!/usr/bin/env bash +DOMAIN='' +INSTALL='' +REMOVE='' +CONT_NAME='litespeed' +CERT_DIR='./certs' +EPACE=' ' + +# Function to print messages with a specific format +echow(){ + FLAG=${1} + shift + echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}" +} + +# Function to display help message +help_message(){ + echo -e "\033[1mUSAGE\033[0m" + echo "${EPACE}mkcert.sh [OPTIONS]" + echo "" + echo -e "\033[1mOPTIONS\033[0m" + echow '-D, --domain [DOMAIN_NAME]' + echo "${EPACE}${EPACE}Example: mkcert.sh --domain example.test" + echo "${EPACE}${EPACE}Will create certificate for example.test and www.example.test" + echow '-I, --install' + echo "${EPACE}${EPACE}Install mkcert on Windows (requires Chocolatey)" + echow '-R, --remove' + echo "${EPACE}${EPACE}Remove certificate for a specific domain. Must be used with --domain." + echo "${EPACE}${EPACE}Example: mkcert.sh --remove --domain example.test" + echow '-H, --help' + echo "${EPACE}${EPACE}Display help and exit" + exit 0 +} + +# Function to check input parameters +check_input(){ + if [ -z "${1}" ]; then + help_message + fi +} + +# Function to filter and extract domain name +domain_filter(){ + if [ -z "${1}" ]; then + echo "[X] Domain name is required!" + exit 1 + fi + DOMAIN="${1}" + DOMAIN="${DOMAIN#http://}" + DOMAIN="${DOMAIN#https://}" + DOMAIN="${DOMAIN#ftp://}" + DOMAIN="${DOMAIN%%/*}" +} + +# Function to get www version of the domain +www_domain(){ + CHECK_WWW=$(echo ${1} | cut -c1-4) + if [[ ${CHECK_WWW} == www. ]] ; then + DOMAIN=$(echo ${1} | cut -c 5-) + else + DOMAIN=${1} + fi + WWW_DOMAIN="www.${DOMAIN}" +} + +# Function to check if mkcert is installed +check_mkcert() { + echo "[Start] Checking mkcert installation..." + + # Detect mkcert command (Windows supported, other OS can be added later) + if MKCERT_CMD=$(command -v mkcert.exe 2>/dev/null || command -v mkcert 2>/dev/null); then + echo "[✔] mkcert found at: ${MKCERT_CMD}" + else + echo "[✖] mkcert not found!" + echo "→ Please run 'bash bin/mkcert.sh --install' or install it manually." + echo " Windows: choco install mkcert" + echo " (Linux/macOS support can be added here later)" + exit 1 + fi + + echo "[End] mkcert check completed." +} + +# Function to install mkcert on Windows using Chocolatey +# ------------------------------------------------------------------------------ +# 💡 Notes for contributors: +# - This script currently supports Windows / WSL / Git Bash only. +# - To extend for macOS or Linux, add logic below: +# macOS: brew install mkcert nss +# Ubuntu: sudo apt install mkcert libnss3-tools +# Fedora: sudo dnf install mkcert nss-tools +# ------------------------------------------------------------------------------ +install_mkcert() { + echo "[Start] Installing mkcert..." + + # Step 1 Check if mkcert is already installed + if command -v mkcert.exe >/dev/null 2>&1 || command -v mkcert >/dev/null 2>&1; then + echo "[O] mkcert is already installed." + echo "[!] Ensuring local CA is installed..." + # Ensure local CA is installed + (mkcert.exe -install || mkcert -install) + echo "[O] Local CA configured." + echo "[End] mkcert installation check complete." + return 0 + fi + + # Step 2 Check if Chocolatey is available + if ! command -v choco.exe >/dev/null 2>&1 && ! command -v choco >/dev/null 2>&1; then + echo "[X] Chocolatey not found!" + echo "→ Please install Chocolatey from: https://chocolatey.org/install" + echo "→ After installation, restart your terminal and re-run this script." + exit 1 + fi + + # Step 3 Install mkcert using Chocolatey + echo "[*] Installing mkcert via Chocolatey..." + (choco.exe install mkcert -y || choco install mkcert -y) + + # Step 4 Verify installation result + if command -v mkcert.exe >/dev/null 2>&1 || command -v mkcert >/dev/null 2>&1; then + echo "[O] mkcert installed successfully." + echo "[!] Creating local CA..." + (mkcert.exe -install || mkcert -install) + echo "[O] Local CA configured." + echo "[End] mkcert installation complete." + else + echo "[X] mkcert installation failed!" + exit 1 + fi +} + +# Function to create certificate directory if it doesn't exist +create_cert_dir(){ + if [ ! -d "${CERT_DIR}" ]; then + echo "[!] Creating certificate directory: ${CERT_DIR}" + mkdir -p "${CERT_DIR}" + fi +} + +# Function to verify if domain has been added (by checking document root existence) +domain_verify(){ + local domain="${1}" + local doc_path="/var/www/vhosts/${domain}/html" + + echo "[!] Checking if domain '${domain}' has been added..." + + if docker compose exec -T ${CONT_NAME} bash -c "[ -d ${doc_path} ]" 2>/dev/null; then + echo -e "[O] Domain \033[32m${domain}\033[0m exists (document root found)" + return 0 + else + echo -e "[X] Domain \033[31m${domain}\033[0m has NOT been added yet!" + echo "[!] Document root not found: ${doc_path}" + echo "[!] Please add this domain first using: bash bin/domain.sh -a ${domain}" + exit 1 + fi +} + +# Function to generate SSL certificate using mkcert +generate_cert(){ + echo '[Start] Generating SSL certificate' + www_domain "${DOMAIN}" + + create_cert_dir + + mkdir -p "${CERT_DIR}/${DOMAIN}" + + cd "${CERT_DIR}/${DOMAIN}" + + echo -e "[!] Generating certificate for: \033[32m${DOMAIN}\033[0m and \033[32m${WWW_DOMAIN}\033[0m" + + # Use the detected mkcert command + ${MKCERT_CMD} -key-file key.pem -cert-file cert.pem "${DOMAIN}" "${WWW_DOMAIN}" >/dev/null 2>&1 + + if [ ${?} = 0 ]; then + echo -e "[O] Certificate generated successfully" + echo "[!] Certificate files:" + echo "${EPACE}Cert: ${CERT_DIR}/${DOMAIN}/cert.pem" + echo "${EPACE}Key: ${CERT_DIR}/${DOMAIN}/key.pem" + else + echo "[X] Failed to generate certificate" + cd ../.. + rm -rf "${CERT_DIR}/${DOMAIN}" + exit 1 + fi + + cd - > /dev/null + echo '[End] Generating SSL certificate' +} + +# Function to create docker-local.conf template for local development +create_local_template(){ + echo '[Start] Creating docker-local.conf template' + + local source_file="/usr/local/lsws/conf/templates/docker.conf" + local dest_file="/usr/local/lsws/conf/templates/docker-local.conf" + + # Check if template file already exists + if docker compose exec -T ${CONT_NAME} bash -c "[ -f ${dest_file} ]" 2>/dev/null; then + echo "[i] Template file already exists: ${dest_file}" + echo '[End] Creating docker-local.conf template' + return 0 + fi + + # Copy and modify template file in a single command + docker compose exec -T ${CONT_NAME} bash -c " + # Copy template file + cp ${source_file} ${dest_file} + + # Remove old vhssl block and last closing brace + sed -i '/^ vhssl {/,/^ }/d; \$d' ${dest_file} + + # Append new vhssl configuration + cat >> ${dest_file} <<'VHSSL_EOF' + vhssl { + keyFile /usr/local/lsws/conf/cert/\$VH_NAME/key.pem + certFile /usr/local/lsws/conf/cert/\$VH_NAME/cert.pem + certChain 1 + } +} +VHSSL_EOF + + # Fix ownership and permissions + chown nobody:nogroup ${dest_file} 2>/dev/null || chown lsadm:lsadm ${dest_file} + chmod 644 ${dest_file} + " + + echo -e "[O] Template \033[32mdocker-local.conf\033[0m created successfully!" + echo -e " SSL certificates path: /usr/local/lsws/conf/cert/\$VH_NAME/" + echo '[End] Creating docker-local.conf template' +} + +# Function to register dockerLocal vhTemplate in httpd_config.conf +register_local_template() { + echo '[Start] Registering vhTemplate: dockerLocal' + + local config_file="/usr/local/lsws/conf/httpd_config.conf" + local template_name="dockerLocal" + local template_path="conf/templates/docker-local.conf" + + docker compose exec -T ${CONT_NAME} bash -c " + if ! grep -q 'vhTemplate ${template_name} {' ${config_file}; then + cat >> ${config_file} </dev/null' + + if [ ${?} = 0 ]; then + echo -e "[O] OpenLiteSpeed restarted successfully" + else + echo "[X] Failed to restart OpenLiteSpeed" + fi +} + +# Main function to orchestrate the script operations +main(){ + if [ "${INSTALL}" = 'true' ]; then + install_mkcert + exit 0 + fi + + domain_filter "${DOMAIN}" + + if [ "${REMOVE}" = 'true' ]; then + remove_cert + exit 0 + fi + + check_mkcert + domain_verify "${DOMAIN}" + generate_cert + configure_litespeed +} + +# Parse command-line arguments +check_input ${1} +while [ ! -z "${1}" ]; do + case ${1} in + -[hH] | -help | --help) + help_message + ;; + -[dD] | -domain | --domain) + shift + check_input "${1}" + DOMAIN="${1}" + ;; + -[iI] | --install) + INSTALL=true + ;; + -[rR] | --remove) + REMOVE=true + ;; + *) + help_message + ;; + esac + shift +done + +main \ No newline at end of file