ssh-copy-id 在脚本(集群)的循环中不稳定地工作

ssh-copy-id 在脚本(集群)的循环中不稳定地工作

我有一组 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.

相关内容