本文档记录了一套完整的自动化方案,通过交互式脚本收集主机信息,生成 Ansible inventory,并执行一系列 playbook,最终实现所有指定 Linux 主机免密登录、关闭密码登录。
一、环境概览
所有 Linux 主机均通过 frp 映射到公网 IP 8.130.213.85。控制节点为 6 号机(matebook,Debian)。
二、交互式主机信息收集脚本
为确保现状有不掌握的变化,在控制节点上,创建一个交互式脚本 setup_inventory.sh,用于收集所有 Linux 主机的连接信息并生成 inventory.ini 文件。
1. 创建脚本文件
nano ~/ansible-ssh/setup_inventory.sh
2. 脚本内容
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 输出文件
INVENTORY_FILE="inventory.ini"
TMP_FILE="/tmp/host_info.tmp"
# 初始化文件
> "$INVENTORY_FILE"
> "$TMP_FILE"
echo -e "${GREEN}=========================================${NC}"
echo -e "${GREEN}SSH 主机信息收集脚本${NC}"
echo -e "${GREEN}=========================================${NC}"
echo "请依次输入每台 Linux 主机的信息(输入主机名后回车,空主机名结束)"
echo ""
HOST_COUNT=0
while true; do
echo -e "${YELLOW}主机 ${HOST_COUNT} 信息:${NC}"
read -p "主机别名(如 host1): " alias
if [ -z "$alias" ]; then
break
fi
read -p "主机实际名称(如 pve): " realname
read -p "系统类型(debian/ubuntu/openwrt): " os_type
read -p "SSH 用户名: " ssh_user
read -p "当前密码(用于初始连接): " ssh_pass
read -p "映射公网端口: " ssh_port
read -p "当前登录方式(password/key): " login_method
read -p "是否最终关闭密码登录(yes/no): " disable_pw
# 保存到临时文件(用于后续可能的处理)
echo "$alias|$realname|$os_type|$ssh_user|$ssh_pass|$ssh_port|$login_method|$disable_pw" >> "$TMP_FILE"
# 构建 inventory 条目
echo "$alias ansible_host=8.130.213.85 ansible_port=$ssh_port ansible_user=$ssh_user" >> "$INVENTORY_FILE"
if [ "$os_type" = "openwrt" ]; then
echo "$alias ansible_os_family=OpenWrt" >> "$INVENTORY_FILE"
fi
HOST_COUNT=$((HOST_COUNT+1))
echo ""
done
echo -e "${GREEN}已收集 $HOST_COUNT 台主机信息,inventory 文件生成于 $INVENTORY_FILE${NC}"
echo -e "${YELLOW}请检查 $INVENTORY_FILE 内容是否正确,如有误请手动编辑。${NC}"
3. 赋予执行权限并运行
chmod +x setup_inventory.sh
./setup_inventory.sh
脚本运行后会提示依次输入每台主机的信息,包括:
主机别名(用于 Ansible,如 host1)
主机实际名称(仅用于显示)
系统类型(debian/ubuntu/openwrt)
SSH 用户名
当前密码(仅用于记录,后续不会明文保存)
映射公网端口
当前登录方式(password/key)
是否最终关闭密码登录
输入完成后,脚本生成 inventory.ini 文件,并将原始信息保存到 /tmp/host_info.tmp(供后续参考,完成后可删除)。后续 Ansible 操作将直接使用此 inventory 文件。
三、安装 Ansible 并准备目录
sudo apt update && sudo apt install ansible -y
mkdir -p ~/ansible-ssh && cd ~/ansible-ssh
# 将上一步生成的 inventory.ini 移动到当前目录(如果已在则跳过)
四、编写 Ansible Playbook
1. reset_to_password.yml:开启所有主机密码登录并清空 authorized_keys
---
- name: 统一改为密码登录,清空现有 SSH 密钥
hosts: all
gather_facts: yes
vars:
backup_authorized_keys: yes
tasks:
- name: 备份 authorized_keys (OpenSSH)
copy:
src: "~/.ssh/authorized_keys"
dest: "~/.ssh/authorized_keys.backup_{{ ansible_date_time.epoch }}"
remote_src: yes
mode: '0600'
when:
- backup_authorized_keys | bool
- ansible_os_family != "OpenWrt"
ignore_errors: yes
- name: 删除 authorized_keys (OpenSSH)
file:
path: "~/.ssh/authorized_keys"
state: absent
when: ansible_os_family != "OpenWrt"
- name: 删除 authorized_keys (dropbear)
file:
path: "~/.ssh/authorized_keys"
state: absent
when: ansible_os_family == "OpenWrt"
- name: 启用密码认证 (OpenSSH)
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication yes'
backup: yes
become: yes
when: ansible_os_family != "OpenWrt"
notify: restart sshd
- name: 启用密码认证 (dropbear)
lineinfile:
path: /etc/config/dropbear
regexp: 'option PasswordAuth'
line: " option PasswordAuth 'on'"
backup: yes
become: yes
when: ansible_os_family == "OpenWrt"
notify: restart dropbear
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
become: yes
when: ansible_os_family != "OpenWrt"
- name: restart dropbear
service:
name: dropbear
state: restarted
become: yes
when: ansible_os_family == "OpenWrt"
2. deploy_keys.yml:分发客户端公钥
---
- name: 将所有客户端公钥分发到所有 Linux 主机
hosts: all
gather_facts: no
vars:
key_dir: "~/ansible-ssh/keys"
key_files:
- id_ed25519_matebook.pub
- id_ed25519_maxgem.pub
- id_ed25519_matebook-w.pub
- id_ed25519_tiny10.pub
- id_ed25519_j1900.pub
tasks:
- name: 确保 .ssh 目录存在
file:
path: "~/.ssh"
state: directory
mode: '0700'
- name: 添加客户端公钥
authorized_key:
user: "{{ ansible_user }}"
state: present
key: "{{ lookup('file', key_dir + '/' + item) }}"
loop: "{{ key_files }}"
loop_control:
label: "{{ item }}"
3. disable_password.yml:关闭密码登录(排除 host5)
---
- name: 禁用 SSH 密码登录(排除 host5)
hosts: all
gather_facts: yes
tasks:
- name: 关闭密码认证 (OpenSSH)
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PasswordAuthentication'
line: 'PasswordAuthentication no'
backup: yes
become: yes
when:
- ansible_os_family != "OpenWrt"
- inventory_hostname != "host5"
notify: restart sshd
- name: 关闭密码认证 (dropbear)
lineinfile:
path: /etc/config/dropbear
regexp: 'option PasswordAuth'
line: " option PasswordAuth 'off'"
backup: yes
become: yes
when:
- ansible_os_family == "OpenWrt"
- inventory_hostname != "host5"
notify: restart dropbear
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
become: yes
when:
- ansible_os_family != "OpenWrt"
- inventory_hostname != "host5"
- name: restart dropbear
service:
name: dropbear
state: restarted
become: yes
when:
- ansible_os_family == "OpenWrt"
- inventory_hostname != "host5"
五、生成并收集客户端公钥
1. 创建公钥存放目录
mkdir -p ~/ansible-ssh/keys
2. 6 号机(matebook)自身密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_matebook -C "matebook@$(hostname)" -N ""
cp ~/.ssh/id_ed25519_matebook.pub ~/ansible-ssh/keys/
3. 11 号机(j1900)密钥
ssh max@8.130.213.85 -p 5222 "ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_j1900 -C 'j1900@$(hostname)' -N ''"
scp -P 5222 max@8.130.213.85:~/.ssh/id_ed25519_j1900.pub ~/ansible-ssh/keys/
4. Windows 客户端(7、9 号)密钥(PowerShell)
RDP 登录到 Windows(通过 frp 端口 13389/23389)。
打开 PowerShell,执行:
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.ssh" ssh-keygen -t ed25519 -f "$env:USERPROFILE\.ssh\id_ed25519_maxgem" -C "maxgem@$env:COMPUTERNAME"查看公钥内容:
Get-Content "$env:USERPROFILE\.ssh\id_ed25519_maxgem.pub" | Set-Clipboard在 6 号机上创建公钥文件:
nano ~/ansible-ssh/keys/id_ed25519_maxgem.pub # 粘贴内容,保存对 9 号机 tiny10 重复,文件名为 id_ed25519_tiny10.pub。
5. 8 号机(matebook-w,双系统)密钥
ssh-keygen -t ed25519 -f ~/ansible-ssh/keys/id_ed25519_matebook-w -C "matebook-w@$(hostname)" -N ""
# 挂载 Windows 分区(如 /dev/sda2,根据实际调整)
sudo mount /dev/sda2 /mnt/windows
cp ~/ansible-ssh/keys/id_ed25519_matebook-w /mnt/windows/Users/用户名/.ssh/
cp ~/ansible-ssh/keys/id_ed25519_matebook-w.pub /mnt/windows/Users/用户名/.ssh/
也可将密钥拷贝到第三方,重启 6 号机进入 Windows 复制到用户主目录下的 .ssh 目录。
6. 验证公钥文件
ls ~/ansible-ssh/keys/
应包含 5 个公钥文件。
六、执行 Ansible Playbook
1. 预先添加主机指纹
# 从 inventory 中提取端口并添加指纹
grep -E 'ansible_port' inventory.ini | awk -F'ansible_port=' '{print $2}' | while read port; do
ssh-keyscan -p $port -H 8.130.213.85 >> ~/.ssh/known_hosts 2>/dev/null
done
2. 执行 reset_to_password.yml
ansible-playbook -i inventory.ini reset_to_password.yml -k --ask-become-pass
输入各主机的 SSH 密码(由于密码可能不同,Ansible 会依次提示,可提前准备好密码列表)。如果密码统一,则只需输入一次。
3. 执行 deploy_keys.yml
ansible-playbook -i inventory.ini deploy_keys.yml -k
4. 验证免密登录
从 6 号机测试所有主机(使用对应私钥):
ssh -i ~/.ssh/id_ed25519_matebook root@8.130.213.85 -p 5322 # 1 号机
ssh -i ~/.ssh/id_ed25519_matebook root@8.130.213.85 -p 5422 # 2 号机
ssh -i ~/.ssh/id_ed25519_matebook max@8.130.213.85 -p 5022 # 3 号机
ssh -i ~/.ssh/id_ed25519_matebook max@8.130.213.85 -p 5122 # 4 号机
ssh -i ~/.ssh/id_ed25519_matebook root@8.130.213.85 -p 5522 # 5 号机仍密码
ssh -i ~/.ssh/id_ed25519_matebook max@8.130.213.85 -p 5222 # 11 号机
ssh -i ~/.ssh/id_ed25519_matebook root@8.130.213.85 -p 29340 # 10 号机
Windows 客户端 (7、8、9)在 PowerShell 中测试:
ssh -i "$env:USERPROFILE\.ssh\id_ed25519_maxgem" max@8.130.213.85 -p 5022 # 7 号机
ssh -i "$env:USERPROFILE\.ssh\id_ed25519_tiny10" max@8.130.213.85 -p 5022 # 8 号机
ssh -i "$env:USERPROFILE\.ssh\id_ed25519_matebook-w" max@8.130.213.85 -p 5022 # 9 号机
...
Windows 客户端也可使用 Finalshell 等客户端软件逐一测试。
5. 执行 disable_password.yml
ansible-playbook -i inventory.ini disable_password.yml -k --ask-become-pass
6. 最终验证
测试密码登录应被拒绝。
密钥登录依然有效。
七、私钥加密与备份
1. 为客户端私钥添加密码短语
# Linux
ssh-keygen -p -f ~/.ssh/id_ed25519_matebook
# Windows (PowerShell)
ssh-keygen -p -f "$env:USERPROFILE\.ssh\id_ed25519_maxgem"
2. 备份所有密钥和脚本(在 6 号机上打包加密)
cd ~/ansible-ssh
tar czf ssh-auth-backup.tar.gz inventory.ini *.yml keys/ ~/.ssh/id_ed25519_*
openssl enc -aes-256-cbc -pbkdf2 -salt -in ssh-auth-backup.tar.gz -out ssh-auth-backup.tar.gz.enc
rm ssh-auth-backup.tar.gz
将加密文件 .enc 安全存储。
3. 解密恢复步骤
openssl enc -d -aes-256-cbc -pbkdf2 -in ssh-auth-backup.tar.gz.enc -out ssh-auth-backup.tar.gz
tar xzf ssh-auth-backup.tar.gz
八、清理临时文件
脚本生成的 /tmp/host_info.tmp 可在确认无问题后删除:
rm -f /tmp/host_info.tmp
此外,如果不再需要原始输入记录,可一并删除。
九、5 号机(maxwrt)后续处理
由于 5 号机公钥认证失败,目前保持密码登录。后续可尝试:
检查 dropbear 版本,升级或改用 OpenSSH。
手动测试 RSA 密钥。
安装 OpenSSH-server 替代 dropbear。
暂不处理不影响其他主机安全。
十、手机等移动端登录的办法
只要把电脑上那个私钥文件(任意客户端 id_ed25519私钥均可)安全地传输到手机,导入到 App 里就能直接登录。
操作上主要有两点需要注意:
1. 传输方式
推荐(最安全):用数据线连接电脑,将私钥文件复制到手机 Download 文件夹。
便捷(局域网):用 LocalSend、Snapdrop 等工具传输,避免通过微信、QQ等云端服务器传输。
慎用:尽量不要直接复制私钥里的文本内容去粘贴,容易因格式错乱(如换行符丢失)导致 App 提示“无效密钥”。
2. 导入手机 App
以 Termius 为例:进入 Keychain -> New Key -> Import from file,选中你传进来的私钥文件即可。若生成时设置了密码短语(Passphrase),导入时需要填写。
十一、总结
本流程通过交互式脚本收集主机信息,自动生成 inventory 文件,然后执行三个核心 playbook,最终实现所有指定主机的密钥登录和密码登录禁用。所有 Windows 客户端使用 PowerShell 操作,私钥已加密备份,确保安全。流程可重复执行,只需根据实际环境调整。