#!/bin/bash set -euo pipefail TARGET_USER=${TARGET_USER:-leador} TARGET_IP=${TARGET_IP:-192.168.191.37} SSH_PORT=${SSH_PORT:-22} PASSWORD_AUTH=${PASSWORD_AUTH:-yes} PUBKEY_AUTH=${PUBKEY_AUTH:-yes} PERMIT_ROOT_LOGIN=${PERMIT_ROOT_LOGIN:-no} CONNECTIVITY_HOSTS=${CONNECTIVITY_HOSTS:-"223.5.5.5 1.1.1.1 8.8.8.8"} ROOT_PASSWORD=${ROOT_PASSWORD:-} log_info() { echo "[INFO] $*" } log_warn() { echo "[WARN] $*" >&2 } log_error() { echo "[ERROR] $*" >&2 } need_cmd() { local name="$1" if ! command -v "$name" >/dev/null 2>&1; then log_error "缺少命令: $name" exit 1 fi } validate_port() { if ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || ((SSH_PORT < 1 || SSH_PORT > 65535)); then log_error "SSH_PORT 必须是 1-65535 之间的数字,当前值: $SSH_PORT" exit 1 fi } validate_yes_no() { local name="$1" local value="$2" if [[ "$value" != "yes" && "$value" != "no" ]]; then log_error "$name 只能是 yes 或 no,当前值: $value" exit 1 fi } run_root() { if [[ "$(id -u)" -eq 0 ]]; then "$@" else sudo "$@" fi } write_sudo_bootstrap_script() { local script_path="$1" cat > "$script_path" <<'BOOTSTRAP_SUDO' #!/bin/bash set -euo pipefail target_user="$1" if ! id "$target_user" >/dev/null 2>&1; then echo "[ERROR] 用户不存在: $target_user" >&2 exit 1 fi if ! command -v sudo >/dev/null 2>&1; then if command -v apt-get >/dev/null 2>&1; then apt-get update apt-get install -y sudo else echo "[ERROR] 系统未安装 sudo,且找不到 apt-get 自动安装" >&2 exit 1 fi fi if command -v groupadd >/dev/null 2>&1 && ! getent group sudo >/dev/null 2>&1; then groupadd sudo fi if command -v usermod >/dev/null 2>&1 && getent group sudo >/dev/null 2>&1; then usermod -aG sudo "$target_user" fi mkdir -p /etc/sudoers.d tmp_file=$(mktemp /tmp/start-bootstrap-sudoers.XXXXXX) trap 'rm -f "$tmp_file"' EXIT cat > "$tmp_file" </dev/null 2>&1; then visudo -cf "$tmp_file" >/dev/null fi install -o root -g root -m 0440 "$tmp_file" "/etc/sudoers.d/90-${target_user}-sudo" BOOTSTRAP_SUDO chmod 700 "$script_path" } bootstrap_sudo_with_root() { local target_user="$1" log_info "当前 shell 是 root,正在给用户 ${target_user} 添加 sudo 权限" local bootstrap_script="" bootstrap_script=$(mktemp /tmp/start-bootstrap-sudo.XXXXXX) write_sudo_bootstrap_script "$bootstrap_script" local status=0 set +e "$bootstrap_script" "$target_user" status=$? set -e rm -f "$bootstrap_script" return "$status" } bootstrap_sudo_with_su() { local target_user="$1" need_cmd su log_warn "当前用户没有可用 sudo 权限,尝试通过 root 用户为 ${target_user} 添加 sudo 权限" local bootstrap_script="" bootstrap_script=$(mktemp /tmp/start-bootstrap-sudo.XXXXXX) write_sudo_bootstrap_script "$bootstrap_script" local status=0 set +e if [[ -n "$ROOT_PASSWORD" ]]; then printf '%s\n' "$ROOT_PASSWORD" | su root -c "bash '$bootstrap_script' '$target_user'" status=$? else su root -c "bash '$bootstrap_script' '$target_user'" status=$? fi set -e rm -f "$bootstrap_script" return "$status" } ensure_sudo_access() { log_info "检查 ${TARGET_USER} 的 sudo 权限" if [[ "$(id -u)" -eq 0 ]]; then bootstrap_sudo_with_root "$TARGET_USER" return fi if [[ "$(id -un)" == "$TARGET_USER" ]] && command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then log_info "当前用户已有免密 sudo 权限" return fi if [[ "$(id -un)" == "$TARGET_USER" ]] && [[ -t 0 ]] && command -v sudo >/dev/null 2>&1 && sudo -v; then log_info "当前用户已有 sudo 权限" return fi bootstrap_sudo_with_su "$TARGET_USER" if [[ "$(id -un)" == "$TARGET_USER" ]] && command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then log_info "sudo 权限已补齐" return fi if [[ "$(id -un)" == "$TARGET_USER" ]] && [[ -t 0 ]] && command -v sudo >/dev/null 2>&1 && sudo -v; then log_info "sudo 权限已补齐" return fi if [[ "$(id -un)" != "$TARGET_USER" ]]; then log_info "已为 ${TARGET_USER} 写入 sudo 授权,后续 root 操作继续由当前用户执行" return fi log_error "已尝试添加 sudo 权限,但 ${TARGET_USER} 仍无法执行 sudo" log_error "如果刚加入 sudo 组仍不生效,请重新登录;如果 root 被禁用,请先用具备管理员权限的账号处理" exit 1 } detect_target_ip() { if [[ -n "$TARGET_IP" ]]; then return fi local detected="" detected=$(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n 1 || true) if [[ -n "$detected" ]]; then TARGET_IP="$detected" else TARGET_IP="" fi } check_connectivity() { log_info "检查目标设备联网状态" local ok=0 local host="" for host in $CONNECTIVITY_HOSTS; do if ping -c 1 -W 2 "$host" >/dev/null 2>&1; then log_info "网络连通: ping $host 成功" ok=1 break fi done if [[ "$ok" -ne 1 ]]; then log_error "目标设备无法 ping 通联网检测地址: $CONNECTIVITY_HOSTS" log_error "请先修复网络、DNS、网关、代理或 ZeroTier/LAN 路由,再重新执行本脚本" exit 1 fi if ! getent hosts archive.ubuntu.com >/dev/null 2>&1 && ! getent hosts mirrors.aliyun.com >/dev/null 2>&1; then log_warn "DNS 解析 apt 源域名失败。ping IP 虽然成功,但 apt update 可能失败" fi } install_ssh_server() { if command -v sshd >/dev/null 2>&1; then log_info "openssh-server 已存在,跳过安装" return fi if command -v dpkg >/dev/null 2>&1 && dpkg -s openssh-server >/dev/null 2>&1; then log_info "openssh-server 已安装,跳过安装" return fi log_info "安装 openssh-server" need_cmd apt-get run_root apt-get update run_root apt-get install -y openssh-server } configure_sshd() { log_info "写入 SSH 配置: port=${SSH_PORT}, password=${PASSWORD_AUTH}, pubkey=${PUBKEY_AUTH}, root=${PERMIT_ROOT_LOGIN}" run_root mkdir -p /etc/ssh/sshd_config.d run_root tee /etc/ssh/sshd_config.d/10-remote-login.conf >/dev/null </dev/null 2>&1; then local ufw_status="" ufw_status=$(run_root ufw status | head -n 1 || true) if [[ "$ufw_status" == *active* || "$ufw_status" == *活动* ]]; then run_root ufw allow "${SSH_PORT}/tcp" run_root ufw reload log_info "UFW 已放行 ${SSH_PORT}/tcp" else log_info "UFW 未启用,跳过 UFW 放行" fi fi if command -v firewall-cmd >/dev/null 2>&1; then if run_root firewall-cmd --state >/dev/null 2>&1; then run_root firewall-cmd --add-port="${SSH_PORT}/tcp" --permanent run_root firewall-cmd --reload log_info "firewalld 已放行 ${SSH_PORT}/tcp" else log_info "firewalld 未运行,跳过 firewalld 放行" fi fi } restart_ssh() { log_info "启动并重启 SSH 服务" run_root systemctl enable --now ssh run_root systemctl restart ssh } verify_ssh() { log_info "验证 SSH 服务状态" run_root sshd -t systemctl is-active --quiet ssh if ss -lntp 2>/dev/null | grep -qE ":${SSH_PORT}[[:space:]]"; then log_info "SSH 端口已监听: ${SSH_PORT}" else log_error "未发现 SSH 端口监听: ${SSH_PORT}" log_error "最近 SSH 日志如下:" run_root journalctl -u ssh -n 80 --no-pager || true exit 1 fi log_info "当前 sshd 关键配置:" run_root sshd -T | grep -E '^(port|passwordauthentication|pubkeyauthentication|permitrootlogin|usepam) ' } resolve_cmd_path() { local name="$1" local path="" path=$(type -P "$name" 2>/dev/null || true) if [[ -z "$path" ]]; then log_error "目标设备缺少必要命令: $name" exit 1 fi printf '%s' "$path" } write_deploy_sudoers() { local deploy_user="$1" local sudoers_tmp="" log_info "写入完整免密 sudo 规则: user=${deploy_user}" resolve_cmd_path install >/dev/null resolve_cmd_path mkdir >/dev/null resolve_cmd_path visudo >/dev/null sudoers_tmp=$(mktemp /tmp/ecu-debug-client-deploy-sudoers.XXXXXX) cat > "$sudoers_tmp" </dev/null run_root mkdir -p /etc/sudoers.d run_root install -o root -g root -m 0440 "$sudoers_tmp" /etc/sudoers.d/ecu-debug-client-deploy run_root visudo -cf /etc/sudoers.d/ecu-debug-client-deploy >/dev/null rm -f "$sudoers_tmp" } verify_deploy_sudoers() { local deploy_user="$1" local systemctl_path="" systemctl_path=$(resolve_cmd_path systemctl) log_info "验证 ${deploy_user} 可以无密码执行部署 sudo 命令" if [[ "$(id -u)" -ne 0 && "$(id -un)" == "$deploy_user" ]]; then sudo -k sudo -n "$systemctl_path" daemon-reload return fi need_cmd su run_root su -s /bin/bash -c "sudo -k; sudo -n '$systemctl_path' daemon-reload" "$deploy_user" } print_next_steps() { cat <