From afe734e93ad82ddb898d7f2fff5fa854e37f7022 Mon Sep 17 00:00:00 2001 From: sanyitec Date: Fri, 12 Jun 2026 09:25:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20sudo-ssh.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sudo-ssh.sh | 408 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) diff --git a/sudo-ssh.sh b/sudo-ssh.sh index e69de29..49c3e05 100644 --- a/sudo-ssh.sh +++ b/sudo-ssh.sh @@ -0,0 +1,408 @@ +#!/bin/bash + +# 一键准备目标 Linux 设备的 SSH 和部署 sudo 权限。 + +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 -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 -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() { + need_cmd apt-get + + log_info "安装 openssh-server" + 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 <