Last active
August 19, 2025 13:03
-
-
Save irmowan/ffd909ce02db21131ab5383e00538148 to your computer and use it in GitHub Desktop.
vps_boostrap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #cloud-config | |
| package_update: true | |
| package_upgrade: true | |
| runcmd: | |
| - [ bash, -lc, "/opt/bootstrap/install.sh > /var/log/bootstrap.log 2>&1" ] | |
| runcmd: | |
| # Set domain | |
| - DOMAIN="irmo.westus2.cloudapp.azure.com" | |
| # Gist RAW script URL (replace with your own) | |
| - RAW="<RAW_SCRIPT_URL>" | |
| # Download script, grant execute permissions, and execute | |
| - curl -fsSL "$RAW" -o /root/setup-xray.sh | |
| - chmod +x /root/setup-xray.sh | |
| - /root/setup-xray.sh --domain "$DOMAIN" --port 10000 --ws-path /ray |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euxo pipefail | |
| LOCATION="eastasia" | |
| $RG = "vps-$LOCATION" | |
| $VM = "vps-$LOCATION" | |
| $VM_SIZE = "Standard_B1s" | |
| $USER_NAME = "irmo" | |
| az group create -n vps-$LOCATION -l $LOCATION | |
| az vm create \ | |
| -g $RG -n $VM \ | |
| --image Ubuntu2204 \ | |
| --size $VM_SIZE \ | |
| --admin-username $USER_NAME \ | |
| --ssh-key-values ~/.ssh/id_rsa.pub \ | |
| --public-ip-sku Standard \ | |
| --nsg-rule NONE \ | |
| --custom-data cloud-init.yaml \ | |
| --location $LOCATION | |
| az network nsg create -g $RG -n ${VM}-nsg -l $LOCATION | |
| az network nsg rule create -g $RG --nsg-name ${VM}-nsg -n allow-ssh \ | |
| --priority 1000 --access Allow --protocol Tcp --direction Inbound \ | |
| --source-address-prefixes '*' --destination-port-ranges 22 | |
| az network nic update -g $RG -n ${VM}VMNic --network-security-group ${VM}-nsg |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| RAW="<RAW_SCRIPT_URL>" | |
| sudo curl -fsSL "$RAW" -o ~/setup-xray.sh | |
| sudo chmod +x ~/setup-xray.sh | |
| DOMAIN="irmo.westus2.cloudapp.azure.com" | |
| LABEL="irmo.westus2" | |
| sudo bash ~/setup-xray.sh \ | |
| --domain $DOMAIN \ | |
| --label $LABEL |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # ============================================================================= | |
| # Xray VLESS + TCP + TLS (self-signed) — no nginx, no acme | |
| # - Xray listens on :443 directly with a self-signed cert | |
| # - Creates/renews cert at /etc/xray-tls/self.{crt,key} | |
| # - Auto-renew via systemd timer before expiration | |
| # | |
| # Usage: | |
| # sudo bash setup-xray.sh --domain example.com [--uuid <uuid>] [--label <tag>] | |
| # [--days 365] [--renew-before 30] | |
| # ============================================================================= | |
| DOMAIN="" | |
| USER_UUID="" | |
| LABEL="" | |
| DAYS=365 | |
| RENEW_BEFORE=30 | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --domain) DOMAIN="${2:-}"; shift 2;; | |
| --uuid) USER_UUID="${2:-}"; shift 2;; | |
| --label) LABEL="${2:-}"; shift 2;; | |
| --days) DAYS="${2:-}"; shift 2;; | |
| --renew-before) RENEW_BEFORE="${2:-}"; shift 2;; | |
| -h|--help) | |
| grep -E '^# ' "$0" | sed 's/^# \{0,1\}//'; exit 0;; | |
| *) echo "Unknown arg: $1"; exit 2;; | |
| esac | |
| done | |
| [[ -n "$DOMAIN" ]] || { echo "ERROR: --domain is required"; exit 1; } | |
| [[ $EUID -eq 0 ]] || { echo "ERROR: run as root (sudo)"; exit 1; } | |
| LABEL="${LABEL:-$DOMAIN}" | |
| log(){ echo -e "\033[1;32m[+]\033[0m $*"; } | |
| warn(){ echo -e "\033[1;33m[!]\033[0m $*"; } | |
| err(){ echo -e "\033[1;31m[x]\033[0m $*"; } | |
| # ---------------- A. swap & deps | |
| setup_swap(){ | |
| if swapon --show | grep -q '/swapfile' && grep -q '/swapfile' /etc/fstab; then | |
| log "Swap exists, skip." | |
| else | |
| log "Create 1G swapfile ..." | |
| fallocate -l 1G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=1024 | |
| chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile | |
| grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab | |
| fi | |
| } | |
| install_deps(){ | |
| log "Install deps: curl jq openssl qrencode uuid-runtime" | |
| apt-get update -y | |
| DEBIAN_FRONTEND=noninteractive apt-get install -y curl jq openssl qrencode uuid-runtime | |
| if ! command -v xray >/dev/null 2>&1; then | |
| log "Install Xray ..." | |
| bash -c "curl -fsSL https://github.com/XTLS/Xray-install/raw/main/install-release.sh | bash" | |
| else | |
| log "Xray already installed." | |
| fi | |
| } | |
| # ---------------- B. self-signed cert (create/renew helpers) | |
| CERT_DIR="/etc/xray-tls" | |
| CERT_FULL="$CERT_DIR/self.crt" | |
| CERT_KEY="$CERT_DIR/self.key" | |
| gen_self_signed(){ | |
| mkdir -p "$CERT_DIR" | |
| log "Generate self-signed cert (CN=$DOMAIN, days=$DAYS)" | |
| openssl req -x509 -nodes -newkey rsa:2048 -days "$DAYS" \ | |
| -keyout "$CERT_KEY" -out "$CERT_FULL" \ | |
| -subj "/CN=${DOMAIN}" | |
| } | |
| days_left(){ | |
| # prints integer days left; returns large negative on parse error | |
| if [[ -s "$CERT_FULL" ]]; then | |
| end_raw="$(openssl x509 -enddate -noout -in "$CERT_FULL" 2>/dev/null | cut -d= -f2 || true)" | |
| if [[ -n "$end_raw" ]]; then | |
| end_epoch="$(date -d "$end_raw" +%s 2>/dev/null || true)" | |
| now_epoch="$(date +%s)" | |
| if [[ -n "$end_epoch" ]]; then | |
| echo $(( (end_epoch - now_epoch) / 86400 )) | |
| return 0 | |
| fi | |
| fi | |
| fi | |
| echo -999999 | |
| } | |
| ensure_cert(){ | |
| mkdir -p "$CERT_DIR" | |
| if [[ ! -s "$CERT_FULL" || ! -s "$CERT_KEY" ]]; then | |
| gen_self_signed | |
| return | |
| fi | |
| dl="$(days_left)" | |
| if (( dl <= RENEW_BEFORE )); then | |
| log "Cert days left: $dl ≤ $RENEW_BEFORE → renew" | |
| gen_self_signed | |
| else | |
| log "Cert days left: $dl (> $RENEW_BEFORE) → reuse" | |
| fi | |
| } | |
| # ---------------- C. Xray config | |
| write_xray_config(){ | |
| local uuid | |
| if [[ -n "$USER_UUID" ]]; then | |
| uuid="$USER_UUID" | |
| elif [[ -s /etc/xray-uuid ]]; then | |
| uuid="$(cat /etc/xray-uuid)" | |
| else | |
| uuid="$(uuidgen)" | |
| echo "$uuid" >/etc/xray-uuid | |
| fi | |
| mkdir -p /usr/local/etc/xray | |
| cat >/usr/local/etc/xray/config.json <<JSON | |
| { | |
| "log": { "loglevel": "info" }, | |
| "inbounds": [ | |
| { | |
| "port": 443, | |
| "listen": "0.0.0.0", | |
| "protocol": "vless", | |
| "settings": { | |
| "clients": [ { "id": "${uuid}", "level": 0 } ], | |
| "decryption": "none" | |
| }, | |
| "streamSettings": { | |
| "network": "tcp", | |
| "security": "tls", | |
| "tlsSettings": { | |
| "minVersion": "1.2", | |
| "alpn": ["h2", "http/1.1"], | |
| "certificates": [ | |
| { | |
| "certificateFile": "${CERT_FULL}", | |
| "keyFile": "${CERT_KEY}" | |
| } | |
| ] | |
| } | |
| } | |
| } | |
| ], | |
| "outbounds": [ { "protocol": "freedom" } ] | |
| } | |
| JSON | |
| log "Xray config written." | |
| } | |
| # ---------------- D. BBR & systemd | |
| enable_bbr_and_service(){ | |
| cat >/etc/sysctl.d/99-bbr.conf <<'SYS' | |
| net.core.default_qdisc=fq | |
| net.ipv4.tcp_congestion_control=bbr | |
| net.ipv4.tcp_fastopen=3 | |
| SYS | |
| sysctl --system >/dev/null | |
| cat >/etc/systemd/system/xray.service <<'UNIT' | |
| [Unit] | |
| Description=Xray Service | |
| After=network-online.target | |
| Wants=network-online.target | |
| [Service] | |
| User=root | |
| ExecStart=/usr/local/bin/xray run -confdir /usr/local/etc/xray | |
| Restart=on-failure | |
| RestartSec=10s | |
| LimitNPROC=10000 | |
| LimitNOFILE=1000000 | |
| [Install] | |
| WantedBy=multi-user.target | |
| UNIT | |
| systemctl daemon-reload | |
| systemctl enable xray | |
| systemctl restart xray | |
| } | |
| # ---------------- E. auto-renew via systemd timer | |
| install_cert_timer(){ | |
| # Renew script | |
| cat >/usr/local/sbin/xray-renew-cert.sh <<'SH' | |
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| CERT_DIR="/etc/xray-tls" | |
| CERT_FULL="$CERT_DIR/self.crt" | |
| CERT_KEY="$CERT_DIR/self.key" | |
| DOMAIN_FILE="/etc/xray-domain" | |
| DAYS_DEFAULT=365 | |
| RENEW_BEFORE_DEFAULT=30 | |
| log(){ echo "[renew] $*"; } | |
| DOMAIN="$(cat "$DOMAIN_FILE" 2>/dev/null || true)" | |
| [[ -n "$DOMAIN" ]] || { log "Domain file missing"; exit 0; } | |
| # helpers | |
| days_left(){ | |
| if [[ -s "$CERT_FULL" ]]; then | |
| end_raw="$(openssl x509 -enddate -noout -in "$CERT_FULL" 2>/dev/null | cut -d= -f2 || true)" | |
| if [[ -n "$end_raw" ]]; then | |
| end_epoch="$(date -d "$end_raw" +%s 2>/dev/null || true)" | |
| now_epoch="$(date +%s)" | |
| if [[ -n "$end_epoch" ]]; then | |
| echo $(( (end_epoch - now_epoch) / 86400 )) | |
| return 0 | |
| fi | |
| fi | |
| fi | |
| echo -999999 | |
| } | |
| # read policy | |
| DAYS="$(cat /etc/xray-cert-days 2>/dev/null || echo $DAYS_DEFAULT)" | |
| RENEW_BEFORE="$(cat /etc/xray-cert-renew-before 2>/dev/null || echo $RENEW_BEFORE_DEFAULT)" | |
| dl="$(days_left)" | |
| log "Domain=$DOMAIN, days_left=$dl, renew_before=$RENEW_BEFORE, days=$DAYS" | |
| if (( dl <= RENEW_BEFORE )); then | |
| log "Renewing self-signed cert..." | |
| mkdir -p "$CERT_DIR" | |
| openssl req -x509 -nodes -newkey rsa:2048 -days "$DAYS" \ | |
| -keyout "$CERT_KEY" -out "$CERT_FULL" \ | |
| -subj "/CN=${DOMAIN}" | |
| systemctl restart xray | |
| log "Renewed and restarted xray." | |
| else | |
| log "No renew needed." | |
| fi | |
| SH | |
| chmod +x /usr/local/sbin/xray-renew-cert.sh | |
| # Persist policy for the renewer | |
| echo "$DOMAIN" > /etc/xray-domain | |
| echo "$DAYS" > /etc/xray-cert-days | |
| echo "$RENEW_BEFORE" > /etc/xray-cert-renew-before | |
| # Service | |
| cat >/etc/systemd/system/xray-cert-renew.service <<'UNIT' | |
| [Unit] | |
| Description=Renew self-signed TLS cert for Xray if expiring | |
| After=network-online.target | |
| [Service] | |
| Type=oneshot | |
| ExecStart=/usr/local/sbin/xray-renew-cert.sh | |
| UNIT | |
| # Timer (daily) | |
| cat >/etc/systemd/system/xray-cert-renew.timer <<'UNIT' | |
| [Unit] | |
| Description=Daily check to renew Xray self-signed cert | |
| [Timer] | |
| OnCalendar=daily | |
| Persistent=true | |
| RandomizedDelaySec=1800 | |
| [Install] | |
| WantedBy=timers.target | |
| UNIT | |
| systemctl daemon-reload | |
| systemctl enable --now xray-cert-renew.timer | |
| } | |
| # ---------------- F. output | |
| print_info(){ | |
| local uuid="$(cat /etc/xray-uuid)" | |
| echo | |
| log "==== Summary ====" | |
| echo " Domain : $DOMAIN" | |
| echo " UUID : $uuid" | |
| echo " Transport : VLESS + TCP + TLS (self-signed)" | |
| echo " Listen : :443" | |
| echo " Cert : $CERT_FULL" | |
| echo " Valid (d) : $DAYS, renew-before: $RENEW_BEFORE" | |
| echo | |
| local vless_uri="vless://${uuid}@${DOMAIN}:443?encryption=none&security=tls&sni=${DOMAIN}#${LABEL}" | |
| echo "VLESS URI:" | |
| echo " $vless_uri" | |
| echo | |
| if command -v qrencode >/dev/null 2>&1; then | |
| echo "QR (scan to import):" | |
| echo "$vless_uri" | qrencode -t ANSIUTF8 | |
| echo | |
| fi | |
| echo "Timer status: systemctl status xray-cert-renew.timer" | |
| echo "Force renew : sudo systemctl start xray-cert-renew.service" | |
| echo "Xray status : systemctl status xray --no-pager -n 30" | |
| } | |
| # ---------------- main | |
| main(){ | |
| setup_swap | |
| install_deps | |
| ensure_cert | |
| write_xray_config | |
| enable_bbr_and_service | |
| install_cert_timer | |
| print_info | |
| log "All done ✅" | |
| } | |
| main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment