Linux命令 ssh-agent


原文链接: Linux命令 ssh-agent

SSH Config 那些你所知道和不知道的事 | Deepzz's Blog

ControlPath
指定 control socket 的路径,值可以直接指定也可以用一下参数代替:

%L 本地主机名的第一个组件
%l 本地主机名(包括域名)
%h 远程主机名(命令行输入)
%n 远程原始主机名
%p 远程主机端口
%r 远程登录用户名
%u 本地 ssh 正在使用的用户名
%i 本地 ssh 正在使用 uid
%C 值为 %l%h%p%r 的 hash

github多账号

  1. 配置好了第二个github账号的信息
    vi ~/.ssh/config
    ```ini
    #Default GitHub
    Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa

Host rinetd.github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_rinetd
Host pytool.github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_pytool

2. 如何使用

git remote add origin git@pytool.github.com:pytool/testing.git
git push origin master

注意这里要使用 ssh 地址形式而不是 https 的形式。



SSH 的配置文件有两个:
$ ~/.ssh/config            # 用户配置文件
$ /etc/ssh/ssh_config      # 系统配置文件

## ssh-agent 用于设置带有的密码的秘钥
ssh-add的作用主要将密钥添加到 ssh-agent 的高速缓存中,这样在当前会话中就不需要再次输入密码了 
单独为某个程序设置 ssh-agent
```sh
$ ssh-agent bash
$ ssh-add ~/.ssh/id_rsa

一直不知道啥是ssh-agent,今晚看了几篇文章,终于领悟到了。
一般的ssh过程

ssh-keygen 生成一个公钥私钥,注意,这里生成私钥的时候可以选密码,也可以不选密码。 openssh牛的地方在于能用key 登陆,而非用密码。
然后做免密码登陆,一般是复制到.ssh/authorized_keys ,或者用ssh-copy-id 。

其实第二步做完以后基本上就不输密码登陆。那需要ssh-agent 以及ssh-add有什么用呢?

其实ssh-keygen的时候,可以输密码,也可以不输密码,刚才那种就是不输密码的情况,那么如果你输了密码,ssh 登陆的时候还是会提示你将私钥的密码输入的(不需要输入服务器的密码)。而ssh-agent能避免这种反复输入私钥的烦恼。
用法很简单

ssh-agent
ssh-add 私钥 (这里会提示一次输密码,之后将密码保存在缓存中,之后便不能再input了)

mac上面还有个叫keychain Access 的东西,是管理密码的软件,也是通过ssh-add来实现ssh免密码登陆。

ssh认证和agent 和 ssh forwarding

终于到正题了,下面开始讲SSH.

那SSH (Secure Shell)和SSL/TLS是什么关系呢?SSH也是一个网络协议,用来进行安全数据交流,远程shell服务和命令执行等。SSH由传输,认证和连接等协议组成。SSH的传输协议类似SSL/TLS (Diffie-Hellman key exchange以及对称钥匙加密)。

本文的重点是SSH的认证部分。client和server通过key exchange获得共享密钥(shared session key)后,所有之后的传输数据都进行了加密。然后进入认证部分,认证成功后,则双向连接通道建立,通常是login shell.

来自securecrt官网关于shared session key的描述

Session keys are the “shared keys” described above and are randomly generated by both the client and the server during establishment of a connection. Both the client and host use the same session key to encrypt and decrypt data although a different key is used for the send and receive channels. Session keys are generated after host authentication is successfully performed but before user authentication so that usernames and passwords can be sent encrypted. These keys may be replaced at regular intervals (e.g., every one to two hours) during the session and are destroyed at its conclusion.

SSH认证有多种方法,本文着重讲最常见了两种:密码认证和公钥认证。

  1. 密码认证

密码认证最简单:

ssh client向目标机器发起tcp连接(一般22端口)并发送username (username是SSH协议的一部分)
目标机器ssh daemon回应需要密码
ssh client提示用户输入密码,然后将密码发送到服务器端
ssh daemon如果密码匹配成功, 则认证通过,

基于密码认证的缺点是

容易被brute-force password guessing
不适合于管理多台机器
若每台机器使用相同的密码,如果密码泄露,所有机器都被攻破。若使用不同密码,则密码太多很难记住,因此也不可能使用很强的密码。
  1. 公钥认证

公钥认证详细协议见RFC4252的publickey部分

公钥认证需要先在本地机器生成公钥私钥对,然后将公钥放到目标机器的$HOME/.ssh/authorized_keys中。具体过程如下

ssh client向目标机器发起tcp连接(一般22端口)
ssh client提示用户输入passphrase以解密私钥
ssh client发送私钥签名的包含username和公钥等信息的message.
目标机器ssh daemon通过检查消息中指定用户的$HOME/.ssh/authorized_keys,确定公钥是否可用作认证并验证签名的合法性, 如果两者都ok, 则通过认证

如果公钥认证失败,ssh还会尝试其他认证策略,比如密码认证。多个认证策略的尝试顺序和服务器端没关系,由客户端的配置来决定。

需要说明的是,即使把本机的公钥(如.ssh/id_rsa.pub)删除掉,认证仍然可以成功。那第三步中提到的公钥从哪里来的呢?实际上,上面(如第二步)提到的私钥(如.ssh/id_rsa)是广义的,既包含了私钥,也包含了公钥,也有可能还包含了其他信息(比如证书)。比如通过ssh-keygen -y ~/.ssh/id_rsa就可以看到id_rsa里面的公钥。

