我有一组 RPi4(其中 7 个)运行 Ubuntu 20.04。其中之一是 GUI,我从那里运行命令。有 3 名经理和 3 名工作人员(/etc/hosts 均已正确设置)。
最初,它们都有一个具有相同名称和密码的用户。
我想在 gui (gui0) 中运行一次的单个脚本中设置 SSH,这样我就可以使用无密码 SSH。
gui* 机器应该可以访问所有内容; manager* 机器应该能够访问经理和工人;并且工人应该只能访问其他工人。
原因是我将使用 GlusterFS(以及其他一些东西)设置存储,因此机器需要能够在它们之间进行通信。
我开发了一个脚本,或多或少有效:根据我在某些命令之间睡眠的时间,实际复制的键更多,但我已经睡了很长时间,但仍然没有达到 100% 。
脚本:
#!/bin/bash
################################################################################
## source ##
################################################################################
source lib/libalx/sh/sysexits.sh; ## This provides EX_USAGE=64
################################################################################
## definitions ##
################################################################################
ARGC=0;
guis="gui0";
managers="manager0 manager1 manager2";
workers="worker0 worker1 worker2";
all_machines="${guis} ${managers} ${workers}";
gui_accessible_machines="${all_machines}";
manager_accessible_machines="${managers} ${workers}";
worker_accessible_machines="${workers}";
################################################################################
## functions ##
################################################################################
## XXX: Pair calls to this function with "unset SSHPASS"!!!
function read_ssh_password()
{
echo "This script will set up keyless ssh."
echo "After this script, ssh will not accept passwords again."
echo "Enter the current password for ssh connections."
read -s -p "Password to use: " SSHPASS;
echo;
export SSHPASS;
}
function create_ssh_keys()
{
for remote in ${all_machines}; do
echo " SSH-KEYGEN ${remote};"
sshpass -e ssh ${remote} "
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa;
";
done
}
function distribute_ssh_keys_to()
{
local accessible_machines="$1";
# local ssh_opts="-o PreferredAuthentications=keyboard-interactive";
# ssh_opts="${ssh_opts} -o PubkeyAuthentication=no";
for remote in ${accessible_machines}; do
echo " SSH-COPY-ID $(cat /etc/hostname) ${remote};"
sshpass -e ssh-copy-id -i ~/.ssh/id_rsa.pub ${remote} \
2>&1 | grep -e WARNING -e ERROR -e added;
sleep 60;
done
}
function distribute_ssh_keys_from()
{
# ssh_opts="-o PreferredAuthentications=keyboard-interactive";
# ssh_opts="${ssh_opts} -o PubkeyAuthentication=no";
local machines="$1";
local accessible_machines="$2";
for remote in ${machines}; do
sshpass -e ssh ${remote} "
$(declare -fg);
export SSHPASS=${SSHPASS};
distribute_ssh_keys_to \"${accessible_machines}\";
unset SSHPASS;
";
sleep 300;
done
sleep 300;
}
function distribute_ssh_keys()
{
distribute_ssh_keys_from "${guis}" "${gui_accessible_machines}";
distribute_ssh_keys_from "${managers}" "${manager_accessible_machines}";
distribute_ssh_keys_from "${workers}" "${worker_accessible_machines}";
for remote in ${all_machines}; do
ssh ${remote} "
$(declare -fg);
secure_ssh;
";
sleep 60;
done
}
function secure_ssh()
{
:; ## TODO
}
function create_distribute_ssh_keys()
{
read_ssh_password;
create_ssh_keys;
sleep 300;
distribute_ssh_keys;
unset SSHPASS;
}
################################################################################
## main ##
################################################################################
function main()
{
create_distribute_ssh_keys;
}
################################################################################
## run ##
################################################################################
argc=$#;
if [ ${argc} -ne ${ARGC} ]; then
echo "Illegal number of parameters (Requires ${ARGC})";
exit ${EX_USAGE};
fi
main;
输出:
ubuntu@gui0:~$ ./bin/setup_ssh.sh
This script will set up keyless ssh.
After this script, ssh will not accept passwords again.
Enter the current password for ssh connections.
Password to use:
SSH-KEYGEN gui0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager1;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager2;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker1;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker2;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-COPY-ID gui0 gui0;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 manager0;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 manager1;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 manager2;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 worker0;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 worker1;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 worker2;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID manager0 manager0;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID manager0 manager1;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID manager0 manager2;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID manager0 worker0;
Number of key(s) added: 1
SSH-COPY-ID manager0 worker1;
SSH-COPY-ID manager0 worker2;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID manager1 manager0;
SSH-COPY-ID manager1 manager1;
SSH-COPY-ID manager1 manager2;
SSH-COPY-ID manager1 worker0;
SSH-COPY-ID manager1 worker1;
SSH-COPY-ID manager1 worker2;
SSH-COPY-ID manager2 manager0;
SSH-COPY-ID manager2 manager1;
SSH-COPY-ID manager2 manager2;
SSH-COPY-ID manager2 worker0;
SSH-COPY-ID manager2 worker1;
SSH-COPY-ID manager2 worker2;
SSH-COPY-ID worker0 worker0;
SSH-COPY-ID worker0 worker1;
SSH-COPY-ID worker0 worker2;
SSH-COPY-ID worker1 worker0;
SSH-COPY-ID worker1 worker1;
SSH-COPY-ID worker1 worker2;
SSH-COPY-ID worker2 worker0;
SSH-COPY-ID worker2 worker1;
SSH-COPY-ID worker2 worker2;
我希望总是收到一行说明添加的键的数量,或者一些错误或警告。但在某些情况下,我只收到 INFO 行(我用 grep 将其作为噪音丢弃)。正如您所看到的,前几个工作正常(它显示警告,因为这不是我第一次运行它,因此密钥已经在之前的运行中安装,但这很好),然后一些开始失败,然后全部失败失败。
如果我减少睡眠时间,它就会更早开始失效。
作为不使用 grep 时发生的情况的示例:
SSH-COPY-ID manager0; worker0;
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ubuntu/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'worker0'"
and check to make sure that only the key(s) you wanted were added.
SSH-COPY-ID manager0; worker1;
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ubuntu/.ssh/id_rsa.pub"
SSH-COPY-ID manager0; worker2;
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/ubuntu/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'worker2'"
and check to make sure that only the key(s) you wanted were added.
为什么它会如此不稳定地失败?
答案1
我不明白为什么你要sleep
散布这么长的语句,而且我不相信我已经正确地跟踪了你的程序的流程。但是,考虑到要求,您需要
gui* 机器应该可以访问所有内容; manager* 机器应该能够访问经理和工人;并且工人应该只能访问其他工人
所以
- GUI → GUI 经理 工人
- 经理 → 经理 工人
- 工人 → 工人
那就这样吧。我敢肯定,您会想要添加自己的进度更新和退出状态,但是这个准系统应该可以让您运行。
#!/bin/bash
#
guis=(gui0)
managers=(manager0 manager1 manager2)
workers=(worker0 worker1 worker2)
# Grab the password
#
IFS= read -rsp "Master password: " sshpass && echo
# First, GUI to everything
#
if [[ ! -f "$HOME/.ssh/id_rsa" ]] || [[ ! -f "$HOME/.ssh/id_rsa.pub" ]]
then
# Start clean
rm -f "$HOME/.ssh/id_rsa" "$HOME/.ssh/id_rsa.pub"
ssh-keygen -t rsa -b 4096 -f "$HOME/.ssh/id_rsa" -P ""
fi
for dst in "${guis[@]}" "${managers[@]}" "${workers[@]}"
do
# Using the password we entered at the beginning, copy the keys everywhere
SSHPASS=$sshpass sshpass -ev ssh-copy-id -o StrictHostKeyChecking=no -i "$HOME/.ssh/id_rsa" "$dst"
done
# Now generate a key on each host in turn
#
for dst in "${managers[@]}" "${workers[@]}"
do
# Ensure the target is clean and then generate a new key
ssh -n "$dst" 'rm -f .ssh/id_rsa .ssh/id_rsa.pub'
ssh -n "$dst" 'ssh-keygen -t rsa -b 4096 -f .ssh/id_rsa -P ""'
done
# Grab each host's key pair
#
for src in "${managers[@]}" "${workers[@]}"
do
scp -p "$src:.ssh/id_rsa" "$HOME/.ssh/id_rsa.$src"
scp -p "$src:.ssh/id_rsa.pub" "$HOME/.ssh/id_rsa.$src.pub"
done
# Push each Manager key out to the Managers and Workers
#
for src in "${managers[@]}"
do
for dst in "${managers[@]}" "${workers[@]}"
do
ssh-copy-id -i "$HOME/.ssh/id_rsa.$src" "$dst"
done
done
# Push each Worker key out to the Workers
#
for src in "${workers[@]}"
do
for dst in "${workers[@]}"
do
ssh-copy-id -i "$HOME/.ssh/id_rsa.$src" "$dst"
done
done
# Now fix up the "authenticity of host" warnings by connecting everywhere
#
for src in "${managers[@]}"
do
for dst in "${managers[@]}" "${workers[@]}"
do
ssh -n "$src" ssh -n -o StrictHostKeyChecking=no "$dst" id >/dev/null
done
done
for src in "${workers[@]}"
do
for dst in "${workers[@]}"
do
ssh -n "$src" ssh -n -o StrictHostKeyChecking=no "$dst" id >/dev/null
done
done
# Delete the unwanted key pairs from this host
#
for src in "${managers[@]}" "${workers[@]}"
do
rm -f "$HOME/.ssh/id_rsa.$src" "$HOME/.ssh/id_rsa.$src.pub"
done
# All done
#
exit 0
请注意,一切都由客户端 ( gui0
) 控制,并且在此过程中,任何 Manager 或 Workers 都不会向任何其他计算机发起任何文件的副本。
答案2
主要原因是 SSH 不知道主机(~/.ssh/known_hosts
),因此提示将新主机添加到文件中。当然,脚本无法对此做出反应。
解决方案很简单,正如@roaima 在对其答案的评论中指出的那样:
ssh ... -o StrictHostKeyChecking=no
我的脚本还有一些其他问题,我已经修复了(感谢 @roaima 的代码,我还改进了一些东西)。
对于将来可能需要执行此操作的任何人,这是我的工作脚本:
#!/bin/bash
set -Eeo pipefail
################################################################################
##
## Set up ssh network
## ==================
##
## Run this script from a guiX machine
##
################################################################################
################################################################################
## source ##
################################################################################
source lib/libalx/sh/sysexits.sh;
################################################################################
## definitions ##
################################################################################
ARGC=0;
guis=(gui0);
managers=(manager0 manager1 manager2);
workers=(worker0 worker1 worker2);
all_machines="${guis[*]} ${managers[*]} ${workers[*]}";
gui_accessible_machines="${all_machines}";
manager_accessible_machines="${managers[*]} ${workers[*]}";
worker_accessible_machines="${workers[*]}";
ssh_opts='-o StrictHostKeyChecking=no';
################################################################################
## functions ##
################################################################################
## XXX: Pair calls to this function with "unset SSHPASS"!!!
function read_ssh_password()
{
echo "This script will set up keyless ssh."
echo "After this script, ssh will not accept passwords again."
echo "Enter the current password for ssh connections."
read -s -p "Password to use: " SSHPASS;
echo;
export SSHPASS;
}
function create_ssh_keys()
{
for remote in ${all_machines}; do
echo " SSH-KEYGEN ${remote};"
sshpass -e ssh ${ssh_opts} ${remote} "
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -P '' ||:;
";
done
}
function distribute_ssh_keys_to()
{
local accessible_machines="$1";
for remote in ${accessible_machines}; do
echo " SSH-COPY-ID $(cat /etc/hostname) ${remote};"
sshpass -e ssh-copy-id -i ~/.ssh/id_rsa.pub ${ssh_opts} \
${remote} \
2>&1 | { grep -e WARNING -e ERROR ||:; };
done
}
function distribute_ssh_keys_from()
{
local machines="$1";
local accessible_machines="$2";
for remote in ${machines}; do
sshpass -e ssh -n ${ssh_opts} ${remote} "
set -Eeo pipefail
$(declare -fg);
export SSHPASS=\"${SSHPASS}\";
ssh_opts=\"${ssh_opts}\";
distribute_ssh_keys_to \"${accessible_machines}\";
unset SSHPASS;
";
done
}
function distribute_ssh_keys()
{
distribute_ssh_keys_from "${guis[*]}" "${gui_accessible_machines}";
distribute_ssh_keys_from "${managers[*]}" "${manager_accessible_machines}";
distribute_ssh_keys_from "${workers[*]}" "${worker_accessible_machines}";
for remote in ${all_machines}; do
ssh -n ${remote} "
$(declare -fg);
secure_ssh;
";
done
}
function secure_ssh()
{
:; ## TODO
}
function create_distribute_ssh_keys()
{
read_ssh_password;
create_ssh_keys;
distribute_ssh_keys;
unset SSHPASS;
}
################################################################################
## main ##
################################################################################
function main()
{
create_distribute_ssh_keys;
}
################################################################################
## run ##
################################################################################
argc=$#;
if [ ${argc} -ne ${ARGC} ]; then
echo "Illegal number of parameters (Requires ${ARGC})";
exit ${EX_USAGE};
fi
main;
################################################################################
## end of file ##
################################################################################
输出如下:
$ ./bin/setup_ssh.sh
This script will set up keyless ssh.
After this script, ssh will not accept passwords again.
Enter the current password for ssh connections.
Password to use:
SSH-KEYGEN gui0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager1;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN manager2;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker0;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker1;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-KEYGEN worker2;
Generating public/private rsa key pair.
/home/ubuntu/.ssh/id_rsa already exists.
Overwrite (y/n)? n
SSH-COPY-ID gui0 gui0;
/usr/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.
SSH-COPY-ID gui0 manager0;
SSH-COPY-ID gui0 manager1;
SSH-COPY-ID gui0 manager2;
SSH-COPY-ID gui0 worker0;
SSH-COPY-ID gui0 worker1;
SSH-COPY-ID gui0 worker2;
SSH-COPY-ID manager0 manager0;
SSH-COPY-ID manager0 manager1;
SSH-COPY-ID manager0 manager2;
SSH-COPY-ID manager0 worker0;
SSH-COPY-ID manager0 worker1;
SSH-COPY-ID manager0 worker2;
SSH-COPY-ID manager1 manager0;
SSH-COPY-ID manager1 manager1;
SSH-COPY-ID manager1 manager2;
SSH-COPY-ID manager1 worker0;
SSH-COPY-ID manager1 worker1;
SSH-COPY-ID manager1 worker2;
SSH-COPY-ID manager2 manager0;
SSH-COPY-ID manager2 manager1;
SSH-COPY-ID manager2 manager2;
SSH-COPY-ID manager2 worker0;
SSH-COPY-ID manager2 worker1;
SSH-COPY-ID manager2 worker2;
SSH-COPY-ID worker0 worker0;
SSH-COPY-ID worker0 worker1;
SSH-COPY-ID worker0 worker2;
SSH-COPY-ID worker1 worker0;
SSH-COPY-ID worker1 worker1;
SSH-COPY-ID worker1 worker2;
SSH-COPY-ID worker2 worker0;
SSH-COPY-ID worker2 worker1;
SSH-COPY-ID worker2 worker2;
测试:
ubuntu@gui0:~$ ssh manager0 'ssh worker2 cat /etc/hostname'
worker2
ubuntu@gui0:~$ ssh worker2 'ssh manager1 cat /etc/hostname'
Host key verification failed.