Files
cs-note/hhs/NETWORK/05-应用层协议/06-SSH与邮件协议.md
T
2026-05-24 11:42:38 +08:00

6.4 KiB
Raw Blame History

tags, create time
tags create time
计算机网络
SSH
SFTP
SCP
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()

关联笔记