首页 > 其他分享 >【小记】Docker容器间SSH公钥自动交换实现免密登录的一次尝试

【小记】Docker容器间SSH公钥自动交换实现免密登录的一次尝试

时间:2024-01-26 20:59:30浏览次数:30  
标签:容器 公钥 密钥 免密 ssh root SSH

咋想到这茬了

最近开始忙毕设的事儿了,想部署个伪分布式的Spark + Hadoop集群来进行测试。思来考去,最终咱把目光放在了Docker上。

盘了两天,发现这玩意意外的有趣,镜像构建好后开箱即用,省去了些配置环境的成本。

不过呢,在配置Hadoop的时候我发现了一个问题——Hadoop分布式搭建要求各节点间能通过SSH执行指令(比如启动时就需要在其他节点上执行指令以启动相应守护进程),即需要配置免密登录(Passphraseless),怎么在尽量保证安全的情况下尽快让容器之间交换SSH公钥以信任对方呢?

首先咱当然是去Github找了个Hadoop容器化部署项目的Dockerfile看了看,发现有一种方案是在构建镜像的时候就建立SSH密钥对,然后把公钥加入到自己authorized_keys文件中。

# Dockerfile中
RUN ssh-keygen -t rsa -f ~/.ssh/id_rsa && 
    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys &&
    chmod 600 ~/.ssh/authorized_keys

理论上说,用这个镜像建立的容器之间就可以实现免密登录了(毕竟每台机器上authorized_keys中都有相同的公钥)。

然鹅,我在本机WSL上装的docker中测试时发现这种方式怎么也行不通...盘了大半天,文件权限和SSH配置改来改去,SSH服务硬是没给我个好脸色,查了很久也没解决这问题。

于是我索性想了另一套方案来解决各容器间交换SSH公钥的问题。

losingMemories-2024-01-25

:这篇文章写到这,我恍然大悟,发现自己犯了个很蠢的错误 ↓ 展开查看

展开查看

我在生成密钥对的时候不知是脑袋哪里缺根筋,偏偏就改了密钥对的默认文件名,当时在Dockerfile里是这样写的:

RUN ssh-keygen -t rsa -f /root/.ssh/rsa_pair -N '' \
    && cat /root/.ssh/rsa_pair.pub >> /root/.ssh/authorized_keys

这样一来/root/.ssh目录下会生成rsa_pair(密钥)和res_pair.pub(公钥)。

这里简述一下SSH密钥对认证的过程:

  1. 客户端发送连接请求给服务端;
  2. 服务端用客户端的公钥对一串随机数据data进行加密生成challenge,返回给客户端;
  3. 客户端用客户端的私钥challenge进行解密,得到data,然后利用哈希算法HASH计算data的摘要,将摘要用客户端的私钥签名为signature后,再发给服务端。
  4. 服务端收到数字签名signature后用客户端的公钥解密,得到摘要。此时服务端用相同的哈希算法计算出第2步中原数据data的摘要,然后比较摘要是否一致。
  5. 如果摘要一致,签名成功验证,认证成功。

我测试的时候用的命令是ssh root@主机名,而这个命令默认会扫描id_开头的密钥文件,而我此时的密钥对文件名是rsa_pair,程序自然就找不到对应的密钥对了,也就无法正常进行认证。

其实这种情况下手动指定密钥文件路径就OK啦,sshssh-copy-id命令都能接受参数-i,指定密钥文件路径:

ssh -i /root/.ssh/rsa_pair root@HOST

我这小脑袋瓜儿当时硬是没想到这点,所以说有的时候小错误往往可能浪费更多时间哇!╰(‵□′)╯

菜是原罪


利用Bash脚本实现SSH公钥自动交换的尝试

相关代码已经开源:

https://github.com/SomeBottle/haspark

