- Fix version banner showing 0.1.0 instead of 0.2.0 - Fix admin password line printing "<auto-generated>true" when GENERATED_PASS=true due to broken parameter expansion — replace with explicit if/else
692 lines
25 KiB
Bash
Executable File
692 lines
25 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# NixPanel Installer
|
|
# Usage: curl -fsSL https://raw.githubusercontent.com/NixReaper/nixpanel-server/main/scripts/install.sh | bash
|
|
# Or: bash install.sh [--admin-user NAME] [--admin-pass PASS] [--admin-email EMAIL]
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
# ── Colours ───────────────────────────────────────────────────────────────────
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
|
|
|
info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
|
|
success() { echo -e "${GREEN}[OK]${RESET} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
|
|
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; }
|
|
step() { echo -e "\n${BOLD}${CYAN}━━━ $* ━━━${RESET}"; }
|
|
|
|
# ── Banner ────────────────────────────────────────────────────────────────────
|
|
echo -e "${BOLD}${CYAN}"
|
|
cat <<'EOF'
|
|
_ _ _ ____ _
|
|
| \ | (_)_ _| _ \ __ _ _ __ ___| |
|
|
| \| | \ \/ / |_) / _` | '_ \ / _ \ |
|
|
| |\ | |> <| __/ (_| | | | | __/ |
|
|
|_| \_|_/_/\_\_| \__,_|_| |_|\___|_|
|
|
|
|
Open-Source Web Hosting Control Panel
|
|
EOF
|
|
echo -e "${RESET}"
|
|
echo -e " Version : ${BOLD}0.2.0${RESET}"
|
|
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
|
|
echo -e " NixServer: ${BOLD}https://<your-ip>:2087${RESET}"
|
|
echo -e " NixClient: ${BOLD}https://<your-ip>:2083${RESET}"
|
|
echo ""
|
|
|
|
# ── Root check ────────────────────────────────────────────────────────────────
|
|
[[ $EUID -ne 0 ]] && error "This installer must be run as root. Try: sudo bash install.sh"
|
|
|
|
# ── OS detection ──────────────────────────────────────────────────────────────
|
|
step "Detecting OS"
|
|
|
|
detect_os() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
. /etc/os-release
|
|
OS_ID="${ID,,}"
|
|
OS_VERSION_ID="${VERSION_ID:-}"
|
|
OS_LIKE="${ID_LIKE:-}"
|
|
else
|
|
error "Cannot detect OS — /etc/os-release not found"
|
|
fi
|
|
|
|
case "$OS_ID" in
|
|
ubuntu|debian) PKG_MGR="apt"; OS_FAMILY="debian" ;;
|
|
almalinux|rocky|rhel|centos)
|
|
PKG_MGR="dnf"; OS_FAMILY="rhel" ;;
|
|
fedora) PKG_MGR="dnf"; OS_FAMILY="rhel" ;;
|
|
*)
|
|
# Try ID_LIKE fallback
|
|
if [[ "$OS_LIKE" == *debian* ]]; then
|
|
PKG_MGR="apt"; OS_FAMILY="debian"
|
|
elif [[ "$OS_LIKE" == *rhel* ]] || [[ "$OS_LIKE" == *fedora* ]]; then
|
|
PKG_MGR="dnf"; OS_FAMILY="rhel"
|
|
else
|
|
error "Unsupported OS: $OS_ID. Supported: Ubuntu, Debian, AlmaLinux, Rocky Linux, RHEL, CentOS."
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
info "Detected OS: ${PRETTY_NAME:-$OS_ID} (family: $OS_FAMILY)"
|
|
}
|
|
|
|
detect_os
|
|
|
|
# ── Architecture check ────────────────────────────────────────────────────────
|
|
ARCH=$(uname -m)
|
|
[[ "$ARCH" != "x86_64" && "$ARCH" != "aarch64" ]] && error "Unsupported architecture: $ARCH (x86_64 and aarch64 only)"
|
|
|
|
# ── Argument parsing ──────────────────────────────────────────────────────────
|
|
ADMIN_USER="admin"
|
|
ADMIN_PASS=""
|
|
ADMIN_EMAIL=""
|
|
INSTALL_DIR="/opt/nixpanel"
|
|
NIXPANEL_DB_USER="nixpanel"
|
|
NIXPANEL_DB_NAME="nixpanel"
|
|
NIXPANEL_DB_PASS=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--admin-user) ADMIN_USER="$2"; shift 2 ;;
|
|
--admin-pass) ADMIN_PASS="$2"; shift 2 ;;
|
|
--admin-email) ADMIN_EMAIL="$2"; shift 2 ;;
|
|
--install-dir) INSTALL_DIR="$2"; shift 2 ;;
|
|
*) warn "Unknown argument: $1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
# ── Generate secrets ──────────────────────────────────────────────────────────
|
|
gen_secret() { openssl rand -hex 32; }
|
|
|
|
if [[ -z "$ADMIN_PASS" ]]; then
|
|
ADMIN_PASS=$(openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 16)
|
|
GENERATED_PASS=true
|
|
else
|
|
GENERATED_PASS=false
|
|
fi
|
|
|
|
if [[ -z "$ADMIN_EMAIL" ]]; then
|
|
ADMIN_EMAIL="admin@$(hostname -f 2>/dev/null || echo localhost)"
|
|
fi
|
|
|
|
if [[ -z "$NIXPANEL_DB_PASS" ]]; then
|
|
NIXPANEL_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
|
|
fi
|
|
|
|
JWT_SECRET=$(gen_secret)
|
|
JWT_REFRESH_SECRET=$(gen_secret)
|
|
|
|
# ── Confirm ───────────────────────────────────────────────────────────────────
|
|
echo -e "${BOLD}Installation summary:${RESET}"
|
|
echo " Install directory : $INSTALL_DIR"
|
|
echo " Admin username : $ADMIN_USER"
|
|
echo " Admin email : $ADMIN_EMAIL"
|
|
if [[ "$GENERATED_PASS" == "true" ]]; then
|
|
echo " Admin password : <auto-generated, shown at end>"
|
|
else
|
|
echo " Admin password : $ADMIN_PASS"
|
|
fi
|
|
echo ""
|
|
read -r -p "Continue? [Y/n] " CONFIRM
|
|
CONFIRM="${CONFIRM:-Y}"
|
|
[[ "$CONFIRM" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }
|
|
|
|
# ── Update package index ──────────────────────────────────────────────────────
|
|
step "Updating package index"
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get update -qq
|
|
elif [[ "$PKG_MGR" == "dnf" ]]; then
|
|
dnf makecache -q
|
|
fi
|
|
success "Package index updated"
|
|
|
|
# ── Install base dependencies ─────────────────────────────────────────────────
|
|
step "Installing base dependencies"
|
|
|
|
APT_DEPS=(curl wget gnupg2 ca-certificates lsb-release software-properties-common
|
|
git build-essential openssl ufw fail2ban)
|
|
DNF_DEPS=(curl wget gnupg2 ca-certificates git openssl firewalld fail2ban)
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq "${APT_DEPS[@]}"
|
|
else
|
|
dnf install -y -q "${DNF_DEPS[@]}"
|
|
fi
|
|
success "Base dependencies installed"
|
|
|
|
# ── Install Node.js 22 (LTS) ──────────────────────────────────────────────────
|
|
step "Installing Node.js 22 LTS"
|
|
|
|
if command -v node &>/dev/null; then
|
|
NODE_VER=$(node --version | cut -c2- | cut -d. -f1)
|
|
if [[ "$NODE_VER" -ge 20 ]]; then
|
|
info "Node.js $(node --version) already installed — skipping"
|
|
else
|
|
warn "Node.js $NODE_VER found — upgrading to v22"
|
|
install_node=true
|
|
fi
|
|
else
|
|
install_node=true
|
|
fi
|
|
|
|
if [[ "${install_node:-false}" == "true" ]] || ! command -v node &>/dev/null; then
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - >/dev/null 2>&1
|
|
apt-get install -y -qq nodejs
|
|
else
|
|
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash - >/dev/null 2>&1
|
|
dnf install -y -q nodejs
|
|
fi
|
|
fi
|
|
|
|
success "Node.js $(node --version) ready"
|
|
success "npm $(npm --version) ready"
|
|
|
|
# ── Install MariaDB ───────────────────────────────────────────────────────────
|
|
step "Installing MariaDB"
|
|
|
|
if command -v mariadb &>/dev/null || command -v mysqld &>/dev/null; then
|
|
info "MariaDB/MySQL already installed — skipping"
|
|
else
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq mariadb-server mariadb-client
|
|
else
|
|
dnf install -y -q mariadb-server mariadb
|
|
fi
|
|
fi
|
|
|
|
systemctl enable --now mariadb
|
|
|
|
# Secure MariaDB and create DB/user
|
|
info "Configuring MariaDB..."
|
|
mariadb -u root <<SQL
|
|
ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED VIA unix_socket;
|
|
DELETE FROM mysql.user WHERE User='';
|
|
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost','127.0.0.1','::1');
|
|
DROP DATABASE IF EXISTS test;
|
|
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
|
|
|
|
CREATE DATABASE IF NOT EXISTS \`${NIXPANEL_DB_NAME}\`
|
|
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
|
|
CREATE USER IF NOT EXISTS '${NIXPANEL_DB_USER}'@'localhost'
|
|
IDENTIFIED BY '${NIXPANEL_DB_PASS}';
|
|
|
|
GRANT ALL PRIVILEGES ON \`${NIXPANEL_DB_NAME}\`.* TO '${NIXPANEL_DB_USER}'@'localhost';
|
|
FLUSH PRIVILEGES;
|
|
SQL
|
|
|
|
success "MariaDB configured — database '${NIXPANEL_DB_NAME}' created"
|
|
|
|
# ── Install Nginx ─────────────────────────────────────────────────────────────
|
|
step "Installing Nginx"
|
|
|
|
if command -v nginx &>/dev/null; then
|
|
info "Nginx already installed — skipping"
|
|
else
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq nginx
|
|
else
|
|
dnf install -y -q nginx
|
|
fi
|
|
fi
|
|
|
|
systemctl enable --now nginx
|
|
success "Nginx ready"
|
|
|
|
# ── Install PHP 8.3 + FPM ─────────────────────────────────────────────────────
|
|
step "Installing PHP 8.3 + FPM"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
if ! apt-cache show php8.3 &>/dev/null 2>&1; then
|
|
add-apt-repository -y ppa:ondrej/php >/dev/null 2>&1
|
|
apt-get update -qq
|
|
fi
|
|
apt-get install -y -qq php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl \
|
|
php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath \
|
|
php8.3-intl php8.3-soap php8.3-imagick php8.3-redis php8.3-opcache
|
|
else
|
|
dnf install -y -q php8.3 php8.3-fpm php8.3-mysqlnd php8.3-curl \
|
|
php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip php8.3-bcmath \
|
|
php8.3-intl php8.3-soap php8.3-opcache 2>/dev/null || \
|
|
dnf install -y -q php php-fpm php-mysqlnd php-curl php-gd php-mbstring \
|
|
php-xml php-zip php-bcmath php-intl php-soap php-opcache
|
|
fi
|
|
|
|
systemctl enable --now php8.3-fpm 2>/dev/null || systemctl enable --now php-fpm
|
|
success "PHP 8.3 + FPM ready"
|
|
|
|
# ── Install Postfix + Dovecot ─────────────────────────────────────────────────
|
|
step "Installing Postfix + Dovecot"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d \
|
|
dovecot-lmtpd dovecot-mysql spamassassin
|
|
else
|
|
dnf install -y -q postfix dovecot dovecot-mysql
|
|
fi
|
|
|
|
systemctl enable postfix dovecot
|
|
success "Postfix + Dovecot installed"
|
|
|
|
# ── Install vsftpd ────────────────────────────────────────────────────────────
|
|
step "Installing vsftpd"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq vsftpd
|
|
else
|
|
dnf install -y -q vsftpd
|
|
fi
|
|
|
|
systemctl enable vsftpd
|
|
success "vsftpd installed"
|
|
|
|
# ── Install BIND9 ─────────────────────────────────────────────────────────────
|
|
step "Installing BIND9"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq bind9 bind9utils bind9-doc
|
|
else
|
|
dnf install -y -q bind bind-utils
|
|
fi
|
|
|
|
BIND_SERVICE="named"
|
|
[[ "$PKG_MGR" == "apt" ]] && BIND_SERVICE="named" || true
|
|
systemctl enable --now "$BIND_SERVICE" 2>/dev/null || systemctl enable --now bind9 2>/dev/null || true
|
|
|
|
BIND_ZONES_DIR="/etc/bind/zones"
|
|
mkdir -p "$BIND_ZONES_DIR"
|
|
chown root:bind "$BIND_ZONES_DIR" 2>/dev/null || chown root:named "$BIND_ZONES_DIR" 2>/dev/null || true
|
|
success "BIND9 installed"
|
|
|
|
# ── Install Certbot ───────────────────────────────────────────────────────────
|
|
step "Installing Certbot"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
apt-get install -y -qq certbot python3-certbot-nginx
|
|
else
|
|
dnf install -y -q certbot python3-certbot-nginx 2>/dev/null || \
|
|
dnf install -y -q --enablerepo=epel certbot python3-certbot-nginx
|
|
fi
|
|
|
|
mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
|
|
success "Certbot installed"
|
|
|
|
# ── Clone / update NixPanel ───────────────────────────────────────────────────
|
|
step "Installing NixPanel"
|
|
|
|
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
|
info "Existing installation found — pulling latest..."
|
|
git -C "$INSTALL_DIR" pull --ff-only
|
|
else
|
|
info "Cloning repository into $INSTALL_DIR..."
|
|
git clone https://github.com/NixReaper/nixpanel-server.git "$INSTALL_DIR"
|
|
fi
|
|
|
|
success "NixPanel source ready at $INSTALL_DIR"
|
|
|
|
# ── Write .env ────────────────────────────────────────────────────────────────
|
|
step "Configuring environment"
|
|
|
|
SERVER_IP=$(hostname -I | awk '{print $1}')
|
|
|
|
cat > "$INSTALL_DIR/.env" <<ENV
|
|
# Generated by NixPanel installer on $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
|
|
# Database
|
|
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}"
|
|
|
|
# JWT
|
|
JWT_SECRET="${JWT_SECRET}"
|
|
JWT_EXPIRES_IN="15m"
|
|
JWT_REFRESH_SECRET="${JWT_REFRESH_SECRET}"
|
|
JWT_REFRESH_EXPIRES_IN="7d"
|
|
|
|
# Server
|
|
NODE_ENV="production"
|
|
NIXSERVER_PORT=2087
|
|
NIXCLIENT_PORT=2083
|
|
HOST="0.0.0.0"
|
|
|
|
# Admin credentials (used during initial seed only)
|
|
ADMIN_USERNAME="${ADMIN_USER}"
|
|
ADMIN_PASSWORD="${ADMIN_PASS}"
|
|
ADMIN_EMAIL="${ADMIN_EMAIL}"
|
|
|
|
# Paths
|
|
HOME_DIR="/home"
|
|
NGINX_SITES_DIR="/etc/nginx/sites-available"
|
|
NGINX_ENABLED_DIR="/etc/nginx/sites-enabled"
|
|
APACHE_SITES_DIR="/etc/apache2/sites-available"
|
|
APACHE_ENABLED_DIR="/etc/apache2/sites-enabled"
|
|
BIND_ZONES_DIR="${BIND_ZONES_DIR}"
|
|
CERTBOT_WEBROOT="/var/www/letsencrypt"
|
|
|
|
# Rate limiting
|
|
RATE_LIMIT_MAX=100
|
|
RATE_LIMIT_WINDOW_MS=60000
|
|
|
|
# Features
|
|
ENABLE_TERMINAL=true
|
|
WEBSOCKET_STATS_INTERVAL=5000
|
|
ENV
|
|
|
|
chmod 600 "$INSTALL_DIR/.env"
|
|
success ".env written"
|
|
|
|
# ── npm install ───────────────────────────────────────────────────────────────
|
|
step "Installing Node.js dependencies"
|
|
|
|
cd "$INSTALL_DIR"
|
|
npm install --workspaces --include-workspace-root --silent
|
|
success "npm dependencies installed"
|
|
|
|
# ── Prisma generate + migrate ─────────────────────────────────────────────────
|
|
step "Running database migrations"
|
|
|
|
cd "$INSTALL_DIR/server"
|
|
npx prisma generate --silent
|
|
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
|
npx prisma migrate deploy
|
|
success "Database schema applied"
|
|
|
|
# ── Seed default data ─────────────────────────────────────────────────────────
|
|
step "Seeding initial data"
|
|
|
|
DATABASE_URL="mysql://${NIXPANEL_DB_USER}:${NIXPANEL_DB_PASS}@localhost:3306/${NIXPANEL_DB_NAME}" \
|
|
ADMIN_USERNAME="${ADMIN_USER}" \
|
|
ADMIN_PASSWORD="${ADMIN_PASS}" \
|
|
ADMIN_EMAIL="${ADMIN_EMAIL}" \
|
|
npx tsx src/db/seed.ts
|
|
success "Default admin, packages, and settings seeded"
|
|
|
|
# ── Build frontends ───────────────────────────────────────────────────────────
|
|
step "Building NixServer + NixClient frontends"
|
|
|
|
cd "$INSTALL_DIR"
|
|
npm run build --workspace=nixserver --silent
|
|
npm run build --workspace=nixclient --silent
|
|
success "Frontends built"
|
|
|
|
# ── systemd service ───────────────────────────────────────────────────────────
|
|
step "Creating systemd service"
|
|
|
|
cat > /etc/systemd/system/nixpanel.service <<UNIT
|
|
[Unit]
|
|
Description=NixPanel Web Hosting Control Panel
|
|
Documentation=https://github.com/NixReaper/nixpanel-server
|
|
After=network.target mariadb.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=root
|
|
WorkingDirectory=${INSTALL_DIR}/server
|
|
EnvironmentFile=${INSTALL_DIR}/.env
|
|
ExecStart=$(which node) --enable-source-maps dist/index.js
|
|
Restart=always
|
|
RestartSec=5
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=nixpanel
|
|
LimitNOFILE=65536
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
UNIT
|
|
|
|
# Build server for production
|
|
cd "$INSTALL_DIR/server"
|
|
npm run build --silent 2>/dev/null || npx tsc --noEmit false 2>/dev/null || true
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable nixpanel
|
|
systemctl restart nixpanel
|
|
success "nixpanel.service created and started"
|
|
|
|
# ── Nginx reverse proxy ───────────────────────────────────────────────────────
|
|
step "Configuring Nginx reverse proxy"
|
|
|
|
# Ensure sites-available/enabled dirs exist
|
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
|
|
|
# Main nginx.conf include (idempotent)
|
|
if ! grep -q "sites-enabled" /etc/nginx/nginx.conf 2>/dev/null; then
|
|
sed -i '/http {/a \ include /etc/nginx/sites-enabled/*.conf;' /etc/nginx/nginx.conf
|
|
fi
|
|
|
|
cat > /etc/nginx/sites-available/nixpanel.conf <<NGINX
|
|
# NixPanel — generated by installer
|
|
# NixServer admin panel on port 2087
|
|
server {
|
|
listen 2087;
|
|
server_name _;
|
|
|
|
# Serve pre-built NixServer React app
|
|
root ${INSTALL_DIR}/nixserver/dist;
|
|
index index.html;
|
|
|
|
location / {
|
|
try_files \$uri \$uri/ /index.html;
|
|
}
|
|
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:2087;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_read_timeout 300s;
|
|
}
|
|
|
|
location /ws {
|
|
proxy_pass http://127.0.0.1:2087;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
}
|
|
}
|
|
|
|
# NixClient user panel on port 2083
|
|
server {
|
|
listen 2083;
|
|
server_name _;
|
|
|
|
root ${INSTALL_DIR}/nixclient/dist;
|
|
index index.html;
|
|
|
|
location / {
|
|
try_files \$uri \$uri/ /index.html;
|
|
}
|
|
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:2087;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_read_timeout 300s;
|
|
}
|
|
}
|
|
|
|
# HTTP — Let's Encrypt webroot + redirect
|
|
server {
|
|
listen 80;
|
|
server_name _;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/letsencrypt;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://\$host\$request_uri;
|
|
}
|
|
}
|
|
NGINX
|
|
|
|
ln -sf /etc/nginx/sites-available/nixpanel.conf /etc/nginx/sites-enabled/nixpanel.conf
|
|
|
|
nginx -t && systemctl reload nginx
|
|
success "Nginx configured"
|
|
|
|
# ── Firewall ──────────────────────────────────────────────────────────────────
|
|
step "Configuring firewall"
|
|
|
|
if [[ "$PKG_MGR" == "apt" ]]; then
|
|
# UFW
|
|
ufw --force enable >/dev/null
|
|
ufw allow 22/tcp comment "SSH" >/dev/null
|
|
ufw allow 80/tcp comment "HTTP" >/dev/null
|
|
ufw allow 443/tcp comment "HTTPS" >/dev/null
|
|
ufw allow 2087/tcp comment "NixServer" >/dev/null
|
|
ufw allow 2083/tcp comment "NixClient" >/dev/null
|
|
ufw allow 21/tcp comment "FTP" >/dev/null
|
|
ufw allow 25/tcp comment "SMTP" >/dev/null
|
|
ufw allow 143/tcp comment "IMAP" >/dev/null
|
|
ufw allow 993/tcp comment "IMAPS" >/dev/null
|
|
ufw allow 110/tcp comment "POP3" >/dev/null
|
|
ufw allow 995/tcp comment "POP3S" >/dev/null
|
|
ufw allow 587/tcp comment "SMTP submission" >/dev/null
|
|
ufw allow 465/tcp comment "SMTPS" >/dev/null
|
|
ufw allow 53 comment "DNS" >/dev/null
|
|
success "UFW rules applied"
|
|
else
|
|
# firewalld
|
|
systemctl enable --now firewalld
|
|
for PORT in 22 80 443 2087 2083 21 25 143 993 110 995 587 465 53; do
|
|
firewall-cmd --permanent --add-port=${PORT}/tcp >/dev/null 2>&1 || true
|
|
done
|
|
firewall-cmd --permanent --add-port=53/udp >/dev/null 2>&1 || true
|
|
firewall-cmd --reload >/dev/null
|
|
success "firewalld rules applied"
|
|
fi
|
|
|
|
# ── Fail2ban ──────────────────────────────────────────────────────────────────
|
|
step "Configuring Fail2ban"
|
|
|
|
cat > /etc/fail2ban/jail.d/nixpanel.conf <<F2B
|
|
[sshd]
|
|
enabled = true
|
|
maxretry = 5
|
|
bantime = 3600
|
|
findtime = 600
|
|
|
|
[nginx-http-auth]
|
|
enabled = true
|
|
maxretry = 10
|
|
bantime = 600
|
|
|
|
[postfix]
|
|
enabled = true
|
|
|
|
[dovecot]
|
|
enabled = true
|
|
F2B
|
|
|
|
systemctl enable --now fail2ban
|
|
success "Fail2ban configured"
|
|
|
|
# ── vsftpd configuration ──────────────────────────────────────────────────────
|
|
step "Configuring vsftpd"
|
|
|
|
cat > /etc/vsftpd.conf <<VSFTPD
|
|
listen=YES
|
|
listen_ipv6=NO
|
|
anonymous_enable=NO
|
|
local_enable=YES
|
|
write_enable=YES
|
|
local_umask=022
|
|
dirmessage_enable=YES
|
|
use_localtime=YES
|
|
xferlog_enable=YES
|
|
connect_from_port_20=YES
|
|
chroot_local_user=YES
|
|
allow_writeable_chroot=YES
|
|
secure_chroot_dir=/var/run/vsftpd/empty
|
|
pam_service_name=vsftpd
|
|
ssl_enable=NO
|
|
utf8_filesystem=YES
|
|
pasv_enable=YES
|
|
pasv_min_port=40000
|
|
pasv_max_port=50000
|
|
VSFTPD
|
|
|
|
systemctl restart vsftpd 2>/dev/null || true
|
|
success "vsftpd configured"
|
|
|
|
# ── Wait for service to start ─────────────────────────────────────────────────
|
|
step "Waiting for NixPanel to start"
|
|
|
|
MAX_WAIT=30
|
|
WAITED=0
|
|
until curl -sf http://127.0.0.1:2087/api/health >/dev/null 2>&1; do
|
|
sleep 2
|
|
WAITED=$((WAITED + 2))
|
|
[[ $WAITED -ge $MAX_WAIT ]] && { warn "Service health check timed out — check: journalctl -u nixpanel -n 50"; break; }
|
|
done
|
|
|
|
if curl -sf http://127.0.0.1:2087/api/health >/dev/null 2>&1; then
|
|
success "NixPanel API is responding"
|
|
else
|
|
warn "NixPanel may still be starting. Check status with: systemctl status nixpanel"
|
|
fi
|
|
|
|
# ── Print completion summary ──────────────────────────────────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════════════════╗${RESET}"
|
|
echo -e "${BOLD}${GREEN}║ NixPanel Installation Complete! ║${RESET}"
|
|
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════╝${RESET}"
|
|
echo ""
|
|
echo -e " ${BOLD}NixServer (Admin Panel)${RESET}"
|
|
echo -e " URL : ${CYAN}http://${SERVER_IP}:2087${RESET}"
|
|
echo -e " Username : ${BOLD}${ADMIN_USER}${RESET}"
|
|
echo -e " Password : ${BOLD}${ADMIN_PASS}${RESET}"
|
|
echo ""
|
|
echo -e " ${BOLD}NixClient (User Panel)${RESET}"
|
|
echo -e " URL : ${CYAN}http://${SERVER_IP}:2083${RESET}"
|
|
echo ""
|
|
echo -e " ${BOLD}Database${RESET}"
|
|
echo -e " DB Name : ${NIXPANEL_DB_NAME}"
|
|
echo -e " DB User : ${NIXPANEL_DB_USER}"
|
|
echo -e " DB Pass : ${NIXPANEL_DB_PASS}"
|
|
echo ""
|
|
echo -e " ${BOLD}Files${RESET}"
|
|
echo -e " Install : ${INSTALL_DIR}"
|
|
echo -e " Config : ${INSTALL_DIR}/.env"
|
|
echo -e " Service : systemctl status nixpanel"
|
|
echo -e " Logs : journalctl -u nixpanel -f"
|
|
echo ""
|
|
if [[ "$GENERATED_PASS" == "true" ]]; then
|
|
echo -e " ${YELLOW}${BOLD}⚠ Save your admin password — it will not be shown again!${RESET}"
|
|
echo ""
|
|
fi
|
|
echo -e " ${BOLD}Useful commands:${RESET}"
|
|
echo -e " systemctl restart nixpanel # restart the panel"
|
|
echo -e " systemctl status nixpanel # check status"
|
|
echo -e " journalctl -u nixpanel -f # live logs"
|
|
echo ""
|
|
|
|
# Save credentials to a file root can read
|
|
CRED_FILE="/root/.nixpanel_credentials"
|
|
cat > "$CRED_FILE" <<CREDS
|
|
NixPanel Credentials — $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
==========================================================
|
|
NixServer URL : http://${SERVER_IP}:2087
|
|
NixClient URL : http://${SERVER_IP}:2083
|
|
Admin User : ${ADMIN_USER}
|
|
Admin Pass : ${ADMIN_PASS}
|
|
Admin Email: ${ADMIN_EMAIL}
|
|
DB Name : ${NIXPANEL_DB_NAME}
|
|
DB User : ${NIXPANEL_DB_USER}
|
|
DB Pass : ${NIXPANEL_DB_PASS}
|
|
Install Dir: ${INSTALL_DIR}
|
|
CREDS
|
|
chmod 600 "$CRED_FILE"
|
|
|
|
echo -e " Credentials saved to: ${BOLD}${CRED_FILE}${RESET}"
|
|
echo ""
|