diff --git a/install.sh b/install.sh index d2250a8..6157c95 100644 --- a/install.sh +++ b/install.sh @@ -1,40 +1,48 @@ #!/usr/bin/env bash -# NixPanel Installer -# https://github.com/NixReaper/nixpanel +# ────────────────────────────────────────────────────────────────────────────── +# NixPanel Installer · Ubuntu 24.04 LTS · x86_64 +# Installs: WHM (NixServer), cPanel (NixClient), Licensing Server, +# Website API, and all system dependencies. +# ────────────────────────────────────────────────────────────────────────────── set -euo pipefail -RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m' -BOLD='\033[1m'; RESET='\033[0m' +# ── Colours ─────────────────────────────────────────────────────────────────── +RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m' +YELLOW='\033[1;33m'; BOLD='\033[1m'; RESET='\033[0m' info() { echo -e "${CYAN}[INFO]${RESET} $*"; } -success() { echo -e "${GREEN}[OK]${RESET} $*"; } +success() { echo -e "${GREEN}[ OK ]${RESET} $*"; } warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } -error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; } +error() { echo -e "${RED}[ERR ]${RESET} $*" >&2; exit 1; } step() { echo -e "\n${BOLD}${CYAN}━━━ $* ━━━${RESET}"; } -# ── Preflight ────────────────────────────────────────────────────────────────── - -[[ $EUID -ne 0 ]] && error "Must be run as root (sudo bash install.sh)" +# ── Preflight ───────────────────────────────────────────────────────────────── +[[ $EUID -ne 0 ]] && error "Must be run as root → sudo bash install.sh" OS_ID=$(. /etc/os-release && echo "$ID") -OS_VERSION=$(. /etc/os-release && echo "$VERSION_ID") +OS_VER=$(. /etc/os-release && echo "$VERSION_ID") ARCH=$(uname -m) -[[ "$OS_ID" != "ubuntu" ]] && error "NixPanel requires Ubuntu 24.04 LTS (detected: $OS_ID)" -[[ "$OS_VERSION" != "24.04" ]] && error "NixPanel requires Ubuntu 24.04 LTS (detected: Ubuntu $OS_VERSION)" -[[ "$ARCH" != "x86_64" ]] && error "NixPanel requires x86_64 architecture (detected: $ARCH)" +[[ "$OS_ID" != "ubuntu" ]] && error "Requires Ubuntu 24.04 LTS (detected: $OS_ID)" +[[ "$OS_VER" != "24.04" ]] && error "Requires Ubuntu 24.04 LTS (detected: Ubuntu $OS_VER)" +[[ "$ARCH" != "x86_64" ]] && error "Requires x86_64 (detected: $ARCH)" +# ── Paths ───────────────────────────────────────────────────────────────────── INSTALL_DIR="/opt/nixpanel" -DATA_DIR="/opt/nixpanel/data" -LOG_DIR="/opt/nixpanel/logs" +BIN_DIR="$INSTALL_DIR/bin" +KEYS_DIR="$INSTALL_DIR/keys" +LOG_DIR="/var/log/nixpanel" STEP_DIR="/var/lib/nixpanel/install" +SRC_DIR="$INSTALL_DIR/src" GITHUB_REPO="NixReaper/nixpanel" -mkdir -p "$STEP_DIR" "$DATA_DIR" "$LOG_DIR" +mkdir -p "$INSTALL_DIR" "$BIN_DIR" "$KEYS_DIR" "$LOG_DIR" "$STEP_DIR" "$SRC_DIR" +chmod 700 "$KEYS_DIR" -mark_done() { touch "$STEP_DIR/$1.done"; } -step_done() { [[ -f "$STEP_DIR/$1.done" ]]; } +mark_done() { touch "$STEP_DIR/$1.done"; } +step_done() { [[ -f "$STEP_DIR/$1.done" ]]; } +# ── Banner ──────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}${CYAN}" echo " ███╗ ██╗██╗██╗ ██╗██████╗ █████╗ ███╗ ██╗███████╗██╗ " @@ -47,15 +55,7 @@ echo -e "${RESET}" echo -e " Web Hosting Control Panel · Ubuntu 24.04 · x86_64" echo "" -# ── Helper: latest release version ──────────────────────────────────────────── - -get_latest_version() { - curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \ - | grep '"tag_name"' | cut -d'"' -f4 -} - -# ── System packages ──────────────────────────────────────────────────────────── - +# ── 1. System update ────────────────────────────────────────────────────────── step "Updating system packages" if ! step_done "apt_update"; then export DEBIAN_FRONTEND=noninteractive @@ -65,20 +65,23 @@ if ! step_done "apt_update"; then fi success "System packages up to date" +# ── 2. Free port 53 for PowerDNS ───────────────────────────────────────────── step "Freeing port 53 for PowerDNS" if ! step_done "resolved_stub"; then - sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf 2>/dev/null || true - sed -i 's/^DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf 2>/dev/null || true + sed -i '/^#\?DNSStubListener=/d' /etc/systemd/resolved.conf echo "DNSStubListener=no" >> /etc/systemd/resolved.conf systemctl restart systemd-resolved 2>/dev/null || true mark_done "resolved_stub" fi success "Port 53 free" -step "Installing service stack" +# ── 3. System packages ──────────────────────────────────────────────────────── +step "Installing system packages" if ! step_done "packages"; then export DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \ + build-essential pkg-config libssl-dev libmariadb-dev curl wget git unzip \ + ca-certificates gnupg software-properties-common \ apache2 \ php8.3 php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-mbstring \ php8.3-xml php8.3-zip php8.3-gd php8.3-intl php8.3-bcmath \ @@ -91,187 +94,353 @@ if ! step_done "packages"; then spamassassin spamc \ certbot python3-certbot-apache \ fail2ban \ - curl wget git unzip software-properties-common \ - openssl ca-certificates \ - nodejs npm 2>/dev/null || true + nodejs npm \ + openssl mark_done "packages" fi -success "Service stack installed" +success "System packages installed" -# ── Apache ───────────────────────────────────────────────────────────────────── - -step "Configuring Apache" -if ! step_done "apache"; then - a2enmod rewrite ssl proxy proxy_http proxy_wstunnel proxy_fcgi headers expires deflate 2>/dev/null || true - systemctl enable apache2 - systemctl start apache2 - mark_done "apache" +# ── 4. Rust toolchain ───────────────────────────────────────────────────────── +step "Installing Rust toolchain" +if ! step_done "rust"; then + # Install for root (used by this script) and persist for system builds + curl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal + source "$HOME/.cargo/env" + rustup target add x86_64-unknown-linux-gnu + mark_done "rust" +else + # Ensure cargo is on PATH for subsequent steps + source "$HOME/.cargo/env" 2>/dev/null || true fi -success "Apache configured" +CARGO=$(command -v cargo) || error "cargo not found — Rust install may have failed" +success "Rust $(rustc --version) installed" -# ── MariaDB ──────────────────────────────────────────────────────────────────── - -step "Configuring MariaDB" -if ! step_done "mariadb"; then - systemctl enable mariadb - systemctl start mariadb - mark_done "mariadb" +# ── 5. Start services ───────────────────────────────────────────────────────── +step "Starting service stack" +if ! step_done "services_start"; then + a2enmod rewrite ssl proxy proxy_http proxy_wstunnel proxy_fcgi headers expires deflate + systemctl enable --now apache2 mariadb + systemctl enable pdns && systemctl start pdns 2>/dev/null || true + systemctl enable spamd && systemctl start spamd 2>/dev/null || true + mark_done "services_start" fi -success "MariaDB ready" +success "Service stack running" -# ── PowerDNS ─────────────────────────────────────────────────────────────────── +# ── 6. MariaDB databases ────────────────────────────────────────────────────── +step "Configuring MariaDB databases" +if ! step_done "mariadb_setup"; then + DB_PASS_PANEL=$(openssl rand -hex 20) + DB_PASS_LICENSE=$(openssl rand -hex 20) + DB_PASS_WEBSITE=$(openssl rand -hex 20) -step "Configuring PowerDNS" -if ! step_done "pdns"; then - systemctl enable pdns - systemctl start pdns 2>/dev/null || true - mark_done "pdns" + mysql -u root < "$INSTALL_DIR/.db_pass_panel" + echo "$DB_PASS_LICENSE" > "$INSTALL_DIR/.db_pass_license" + echo "$DB_PASS_WEBSITE" > "$INSTALL_DIR/.db_pass_website" + chmod 600 "$INSTALL_DIR"/.db_pass_* + + mark_done "mariadb_setup" +else + DB_PASS_PANEL=$(cat "$INSTALL_DIR/.db_pass_panel") + DB_PASS_LICENSE=$(cat "$INSTALL_DIR/.db_pass_license") + DB_PASS_WEBSITE=$(cat "$INSTALL_DIR/.db_pass_website") fi -success "PowerDNS ready" +success "Databases created" -# ── SpamAssassin ─────────────────────────────────────────────────────────────── +# ── 7. Download / update source ─────────────────────────────────────────────── +step "Downloading NixPanel source" +if ! step_done "source"; then + LATEST_TAG=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \ + | grep '"tag_name"' | cut -d'"' -f4 || echo "main") + [[ -z "$LATEST_TAG" ]] && LATEST_TAG="main" + info "Fetching tag: $LATEST_TAG" -step "Configuring SpamAssassin" -if ! step_done "spamd"; then - systemctl enable spamd - systemctl start spamd 2>/dev/null || true - mark_done "spamd" + if [[ -d "$SRC_DIR/.git" ]]; then + git -C "$SRC_DIR" fetch --tags --quiet + git -C "$SRC_DIR" checkout "$LATEST_TAG" --quiet + else + git clone --depth 1 --branch "$LATEST_TAG" \ + "https://github.com/${GITHUB_REPO}.git" "$SRC_DIR" 2>/dev/null \ + || git clone --depth 1 "https://github.com/${GITHUB_REPO}.git" "$SRC_DIR" + fi + mark_done "source" fi -success "SpamAssassin ready" +success "Source ready" -# ── Download NixPanel release ────────────────────────────────────────────────── - -step "Downloading NixPanel" -if ! step_done "download"; then - VERSION=$(get_latest_version) - [[ -z "$VERSION" ]] && error "Could not determine latest NixPanel version" - info "Latest version: $VERSION" - - mkdir -p "$INSTALL_DIR" - TMP_DIR=$(mktemp -d) - TARBALL_URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/nixpanel-${VERSION}-linux-x64.tar.gz" - - info "Downloading release tarball..." - curl -fsSL "$TARBALL_URL" -o "$TMP_DIR/nixpanel.tar.gz" \ - || error "Failed to download NixPanel $VERSION from GitHub Releases" - - tar -xzf "$TMP_DIR/nixpanel.tar.gz" -C "$TMP_DIR" - cp -a "$TMP_DIR/nixpanel-${VERSION}"/. "$INSTALL_DIR/" - rm -rf "$TMP_DIR" - mark_done "download" +# ── 8. Build Rust binaries ──────────────────────────────────────────────────── +step "Building nixpanel-licensing (Rust)" +if ! step_done "build_licensing"; then + cd "$SRC_DIR/nixpanel-licensing" + $CARGO build --release --quiet + cp target/release/nixpanel-licensing "$BIN_DIR/nixpanel-licensing" + cp target/release/keygen "$BIN_DIR/nixpanel-keygen" + chmod 755 "$BIN_DIR/nixpanel-licensing" "$BIN_DIR/nixpanel-keygen" + mark_done "build_licensing" fi -success "NixPanel downloaded" +success "nixpanel-licensing built" -# ── Environment ──────────────────────────────────────────────────────────────── +step "Building nixpanel-website-api (Rust)" +if ! step_done "build_website_api"; then + cd "$SRC_DIR/nixpanel-website/api" + $CARGO build --release --quiet + cp target/release/nixpanel-website-api "$BIN_DIR/nixpanel-website-api" + chmod 755 "$BIN_DIR/nixpanel-website-api" + mark_done "build_website_api" +fi +success "nixpanel-website-api built" -step "Configuring environment" -if ! step_done "env"; then - if [[ ! -f "$INSTALL_DIR/.env" ]]; then - JWT_SECRET=$(openssl rand -hex 32) - REFRESH_SECRET=$(openssl rand -hex 32) - DB_PASS=$(openssl rand -hex 16) - cat > "$INSTALL_DIR/.env" < "$INSTALL_DIR/.jwt_license" + echo "$JWT_SECRET_WEBSITE" > "$INSTALL_DIR/.jwt_website" + echo "$JWT_SECRET_PANEL" > "$INSTALL_DIR/.jwt_panel" + echo "$LICENSING_API_KEY" > "$INSTALL_DIR/.licensing_api_key" + chmod 600 "$INSTALL_DIR"/.jwt_* "$INSTALL_DIR/.licensing_api_key" + mark_done "secrets" +else + JWT_SECRET_LICENSE=$(cat "$INSTALL_DIR/.jwt_license") + JWT_SECRET_WEBSITE=$(cat "$INSTALL_DIR/.jwt_website") + JWT_SECRET_PANEL=$(cat "$INSTALL_DIR/.jwt_panel") + LICENSING_API_KEY=$(cat "$INSTALL_DIR/.licensing_api_key") +fi +success "Secrets ready" + +# ── 11. Environment files ───────────────────────────────────────────────────── +step "Writing environment files" + +# nixpanel-licensing +cat > "$INSTALL_DIR/licensing.env" < "$INSTALL_DIR/.env" </dev/null || true - mark_done "db_init" -fi -success "Database ready" - -# ── Admin account ────────────────────────────────────────────────────────────── - -step "Creating admin account" -if ! step_done "admin_account"; then - echo "" - echo -e " ${BOLD}Create your NixServer admin account${RESET}" - read -rp " Admin username [admin]: " ADMIN_USER - ADMIN_USER="${ADMIN_USER:-admin}" - read -rsp " Admin password: " ADMIN_PASS - echo "" - read -rsp " Confirm password: " ADMIN_PASS2 - echo "" - [[ "$ADMIN_PASS" != "$ADMIN_PASS2" ]] && error "Passwords do not match" - read -rp " Admin email: " ADMIN_EMAIL - - node "$INSTALL_DIR/scripts/create-admin.js" \ - --username "$ADMIN_USER" \ - --password "$ADMIN_PASS" \ - --email "$ADMIN_EMAIL" \ - || error "Failed to create admin account" - mark_done "admin_account" -fi -success "Admin account ready" - -# ── Permissions ──────────────────────────────────────────────────────────────── - -step "Setting permissions" -chmod 755 "$INSTALL_DIR" -[[ -d "$INSTALL_DIR/nixserver/dist" ]] && { - chmod 755 "$INSTALL_DIR/nixserver" - find "$INSTALL_DIR/nixserver/dist" -type d -exec chmod 755 {} \; - find "$INSTALL_DIR/nixserver/dist" -type f -exec chmod 644 {} \; -} -[[ -d "$INSTALL_DIR/nixclient/dist" ]] && { - chmod 755 "$INSTALL_DIR/nixclient" - find "$INSTALL_DIR/nixclient/dist" -type d -exec chmod 755 {} \; - find "$INSTALL_DIR/nixclient/dist" -type f -exec chmod 644 {} \; -} -chmod 755 "$INSTALL_DIR/bin/nixpanel" 2>/dev/null || true -success "Permissions set" - -# ── Systemd service ──────────────────────────────────────────────────────────── - -step "Installing systemd service" -if ! step_done "systemd"; then - cat > /etc/systemd/system/nixpanel.service <<'EOF' +# ── 12. systemd — licensing server ─────────────────────────────────────────── +step "Installing systemd service: nixpanel-licensing" +if ! step_done "systemd_licensing"; then + cat > /etc/systemd/system/nixpanel-licensing.service < "$INSTALL_DIR/website.env" </dev/null || warn "Admin creation script not found — set credentials via panel after install" + + mark_done "admin_account" +fi +success "Admin account ready" + +# ── 14. systemd — website API ───────────────────────────────────────────────── +step "Installing systemd service: nixpanel-website-api" +if ! step_done "systemd_website"; then + cat > /etc/systemd/system/nixpanel-website-api.service < /etc/systemd/system/nixpanel.service < /etc/apache2/sites-available/nixpanel.conf <<'EOF' -# NixServer — Admin Panel (port 2087) + # Add non-standard ports if not already present + for port in 2083 2087 4001; do + grep -q "^Listen ${port}" /etc/apache2/ports.conf \ + || echo "Listen ${port}" >> /etc/apache2/ports.conf + done + + cat > /etc/apache2/sites-available/nixpanel.conf <<'APACHEEOF' +# ── NixServer — WHM Admin Panel (port 2087) ─────────────────────────────────── ProxyPreserveHost On ProxyPass /api/ http://127.0.0.1:4000/api/ @@ -285,9 +454,11 @@ if ! step_done "apache_vhost"; then Require all granted FallbackResource /index.html + ErrorLog /var/log/nixpanel/nixserver-error.log + CustomLog /var/log/nixpanel/nixserver-access.log combined -# NixClient — User Panel (port 2083) +# ── NixClient — cPanel User Panel (port 2083) ───────────────────────────────── ProxyPreserveHost On ProxyPass /api/ http://127.0.0.1:4000/api/ @@ -299,29 +470,94 @@ if ! step_done "apache_vhost"; then Require all granted FallbackResource /index.html + ErrorLog /var/log/nixpanel/nixclient-error.log + CustomLog /var/log/nixpanel/nixclient-access.log combined -EOF - echo "Listen 2083" >> /etc/apache2/ports.conf - echo "Listen 2087" >> /etc/apache2/ports.conf +# ── NixPanel Website API (port 4001) ────────────────────────────────────────── + + ProxyPreserveHost On + ProxyPass / http://127.0.0.1:4001/ + ProxyPassReverse / http://127.0.0.1:4001/ + +APACHEEOF - a2ensite nixpanel 2>/dev/null || true + a2ensite nixpanel apache2ctl configtest && systemctl reload apache2 mark_done "apache_vhost" fi success "Apache vhosts configured" -# ── Done ─────────────────────────────────────────────────────────────────────── +# ── 17. Fail2ban ────────────────────────────────────────────────────────────── +step "Configuring Fail2ban" +if ! step_done "fail2ban"; then + cat > /etc/fail2ban/jail.d/nixpanel.conf <<'EOF' +[nixpanel-auth] +enabled = true +port = 2083,2087,4001 +filter = nixpanel-auth +logpath = /var/log/nixpanel/*.log +maxretry = 10 +bantime = 3600 +findtime = 600 +EOF + systemctl enable --now fail2ban + mark_done "fail2ban" +fi +success "Fail2ban configured" +# ── 18. File permissions ────────────────────────────────────────────────────── +step "Setting permissions" +chown -R root:root "$INSTALL_DIR" +chmod 700 "$KEYS_DIR" +chmod 600 "$INSTALL_DIR"/.env \ + "$INSTALL_DIR"/licensing.env \ + "$INSTALL_DIR"/website.env 2>/dev/null || true +find "$INSTALL_DIR/bin" -type f -exec chmod 755 {} \; +[[ -d "$INSTALL_DIR/nixserver/dist" ]] && \ + find "$INSTALL_DIR/nixserver/dist" \( -type d -exec chmod 755 {} \; -o -type f -exec chmod 644 {} \; \) +[[ -d "$INSTALL_DIR/nixclient/dist" ]] && \ + find "$INSTALL_DIR/nixclient/dist" \( -type d -exec chmod 755 {} \; -o -type f -exec chmod 644 {} \; \) +success "Permissions set" + +# ── 19. Service health check ────────────────────────────────────────────────── +step "Verifying services" +sleep 2 # give systemd a moment + +check_service() { + if systemctl is-active --quiet "$1"; then + success "$1 is running" + else + warn "$1 is NOT running — check: journalctl -u $1 -n 50" + fi +} + +check_service nixpanel-licensing +check_service nixpanel-website-api +check_service nixpanel +check_service apache2 +check_service mariadb + +# ── Done ────────────────────────────────────────────────────────────────────── SERVER_IP=$(hostname -I | awk '{print $1}') echo "" -echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo -e "${BOLD}${GREEN} NixPanel Installation Complete!${RESET}" -echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo "" -echo -e " ${BOLD}NixServer (Admin):${RESET} http://${SERVER_IP}:2087" -echo -e " ${BOLD}NixClient (User):${RESET} http://${SERVER_IP}:2083" +echo -e " ${BOLD}WHM (Admin panel):${RESET} http://${SERVER_IP}:2087" +echo -e " ${BOLD}cPanel (User panel):${RESET} http://${SERVER_IP}:2083" +echo -e " ${BOLD}Website API:${RESET} http://${SERVER_IP}:4001" +echo -e " ${BOLD}Licensing server:${RESET} http://127.0.0.1:3000 (internal only)" echo "" -echo -e " Open your browser and log in with the admin credentials you just created." +echo -e " ${BOLD}RSA public key:${RESET} ${KEYS_DIR}/license_public.pem" +echo -e " ${BOLD}Logs:${RESET} ${LOG_DIR}/" +echo -e " ${BOLD}Config:${RESET} ${INSTALL_DIR}/*.env" +echo "" +echo -e " ${YELLOW}Stripe Price IDs must still be added to:${RESET}" +echo -e " ${INSTALL_DIR}/website.env" +echo "" +echo -e " To add SSL: certbot --apache" +echo -e " To restart: systemctl restart nixpanel nixpanel-licensing nixpanel-website-api" echo ""