↓ 跳过正文

ssh_windows_openssh_linux_ 指南

作者
Hexin
在确定的逻辑与不确定的现实之间,做一名耐心的构建者。
目录

SSH 到底解决了什么问题?
#

SSH(Secure Shell)最常见的用途,是让本地计算机安全地连接远程 Linux 服务器。

最简单的连接方式是:

ssh root@123.123.123.123 -p 6000

这里包含了几个信息:

  • root:远程服务器上的登录用户
  • 123.123.123.123:服务器 IP
  • 6000:SSH 服务端口
  • ssh:使用 OpenSSH 客户端建立连接

如果每次都输入这么长的一串命令,会比较麻烦。

因此 SSH 提供了 SSH Key、ssh-agent 和 ~/.ssh/config 等机制,可以把登录过程变得更加方便。

最终我们希望达到这样的效果:

ssh cloud

甚至可以让:

scp test.zip cloud:/root/
sftp cloud

都直接使用同一个连接配置。


Windows 上的 SSH 文件在哪里?
#

Windows OpenSSH 通常使用:

C:\Users\用户名\.ssh\

例如:

C:\Users\abc\.ssh\

其中可能存在:

.ssh/
├── id_ed25519
├── id_ed25519.pub
├── config
└── known_hosts

这些文件作用不同。

文件 作用
id_ed25519 SSH 私钥,必须保护好
id_ed25519.pub SSH 公钥,可以放到服务器
config SSH 客户端配置
known_hosts 保存已经验证过的服务器主机信息

其中最重要的原则是:

私钥留在自己的电脑上,公钥放到服务器上。

不要把 id_ed25519 私钥发给任何人。


生成第一对 SSH Key
#

推荐使用 Ed25519:

ssh-keygen -t ed25519

程序会询问:

Enter file in which to save the key:

直接按 Enter,可以使用默认路径:

C:\Users\abc\.ssh\id_ed25519

然后会询问:

Enter passphrase:

建议设置一个 passphrase。

完成后会得到:

id_ed25519
id_ed25519.pub

可以检查:

Get-ChildItem $HOME\.ssh -Force

例如:

Mode   Length Name
----   ------ ----
-a---- 464    id_ed25519
-a---- 102    id_ed25519.pub

这里:

id_ed25519

是私钥。

id_ed25519.pub

是公钥。


私钥、公钥和 SSH 登录是什么关系?
#

可以把它简单理解成:

本地 Windows

id_ed25519
    │
    │ 私钥
    │
    ▼

SSH 客户端
    │
    │ 证明“我拥有对应的私钥”
    ▼

Debian 服务器

~/.ssh/authorized_keys
    │
    │ 保存公钥
    ▼

验证通过

服务器不需要保存你的私钥。

服务器只需要保存你的公钥。

因此:

Windows
└── id_ed25519          ← 私钥,只保存在本地

Debian
└── ~/.ssh/authorized_keys
                         ← 保存公钥

这是 SSH Key 认证最核心的结构。


在 Debian 上手动安装公钥
#

先在 Windows 查看公钥:

Get-Content $HOME\.ssh\id_ed25519.pub

得到类似:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... abc@DESKTOP-aabbcc1122

复制完整的一行。

然后登录 Debian。

创建 SSH 目录
#

mkdir -p ~/.ssh

设置目录权限:

chmod 700 ~/.ssh

创建 authorized_keys
#

nano ~/.ssh/authorized_keys

把刚才复制的公钥粘贴进去。

例如:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... abc@DESKTOP-aabbcc1122

保存后:

chmod 600 ~/.ssh/authorized_keys

现在服务器上的结构大致是:

/root/
└── .ssh/
    └── authorized_keys

如果当前登录用户不是 root,那么 ~ 会对应当前用户的家目录。

例如:

/home/debian/.ssh/

~ 到底是什么?
#

在 Linux 中:

~

代表当前用户的家目录。

可以通过:

echo $HOME

查看。

如果当前用户是 root:

