206 lines
6.4 KiB
Markdown
206 lines
6.4 KiB
Markdown
---
|
||
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 在局域网内的常见部署场景
|