--- 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 :: # 本地 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 在局域网内的常见部署场景