#!/usr/bin/env bash
#
# cloudflared-ssh setup
# ---------------------------------------------------------------------------
# 让任何云 Linux 机器通过 Cloudflare quick tunnel 实现 SSH 公网访问。
# 无需公网 IP、无需自有域名、无需 Cloudflare 账号。
#
# 用法:
#   bash setup.sh                 # 留空密码 -> 自动生成 16 位随机密码
#   bash setup.sh myStrongPass    # 自定义 root 密码
#   bash setup.sh --no-root       # 不启用 root 登录，改为新建 cfuser 并启用密钥
#
# 设计目标:
#   - 幂等: 重复运行不会破坏已有配置（会重启 tunnel 以刷新域名）
#   - 兜底: github 下载被墙时，自动改用 pkg.cloudflare.com apt 仓库
#   - 自包含: 安装 sshd + cloudflared + 配置 + 启动 + 抓域名 + 输出连接信息
#   - 多架构: 自动探测 x86_64 / aarch64 等，下载对应 cloudflared
# ---------------------------------------------------------------------------
set -euo pipefail

PASSWORD="${1:-}"
NO_ROOT=0
[[ "${1:-}" == "--no-root" ]] && { NO_ROOT=1; PASSWORD=""; }

TUN_LOG="/tmp/cf-ssh-tunnel.log"
INFO_FILE="/root/.cf-ssh-info"
CF_DEB_DIR="/tmp"

log(){ echo "[cloudflared-ssh] $*"; }

# ---------- 0. 权限: 非 root 自动 sudo 重跑 ----------
if [ "$(id -u)" -ne 0 ]; then
  if command -v sudo >/dev/null 2>&1; then
    log "非 root，使用 sudo 重跑 ..."
    exec sudo "$0" "$@"
  else
    echo "[!] 需要 root 权限，且系统无 sudo，无法继续"; exit 1
  fi
fi

# ---------- 1. 安装 cloudflared（github 主路径，pkg.cloudflare.com 兜底） ----------
detect_arch(){
  case "$(uname -m)" in
    x86_64)            echo amd64  ;;
    aarch64|arm64)     echo arm64  ;;
    armv7l|armv7|arm)  echo arm    ;;
    i386|i686)         echo 386    ;;
    *)                 echo "$(uname -m)" ;;
  esac
}

is_elf(){ [ -s "$1" ] && head -c 4 "$1" | grep -q $'\x7fELF'; }

install_cloudflared(){
  if command -v cloudflared >/dev/null 2>&1; then
    log "cloudflared 已存在: $(cloudflared --version 2>/dev/null | head -1)"
    return 0
  fi
  ARCH="$(detect_arch)"
  log "安装 cloudflared (arch=$ARCH) ..."
  # 方式 A: 官方 github release（大多数云机器直连可用）
  if curl -fsSL --max-time 25 -o "$CF_DEB_DIR/cloudflared" \
       "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${ARCH}" \
       2>/dev/null \
     && is_elf "$CF_DEB_DIR/cloudflared"; then
    chmod +x "$CF_DEB_DIR/cloudflared" && mv "$CF_DEB_DIR/cloudflared" /usr/local/bin/cloudflared
    log "已从 github 安装 cloudflared ($ARCH)"
    return 0
  fi
  # 方式 B: Cloudflare 官方 apt 仓库（github 被墙时的兜底，仅 amd64）
  # 注意: pkg.cloudflare.com 只提供 amd64 的 deb
  if [ "$ARCH" != "amd64" ]; then
    echo "[!] github 不可达，且 pkg.cloudflare.com 仅提供 amd64 包，无法为 $ARCH 安装"
    exit 1
  fi
  log "github 不可达，改用 pkg.cloudflare.com apt 仓库 ..."
  curl -fsSL --max-time 40 https://pkg.cloudflare.com/cloudflared/dists/jammy/main/binary-amd64/Packages \
    -o "$CF_DEB_DIR/cf_pkgs.txt"
  DEB=$(awk '/^Filename:/{print $2; exit}' "$CF_DEB_DIR/cf_pkgs.txt")
  curl -fsSL --max-time 180 -L -o "$CF_DEB_DIR/cloudflared.deb" "https://pkg.cloudflare.com/cloudflared/$DEB"
  dpkg -i "$CF_DEB_DIR/cloudflared.deb"
  command -v cloudflared >/dev/null 2>&1 || { echo "[!] cloudflared 安装失败"; exit 1; }
  log "已从 pkg.cloudflare.com 安装 cloudflared"
}

# ---------- 2. 安装并启动 sshd ----------
setup_sshd(){
  if ! [ -x /usr/sbin/sshd ] && ! command -v sshd >/dev/null 2>&1; then
    log "安装 openssh-server ..."
    apt-get update -qq
    DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-server >/dev/null 2>&1
  fi
  mkdir -p /run/sshd
  # 关键修复: 容器/新镜像常缺 host key，否则 sshd 启动即退出
  if ! ls /etc/ssh/ssh_host_*key >/dev/null 2>&1; then
    log "生成 SSH host key ..."
    ssh-keygen -A
  fi
  # 允许 root 密码登录（--no-root 时改走专用账号 + 密钥）
  if [ "$NO_ROOT" -eq 1 ]; then
    ensure_cfuser
  else
    set_conf '^PermitRootLogin'      'PermitRootLogin yes'
    set_conf '^PasswordAuthentication' 'PasswordAuthentication yes'
  fi
  if ! pgrep -x sshd >/dev/null 2>&1; then
    /usr/sbin/sshd
    log "sshd 已启动"
  else
    log "sshd 已在运行"
  fi
  sleep 1
  if ! (ss -tlnp 2>/dev/null | grep -q ':22 ' || netstat -tlnp 2>/dev/null | grep -q ':22 '); then
    log "[!] 警告: 22 端口未监听，SSH 可能不可用（检查 host key 与 sshd_config）"
  fi
}