~
=
/root

如果当前用户是 debian:

~
=
/home/debian

所以:

~/.ssh

并不是固定的 /root/.ssh。

它表示:

当前登录用户家目录下面的 .ssh。

另外,Linux 中以 . 开头的文件和目录默认不会被普通 ls 显示。

因此:

ls

可能看不到 .ssh。

使用:

ls -la

就可以看到隐藏目录。

检查 SSH 目录:

ls -la ~/.ssh

ssh-agent 是什么?
#

如果私钥设置了 passphrase,每次使用私钥时理论上都可能需要解锁。

ssh-agent 可以理解成一个本地的“私钥使用代理”。

大致流程:

第一次使用 SSH Key

id_ed25519
     │
     │ 输入 passphrase
     ▼
ssh-agent
     │
     │ 保存已经解锁的身份
     ▼
后续 SSH / SCP / SFTP 使用

这样就不需要每次重新输入私钥 passphrase。

注意:

ssh-agent 不会把私钥上传到服务器。

它主要负责在本机管理已经加载的身份。


Windows 上启用 ssh-agent
#

Windows 自带 OpenSSH Authentication Agent 服务。

查看状态:

Get-Service ssh-agent

如果看到:

Status   Name       DisplayName
------   ----       -----------
Stopped  ssh-agent  OpenSSH Authentication Agent

可以设置为自动启动:

Set-Service -Name ssh-agent -StartupType Automatic

如果出现:

Access is denied

说明当前 PowerShell 没有足够权限。

此时需要使用:

以管理员身份运行 PowerShell

然后执行:

Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent

检查:

Get-Service ssh-agent

应该看到:

Running  ssh-agent  OpenSSH Authentication Agent

把 SSH Key 加入 ssh-agent
#

回到普通 PowerShell:

ssh-add $HOME\.ssh\id_ed25519

输入创建 SSH Key 时设置的 passphrase。

成功后类似:

Identity added: C:\Users\abc\.ssh\id_ed25519

检查 agent 中的身份:

ssh-add -l

可能看到:

256 SHA256:xxxxxxxxxxxxxxxx... C:\Users\abc\.ssh\id_ed25519 (ED25519)

这里显示的是公钥指纹,不是私钥内容。


ssh-agent、passphrase 和私钥之间的关系
#

三个东西不要混淆:

私钥
#

id_ed25519

真正用于身份认证的秘密材料。

passphrase
#

保护私钥的密码。

它不是服务器登录密码。

ssh-agent
#

本地进程,用来管理已经解锁的 SSH Key。

可以理解成:

id_ed25519
     │
     │ passphrase 解锁
     ▼
ssh-agent
     │
     │ 提供身份认证
     ▼
SSH / SCP / SFTP

配置 ~/.ssh/config
#

当服务器越来越多时,每次输入:

ssh root@123.123.123.123 -p 6000

会非常麻烦。

可以创建:

C:\Users\abc\.ssh\config

例如:

Host cloud
    HostName 123.123.123.123
    User root
    Port 6000
    IdentityFile ~/.ssh/id_ed25519

这里:

Host cloud

是自己定义的别名。

它不是服务器真实的主机名。

于是:

ssh cloud

就相当于:

ssh root@123.123.123.123 -p 6000

SSH 会自动读取:

HostName
User
Port
IdentityFile

为什么建议保留 IdentityFile?
#

如果只有一个 SSH Key,OpenSSH 有时可以直接从 ssh-agent 找到合适的 Key。

但以后可能有多个 Key:

id_ed25519
id_work
id_github
id_server

这时候可以明确指定:

IdentityFile ~/.ssh/id_ed25519

告诉 SSH:

连接这个 Host 时使用这个身份。

所以即使当前不是绝对必要,也建议在配置中明确写出来。


SSH Config 不只是给 ssh 用的
#

这是 config 非常方便的一点。

例如:

Host cloud
    HostName 123.123.123.123
    User root
    Port 6000
    IdentityFile ~/.ssh/id_ed25519

