Files

206 lines
6.4 KiB
Markdown
Raw Permalink Normal View History

2026-05-24 11:42:38 +08:00
---
tags: [计算机网络, SSH, SFTP, SCP]
create time: 2026-05-18 03:20
---
# SSH 远程安全登录
## 概述
SSH(Secure Shell)是替代 Telnet、rlogin 等明文协议的加密外壳工具。它不仅仅用于远程登录——SSH 的隧道转发能力使其成为网络安全的瑞士军刀。
## SSH 架构三层模型
```
┌─────────────────────────────────┐
│ Application Layer │ ← SSH-CONNECT, SSH-USERAUTH, SSH-CONNECTION
│ • scp / sftp │ 每个子协议独立协商版本
│ • port forwarding │
│ • X11 forwarding │
├─────────────────────────────────┤
│ Transport Layer │ ← Host-key auth + encryption + integrity
│ • Server host-key authentication│ 一旦建立隧道,所有上层协议自动加密
│ • Server/pubkey exchange │
│ • Symmetric encryption │ 默认 AES-128-GCM or ChaCha20-Poly1305
│ • HMAC integrity │
├─────────────────────────────────┤
│ User Authentication Layer │ ← 多种认证方式
│ • password │ SSH-CONN USER_AUTH_REQUEST
│ • public key │
│ • keyboard-interactive │ MFA/TOTP
│ • GSSAPI (Kerberos) │
│ • OS Login / Certificate │
└─────────────────────────────────┘
```
## SSH 密钥交换过程
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: Phase 1: Key Exchange (Diffie-Hellman)
C->>S: KEXINIT (supported ciphers, DH groups, MACs, compressions)
S-->>C: KEXINIT (negotiated algorithms)
Note over C,S: 服务器选择双方都支持的最强算法
S->>C: Server Key Exchange (DH params)
S->>C: Server Host Key (RSA/ED25519) + signature
Note over C: Verify host key fingerprint!
C->>S: Client Key Exchange (DH response)
Note over C,S: Both compute shared secret → derive session keys
Note over C,S: Phase 2: User Authentication
C->>S: SSH_USERAUTH_REQUEST "root" "password"
S-->>C: SSH_USERAUTH_SUCCESS ✅
Note over C,S: Phase 3: Channel Open
C->>S: SSH_CHANNEL_OPEN "session"
S-->>C: SSH_CHANNEL_OPEN_CONFIRMATION
```
### SSH 支持的公钥算法
| 算法 | 密钥大小 | 安全性等级 | 备注 |
|------|---------|-----------|------|
| **ed25519** | 32 bytes | ~128 bits | ✅ **推荐**,EdDSA 椭圆曲线,速度快 |
| rsa | 4096 bits | ~128 bits | 通用兼容,但体积大 |
| ecdsa | 384 bits | ~128 bits | NIST 曲线,争议因 DualECDRBG |
| dsa | 1024 bits | ~80 bits | ❌ **已弃用**,OpenSSH 7.0+ 禁用 |
| ssh-rsa (SHA-1) | variable | 弱 | ❌ OpenSSH 8.8+ 默认禁用 |
```bash
# 生成 ed25519 密钥
$ ssh-keygen -t ed25519 -C "alice@example.com"
# 或带注释和自定义路径
$ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_work -C "work@company.com"
# 复制公钥到远程服务器
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote-server
# 手动添加
$ cat ~/.ssh/id_ed25519.pub | ssh user@remote 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'
```
## SSH 端口转发(Tunneling)
### 本地端口转发(Local Forwarding)⭐
```bash
# 通过 jump server 访问内网数据库
ssh -L 3307:db.internal:3306 user@jump.example.com
# 等效命令: ssh -L <local_port>:<dest_host>:<dest_port> <jump_host>
# 本地 localhost:3307 → SSH 隧道 → jump.example.com → db.internal:3306
```
```
Client Jump Server DB Server
──────── ─────────── ─────────
localhost:3307 ──→ [SSH Tunnel] ──→ db.internal:3306
↑ ↑
MySQL CLI SSH 加密通道 🔒
```
### 远程端口转发(Remote Forwarding)
```bash
# 让外网访问我本地服务(反向穿透 NAT)
ssh -R 8080:localhost:3000 user@public-server
# 我的 Mac:3000 ← SSH -R ← public-server:8080
# 任何人访问 public-server:8080 都能到我的本地服务!
```
### 动态端口转发(SOCKS Proxy)
```bash
# 创建 SOCKS5 代理
ssh -D 1080 user@bastion
# 浏览器设置 SOCKS proxy: 127.0.0.1:1080
# → 所有流量经过 bastion 转发
```
## SSH 配置文件
```bash
# ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host bastion
HostName 203.0.113.5
User deploy
IdentityFile ~/.ssh/id_ed25519_bastion
Port 2222
Host internal-*
ProxyJump bastion
User admin
IdentityFile ~/.ssh/id_ed25519_internal
ServerAliveInterval 60
ServerAliveCountMax 3
# 使用: ssh internal-webapp1 (自动经 bastion 跳转)
```
### 关键参数说明
| 参数 | 说明 |
|------|------|
| `ProxyJump` | 通过堡垒机跳转 |
| `ServerAliveInterval` | 客户端发送心跳间隔(秒),防防火墙超时 |
| `ServerAliveCountMax` | 最多连续无响应次数后断开 |
| `IdentitiesOnly` | 仅使用指定的 identity file,不尝试其他密钥 |
| `StrictHostKeyChecking` | `ask`(默认) / `no`(不检查) / `accept-new`(首次接受) |
## Go 中的 SSH
```go
import (
"golang.org/x/crypto/ssh"
"os"
)
// 读取私钥文件
key, err := os.ReadFile("~/.ssh/id_ed25519")
if err != nil { panic(err) }
signer, err := ssh.ParsePrivateKey(key)
if err != nil { panic(err) }
config := &ssh.ClientConfig{
User: "admin",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 生产环境用 ssh.FixedHostKey()
}
client, err := ssh.Dial("tcp", "server.example.com:22", config)
if err != nil { panic(err) }
// 执行远程命令
session, _ := client.NewSession()
defer session.Close()
output, _ := session.CombinedOutput("uname -a; uptime")
fmt.Println(string(output))
// SFTP client
sftpClient, _ := sftp.NewClient(client)
defer sftpClient.Close()
```
## 关联笔记
- [[hhs/NETWORK/HTTPS与TLS握手]] — SSH 也使用非对称加密 + 对称加密混合模式
- [[hhs/NETWORK/NAT原理与应用]] — SSH 端口转发可穿透 NAT
- [[hhs/NETWORK/MAC地址与广播域]] — SSH 在局域网内的常见部署场景