6.4 KiB
6.4 KiB
tags, create time
| tags | 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 密钥交换过程
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+ 默认禁用 |
# 生成 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)⭐
# 通过 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)
# 让外网访问我本地服务(反向穿透 NAT)
ssh -R 8080:localhost:3000 user@public-server
# 我的 Mac:3000 ← SSH -R ← public-server:8080
# 任何人访问 public-server:8080 都能到我的本地服务!
动态端口转发(SOCKS Proxy)
# 创建 SOCKS5 代理
ssh -D 1080 user@bastion
# 浏览器设置 SOCKS proxy: 127.0.0.1:1080
# → 所有流量经过 bastion 转发
SSH 配置文件
# ~/.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
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 在局域网内的常见部署场景