Dockerfile

  1. 导出主机名列表

    # 环境变量配置
    # 所有节点的主机名,用于SSH配置
    ENV SH_HOSTS="shmain shworker1 shworker2"
    

    构建镜像时导出所有节点的主机名作为环境变量SH_HOSTS

  2. 生成随机密码temp.pass并临时设为用户密码

    # 先生成一个临时SSH密码,用于首次启动时交换ssh密钥
    RUN echo $(openssl rand -base64 32) > /root/temp.pass
    # 修改root用户的密码
    # 管道符重定向输入
    RUN echo -e "$(cat /root/temp.pass)\n$(cat /root/temp.pass)" | passwd root
    

    利用openssl生成了32字节的随机数据并用base64方式进行了编码,作为随机密码存储到/root/temp.pass

    其实我想把路径写成~/temp.pass的,但bitnami镜像竟然把HOME环境变量指定为了/...

    然后将随机密码设置为用户的密码,临时用于后面的密钥交换过程。

  3. 建立标记文件目录exchange_flags

    # 若.ssh目录不存在则建立
    RUN [ -d /root/.ssh ] || mkdir -p /root/.ssh
    # 建立标记目录
    RUN mkdir -p /root/.ssh/exchange_flags
    

    这个目录是为了配合后面的脚本而存在的。

  4. 复制用户SSH配置并调整权限

    # 拷贝本地写好的配置文件
    COPY configs/ssh_config /root/.ssh/config  
    # 调整.ssh目录下文件权限
    RUN chmod 600 /root/.ssh/config \
        && chmod 700 /root/.ssh
    

    这个配置文件中主要配置了StrictHostKeyChecking策略,使得初次连接时跳过指纹确认,不然SSH连接建立过程可能卡在这里。

    因为我使用了sshpass这个工具来实现自动向ssh命令行传入密码实现登录,而默认情况下首次登录远程主机会列出对方指纹并询问用户是否信任对方,这一步会让sshpass失效。

    同时.ssh目录有严格的权限要求,必须是700rwx------(仅当前用户读/写/执行)。

  5. 最后给予脚本可执行权限,并设置ENTRYPOINT

    增加执行权限用chown +x即可,这里不多赘述。

    # 容器启动待执行的脚本
    ENTRYPOINT [ "/opt/entry.sh" ]
    

    这里将/opt/entry.sh脚本作为ENTRYPOINT,即容器启动时会执行的脚本。

我的方案是把SSH密钥生成与交换放到了容器初次启动的时候。

SSH密钥生成与交换脚本

利用上述Dockerfile构建的镜像创建多个容器作为伪分布式节点并启动后:

在每个容器中,首先会执行/opt/entry.sh,这个脚本首先会启动SSH服务,然后转后台执行ssh_key_exchange.sh脚本,以下主要介绍这个脚本的思路。

脚本地址:https://github.com/SomeBottle/haspark/blob/main/scripts/ssh_key_exchange.sh

  1. 生成密钥对

    脚本首先会为本容器生成一个密钥对id_rsa.pubid_rsa,然后把公钥id_rsa.pub写入到本容器的authorized_keys文件中。

    同时把本机的主机名作为文件名,建立一个新文件放在/root/.ssh/exchange_flags目录下,作为标记(在下图中以黑色文件名表示)。

    因为Hadoop进程启动时也会通过SSH连接到本机,自己到自己也要能免密登录。

  2. 将本容器A的公钥分发到SH_HOSTS环境变量中指定的其他每台容器上

    • 先通过ssh-copy-id结合sshpass,通过temp.pass临时密码登录到另一个容器B并将本容器A的公钥拷贝到其authorized_keys文件中。
    • 通过ssh连接,在这一个容器B上的/root/.ssh/exchange_flags下建立一个以本容器A的主机名为文件名的文件作为标记。
  3. 轮询标记文件,直到SH_HOSTS所有主机名都存在于exchange_flags目录下的文件名中,则认为本容器已经取得其他所有容器的公钥,进入下一步。

  4. 删除本容器的临时密码文件temp.pass,修改SSH配置文件,禁止通过密码登录,并重启SSH服务。

当所有容器都完成上述过程后,每个容器都掌握有对方的公钥,即实现了免密登录,且公钥互不相同。

如果有三个节点,主机名分别为master, worker1, worker2,三台机器交换公钥的图示如下:

ssh_key_exchange.drawio-2024-01-26

不同颜色的箭头表示公钥传送的方向,以及exchange_flags目录中的标记文件的创建者。

IMG_202401267251_142x158-2024-01-26

大概思路就是这样,写这篇文章主要是做个记录,说不定以后还能把这一套利用起来。

附:Git的一个坑

每次涉及到Windows和Linux之间文本文件交换的时候总是很容易遇到行尾序列(换行字符)问题,最坑的是Windows平台上,Git工具默认会在把仓库克隆下来后自动转换为CRLF(\r\n)。

autoCRLF-2024-01-26

而一旦Shell脚本和一些配置文件末尾出现了多余的\r字符,就很容易导致一些让人摸不着脑袋的问题。(比如Hadoop进程启动时会提示主机名中有无效字符,就是因为workers文件中有多余的\r字符)

