设置SSH密钥登录

前言

本篇博客是学习ssh中windows端ssh服务器是如何使用密钥来代替传统密码登录的记录

步骤

一、首先在访问者(windows)端生成密钥

  1. 在~目录下创建.ssh文件夹并切换过去
    1
    2
    3
    cd ~
    mkdir .ssh
    cd .ssh
  2. 在服务器端创建自己的ssh密钥对[2]
    1
    ssh-keygen -t ed25519 -C "your_email@example.com

如果你使用的是不支持 Ed25519 算法的旧系统,请使用以下命令:

1
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

默认密钥保存位置在~/.ssh目录下,当然在接下来会提示输入别的保存路径,是否设置密码短语(用于加密私钥),建议密码短语不要留空(回车),否则私钥被窃取别人就可以直接获得访问权限。
因为我本机已经有密钥了,所以下面这张图是以服务器执行ssh-keygen -t rsa为例,实际上windows端是要在cmd中执行这个命令的

生成后的密钥在c:/users/<your username>/.ssh目录下,有以下两个文件,.pub对应的是公钥,没有后缀的是私钥

二、将公钥拷贝到服务器要访问的账户的~/.ssh/authorized_keys文件中

~/.ssh/authorized_keys保存已经认证的公钥,这样当访问者访问服务器时会经过密钥验证,从而实现免密登录。
我们可以手工在被访问者(服务器)中创建~/.ssh文件夹,然后将
访问端(windows)的id_ras.pub里的内容拷贝进去实现免密登录。

要将.ssh文件夹权限修改为700,authorized_keys文件权限修改为600,否则会导致ssh服务访问失败从而无法认证。

那有没有更方便快捷的方法呢,答案是有的,ssh为我们提供了ssh-copy-id命令,只要在访问者(windows)上执行

1
ssh-copy-id /path/to/pubkey user@ip

就可以指定执行以上操作,包括创建.ssh文件夹和authorized_keys文件并拷贝公钥,然后设置权限等。

ssh-copy-id 用来将本地公钥复制到远程主机。如果不传入 -i 参数,ssh-copy-id 使用默认 ~/.ssh/identity.pub 作为默认公钥。如果多次运行 ssh-copy-id ,该命令不会检查重复,会在远程主机中多次写入 authorized_keys 。[1]

在windows上cmd和git cmd不支持该命令,需要在git bash里输入。

三、测试

在命令行正常使用ssh连接

1
2
3
4
5
ssh user@your-server-ip-address
//或者
ssh user@your-server-domain
//或者
ssh -i ~/.ssh/your-key user@your-server-ip-address

这时会提示输入passphrase,输入即可。如果没给访问者(windows)设置passphrase的话就无需输入,实现免密登录了

四、给公钥设置了passphrase的小伙伴们需要额外的一步

由于设置了passphrase,在ssh登录服务器时虽然免于输入密码,但为了解密私钥,需要输入passphrase,这样不还是很麻烦吗?那有没有什么方法可以方便一点呢,其实ssh-agent就是为了解决这个问题而生的。

什么是ssh-agent

这里参考[3]的解释

SSH 只是一种协议,其开源实现有 OpenSSH,并且存在服务端(sshd) 和 客户端 (ssh),Windows 中的客户端有 PuTTY;而这两种客户端都有各自的 ssh agent:

ssh-agent 命令:是客户端 ssh 的默认代理
Pageant : 是 客户端 PuTTY 的代理。Pageant是用于PuTTY,PSCP,PSFTP和Plink的SSH身份验证代理。 Pageant会存储您的私钥,只要它正在运行,它就会为PuTTY或其他工具(如IntelliJ IDEA)提供解锁的私钥。(当然ssh-agent也能做)

把私钥交给 ssh agent 管理的好处:
当 其他程序 需要身份验证的时候 可以将验证申请交给 ssh-agent 来完成整个认证过程 。如 git
避免重复输入密码:如果您的私钥使用密码短语来加密了的话,每一次使用 SSH 密钥对 进行登录的时候,您都必须输入正确的密码短语。而 SSH agent 程序能够将您的已解密 的私钥缓存起来,在需要的时候提供给您的 SSH 客户端。

总之,ssh-agent 就是为程序提供ssh服务的代理,这里的程序包括ssh自己的客户端。

(推荐)windows上使用原生ssh-agent

