294 lines
7.3 KiB
Bash
294 lines
7.3 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}"
|
|
|
|
# If nginx stream SNI routing is active, HTTPS listens on internal port
|
|
if grep -q 'stream {' /etc/nginx/nginx.conf 2>/dev/null; then
|
|
HTTPS_LISTEN_PORT=8443
|
|
else
|
|
HTTPS_LISTEN_PORT=443
|
|
fi
|
|
|
|
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 ${HTTPS_LISTEN_PORT} 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"
|