From 30d9dc7fcd66ea698f4eea5fd803ace702d89103 Mon Sep 17 00:00:00 2001 From: H1ghSyst3m <60105043+H1ghSyst3m@users.noreply.github.com> Date: Tue, 14 Jan 2025 23:04:47 +0100 Subject: [PATCH] feat: add NGINX installer and update README (#146) * feat: add NGINX installer script for zero-knowledge setup - Created a new installer script for NGINX-based setups. - The script automates Docker, NGINX, Let's Encrypt, and Neko Rooms configuration. - Designed for users who prefer NGINX as a reverse proxy instead of Traefik. * docs: update README to include zero-knowledge installation instructions with NGINX * feat: enhance IP retrieval method and add user prompt for domain continuation * docs: update README to streamline zero-knowledge installation instructions and clarify NGINX usage * feat: update IP retrieval fallback service to use checkip.amazonaws.com --- README.md | 17 +- nginx/install | 802 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 814 insertions(+), 5 deletions(-) create mode 100644 nginx/install diff --git a/README.md b/README.md index 9a5012f..99c959b 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,25 @@ Simple room management system for [n.eko](https://github.com/m1k1o/neko). Self h n.eko -## Zero-knowledge installation (with HTTPS and Traefik) +## Zero-knowledge installation (with HTTPS) -If you don't have any clue about docker and stuff but only want to have fun with friends in a shared browser, we got you covered! +No experience with Docker and reverse proxy? No problem! Follow these steps to set up your Neko Rooms quickly and securely: - Rent a VPS with public IP and OS Ubuntu. - Get a domain name pointing to your IP (you can even get some for free). - Run install script and follow instructions. -- Secure using HTTPs thanks to Let's Encrypt and Traefik. +- Secure using HTTPs thanks to Let's Encrypt and Traefik or NGINX. +### Traefik version ```bash -wget -O neko-rooms.sh https://raw.githubusercontent.com/m1k1o/neko-rooms/master/traefik/install -sudo bash neko-rooms.sh +wget -O neko-rooms-traefik.sh https://raw.githubusercontent.com/m1k1o/neko-rooms/master/traefik/install +sudo bash neko-rooms-traefik.sh +``` + +### NGINX version +```bash +wget -O neko-rooms-nginx.sh https://raw.githubusercontent.com/m1k1o/neko-rooms/master/nginx/install +sudo bash neko-rooms-nginx.sh ``` ## How to start diff --git a/nginx/install b/nginx/install new file mode 100644 index 0000000..8d453ea --- /dev/null +++ b/nginx/install @@ -0,0 +1,802 @@ +#!/bin/bash + +set -e + +# Colors for output +GREEN='\033[0;32m' +AQUA='\033[0;36m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Variables to track the installation progress +CREATE_DOCKER_COMPOSE=false +CLONE_NEKO_ROOMS=false +CREATE_NGINX_CONFIG=false +CREATE_HTPASSWD=false +OBTAIN_CERTIFICATE=false +CREATE_CRON_JOB=false +CREATE_MANAGE_SCRIPT=false + +# Function to roll back the installation on error +rollback() { + echo -e "${RED}\nAn error occurred. Rolling back partial installation...${NC}" + + # 1. Remove Docker containers/images if the compose file was created + if [ "$CREATE_DOCKER_COMPOSE" == true ]; then + echo -e "${YELLOW}Removing Docker containers and images...${NC}" + cd /opt/neko-rooms || true + docker-compose down || true + + docker rmi m1k1o/neko-rooms:latest -f || true + rm -f docker-compose.yml + cd ~ || true + fi + + # 2. Remove cloned repository + if [ "$CLONE_NEKO_ROOMS" == true ]; then + echo -e "${YELLOW}Removing cloned neko-rooms repo...${NC}" + rm -rf "/opt/neko-rooms" || true + fi + + # 3. Remove NGINX configuration + if [ "$CREATE_NGINX_CONFIG" == true ]; then + echo -e "${YELLOW}Removing NGINX configuration...${NC}" + rm -f "/etc/nginx/sites-available/${DOMAIN}-neko-rooms.conf" + rm -f "/etc/nginx/sites-enabled/${DOMAIN}-neko-rooms.conf" + systemctl reload nginx || true + fi + + # 4. Remove .htpasswd file + if [ "$CREATE_HTPASSWD" == true ]; then + echo -e "${YELLOW}Removing .htpasswd file...${NC}" + rm -f "/etc/nginx/.htpasswd" + fi + + # 5. Remove Certbot certificates and configuration + if [ "$OBTAIN_CERTIFICATE" == true ]; then + echo -e "${YELLOW}Removing generated certificates...${NC}" + certbot delete --cert-name "$DOMAIN" -n || true + + rm -f "/etc/letsencrypt/renewal/${DOMAIN}.conf" || true + fi + + # 6. Remove the cron job + if [ "$CREATE_CRON_JOB" == true ]; then + echo -e "${YELLOW}Removing Certbot cron job...${NC}" + rm -f "/etc/cron.d/certbot-renew" + systemctl restart cron || true + fi + + # 7. Remove the manage_htpasswd script + if [ "$CREATE_MANAGE_SCRIPT" == true ]; then + echo -e "${YELLOW}Removing manage_htpasswd.sh...${NC}" + rm -f "/usr/local/bin/manage_htpasswd.sh" + fi + + echo -e "${GREEN}================================================${NC}" + echo -e "${RED}Rollback complete. Exiting.${NC}" + echo -e "${GREEN}================================================${NC}" + exit 1 +} + +# Function to get all server IPs +get_server_ips() { + local ip_list + local fallback_ip_service="checkip.amazonaws.com" + + ip_list=$(hostname -I | awk '{for (i=1; i<=NF; i++) if ($i !~ /^172\./ && $i !~ /^10\./ && $i !~ /^192\.168\./) print $i}' | tr '\n' ', ' | sed 's/, $//') + + # If the hostname command fails, use the following methods + if [ -z "$ip_list" ]; then + ip_list=$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null) + if [ $? -ne 0 ]; then + echo "dig command failed, falling back to $fallback_ip_service" >&2 + fi + fi + + if [ -z "$ip_list" ]; then + ip_list=$(curl -s $fallback_ip_service) + fi + + echo "$ip_list" +} + +# Get all server IPs +SERVER_IPS=$(get_server_ips) + + + +# Introduction message +echo -e "${GREEN}==================================================${NC}" +echo -e "${GREEN}Neko Rooms Installer${NC} ${AQUA}by H1ghSyst3m${NC}" +echo -e "This scipt uses nginx as reverse proxy, certbot and docker to install Neko Rooms." +echo -e "${GREEN}==================================================${NC}" +echo -e "${YELLOW}This script will:${NC}" +echo -e " 1. Install Docker, Docker Compose, NGINX, and Certbot." +echo -e " 2. Deploy Neko Rooms using Docker Compose." +echo -e " 3. Configure NGINX as a reverse proxy." +echo -e " 4. Obtain and configure an SSL certificate." +echo -e " 5. Add a cron job for automatic SSL renewal." +echo -e "${GREEN}==================================================${NC}" +echo -e "${YELLOW}Please ensure the following before proceeding:${NC}" +echo -e " - ${GREEN}OS:${NC}" +echo -e " - Kernel version 2 or higher." +echo -e " - Debian 9 or higher." +echo -e " - Ubuntu 18.04 or higher." +echo -e " - ${GREEN}Hardware:${NC}" +echo -e " - Memory at least 2GB." +echo -e " - CPU at least 4 cores." +echo -e " - Disk at least 8GB." +echo -e " - ${GREEN}Network:${NC}" +echo -e " - Public IP: $SERVER_IPS" +echo -e " - Free TCP ports: 80 and 443." +echo -e " - Free UDP port range: 59000-59100." +echo -e " - Domain name pointing to your IP: $SERVER_IPS" +echo -e " - Run this script as superuser." +echo -e "${GREEN}==================================================${NC}\n" + +# Detect Debian users running the script with "sh" instead of bash +if readlink /proc/"$$"/exe | grep -q "dash"; then + echo 'This installer needs to be run with "bash", not "sh".' >&2 + exit 1 +fi + +# Check if the script is run as root +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Please run as root or use sudo.${NC}" + exit 1 +fi + +# Function to ask the user to continue +ask_to_continue() { + while true; do + echo -e "${YELLOW}Do you want to continue? (yes/no)${NC}" + read -r response + case "$response" in + [Yy]* ) break ;; + [Nn]* ) + echo -e "${RED}Installation aborted by user.${NC}" + exit 1 ;; + * ) + echo -e "${RED}Invalid input. Please answer yes or no.${NC}" ;; + esac + done +} + +# Function to detect OS and Kernel +detect_os_and_kernel() { + # Detect OS + if grep -qs "ubuntu" /etc/os-release; then + OS_VERSION="$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')" + + if [[ "${OS_VERSION}" -lt 1804 ]]; then + echo "Ubuntu 18.04 or higher is required to use this installer." >&2 + echo "This version of Ubuntu is too old and unsupported." >&2 + exit 1 + fi + elif [[ -e /etc/debian_version ]]; then + OS_VERSION="$(grep -oE '[0-9]+' /etc/debian_version | head -1)" + + if [[ "${OS_VERSION}" -lt 9 ]]; then + echo "Debian 9 or higher is required to use this installer." >&2 + echo "This version of Debian is too old and unsupported." >&2 + exit 1 + fi + else + echo "This installer seems to be running on an unsupported distribution." >&2 + echo "Supported distributions are Ubuntu and Debian." >&2 + exit 1 + fi + + # Detect Kernel + if [[ "$(uname -r | cut -d "." -f 1)" -eq 2 ]]; then + echo "The system is running an old kernel, which is incompatible with this installer." >&2 + exit 1 + fi +} + +# Function to check and install required packages +install_required_packages() { + echo -e "${YELLOW}Checking and installing required packages: dnsutils and git...${NC}" + + # Update package list and install required packages + apt update -y >/dev/null 2>&1 + apt install -y dnsutils git >/dev/null 2>&1 + + # Verify installation of `dnsutils` + if ! command -v dig &>/dev/null; then + echo -e "${RED}Failed to install dnsutils. Please check your network or package manager settings.${NC}" + exit 1 + fi + + # Verify installation of `git` + if ! command -v git &>/dev/null; then + echo -e "${RED}Failed to install git. Please check your network or package manager settings.${NC}" + exit 1 + fi + + echo -e "${GREEN}Required packages installed successfully.${NC}" +} + + +# Function to validate user input +validate_input() { + local prompt="$1" + local var_name="$2" + local default="$3" + + while true; do + echo -e "${YELLOW}${prompt}${NC} ${default:+[Default: $default]}" + read input + input="${input:-$default}" + if [ -z "$input" ]; then + echo -e "${RED}Input cannot be empty. Please try again.${NC}" + else + eval "$var_name=\"$input\"" + break + fi + done +} + +# Function to validate email +validate_email() { + local email="$1" + if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then + return 0 + else + return 1 + fi +} + +# Function to validate domain +validate_domain() { + local domain="$1" + if [[ "$domain" =~ ^(([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,}$ ]]; then + return 0 + else + return 1 + fi +} + +# Function to validate and get domain +validate_and_get_domain() { + while true; do + validate_input "Enter your domain name (e.g., watch.example.com, example.com):" DOMAIN + if validate_domain "$DOMAIN"; then + # Get the A record of the domain + DOMAIN_IP=$(dig +short "$DOMAIN" | tail -n 1) + + # Convert SERVER_IPS into a line-separated list for better matching + SERVER_IPS_LIST=$(echo "$SERVER_IPS" | tr ',' '\n' | tr -d ' ') + + # Check if the domain IP matches any server IP + if echo "$SERVER_IPS_LIST" | grep -q "^$DOMAIN_IP$"; then + echo -e "${GREEN}Domain $DOMAIN is correctly pointing to the server's IP: $DOMAIN_IP.${NC}" + break + else + echo -e "${RED}Domain $DOMAIN does not point to any of the server's IPs.${NC}" + echo -e "${YELLOW}Current IP of $DOMAIN: ${DOMAIN_IP:-Not Found}${NC}" + echo -e "${YELLOW}Your Server IPs: $SERVER_IPS${NC}" + echo -e "${YELLOW}Please update the domain's A record to point to one of the server's IPs and try again.${NC}" + echo -e "" + + # Ask the user if they want to continue with the domain anyway + while true; do + echo -e "${YELLOW}Do you want to continue with this domain anyway? (yes/no)${NC}" + read -r response + case "$response" in + [Yy]* ) echo -e "${YELLOW}Continuing with domain $DOMAIN despite the IP mismatch.${NC}"; break 2 ;; + [Nn]* ) echo -e "${RED}Please update the domain's A record and try again.${NC}"; break ;; + * ) echo -e "${RED}Invalid input. Please answer yes or no.${NC}" ;; + esac + done + fi + else + echo -e "${RED}Invalid domain name. Please enter a valid domain.${NC}" + fi + done +} + +# Function to validate and get email +validate_and_get_email() { + while true; do + validate_input "Enter your email for SSL certificate:" EMAIL + if validate_email "$EMAIL"; then + break + else + echo -e "${RED}Invalid email address. Please enter a valid email.${NC}" + fi + done +} + +# Function to confirm passwords +confirm_password() { + local prompt="$1" + local var_name="$2" + + while true; do + echo -e "${YELLOW}${prompt}:${NC}" + read -s password1 + echo -e "${YELLOW}Confirm password:${NC}" + read -s password2 + + if [ "$password1" != "$password2" ]; then + echo -e "${RED}Passwords do not match. Please try again.${NC}" + else + eval "$var_name=\"$password1\"" + break + fi + done +} + +# Function to validate and check Room port range +check_ports() { + local port_range="$1" + + IFS='-' read -r start_port end_port <<<"$port_range" + if [[ ! "$start_port" =~ ^[0-9]+$ ]] || [[ ! "$end_port" =~ ^[0-9]+$ ]] || ((start_port > end_port)); then + echo -e "${RED}Invalid port range: $port_range. Please use the format START-END (e.g., 59000-59100).${NC}" + return 1 + fi + + for ((port = start_port; port <= end_port; port++)); do + if ss -lnu | grep -q ":$port "; then + echo -e "${RED}Port $port is already in use. Please choose a different range.${NC}" + return 1 + fi + done + + echo -e "${GREEN}Ports $port_range are free.${NC}" + return 0 +} + +# Function to validate a single port +validate_port() { + local port="$1" + if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then + return 0 + else + return 1 + fi +} + +# Check if a port is available +check_port_availability() { + local port="$1" + if ss -ln | grep -q ":$port "; then + return 1 + else + return 0 + fi +} + +# Function to validate and check Room port range +validate_room_port_range() { + while true; do + validate_input "Enter the Room port range [Default: 59000-59100]:" ROOM_PORT_RANGE "59000-59100" + if check_ports "$ROOM_PORT_RANGE"; then + break + else + echo -e "${YELLOW}Please enter a valid and free port range.${NC}" + fi + done +} + +# Function to validate and check Docker port +validate_docker_port() { + while true; do + validate_input "Enter the Docker port for Neko Rooms [Default: 8080]:" DOCKER_PORT "8080" + if validate_port "$DOCKER_PORT" && check_port_availability "$DOCKER_PORT"; then + echo -e "${GREEN}Port $DOCKER_PORT is available.${NC}" + break + else + echo -e "${RED}Port $DOCKER_PORT is either invalid or already in use. Please choose another port.${NC}" + fi + done +} + +# Summarize Configuration +summary() { + echo -e "\n${GREEN}Configuration Summary:${NC}" + echo -e "${YELLOW}Domain:${NC} $DOMAIN" + echo -e "${YELLOW}Email:${NC} $EMAIL" + echo -e "${YELLOW}Timezone:${NC} $TIMEZONE" + echo -e "${YELLOW}Room Port Range:${NC} $ROOM_PORT_RANGE" + echo -e "${YELLOW}Docker Port:${NC} $DOCKER_PORT" + echo -e "${YELLOW}Rooms Path Prefix:${NC} $PATH_PREFIX" + echo -e "${YELLOW}Pointed IP:${NC} $DOMAIN_IP" + echo -e "\n${GREEN}Please review the above settings carefully.${NC}" +} + +ask_to_continue +detect_os_and_kernel +echo "" +install_required_packages +echo "" +echo "" +validate_and_get_domain +echo "" +validate_and_get_email +echo "" +validate_input "Enter admin username for authentication:" ADMIN_USER +echo "" +confirm_password "Enter admin password for authentication" ADMIN_PASSWORD +echo "" +validate_input "Enter your timezone (e.g., Europe/Vienna) [Default: UTC]:" TIMEZONE "UTC" +echo "" +validate_room_port_range +echo "" +validate_docker_port +echo "" +echo -e "${YELLOW}Choose a path prefix for accessing Neko Rooms.${NC}" +echo -e "${YELLOW}Example: If you choose 'room', rooms will be accessible at:${NC} ${GREEN}https://${DOMAIN}/room/${NC}" +validate_input "Enter your path prefix (e.g., room, home, browser) [Default: room]:" PATH_PREFIX "room" + +summary +echo "" +ask_to_continue + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +install_packages() { + local packages=$@ + echo -e "${YELLOW}Installing required packages: $packages...${NC}" + + # Start installation in the background + (apt update && apt install -y $packages) & + + # Get the process ID and show the spinner + show_spinner $! +} + +# Precheck for Docker +check_docker() { + if command_exists docker; then + echo -e "${GREEN}Docker is already installed.${NC}" + else + echo -e "${YELLOW}Docker is not installed. Installing Docker...${NC}" + curl -sSL https://get.docker.com/ | CHANNEL=stable bash + systemctl enable docker + systemctl start docker + fi +} + +# Precheck for Docker Compose +check_docker_compose() { + if command_exists docker-compose; then + echo -e "${GREEN}Docker Compose is already installed.${NC}" + else + echo -e "${YELLOW}Docker Compose is not installed. Installing Docker Compose...${NC}" + DOCKER_COMPOSE_VERSION="1.29.2" + curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + fi +} + +# Precheck for Crontab +check_crontab() { + if command -v crontab >/dev/null 2>&1; then + echo -e "${GREEN}Crontab is already installed.${NC}" + else + echo -e "${YELLOW}Crontab is not installed. Installing...${NC}" + apt update + apt install -y cron + systemctl enable cron + systemctl start cron + fi +} + +# Precheck for NGINX +check_nginx() { + if command_exists nginx; then + echo -e "${GREEN}NGINX is already installed.${NC}" + else + echo -e "${YELLOW}NGINX is not installed. Installing NGINX...${NC}" + apt update + apt install -y nginx + systemctl enable nginx + systemctl start nginx + fi +} + +# Precheck and Installation for Docker, Docker Compose, crontab and NGINX +echo -e "${AQUA}\n==================================================${NC}" +check_docker +echo -e "${AQUA}\n==================================================${NC}" +check_docker_compose +echo -e "${AQUA}\n==================================================${NC}" +check_crontab +echo -e "${AQUA}\n==================================================${NC}" +check_nginx +echo -e "${AQUA}\n==================================================${NC}" + +# Install other dependencies +echo -e "${YELLOW}Installing required packages...${NC}" +apt update +apt install -y certbot python3-certbot-nginx apache2-utils +echo -e "${AQUA}\n==================================================${NC}" + +# Clone Neko Rooms repository +echo -e "${YELLOW}Cloning Neko Rooms repository...${NC}" +git clone https://github.com/m1k1o/neko-rooms.git /opt/neko-rooms || echo -e "${GREEN}Neko Rooms repository already exists. Skipping...${NC}" +cd /opt/neko-rooms +CLONE_NEKO_ROOMS=true +echo -e "${AQUA}\n==================================================${NC}" + +# Modify docker-compose.yml +echo -e "${YELLOW}Configuring Neko Rooms${NC} ${AQUA}docker-compose.yml${NC}${YELLOW}...${NC}" +cat < docker-compose.yml +version: "3.5" + +networks: + default: + attachable: true + name: "neko-rooms-net" + +services: + neko-rooms: + image: "m1k1o/neko-rooms:latest" + restart: "unless-stopped" + environment: + - "TZ=${TIMEZONE}" + - "NEKO_ROOMS_MUX=true" + - "NEKO_ROOMS_EPR=${ROOM_PORT_RANGE}" + - "NEKO_ROOMS_NAT1TO1=${DOMAIN_IP}" + - "NEKO_ROOMS_INSTANCE_URL=https://${DOMAIN}/" + - "NEKO_ROOMS_INSTANCE_NETWORK=neko-rooms-net" + - "NEKO_ROOMS_TRAEFIK_ENABLED=false" + - "NEKO_ROOMS_PATH_PREFIX=/${PATH_PREFIX}/" + - "NEKO_ROOMS_STORAGE_ENABLED=true" + - "NEKO_ROOMS_STORAGE_INTERNAL=/data" + - "NEKO_ROOMS_STORAGE_EXTERNAL=/opt/neko-rooms/data" + ports: + - "127.0.0.1:${DOCKER_PORT}:8080" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock" + - "/opt/neko-rooms/data:/data" +EOF +CREATE_DOCKER_COMPOSE=true +echo -e "${GREEN} Neko Rooms configuration updated.${NC}" +echo -e "${AQUA}\n==================================================${NC}" + +# Start Neko Rooms +echo -e "${GREEN}Starting Neko Rooms service...${NC}" +docker-compose up -d +echo -e "${AQUA}\n==================================================${NC}" + +# Configure NGINX without SSL +echo -e "${YELLOW}Configuring NGINX without SSL...${NC}" +cat < /etc/nginx/sites-available/${DOMAIN}-neko-rooms.conf +server { + listen 80; + server_name ${DOMAIN}; + + # Public rooms: no authentication + location ~ ^/${PATH_PREFIX}/.*$ { + proxy_pass http://127.0.0.1:${DOCKER_PORT}; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_cache_bypass \$http_upgrade; + } + + # Admin Panel Restricted Access + location / { + auth_basic "Restricted Access"; + auth_basic_user_file /etc/nginx/.htpasswd; + + error_log /var/log/nginx/auth_error.log warn; + + proxy_pass http://127.0.0.1:${DOCKER_PORT}; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_cache_bypass \$http_upgrade; + } +} +EOF + +# Enable the NGINX configuration +ln -sf /etc/nginx/sites-available/${DOMAIN}-neko-rooms.conf /etc/nginx/sites-enabled/ +CREATE_NGINX_CONFIG=true +systemctl reload nginx +echo -e "${GREEN}NGINX configuration updated and reloaded.${NC}" +echo -e "${AQUA}\n==================================================${NC}" + + +# Create a password file for admin access +echo -e "${GREEN}Creating admin authentication...${NC}" +htpasswd -cb /etc/nginx/.htpasswd ${ADMIN_USER} ${ADMIN_PASSWORD} +CREATE_HTPASSWD=true +echo -e "${AQUA}\n==================================================${NC}" + +# Obtain SSL certificate and configure NGINX for SSL +echo -e "${GREEN}Obtaining SSL certificate and updating NGINX for SSL...${NC}" +certbot --nginx -d ${DOMAIN} --non-interactive --agree-tos -m ${EMAIL} +OBTAIN_CERTIFICATE=true +systemctl reload nginx +echo -e "${AQUA}\n==================================================${NC}" + +# Create a separate crontab file for Certbot renewal +echo -e "${GREEN}Creating a separate crontab file for Certbot auto-renewal...${NC}" +cat < /etc/cron.d/certbot-renew +# Cron job for Certbot auto-renewal +0 3 * * 0 root certbot renew --quiet && systemctl reload nginx +EOF +CREATE_CRON_JOB=true + +# Set appropriate permissions +chmod 644 /etc/cron.d/certbot-renew + +# Reload cron to apply changes +echo -e "${GREEN}Reloading cron daemon to apply the new cron job...${NC}" +systemctl restart cron + +# Verify the cron job +echo -e "${GREEN}Cron job file created at /etc/cron.d/certbot-renew:${NC}" +cat /etc/cron.d/certbot-renew +echo -e "${AQUA}\n==================================================${NC}" + +# Generate a script to manage admin users +echo -e "${GREEN}Generating script to manage admin users...${NC}" +cat <<'EOF' > /usr/local/bin/manage_htpasswd.sh +#!/bin/bash + +HTPASSWD_FILE="/etc/nginx/.htpasswd" + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Check if htpasswd exists +if ! command -v htpasswd &>/dev/null; then + echo -e "${RED}Error: htpasswd command not found. Please install apache2-utils.${NC}" + exit 1 +fi + +# Function to display usage +usage() { + echo -e "${YELLOW}Usage:${NC} $0 {add|remove|list|change-password} [username]" + echo -e "${YELLOW}Commands:${NC}" + echo -e " ${GREEN}add${NC} Add a new user or update an existing user's password" + echo -e " ${GREEN}remove${NC} Remove a user" + echo -e " ${GREEN}list${NC} List all users" + echo -e " ${GREEN}change-password${NC} Change the password of an existing user" + exit 1 +} + +# Ensure the .htpasswd file exists +if [ ! -f "$HTPASSWD_FILE" ]; then + echo -e "${YELLOW}The .htpasswd file does not exist. Creating a new one...${NC}" + touch "$HTPASSWD_FILE" +fi + +# Function to prompt for a password +prompt_password() { + while true; do + echo -e "${YELLOW}Enter password:${NC}" + read -rsp "Password: " password1 + echo + echo -e "${YELLOW}Confirm password:${NC}" + read -rsp "Confirm Password: " password2 + echo + + if [ "$password1" != "$password2" ]; then + echo -e "${RED}Passwords do not match. Please try again.${NC}" + elif [ -z "$password1" ]; then + echo -e "${RED}Password cannot be empty. Please try again.${NC}" + else + echo "$password1" + return 0 + fi + done +} + +# Add or update a user +add_user() { + local username="$1" + echo -e "${YELLOW}Adding or updating user '$username'.${NC}" + sudo htpasswd "$HTPASSWD_FILE" "$username" + if [ $? -eq 0 ]; then + echo -e "${GREEN}User '$username' has been added or updated successfully.${NC}" + else + echo -e "${RED}Failed to add or update user '$username'. Please check your inputs.${NC}" + exit 1 + fi +} + +# Change user password +change_password() { + local username="$1" + if ! grep -q "^$username:" "$HTPASSWD_FILE"; then + echo -e "${RED}Error: User '$username' does not exist.${NC}" + exit 1 + fi + echo -e "${YELLOW}Changing password for user '$username'.${NC}" + sudo htpasswd "$HTPASSWD_FILE" "$username" + if [ $? -eq 0 ]; then + echo -e "${GREEN}Password for user '$username' has been updated successfully.${NC}" + else + echo -e "${RED}Failed to update password for user '$username'. Please check your inputs.${NC}" + exit 1 + fi +} + +# Remove a user +remove_user() { + local username="$1" + if ! grep -q "^$username:" "$HTPASSWD_FILE"; then + echo -e "${RED}Error: User '$username' does not exist.${NC}" + exit 1 + fi + sed -i "/^$username:/d" "$HTPASSWD_FILE" + echo -e "${GREEN}User '$username' has been removed.${NC}" +} + +# List all users +list_users() { + if [ ! -s "$HTPASSWD_FILE" ]; then + echo -e "${YELLOW}No users found in the .htpasswd file.${NC}" + else + echo -e "${GREEN}Users in the .htpasswd file:${NC}" + cut -d: -f1 "$HTPASSWD_FILE" + fi +} + + +# Main logic +case "$1" in +add) + if [ -z "$2" ]; then + echo -e "${RED}Error: Username is required for the 'add' command.${NC}" + usage + fi + add_user "$2" + ;; +remove) + if [ -z "$2" ]; then + echo -e "${RED}Error: Username is required for the 'remove' command.${NC}" + usage + fi + remove_user "$2" + ;; +list) + list_users + ;; +change-password) + if [ -z "$2" ]; then + echo -e "${RED}Error: Username is required for the 'change-password' command.${NC}" + usage + fi + change_password "$2" + ;; +*) + usage + ;; +esac +EOF +CREATE_MANAGE_SCRIPT=true + +# Make the script executable +chmod +x /usr/local/bin/manage_htpasswd.sh + +echo -e "${GREEN}Script generated: /usr/local/bin/manage_htpasswd.sh${NC}" +echo -e "${YELLOW}You can use this script to manage admin panel users:${NC}" +echo -e "${YELLOW} - Add user: /usr/local/bin/manage_htpasswd.sh add ${NC}" +echo -e "${YELLOW} - Remove user: /usr/local/bin/manage_htpasswd.sh remove ${NC}" +echo -e "${YELLOW} - Change password: /usr/local/bin/manage_htpasswd.sh change-password ${NC}" +echo -e "${YELLOW} - List users: /usr/local/bin/manage_htpasswd.sh list${NC}" +echo -e "${AQUA}\n==================================================${NC}" + + +# Final message +echo -e "${GREEN}Installation complete! Access your Neko Rooms at https://${DOMAIN}${NC}" +