2026-03-24 03:30:16 +07:00

287 lines
7.1 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(dirname "$0")/utils.sh"
require_root
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"
if [ ! -f "$ENV_FILE" ]; then
echo "ERROR: .env file not found at $ENV_FILE"
echo "Create $ENV_FILE with the following variables:"
echo " DOMAIN=example.com"
echo " VLESS_WS_PATH=stream"
echo " PORT=10001"
exit 1
fi
source "$ENV_FILE"
: "${DOMAIN:?DOMAIN is not set in .env}"
: "${VLESS_WS_PATH:?VLESS_WS_PATH is not set in .env}"
: "${PORT:?PORT is not set in .env}"
REAL_USER="${SUDO_USER:-$(whoami)}"
REAL_HOME=$(eval echo "~$REAL_USER")
VLESS_DIR="$REAL_HOME/services/vless"
echo "=== VLESS (Xray-core) Installation ==="
echo " Domain: $DOMAIN"
echo " WS Path: /$VLESS_WS_PATH"
echo " Port: $PORT"
echo " Directory: $VLESS_DIR"
echo ""
# --- 1. Download Xray-core ---
mkdir -p "$VLESS_DIR"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) XRAY_ARCH="64" ;;
aarch64) XRAY_ARCH="arm64-v8a" ;;
*)
echo "ERROR: unsupported architecture: $ARCH"
exit 1
;;
esac
XRAY_ZIP="Xray-linux-${XRAY_ARCH}.zip"
DOWNLOAD_URL="https://github.com/XTLS/Xray-core/releases/latest/download/${XRAY_ZIP}"
if [ -x "$VLESS_DIR/xray" ]; then
echo "[1] Xray already installed: $("$VLESS_DIR/xray" version | head -1)"
else
echo "[1] Downloading Xray-core..."
for cmd in wget unzip; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo " Installing $cmd..."
apt-get install -y -qq "$cmd"
fi
done
wget -q --show-progress -O "/tmp/$XRAY_ZIP" "$DOWNLOAD_URL"
unzip -o -q "/tmp/$XRAY_ZIP" -d "$VLESS_DIR"
chmod +x "$VLESS_DIR/xray"
rm -f "/tmp/$XRAY_ZIP"
echo " Installed: $("$VLESS_DIR/xray" version | head -1)"
fi
echo ""
# --- 2. Generate UUID & create config ---
UUID=$("$VLESS_DIR/xray" uuid)
echo "[2] Generated UUID: $UUID"
cat > "$VLESS_DIR/config.json" <<EOF
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": ${PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${UUID}"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/${VLESS_WS_PATH}"
}
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"routing": {
"rules": [
{
"type": "field",
"outboundTag": "block",
"protocol": ["bittorrent"]
}
]
}
}
EOF
echo " Config: $VLESS_DIR/config.json"
echo ""
# --- 3. Systemd service ---
SERVICE_NAME="xray-vless"
echo "[3] Creating systemd service..."
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Xray VLESS Server
After=network.target
[Service]
Type=simple
ExecStart=${VLESS_DIR}/xray run -config ${VLESS_DIR}/config.json
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "$SERVICE_NAME" --quiet
systemctl restart "$SERVICE_NAME"
echo " Service $SERVICE_NAME started"
echo ""
# --- 4. SSL certificate ---
NGINX_SITE="vless-${DOMAIN}"
if grep -q '^\s*#.*server_names_hash_bucket_size' /etc/nginx/nginx.conf; then
echo " Enabling server_names_hash_bucket_size in nginx.conf..."
sed -i 's/.*#\s*server_names_hash_bucket_size.*/ server_names_hash_bucket_size 128;/' /etc/nginx/nginx.conf
elif ! grep -rq 'server_names_hash_bucket_size' /etc/nginx/nginx.conf /etc/nginx/conf.d/ 2>/dev/null; then
echo " Setting server_names_hash_bucket_size..."
echo 'server_names_hash_bucket_size 128;' > /etc/nginx/conf.d/hash_bucket_size.conf
fi
mkdir -p "/var/www/certbot"
mkdir -p "/var/www/${DOMAIN}"
if [ ! -f "/var/www/${DOMAIN}/index.html" ]; then
echo "<html><body><h1>Welcome</h1></body></html>" > "/var/www/${DOMAIN}/index.html"
fi
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
echo "[4] SSL certificate already exists"
else
echo "[4] Obtaining SSL certificate..."
cat > "/etc/nginx/sites-available/$NGINX_SITE" <<NGINX
server {
listen 80;
server_name ${DOMAIN};
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
root /var/www/${DOMAIN};
index index.html;
}
}
NGINX
ln -sf "/etc/nginx/sites-available/$NGINX_SITE" "/etc/nginx/sites-enabled/$NGINX_SITE"
nginx -t || { echo "ERROR: nginx config test failed"; exit 1; }
systemctl reload nginx
if ! certbot certonly --webroot -w /var/www/certbot -d "$DOMAIN" \
--non-interactive --agree-tos --email "admin@${DOMAIN}" 2>&1; then
echo " Certbot failed, resetting ACME account and retrying..."
rm -rf /etc/letsencrypt/accounts/acme-v02.api.letsencrypt.org/
certbot certonly --webroot -w /var/www/certbot -d "$DOMAIN" \
--non-interactive --agree-tos --email "admin@${DOMAIN}" || {
echo "ERROR: Failed to obtain SSL certificate"
exit 1
}
fi
echo " Certificate obtained"
fi
echo ""
# --- 5. Nginx full config (with SSL) ---
echo "[5] Configuring nginx (HTTPS + WebSocket proxy)..."
cat > "/etc/nginx/sites-available/$NGINX_SITE" <<NGINX
server {
listen 80;
server_name ${DOMAIN};
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://\$host\$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
server_name ${DOMAIN};
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location /${VLESS_WS_PATH} {
proxy_redirect off;
proxy_pass http://127.0.0.1:${PORT};
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;
proxy_send_timeout 300s;
}
location / {
root /var/www/${DOMAIN};
index index.html;
try_files \$uri \$uri/ =404;
}
}
NGINX
ln -sf "/etc/nginx/sites-available/$NGINX_SITE" "/etc/nginx/sites-enabled/$NGINX_SITE"
nginx -t || { echo "ERROR: nginx config test failed"; exit 1; }
systemctl reload nginx
echo " Nginx configured and reloaded"
echo ""
# --- 6. Fix ownership & output ---
chown -R "$REAL_USER:$REAL_USER" "$VLESS_DIR"
VLESS_LINK="vless://${UUID}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}&type=ws&path=%2F${VLESS_WS_PATH}#Fish-VLESS"
echo "$VLESS_LINK" > "$VLESS_DIR/connection.txt"
chown "$REAL_USER:$REAL_USER" "$VLESS_DIR/connection.txt"
echo "=== VLESS READY ==="
echo ""
echo "$VLESS_LINK"
echo ""
echo "Saved to: $VLESS_DIR/connection.txt"