那么怎么使用ssh-agent呢? 其实在windows上,windows10自带的Open-ssh客户端就有一个ssh-agent,并且已经以服务形式注册在系统里的,但是默认是禁用状态,可以在服务里查看一下。

服务禁用的话是无法运行的。那么首先我们需要把服务改为手动状态首先我们需要打开这个服务[2]

1
2
3
# 用powershell管理员模式输入
Get-Service -Name ssh-agent | Set-Service -StartupType Manual
Start-Service ssh-agent

在无提升权限的终端窗口中,将 SSH 私钥添加到 ssh-agent。 如果使用其他名称创建了密钥,或要添加具有其他名称的现有密钥,请将命令中的 id_ed25519 替换为私钥文件的名称。

1
ssh-add c:/Users/YOU/.ssh/id_ed25519

接下来会提示你输入passphrase,输入之后就会自动由ssh-agent保存了,并且重启电脑也不需要重新输入。

windows上使用git-bash自带的ssh-agent

因为git是基于ssh的,例如对远程git仓库执行git push等会首先调用ssh连接仓库。安装了Git for windows后,git会自带两个终端工具分别是git-bash和git-cmd,其中git-cmd经测试调用的是windows的原生ssh-agent,所以无需重新配置;而git-bash里的ssh-agent是单独的,并且无法实现随windows自动启动,但是我们可以通过在.bashrc/.profile里写入脚本实现随git-bash自动启动。[4]
复制以下行并将其粘贴到 Git bash 中的 ~/.profile 或 ~/.bashrc 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
env=~/.ssh/agent.env

agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }

agent_start () {
(umask 077; ssh-agent >| "$env")
. "$env" >| /dev/null ; }

agent_load_env

# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2=agent not running
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)

if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
agent_start
ssh-add
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
ssh-add
fi

unset env

这段脚本的原理是,每次打开git bash时先载入~/.ssh/agent.env,此时如果里面有socket信息则可以复用上一次启动的ssh-agent进程,防止启动多个ssh-agent。然后使用ssh-add -l检查ssh-agent进程信息是否有效(是否已经启动)。如检查ssh-agent是否已经启动,agent_run_state=2代表没启动,则启动ssh-agent并且把该线程的socket信息写入~/.ssh/agent.env;若agent_run_state=0,表示已经启动,不做任何操作。agent_run_state=1表示进程启动但还没有将passphrase纳入ssh-agent管理,则执行ssl-add添加管理。这样做可以实现每次git-bash启动是自动启动ssh-agent并且复用已经存在的ssh-agent进程。

然而这种方式来自动启动ssh-agent有两个缺点。
第一是无法实现真正意义的随windows自动启动,而只能在首次打开git-bash时启动ssh-agent。
第二是无法实现真正意义的持久化,git-bash并不是完整的unix系统,虽然能执行unix命令,启动unix进程但只要重启windows后信息就会丢失,除非像上面那样写入agent.env那样自己手动写入文件里。但windows重启,ssh-agent进程被终止,所保存的passphrase信息会丢失,所以每次重启后打开git-bash都会需要重新输入pasphrase。而windows自带的Open-ssh里的ssh-agent则完全没有这个问题。
第三是写入.profile/.bashrc只对login shell有效,对于non-login shell或者直接第三方程序调用ssh服务时时不会运行.profile/.bashrc的,从而仍然需要输入passphrase。例如部署hexo博客时的git-deployer插件就是直接调用的git-bash。所以会出现始终需要重新输入的情况,目前本人也没找到合适的办法解决这个问题。

其他

一、如何修改/删除passphrase

通过输入以下命令,您可以更改现有私钥的密码而无需重新生成密钥对:[4]

1
2
3
4
5
6
$ ssh-keygen -p -f ~/.ssh/id_ed25519
> Enter old passphrase: [Type old passphrase]
> Key has comment 'your_email@example.com'
> Enter new passphrase (empty for no passphrase): [Type new passphrase]
> Enter same passphrase again: [Repeat the new passphrase]
> Your identification has been saved with the new passphrase.

其他参考文章

  1. How To Set up SSH Keys on a Linux / Unix System
  2. Git使用ssh协议配置Github远程仓库避免踩坑指南(Windows环境)

设置SSH密钥登录
http://lightandsqlt.site/2025/01/12/学习/设置SSH密钥登录/
作者
Ethan
发布于
2025年1月12日
许可协议