因此要保证行尾序列是原模原样的LF(\n),就需要对Git进行一些配置:

  1. 全局关闭自动CRLF转换

    git config --global core.autocrlf false
    
  2. .gitignore一样编写项目的.gitattributes文件,比如:

    # 憋给我整成CRLF了嗷,不然保准没你好果汁吃嗷
    *.sh text eol=lf
    *.md text eol=lf
    

    指定.sh.md为后缀的文件以LF为行尾序列。

标签:容器,公钥,密钥,免密,ssh,root,SSH
From: https://www.cnblogs.com/somebottle/p/17990664/docker_ssh_key_exchange

相关文章

  • VsCode SSH连接Ubuntu
    1.下载官方插件Remote-SSH2.虚拟机查看ip地址ipaddr3.配置config文件连接失败过程试图写入的管道不存在。解决重启ubuntureboot修改设置,将网络连接方式NAT➡桥接网卡如果Ubuntuip地址是10.0.*.*,设法将ip地址变为192.168.*.*连接成功免密连......
  • centos服务器修改ssh端口号
    1、修改ssh配置文件,找到Port=22位置,可以新增Port=20022,或者修改22为20022vi/etc/ssh/sshd_config  2、重启ssh服务systemctl restartsshd 3、查看ssh服务状态systemctl  status  sshd注:这里可以查看到ssh启动时监听端口的日志,如果监听失败会显示failed,失败......
  • git创建SSH keys
    git出现Pleasemakesureyouhavethecorrectaccessrightsandtherepositoryexists,需要创建SSHkeys。步骤:1、打开Git.bash进行命令行界面输入代码如下: gitconfig--globaluser.name"YourName"#配置user.namegitconfig--globaluser.email"your_email@exa......
  • 关于“公钥加密私钥解密,私钥加签公钥验签”的一些理解
    看了网上的很多资料,发现有些点没有说到,也比较复杂,这里根据个人的理解,简单描述,方便记忆。 先理解公/私钥(yue)的意思:私钥,即 私人 的钥匙,是唯一的,所以可以用来证明来源是特定的人公钥,即 公用 的钥匙,我可以将它给很多人(公众)。所以既然那么多人都知道,所以公钥并不能证明来源......
  • Ubuntu20.04安装后,root账户无法登录,ssh无法远程连接处理方法
    摘自:https://blog.csdn.net/Alex_81D/article/details/131512358 二、给root账户设置密码,并保证可成功登录1.设置root用户密码在桌面上使用快捷键Ctrl+Alt+T打开终端模拟器执行sudopasswdroot,然后输入设置的密码,输入两次,完成了设置root用户密码2.修改配置文件修改gdm-auto......
  • linux系统查看ssh登陆记录的方法
     1、下面的提供的命令可以实时检测/var/log/auth.log文件中的SSH登录记录,并只显示包含“ssh”的行sudotail-f/var/log/auth.log|grepssh2、如果你是CentOS、RedHat或Fedora等基于RHEL的发行版,则可以使用以下命令:下面的命令与上面的命令一样,都可以实时的检测/v......
  • 升级openssh后出现xshell、CRT等工具无法连接问题
    描述:某工程在进行ssh漏洞修复过程中升级openssh后输入用户名密码被拒绝(如下图)通过带外重定向到操作系统发现日志出现PAMunabletodlopen和 PAMaddingfaultymodule的报错经排查发现是ssh rpm包升级后会修改/etc/pam.d/sshd文件(如下图)和其他服务器对比,正常可登录的/etc......
  • openssh9.6 源码编译与交叉编译
    环境ubunut18.04。x86平台openssh9.6,这里我是要移植到别的机器。不是在本地使用所以我要编出两个版本x86和armubunutx86版本编译过程准备工作安装依赖库,可以通过apt包管理安装,也可以源码安装,我们这里需要zlib,openssl和pamlinux安装pam库centossudoyuminstall......
  • git ssh链接远程仓库
    一、参考博客——创建ssh链接——解决端口链接超时二、创建ssh链接2.1安装git链接:https://pan.baidu.com/s/19bjRZz_uhXW8HuVVNiLz4A?pwd=bxe0提取码:bxe0选择下载的地址,一路next,当然默认编辑器啥的配置,自己有需要就点击,但是最后一项带有new的next不建议勾选。——下载......
  • windows本地免密登录linux
    环境:虚拟机CentOS7.5\本地Windows11准备:虚拟机已生成秘钥1、在windows本地生成密钥ssh-keygen-trsa密钥生成到 C:\Users\Administrator\.ssh2、上传公钥文件至虚拟机3、公钥追加至虚拟机.ssh/authorized_keys中 本地Windows免密登录虚拟机_windows终端免密登录......