更新 sudo-ssh.sh
This commit is contained in:
408
sudo-ssh.sh
408
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" <<EOF
|
||||||
|
# Generated by start.sh to let the target user prepare SSH and deployment sudo.
|
||||||
|
${target_user} ALL=(ALL:ALL) ALL
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if command -v visudo >/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="<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 <<EOF
|
||||||
|
Port ${SSH_PORT}
|
||||||
|
PasswordAuthentication ${PASSWORD_AUTH}
|
||||||
|
PubkeyAuthentication ${PUBKEY_AUTH}
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
PermitRootLogin ${PERMIT_ROOT_LOGIN}
|
||||||
|
UsePAM yes
|
||||||
|
EOF
|
||||||
|
|
||||||
|
run_root sshd -t
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_firewall() {
|
||||||
|
log_info "配置防火墙放行 ${SSH_PORT}/tcp"
|
||||||
|
|
||||||
|
if command -v ufw >/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" <<EOF
|
||||||
|
# Generated by start.sh for ecu-debug-client deployment.
|
||||||
|
${deploy_user} ALL=(root) NOPASSWD: ALL
|
||||||
|
EOF
|
||||||
|
|
||||||
|
run_root visudo -cf "$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 <<EOF
|
||||||
|
|
||||||
|
[SUCCESS] SSH 和部署 sudo 权限已准备完成
|
||||||
|
|
||||||
|
目标信息:
|
||||||
|
用户: ${TARGET_USER}
|
||||||
|
IP: ${TARGET_IP}
|
||||||
|
端口: ${SSH_PORT}
|
||||||
|
sudoers: /etc/sudoers.d/ecu-debug-client-deploy
|
||||||
|
sudoers 权限: 完整免密 sudo
|
||||||
|
|
||||||
|
请在部署机执行连通性验证:
|
||||||
|
nc -vz -w 3 ${TARGET_IP} ${SSH_PORT}
|
||||||
|
ssh -p ${SSH_PORT} ${TARGET_USER}@${TARGET_IP} "hostname && whoami && sudo -n id"
|
||||||
|
|
||||||
|
验证通过后,在部署机执行:
|
||||||
|
cd /media/mxhou/fast2t/ws/nav2/chassis/param_set/client
|
||||||
|
./client-deploy.sh --deploy-only
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ "$SSH_PORT" != "22" ]]; then
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
[WARN] 当前 SSH_PORT=${SSH_PORT},如果 client-deploy.sh 仍按默认 22 端口连接,
|
||||||
|
需要同步给部署脚本增加端口支持,或在部署机 ~/.ssh/config 中配置目标 Host/Port。
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
validate_port
|
||||||
|
validate_yes_no PASSWORD_AUTH "$PASSWORD_AUTH"
|
||||||
|
validate_yes_no PUBKEY_AUTH "$PUBKEY_AUTH"
|
||||||
|
validate_yes_no PERMIT_ROOT_LOGIN "$PERMIT_ROOT_LOGIN"
|
||||||
|
|
||||||
|
need_cmd ping
|
||||||
|
need_cmd getent
|
||||||
|
need_cmd ip
|
||||||
|
need_cmd systemctl
|
||||||
|
need_cmd ss
|
||||||
|
need_cmd awk
|
||||||
|
need_cmd cut
|
||||||
|
need_cmd head
|
||||||
|
need_cmd grep
|
||||||
|
need_cmd mktemp
|
||||||
|
need_cmd install
|
||||||
|
|
||||||
|
ensure_sudo_access
|
||||||
|
detect_target_ip
|
||||||
|
check_connectivity
|
||||||
|
install_ssh_server
|
||||||
|
configure_sshd
|
||||||
|
configure_firewall
|
||||||
|
restart_ssh
|
||||||
|
verify_ssh
|
||||||
|
write_deploy_sudoers "$TARGET_USER"
|
||||||
|
verify_deploy_sudoers "$TARGET_USER"
|
||||||
|
print_next_steps
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user