用作认证的私钥最好通过passphrase进行加密,否则会有很大安全隐患,只要私钥泄露,别人就能访问你能访问的所有远程机器。

公钥认证由于需要配置公钥私钥,初始配置稍微麻烦一些,但好处是所有机器只需配置一组公私钥对就可以了。由于只有一个私钥,不必设置多个密码,因此可以为其设置比较强的密码。并且仅当私钥和密码一同丢失时才有风险,但这个概率非常小。

不过仍然烦人的是,每次登陆都得输入passphrase。

  1. 使用ssh agent的公钥认证

为解决每次登陆远程机器都需要输入passphrase的问题,ssh-agent被引入了。ssh-agent启动后,可通过ssh-add将私钥加入agent. ssh-add会提示用户输入passphrase以解密私钥,然后将解密后的私钥纳入agent管理。agent可同时管理多个私钥。

连接服务器的步骤如下:

ssh client向目标机器发起tcp连接(一般22端口)
ssh client向本地的agent请求, 得到私钥签名的包含username和公钥等信息的message
ssh client向目标机器发送此message和签名
目标机器ssh daemon通过检查消息中指定用户的$HOME/.ssh/authorized_keys,确定公钥是否可用作认证并验证签名的合法性, 如果两者都ok, 则通过认证

如果ssh-agent中有多个私钥, 会依次尝试,直到认证通过或遍历所有私钥.

在整个过程中,私钥只存在于agent的内部(内存中), ssh client并没有获取到私钥。

使用ssh-agent后,只需在将key纳入agent管理时输入passphrase,之后的ssh相关操作就不必输入passphrase了。但如果从本机A登陆机器B后,又想从B登陆C (或从B传输文件到C),仍然需要输入passphrase (如果B上也配置了用户的私钥)或password。还是比较麻烦。

幸好,ssh agent forwarding解决了这一问题。

  1. 使用ssh agent forwarding的公钥认证
    在 server1: 开启 Agent Forwarding
    vi /etc/ssh/sshd_config
    AllowAgentForwarding yes

    假设用户已经从homepc连接到了第一台机器server。homepc的agent中已保存了用户的私钥
    server1: 用户从server1向server2发起ssh连接请求
    server: ssh client向本地(server)的agent请求, 得到私钥签名的包含username和公钥等信息的message。

    注意server1上其实ssh-agent压根就没有启动,ssh client只是检查 $SSH_AUTH_SOCK 这个环境变量是否存在,如果存在,则和这个变量指定的domain socket进行通信。而这个domain socket其实是由 server1 上的sshd创建的。所以ssh client其实是和sshd在通信。

    而 server1 的sshd并没有私钥信息,所以sshd做的事情其实是转发该请求到homepc的ssh client,再由该client将请求转发给本地(homepc)的agent。该agent将需要的消息和签名准备完毕后,再将此数据按原路返回到 server1 的ssh client. 路径如下所示

    agent_homepc --($SSH_AUTH_SOCK)-- ssh_homepc --(tcp)--
    sshd_server --($SSH_AUTH_SOCK)-- ssh_server --(tcp)--
    sshd_server2

    这下明白为什么叫agent forwarding(转发)了吧,就是所有中间节点的sshd和ssh都充当了数据转发的角色,一直将私钥操作的request转发到了本机的agent,然后再将agent的response原路返回。
    server: ssh client向目标机器server2发送此message和签名
    server2: ssh daemon通过检查消息中指定用户的$HOME/.ssh/authorized_keys,确定公钥是否可用作认证并验证签名的合法性, 如果两者都ok, 则通过认证

上面只是示例,从server2,还可以类似的无密码登陆到server3。事实上,通过ssh agent forwarding, 能实现任意级别的无密码登陆。并且私钥只保存在本地的机器上,保证了私钥的安全。

开启 agent forwarding功能

打开本地ssh client的forwardAgent
方式1:
ssh -A添加 -A 参数开启forwarding
agent forwarding 功能是默认关闭的,为实现任意级别无密码登陆,在ssh到其他机器时,一定要记得添加-A参数, 以打开agent forwarding (在目标机器上会生成$SSH_AUTH_SOCK环境变量)。 比如 ssh -A server2。

方式2:
修改全局配置/etc/ssh/ssh_config或修改个人~/.ssh/config配置推荐第二种,没有就新建文件,然后加入
vi /etc/ssh/ssh_config
Host *

ForwardAgent yes
ForwardX11 yes

vi ~/.ssh/config

Host d2
  HostName 114.114.114.114
  User root
  ForwardAgent yes

agent forwarding打开之后,也会有安全的风险。如果用户A通过ssh连接server并打开了agent forwarding,因为server上的root用户也有权限访问与agent通信的套接字,只要root用户将$SSH_AUTH_SOCK指向用户A对应的套接字地址,就可以以A的身份访问其它A可以访问的机器。因此请确保agent forwarding仅在可信任的服务器上打开。

本文主要从基本原理角度对ssh认证和agent相关问题进行了分析。下文会讲讲最佳实践。

How to enable ssh agent:

on server A: enable it on
vi /etc/ssh/sshd_config
AllowAgentForwarding yes

ssh to server A with option -A to enable it

on scp add -o option:

scp -o "ForwardAgent yes" user@server1:/xx user@server2:/yy

ForwardAgent yes

`