setup-ssh.sh 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #!/bin/bash
  2. set -euo pipefail
  3. # ============================================
  4. # SSH 服务配置脚本
  5. # 用于 安装、配置 和 启动 SSH 服务
  6. # 用法: bash setup-ssh.sh
  7. # 环境变量: SSH_ROOT_PASSWORD(默认 root)
  8. # ============================================
  9. log_info() { echo "[INFO] $*"; }
  10. log_warn() { echo "[WARN] $*" >&2; }
  11. log_error() { echo "[ERROR] $*" >&2; }
  12. log_info "开始配置 SSH 服务..."
  13. # -------------------------------------------
  14. # 环境变量:SSH_ROOT_PASSWORD 可覆盖默认密码
  15. # -------------------------------------------
  16. ROOT_PASSWORD="${SSH_ROOT_PASSWORD:-root}"
  17. # -------------------------------------------
  18. # 安装 SSH 服务(仅在未安装时执行)
  19. # -------------------------------------------
  20. if ! command -v sshd &>/dev/null; then
  21. log_info "openssh-server 未安装,开始安装..."
  22. export DEBIAN_FRONTEND=noninteractive
  23. if ! apt-get update -qq 2>/dev/null; then
  24. log_warn "apt-get update 失败,尝试离线安装..."
  25. fi
  26. if ! apt-get install -y -qq openssh-server; then
  27. log_error "openssh-server 安装失败,请检查网络或镜像源"
  28. exit 1
  29. fi
  30. log_info "openssh-server 安装完成"
  31. else
  32. log_info "openssh-server 已安装,跳过"
  33. fi
  34. # -------------------------------------------
  35. # 创建 sshd 用户(如果不存在)
  36. # -------------------------------------------
  37. if ! id "sshd" &>/dev/null; then
  38. useradd -r -s /bin/false -d /var/run/sshd -c "SSH privilege separation" sshd
  39. log_info "已创建 sshd 用户"
  40. fi
  41. # -------------------------------------------
  42. # 创建并设置运行时目录权限
  43. # /var/run -> /run (符号链接),只创建一次即可
  44. # -------------------------------------------
  45. SSHD_RUN_DIR="/run/sshd"
  46. mkdir -p "${SSHD_RUN_DIR}"
  47. chown root:root "${SSHD_RUN_DIR}"
  48. chmod 755 "${SSHD_RUN_DIR}"
  49. # -------------------------------------------
  50. # 生成 SSH 主机密钥(如果不存在)
  51. # 注意:在容器环境中,如需跨重启持久化,
  52. # 请将 /etc/ssh 挂载为持久化卷
  53. # -------------------------------------------
  54. if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
  55. ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
  56. chmod 600 /etc/ssh/ssh_host_rsa_key
  57. log_info "已生成 RSA 主机密钥"
  58. fi
  59. if [ ! -f /etc/ssh/ssh_host_ecdsa_key ]; then
  60. ssh-keygen -t ecdsa -b 521 -f /etc/ssh/ssh_host_ecdsa_key -N ""
  61. chmod 600 /etc/ssh/ssh_host_ecdsa_key
  62. log_info "已生成 ECDSA 主机密钥"
  63. fi
  64. if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then
  65. ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
  66. chmod 600 /etc/ssh/ssh_host_ed25519_key
  67. log_info "已生成 Ed25519 主机密钥"
  68. fi
  69. # -------------------------------------------
  70. # 设置 root 用户密码
  71. # -------------------------------------------
  72. passwd -u root 2>/dev/null || true
  73. echo "root:${ROOT_PASSWORD}" | chpasswd
  74. log_info "root 密码已设置"
  75. # -------------------------------------------
  76. # 配置 SSH 服务——允许 root 登录 + 密码认证
  77. # 兼顾注释和未注释两种状态
  78. # -------------------------------------------
  79. sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
  80. sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
  81. # 检查 /etc/ssh/sshd_config.d/*.conf 中是否有覆盖配置
  82. OVERRIDE_FILES=$(grep -rls "PermitRootLogin no\|PasswordAuthentication no" /etc/ssh/sshd_config.d/ 2>/dev/null || true)
  83. if [ -n "${OVERRIDE_FILES}" ]; then
  84. log_warn "以下文件可能覆盖主配置,请手动检查:"
  85. for f in ${OVERRIDE_FILES}; do
  86. echo " ${f}"
  87. done
  88. fi
  89. # -------------------------------------------
  90. # 清理 ~/.ssh/config 中重复的 Host localhost 条目
  91. # 避免端口被意外覆盖
  92. # -------------------------------------------
  93. SSH_CONFIG="${HOME}/.ssh/config"
  94. if [ -f "${SSH_CONFIG}" ]; then
  95. HOST_COUNT=$(grep -c "^Host localhost$" "${SSH_CONFIG}" 2>/dev/null || true)
  96. if [ "${HOST_COUNT}" -gt 1 ]; then
  97. log_warn "~/.ssh/config 中发现 ${HOST_COUNT} 个重复的 Host localhost 条目,保留最后一个"
  98. # 用 awk 按块拆分(每块含 Host 行 + 后续缩进行),保留每个块的 Host 名
  99. # 只保留最后一个 Host localhost 块,其他同名块跳过
  100. awk '
  101. BEGIN { idx = 0 }
  102. /^Host / {
  103. idx++
  104. name[idx] = \$0
  105. content[idx] = ""
  106. next
  107. }
  108. {
  109. content[idx] = content[idx] \$0 "\n"
  110. }
  111. END {
  112. for (i = 1; i <= idx; i++) {
  113. if (name[i] == "Host localhost") {
  114. is_last = 0
  115. for (j = i + 1; j <= idx; j++) {
  116. if (name[j] == "Host localhost") is_last = 1
  117. }
  118. if (is_last) continue
  119. }
  120. print name[i]
  121. if (content[i] != "") printf "%s", content[i]
  122. }
  123. }
  124. ' "${SSH_CONFIG}" >"${SSH_CONFIG}.tmp" && mv "${SSH_CONFIG}.tmp" "${SSH_CONFIG}"
  125. chmod 600 "${SSH_CONFIG}"
  126. log_info "已清理重复的 Host localhost 条目"
  127. fi
  128. fi
  129. # -------------------------------------------
  130. # 重启 / 启动 SSH 服务
  131. # -------------------------------------------
  132. if service ssh status &>/dev/null; then
  133. service ssh restart
  134. log_info "SSH 服务已重启"
  135. else
  136. service ssh start
  137. log_info "SSH 服务已启动"
  138. fi
  139. # -------------------------------------------
  140. # 最终状态检查
  141. # -------------------------------------------
  142. sleep 1
  143. if service ssh status >/dev/null 2>&1; then
  144. log_info "SSH 服务运行正常"
  145. echo ""
  146. echo " SSH 连接信息:"
  147. echo " 地址: localhost"
  148. echo " 端口: 22"
  149. echo " 用户: root"
  150. echo " 密码: ${ROOT_PASSWORD}"
  151. echo ""
  152. echo " 提示: 可通过环境变量 SSH_ROOT_PASSWORD 自定义密码"
  153. echo " 例: SSH_ROOT_PASSWORD=mysecret bash setup-ssh.sh"
  154. echo ""
  155. echo " 容器外连接: ssh root@localhost -p 6***1 -A"
  156. echo ""
  157. else
  158. echo ""
  159. log_error "SSH 服务启动失败"
  160. service ssh status 2>&1 || true
  161. exit 1
  162. fi
  163. log_info "SSH 服务配置完成"