Skip to content

Instantly share code, notes, and snippets.

@irmowan
Last active August 19, 2025 13:03
Show Gist options
  • Select an option

  • Save irmowan/ffd909ce02db21131ab5383e00538148 to your computer and use it in GitHub Desktop.

Select an option

Save irmowan/ffd909ce02db21131ab5383e00538148 to your computer and use it in GitHub Desktop.
vps_boostrap
#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
#!/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
#!/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
#!/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