之后:

SSH
#

ssh cloud

SCP
#

本地上传:

scp test.zip cloud:/root/

服务器下载到本地:

scp cloud:/root/test.zip C:\Users\abc\Desktop\

SFTP
#

sftp cloud

这些命令都可以使用 cloud 这个 Host 配置。


SFTP 和 SCP 怎么选择?
#

SCP
#

适合偶尔传一个文件:

scp test.zip cloud:/root/

或者:

scp cloud:/root/test.zip C:\Users\abc\Desktop\

复制目录:

scp -r project cloud:/root/

SFTP
#

适合交互式管理文件:

sftp cloud

进入后可以:

ls
cd /root
lcd C:\Users\abc\Desktop
put test.zip
get log.txt

其中:

ls

查看远程目录。

cd

进入远程目录。

lcd

切换本地目录。

put

上传文件。

get

下载文件。


WinSCP 与 OpenSSH Config
#

如果平时更喜欢图形化文件管理,可以使用 WinSCP 的 SFTP。

WinSCP 可以导入 OpenSSH 的配置。

例如已经存在:

C:\Users\abc\.ssh\config

内容:

Host cloud
    HostName 123.123.123.123
    User root
    Port 6000
    IdentityFile ~/.ssh/id_ed25519

在 WinSCP 登录界面可以通过:

Tools → Import Sites → OpenSSH config

导入 cloud。

这样就不需要重新手动填写:

IP
用户名
端口

具体界面可能随 WinSCP 版本变化。

需要注意:

WinSCP 导入 OpenSSH Config 后,会把连接信息转换成 WinSCP 自己的站点配置;它并不意味着以后每次打开 WinSCP 都实时读取 OpenSSH config。

因此可以把它理解成:

OpenSSH config
       │
       │ Import
       ▼
WinSCP Site

OpenSSH 和 WinSCP 的职责可以这样划分
#

一个比较舒服的 Windows SSH 工作流是:

Windows Terminal
    │
    └── OpenSSH
          │
          ├── ssh cloud
          ├── scp
          └── sftp

WinSCP
    │
    └── SFTP 图形化文件管理

两者可以共享同一套服务器信息,但 WinSCP 有自己的站点配置。

如果习惯命令行:

ssh cloud

非常方便。

如果需要拖拽文件:

WinSCP → SFTP

更加直观。


多台服务器时的 Config
#

当服务器增加以后,可以继续往同一个 config 里添加:

Host cloud
    HostName 123.123.123.123
    User root
    Port 6000
    IdentityFile ~/.ssh/id_ed25519

Host work
    HostName 10.10.10.20
    User dev
    Port 22
    IdentityFile ~/.ssh/id_work

Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_github

于是:

ssh cloud
ssh work
ssh github

分别连接不同的目标。

这也是 SSH Config 真正开始体现价值的地方:

把复杂的连接参数隐藏在配置文件中,让命令本身变得简单。


跳板机 / Bastion Host
#

如果服务器不能直接从公网访问,而必须经过一台跳板机,可以使用 ProxyJump。

例如:

Host bastion
    HostName 123.123.123.123
    User root
    Port 6000
    IdentityFile ~/.ssh/id_ed25519

Host internal
    HostName 10.0.0.100
    User root
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump bastion

以后:

ssh internal

实际路径是:

Windows
   │
   │ SSH
   ▼
bastion
   │
   │ SSH
   ▼
internal

ProxyJump 的作用是让 SSH 连接经过指定的跳板机。

它和 SSH 端口转发不是同一个概念。


SSH 端口转发
#

例如:

ssh -L 8080:10.0.0.100:8080 bastion

意思是:

本地 localhost:8080
        │
        │ SSH 隧道
        ▼
bastion
        │
        ▼
10.0.0.100:8080

本地访问:

http://localhost:8080

实际可能访问的是内网服务器:

10.0.0.100:8080

因此:

ProxyJump

主要解决:

SSH 登录经过跳板机。

而:

-L / -R / -D

主要解决:

网络流量通过 SSH 隧道转发。


known_hosts 是什么?
#

第一次连接一台 SSH 服务器时,经常会看到:

The authenticity of host '...' can't be established.
Are you sure you want to continue connecting?

确认后,OpenSSH 会把服务器的主机密钥记录到:

C:\Users\abc\.ssh\known_hosts

以后再次连接时,SSH 会检查服务器身份是否发生异常变化。

因此:

authorized_keys

和:

known_hosts

不要混淆。

authorized_keys
#

在服务器上。

作用:

哪些客户端公钥允许登录我?

known_hosts
#

在客户端上。

作用:

我以前见过哪些服务器,它们的主机身份是什么?

可以简单记成:

服务器:
authorized_keys
↓
“谁可以登录我?”

客户端:
known_hosts
↓
“我连接的服务器是不是之前那台?”

常见安全原则
#

私钥永远不要上传
#

不要把:

id_ed25519

上传到服务器。

不要发给别人。

不要提交到 Git 仓库。

不要放进公开网盘。

服务器只需要:

id_ed25519.pub

对应的公钥内容。

给私钥设置 passphrase
#

生成 Key 时建议设置 passphrase。

这样即使私钥文件意外泄露,也多了一层保护。

不要随便删除 known_hosts
#

如果 SSH 提示:

REMOTE HOST IDENTIFICATION HAS CHANGED

不要第一时间无脑删除 known_hosts。

服务器重装、IP 复用等情况确实可能导致主机密钥变化,但也可能意味着存在安全问题。

应该先确认服务器是否真的更换了主机密钥。


一套推荐的 Windows SSH 工作流
#

最终可以整理成:

C:\Users\abc\.ssh\
│
├── id_ed25519
│      └── 私钥
│
├── id_ed25519.pub
│      └── 公钥
│
├── config
│      └── SSH 连接配置
│
└── known_hosts
       └── 已知服务器身份

ssh-agent:

ssh-agent
    │
    └── 管理已经加载的 SSH Key

服务器:

~/.ssh/
└── authorized_keys
       └── 保存允许登录的公钥

日常使用:

ssh cloud

文件上传:

scp test.zip cloud:/root/

文件下载:

scp cloud:/root/test.zip .

交互式 SFTP:

sftp cloud

图形化文件管理:

WinSCP
└── SFTP

从零到现在,完整流程
#

如果以后需要重新配置一台服务器,可以按照这个思路:

生成 Key
    ↓
id_ed25519 + id_ed25519.pub
    ↓
启动 ssh-agent
    ↓
ssh-add id_ed25519
    ↓
把公钥放到服务器
    ↓
~/.ssh/authorized_keys
    ↓
测试 SSH Key 登录
    ↓
创建 ~/.ssh/config
    ↓
Host cloud
    ↓
ssh cloud
    ↓
scp / sftp / WinSCP

最终,把复杂的:

ssh root@123.123.123.123 -p 6000

变成:

ssh cloud

这就是 SSH Config 最直接的价值。

而更完整地看,SSH Key、ssh-agent、authorized_keys、config、known_hosts 并不是互相独立的几个知识点,它们实际上共同组成了一套 SSH 工作流:

                 本地 Windows
┌─────────────────────────────────────┐
│                                     │
│  id_ed25519      ← 私钥             │
│       │                             │
│       ▼                             │
│  ssh-agent                         │
│       │                             │
│       ▼                             │
│  OpenSSH                            │
│       │                             │
│       ├── config → cloud             │
│       │                             │
│       └── known_hosts                │
│                                     │
└──────────────┬──────────────────────┘
               │
               │ SSH
               ▼
        ┌─────────────────┐
        │ Debian Server   │
        │                 │
        │ ~/.ssh/         │
        │   └─ authorized_keys
        │                 │
        └─────────────────┘

理解了这套关系之后,SSH 的各种命令就不再是需要死记硬背的一堆参数,而只是对这套体系的不同操作。