set_conf(){
  local pat="$1" val="$2"
  if grep -q "$pat" /etc/ssh/sshd_config; then
    sed -i "s|$pat.*|$val|" /etc/ssh/sshd_config
  else
    echo "$val" >> /etc/ssh/sshd_config
  fi
}

# --no-root: 新建专用账号 cfuser，强制密钥登录，禁用 root 密码
ensure_cfuser(){
  if ! id cfuser >/dev/null 2>&1; then
    useradd -m -s /bin/bash cfuser
    log "已新建专用账号 cfuser"
  fi
  local home; home="$(getent passwd cfuser | cut -d: -f6)"
  mkdir -p "$home/.ssh"; chmod 700 "$home/.ssh"
  if [ ! -f "$home/.ssh/authorized_keys" ]; then
    log "未检测到 cfuser 公钥，请自行把你的公钥写入 $home/.ssh/authorized_keys"
  fi
  chown -R cfuser:cfuser "$home/.ssh"
  # 关闭 root 密码登录，仅保留密钥方式
  set_conf '^PermitRootLogin'        'PermitRootLogin prohibit-password'
  set_conf '^PasswordAuthentication' 'PasswordAuthentication no'
  set_conf '^PubkeyAuthentication'   'PubkeyAuthentication yes'
}

# ---------- 3. 设置密码 ----------
set_password(){
  REMOTE_USER="root"
  if [ "$NO_ROOT" -eq 1 ]; then
    REMOTE_USER="cfuser"
    log "使用 --no-root 模式: 远程用户 cfuser，密钥登录（无需密码）"
    PASSWORD="(使用 SSH 密钥登录，无密码)"
    return 0
  fi
  if [ -z "$PASSWORD" ]; then
    PASSWORD=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 16)
    log "未提供密码，已生成随机 16 位密码"
  else
    log "使用自定义密码"
  fi
  echo "root:$PASSWORD" | chpasswd
}

# ---------- 4. 启动 quick tunnel 并抓取域名 ----------
start_tunnel(){
  log "启动 cloudflared quick tunnel -> ssh://localhost:22 ..."
  pkill -f 'cloudflared tunnel --url ssh://localhost:22' 2>/dev/null || true
  sleep 1
  nohup cloudflared tunnel --url ssh://localhost:22 > "$TUN_LOG" 2>&1 &
  DOMAIN=""
  for i in $(seq 1 45); do
    DOMAIN=$(grep -oE 'https://[a-z0-9.-]+\.trycloudflare\.com' "$TUN_LOG" 2>/dev/null | head -1 || true)
    if [ -n "$DOMAIN" ]; then break; fi
    sleep 1
  done
  if [ -z "$DOMAIN" ]; then
    echo "[!] 未能从日志获取 tunnel 域名，请检查 $TUN_LOG"
    exit 1
  fi
}

# ---------- 5. 输出访问信息 ----------
print_info(){
  {
    echo "域名 : $DOMAIN"
    echo "用户 : $REMOTE_USER"
    echo "密码 : $PASSWORD"
    echo "连接 : ssh -o ProxyCommand=\"cloudflared access ssh --hostname %h\" $REMOTE_USER@$DOMAIN"
  } | tee "$INFO_FILE" >/dev/null
  chmod 600 "$INFO_FILE"

  cat <<EOF

========================================================
  SSH over Cloudflare Quick Tunnel — 访问信息
========================================================
  域名 : $DOMAIN
  用户 : $REMOTE_USER
  密码 : $PASSWORD

  本机连接（需先安装 cloudflared）:
    ssh -o ProxyCommand="cloudflared access ssh --hostname %h" $REMOTE_USER@$DOMAIN

  或写入 ~/.ssh/config:
    Host sandbox
        HostName $DOMAIN
        User $REMOTE_USER
        ProxyCommand cloudflared access ssh --hostname %h
  然后:  ssh sandbox   (输入上面的密码 / 用密钥)
========================================================
  已把以上信息保存到 $INFO_FILE (权限 600)，断开后仍可查看。
  注意:
  - 域名是临时的: tunnel 进程退出 / 机器重启即失效，需重跑本脚本拿新域名。
  - 公网暴露 SSH，建议: 用 --no-root 模式、或连上后立即 passwd 改掉随机密码。
  - 不用时运行 stop.sh 结束 cloudflared 进程以关闭公网暴露。
========================================================
EOF
}

main(){
  log "开始配置 SSH over Cloudflare quick tunnel ..."
  install_cloudflared
  setup_sshd
  set_password
  start_tunnel
  print_info
}
main "$@"
