SSH 高级用法:密钥登录、端口转发、跳板机一键登录

89 阅读10分钟

1. SSH 密钥认证:告别繁琐密码登录

1.1 为什么需要密钥登录?

想象一下,每次回家不用掏钥匙开门,而是要对着一把密码锁念咒语——这就是密码登录SSH的体验。密钥登录就像是指纹识别,安全又便捷。

1.2 生成SSH密钥对

创建代码文件:generate_ssh_keys.sh

#!/bin/bash

# 设置密钥保存目录
KEY_DIR="$HOME/.ssh"
mkdir -p $KEY_DIR

# 生成ED25519密钥对(推荐,更安全更快)
echo "正在生成ED25519密钥对..."
ssh-keygen -t ed25519 -C "你的邮箱@example.com" -f "${KEY_DIR}/id_ed25519" -N ""

# 或者生成RSA密钥对(兼容老系统)
echo "正在生成RSA密钥对..."
ssh-keygen -t rsa -b 4096 -C "你的邮箱@example.com" -f "${KEY_DIR}/id_rsa" -N ""

# 设置正确的权限
chmod 700 $KEY_DIR
chmod 600 $KEY_DIR/id_ed25519
chmod 644 $KEY_DIR/id_ed25519.pub
chmod 600 $KEY_DIR/id_rsa
chmod 644 $KEY_DIR/id_rsa.pub

echo "🎉 密钥生成完成!"
echo "公钥位置: ${KEY_DIR}/id_ed25519.pub"
echo "私钥位置: ${KEY_DIR}/id_ed25519"

运行这个脚本:

chmod +x generate_ssh_keys.sh
./generate_ssh_keys.sh

1.3 部署公钥到服务器

创建代码文件:deploy_key.sh

#!/bin/bash

# 配置参数
SERVER_IP="你的服务器IP"
SERVER_USER="你的用户名"
SSH_PORT="22"
PUBLIC_KEY_FILE="$HOME/.ssh/id_ed25519.pub"

# 检查公钥文件是否存在
if [ ! -f "$PUBLIC_KEY_FILE" ]; then
    echo "❌ 公钥文件不存在: $PUBLIC_KEY_FILE"
    exit 1
fi

# 方法1:使用ssh-copy-id(最简单)
echo "方法1: 使用ssh-copy-id部署公钥..."
ssh-copy-id -i "$PUBLIC_KEY_FILE" -p $SSH_PORT $SERVER_USER@$SERVER_IP

# 方法2:手动部署(当ssh-copy-id不可用时)
echo "方法2: 手动部署公钥..."
ssh -p $SSH_PORT $SERVER_USER@$SERVER_IP "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
cat "$PUBLIC_KEY_FILE" | ssh -p $SSH_PORT $SERVER_USER@$SERVER_IP "cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

echo "✅ 公钥部署完成!现在可以尝试无密码登录:"
echo "ssh -p $SSH_PORT $SERVER_USER@$SERVER_IP"

1.4 配置SSH客户端简化连接

创建代码文件:~/.ssh/config

# SSH全局配置
Host *
    # 连接保持,防止超时断开
    ServerAliveInterval 60
    ServerAliveCountMax 3
    # 连接复用,加速后续连接
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 4h
    # 压缩数据传输
    Compression yes
    # 加密算法优先顺序
    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
    # 密钥交换算法优先顺序
    KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org

# 特定服务器配置
Host myserver
    HostName 你的服务器IP
    Port 22
    User 你的用户名
    IdentityFile ~/.ssh/id_ed25519
    # 禁用密码认证,只使用密钥
    PasswordAuthentication no

Host internal-server
    HostName 192.168.1.100
    Port 22
    User admin
    IdentityFile ~/.ssh/id_rsa

# 跳板机配置(后面会用到)
Host jumpbox
    HostName 跳板机IP
    Port 22
    User jumpuser
    IdentityFile ~/.ssh/id_ed25519

设置配置文件权限:

chmod 600 ~/.ssh/config

2. SSH端口转发:搭建安全隧道

2.1 本地端口转发(Local Port Forwarding)

场景:访问内网中无法直接连接的服务器

创建代码文件:local_forwarding.sh

#!/bin/bash

# 本地端口转发:将本地端口映射到远程服务器
# 语法:-L [本地IP:]本地端口:目标地址:目标端口

# 示例1:转发本地端口到远程数据库
echo "正在建立本地端口转发..."
ssh -L 3306:localhost:3306 -N -f myserver

# 示例2:转发到其他内网服务器
ssh -L 8080:192.168.1.100:80 -N -f jumpbox

# 示例3:多端口转发
ssh -L 3306:localhost:3306 -L 5432:localhost:5432 -L 8080:localhost:80 -N -f myserver

# 参数说明:
# -L: 本地端口转发
# -N: 不执行远程命令,只建立隧道
# -f: 后台运行
# -v: 详细输出(调试时使用)

echo "✅ 本地端口转发已建立"
echo "🔗 本地3306端口 -> 远程数据库"
echo "🔗 本地8080端口 -> 内网Web服务"

2.2 远程端口转发(Remote Port Forwarding)

场景:从外网访问内网服务

创建代码文件:remote_forwarding.sh

#!/bin/bash

# 远程端口转发:将远程端口映射到本地服务
# 语法:-R [远程IP:]远程端口:本地地址:本地端口

# 示例1:将本地Web服务暴露到远程服务器
echo "正在建立远程端口转发..."
ssh -R 8080:localhost:80 -N -f myserver

# 示例2:将本地开发服务器暴露出去
ssh -R 3000:localhost:3000 -R 5173:localhost:5173 -N -f myserver

# 示例3:允许其他机器访问转发的端口
ssh -R 0.0.0.0:8080:localhost:80 -N -f myserver

echo "✅ 远程端口转发已建立"
echo "🔗 远程8080端口 -> 本地Web服务"

2.3 动态端口转发(SOCKS代理)

创建代码文件:socks_proxy.sh

#!/bin/bash

# 动态端口转发:创建SOCKS代理服务器
# 语法:-D [本地IP:]本地端口

# 启动SOCKS5代理
echo "正在启动SOCKS5代理..."
ssh -D 1080 -N -f myserver

# 或者使用跳板机
ssh -D 1080 -N -f jumpbox

# 浏览器配置:
# 1. Firefox: 设置 -> 网络设置 -> 手动配置代理 -> SOCKS主机: localhost, 端口: 1080
# 2. Chrome: chrome --proxy-server="socks5://localhost:1080"

echo "✅ SOCKS5代理已启动"
echo "🌐 代理地址: socks5://localhost:1080"
echo "💡 提示:配置浏览器使用该代理即可科学上网"

3. 跳板机一键登录:穿透多层网络

3.1 传统跳板机登录方式

创建代码文件:jump_host_manual.sh

#!/bin/bash

# 方法1:多次SSH连接(繁琐)
echo "方法1: 传统跳板方式..."
ssh -t jumpbox ssh internal-server

# 方法2:使用ProxyJump(SSH 7.3+ 推荐)
echo "方法2: 使用ProxyJump..."
ssh -J jumpbox internal-server

# 方法3:多级跳板
ssh -J jumpbox1,jumpbox2,jumpbox3 target-server

echo "✅ 跳板机连接测试完成"

3.2 配置SSH config实现一键登录

编辑代码文件:~/.ssh/config(追加内容)

# 跳板机配置
Host jumpbox
    HostName 跳板机IP
    User jumpuser
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes

# 通过跳板机访问的内网服务器
Host internal-web
    HostName 192.168.1.101
    User webadmin
    ProxyJump jumpbox
    IdentityFile ~/.ssh/id_ed25519

Host internal-db
    HostName 192.168.1.102
    User dbadmin
    ProxyJump jumpbox
    IdentityFile ~/.ssh/id_ed25519

# 多级跳板配置
Host production-server
    HostName 10.0.1.100
    User produser
    ProxyJump jumpbox1,jumpbox2
    IdentityFile ~/.ssh/id_ed25519

现在可以直接连接:

ssh internal-web  # 一键登录内网Web服务器
ssh internal-db   # 一键登录内网数据库

3.3 高级跳板机脚本

创建代码文件:smart_jump.sh

#!/bin/bash

# 智能跳板机脚本
# 支持自动检测网络、多路径选择、连接测试

CONFIG_FILE="$HOME/.ssh/jump_hosts.conf"
LOG_FILE="$HOME/.ssh/ssh_jump.log"

# 创建配置文件
create_config() {
    cat > $CONFIG_FILE << 'EOF'
# 跳板机配置格式:
# 名称|跳板机地址|跳板机用户|目标地址|目标用户|端口|密钥文件

# 开发环境
dev-web|192.168.1.10|jumpuser|172.16.1.100|webuser|22|~/.ssh/id_ed25519
dev-db|192.168.1.10|jumpuser|172.16.1.101|dbuser|22|~/.ssh/id_ed25519

# 生产环境
prod-web|10.0.1.10|prodjump|10.0.2.100|produser|2222|~/.ssh/prod_key
prod-db|10.0.1.10|prodjump|10.0.2.101|produser|2222|~/.ssh/prod_key
EOF
    echo "📝 配置文件已创建: $CONFIG_FILE"
}

# 测试连接速度
test_connection() {
    local host=$1
    local port=$2
    
    echo "测试连接 $host:$port ..."
    if timeout 5 bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then
        echo "✅ $host:$port 可达"
        return 0
    else
        echo "❌ $host:$port 不可达"
        return 1
    fi
}

# 显示可用的跳板目标
list_targets() {
    echo "🔄 可用的跳板目标:"
    awk -F'|' '{printf "  %s -> %s@%s:%s\n", $1, $5, $4, $6}' $CONFIG_FILE | grep -v "^#"
}

# 主连接函数
connect_via_jump() {
    local target_name=$1
    
    if [[ -z "$target_name" ]]; then
        echo "❌ 请指定目标名称"
        list_targets
        return 1
    fi
    
    # 从配置文件中查找目标
    local config_line=$(grep "^$target_name|" $CONFIG_FILE)
    
    if [[ -z "$config_line" ]]; then
        echo "❌ 未找到目标: $target_name"
        list_targets
        return 1
    fi
    
    # 解析配置
    IFS='|' read -r name jump_host jump_user target_host target_user target_port identity_file <<< "$config_line"
    
    # 展开路径
    identity_file=$(eval echo $identity_file)
    
    echo "🚀 连接目标: $target_name"
    echo "📍 路径: 本地 -> $jump_host -> $target_host"
    
    # 测试跳板机连接
    if ! test_connection $jump_host $target_port; then
        echo "❌ 跳板机不可达"
        return 1
    fi
    
    # 建立连接
    echo "🔗 建立SSH连接..."
    ssh -i "$identity_file" -J "$jump_user@$jump_host:$target_port" "$target_user@$target_host"
    
    # 记录日志
    echo "$(date): 连接 $target_name ($target_user@$target_host)" >> $LOG_FILE
}

# 菜单界面
show_menu() {
    echo "
    🤖 SSH跳板机管理工具
    ===================
    
    1. 显示可用目标
    2. 连接跳板目标
    3. 测试所有连接
    4. 查看连接日志
    5. 编辑配置文件
    6. 退出
    
    "
}

# 主程序
main() {
    # 检查配置文件
    if [[ ! -f $CONFIG_FILE ]]; then
        echo "⚠️  配置文件不存在,创建中..."
        create_config
    fi
    
    while true; do
        show_menu
        read -p "请选择操作 [1-6]: " choice
        
        case $choice in
            1) list_targets ;;
            2) 
                list_targets
                read -p "输入目标名称: " target
                connect_via_jump $target
                ;;
            3) test_all_connections ;;
            4) cat $LOG_FILE ;;
            5) ${EDITOR:-vim} $CONFIG_FILE ;;
            6) echo "再见!"; exit 0 ;;
            *) echo "❌ 无效选择" ;;
        esac
        
        echo
        read -p "按回车键继续..."
    done
}

# 测试所有连接
test_all_connections() {
    echo "🧪 测试所有跳板机连接..."
    
    grep -v "^#" $CONFIG_FILE | while IFS='|' read -r name jump_host jump_user target_host target_user target_port identity_file; do
        echo "测试 $name ..."
        if test_connection $jump_host $target_port; then
            echo "✅ $name: 跳板机可达"
        else
            echo "❌ $name: 跳板机不可达"
        fi
    done
}

# 如果直接带参数运行,则直接连接
if [[ $# -gt 0 ]]; then
    connect_via_jump $1
else
    main
fi

设置脚本权限并运行:

chmod +x smart_jump.sh
./smart_jump.sh

3.4 连接流程可视化

下面通过流程图展示完整的SSH跳板机连接过程:

graph TD
    A[本地客户端] --> B{选择连接方式}
    B --> C[直接SSH连接]
    B --> D[跳板机连接]
    
    C --> E[目标服务器]
    
    D --> F[跳板机验证]
    F --> G[密钥认证]
    F --> H[密码认证]
    G --> I[建立隧道]
    H --> I
    I --> J[内网目标服务器]
    
    subgraph 安全隧道
        I[建立隧道]
    end
    
    E --> K[连接成功]
    J --> K
    
    style A fill:#4CAF50,color:white
    style E fill:#2196F3,color:white
    style J fill:#2196F3,color:white
    style F fill:#FF9800,color:white
    style I fill:#9C27B0,color:white
    style K fill:#4CAF50,color:white

4. 实战案例:完整的企业级SSH方案

4.1 批量服务器管理脚本

创建代码文件:batch_ssh_manager.sh

#!/bin/bash

# 企业级批量SSH管理脚本
# 支持批量命令执行、文件传输、健康检查

SERVER_LIST="$HOME/.ssh/server_list.conf"
BATCH_LOG="$HOME/.ssh/batch_operation.log"

# 服务器列表格式
init_server_list() {
    cat > $SERVER_LIST << 'EOF'
# 服务器列表格式:
# 服务器名称|SSH连接字符串|说明|标签

# Web服务器集群
web-01|webuser@192.168.1.101:22|主要Web服务器|web,production
web-02|webuser@192.168.1.102:22|备用Web服务器|web,production

# 数据库集群
db-master|dbadmin@192.168.1.201:22|主数据库|db,production,master
db-slave|dbadmin@192.168.1.202:22|从数据库|db,production,slave

# 测试环境
test-01|tester@192.168.2.101:22|测试服务器|web,test
EOF
    echo "✅ 服务器列表初始化完成: $SERVER_LIST"
}

# 执行批量命令
batch_run_command() {
    local command="$1"
    local filter_tag="$2"
    
    if [[ -z "$command" ]]; then
        echo "❌ 请提供要执行的命令"
        return 1
    fi
    
    echo "🚀 批量执行命令: $command"
    echo "🏷️  过滤标签: ${filter_tag:-所有服务器}"
    echo "=" * 50
    
    grep -v "^#" $SERVER_LIST | while IFS='|' read -r name connection description tags; do
        # 标签过滤
        if [[ -n "$filter_tag" && ! "$tags" =~ "$filter_tag" ]]; then
            continue
        fi
        
        echo "🖥️  服务器: $name ($description)"
        echo "🔗 连接: $connection"
        echo "📝 执行: $command"
        
        # 执行远程命令
        if ssh -o ConnectTimeout=10 -o BatchMode=yes $connection "$command"; then
            echo "✅ $name: 执行成功"
            status="成功"
        else
            echo "❌ $name: 执行失败"
            status="失败"
        fi
        
        # 记录日志
        echo "$(date) | $name | $connection | '$command' | $status" >> $BATCH_LOG
        
        echo "-" * 40
    done
    
    echo "📊 批量操作完成!查看日志: $BATCH_LOG"
}

# 批量文件传输
batch_file_transfer() {
    local local_file="$1"
    local remote_path="$2"
    local mode="$3"  # upload or download
    local filter_tag="$4"
    
    if [[ -z "$local_file" || -z "$remote_path" ]]; then
        echo "用法: batch_file_transfer <本地文件> <远程路径> <upload|download> [标签过滤]"
        return 1
    fi
    
    if [[ "$mode" == "upload" ]]; then
        echo "📤 批量上传文件: $local_file -> $remote_path"
    else
        echo "📥 批量下载文件: $remote_path -> $local_file"
    fi
    
    grep -v "^#" $SERVER_LIST | while IFS='|' read -r name connection description tags; do
        # 标签过滤
        if [[ -n "$filter_tag" && ! "$tags" =~ "$filter_tag" ]]; then
            continue
        fi
        
        echo "🖥️  处理: $name"
        
        if [[ "$mode" == "upload" ]]; then
            # 上传文件
            if scp -o ConnectTimeout=10 "$local_file" "$connection:$remote_path"; then
                echo "✅ $name: 上传成功"
            else
                echo "❌ $name: 上传失败"
            fi
        else
            # 下载文件
            local local_dest="${name}_$(basename $remote_path)"
            if scp -o ConnectTimeout=10 "$connection:$remote_path" "$local_dest"; then
                echo "✅ $name: 下载成功 -> $local_dest"
            else
                echo "❌ $name: 下载失败"
            fi
        fi
    done
}

# 服务器健康检查
server_health_check() {
    local filter_tag="$1"
    
    echo "🏥 服务器健康检查"
    echo "🏷️  过滤标签: ${filter_tag:-所有服务器}"
    echo "=" * 60
    
    grep -v "^#" $SERVER_LIST | while IFS='|' read -r name connection description tags; do
        # 标签过滤
        if [[ -n "$filter_tag" && ! "$tags" =~ "$filter_tag" ]]; then
            continue
        fi
        
        echo "🔍 检查: $name ($description)"
        echo "📊 标签: $tags"
        
        # 检查SSH连接
        if ssh -o ConnectTimeout=5 -o BatchMode=yes $connection "echo 'SSH连接正常'" &>/dev/null; then
            echo "✅ SSH连接: 正常"
            
            # 获取系统信息
            echo "📈 系统状态:"
            ssh -o BatchMode=yes $connection "
                echo '  🖥️  主机名: \$(hostname)'
                echo '  💻 负载: \$(uptime | awk -F'load average:' '{print \$2}')'
                echo '  🧠 内存: \$(free -h | grep Mem | awk \"{print \\\$3\\"/\\\"\\$2}\")'
                echo '  💾 磁盘: \$(df -h / | tail -1 | awk \"{print \\\$5\\\" used (\\\"\\$3\\\"/\\\"\\$2\\\")\"})'
                echo '  ⏰ 运行时间: \$(uptime -p)'
            " 2>/dev/null || echo "  ❌ 无法获取详细状态"
        else
            echo "❌ SSH连接: 失败"
        fi
        
        echo "-" * 50
    done
}

# 交互式菜单
show_batch_menu() {
    echo "
    🔧 批量SSH管理工具
    =================
    
    1. 初始化服务器列表
    2. 批量执行命令
    3. 批量上传文件
    4. 批量下载文件
    5. 服务器健康检查
    6. 查看操作日志
    7. 退出
    
    "
}

# 主函数
batch_main() {
    # 检查服务器列表
    if [[ ! -f $SERVER_LIST ]]; then
        echo "⚠️  服务器列表不存在"
        init_server_list
    fi
    
    while true; do
        show_batch_menu
        read -p "请选择操作 [1-7]: " choice
        
        case $choice in
            1) init_server_list ;;
            2) 
                read -p "输入要执行的命令: " cmd
                read -p "输入标签过滤(可选): " tag
                batch_run_command "$cmd" "$tag"
                ;;
            3)
                read -p "输入本地文件路径: " local_file
                read -p "输入远程目标路径: " remote_path
                read -p "输入标签过滤(可选): " tag
                batch_file_transfer "$local_file" "$remote_path" "upload" "$tag"
                ;;
            4)
                read -p "输入远程文件路径: " remote_file
                read -p "输入本地保存路径: " local_path
                read -p "输入标签过滤(可选): " tag
                batch_file_transfer "$local_path" "$remote_file" "download" "$tag"
                ;;
            5)
                read -p "输入标签过滤(可选): " tag
                server_health_check "$tag"
                ;;
            6) 
                echo "📋 操作日志:"
                cat $BATCH_LOG 2>/dev/null || echo "暂无日志"
                ;;
            7) echo "再见!"; exit 0 ;;
            *) echo "❌ 无效选择" ;;
        esac
        
        echo
        read -p "按回车键继续..."
    done
}

# 脚本入口
if [[ $# -eq 0 ]]; then
    batch_main
else
    case $1 in
        "run")
            batch_run_command "$2" "$3"
            ;;
        "health")
            server_health_check "$2"
            ;;
        "upload")
            batch_file_transfer "$2" "$3" "upload" "$4"
            ;;
        "download")
            batch_file_transfer "$2" "$3" "download" "$4"
            ;;
        *)
            echo "用法: $0 [run|health|upload|download] [参数]"
            ;;
    esac
fi

4.2 使用示例

# 初始化脚本
chmod +x batch_ssh_manager.sh

# 交互式使用
./batch_ssh_manager.sh

# 或者命令行直接使用
./batch_ssh_manager.sh run "df -h" web
./batch_ssh_manager.sh health production
./batch_ssh_manager.sh upload ./app.tar.gz /tmp/ web

5. 安全加固和故障排除

5.1 SSH安全配置

创建代码文件:ssh_hardening.sh

#!/bin/bash

# SSH服务端安全加固脚本
# 在服务器上运行以增强SSH安全性

SSH_CONFIG="/etc/ssh/sshd_config"
BACKUP_DIR="/etc/ssh/backup"

echo "🔒 SSH安全加固开始..."

# 备份原配置
mkdir -p $BACKUP_DIR
cp $SSH_CONFIG "$BACKUP_DIR/sshd_config.$(date +%Y%m%d_%H%M%S).bak"

# 应用安全配置
echo "正在应用安全配置..."

# 禁用密码认证(只允许密钥)
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' $SSH_CONFIG
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' $SSH_CONFIG

# 禁用root登录
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' $SSH_CONFIG
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' $SSH_CONFIG

# 更改默认端口
sed -i 's/#Port 22/Port 2222/' $SSH_CONFIG

# 限制用户登录
echo "AllowUsers 你的用户名" >> $SSH_CONFIG

# 配置空闲超时
echo "ClientAliveInterval 300" >> $SSH_CONFIG
echo "ClientAliveCountMax 2" >> $SSH_CONFIG

# 限制最大尝试次数
echo "MaxAuthTries 3" >> $SSH_CONFIG

# 禁用不安全的认证方法
echo "KbdInteractiveAuthentication no" >> $SSH_CONFIG
echo "ChallengeResponseAuthentication no" >> $SSH_CONFIG

# 配置加密算法
echo "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr" >> $SSH_CONFIG
echo "KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256" >> $SSH_CONFIG
echo "MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com" >> $SSH_CONFIG

# 重启SSH服务
echo "🔄 重启SSH服务..."
if systemctl restart sshd; then
    echo "✅ SSH服务重启成功"
else
    service ssh restart
    echo "✅ SSH服务重启成功"
fi

echo "🔐 安全加固完成!"
echo "⚠️  注意:现在只能使用密钥在2222端口登录"
echo "📋 新连接命令: ssh -p 2222 你的用户名@服务器IP"

5.2 常见问题排查

创建代码文件:ssh_troubleshooting.sh

#!/bin/bash

# SSH连接问题排查工具

echo "🐛 SSH故障排查工具"
echo "=================="

check_ssh_connection() {
    local host=$1
    local port=${2:-22}
    
    echo "🔍 检查SSH连接到 $host:$port"
    
    # 检查网络连通性
    echo "1. 检查网络连通性..."
    if ping -c 3 -W 2 $host &>/dev/null; then
        echo "   ✅ 网络连通性: 正常"
    else
        echo "   ❌ 网络连通性: 失败"
        return 1
    fi
    
    # 检查端口是否开放
    echo "2. 检查SSH端口..."
    if nc -z -w 5 $host $port; then
        echo "   ✅ SSH端口 $port: 开放"
    else
        echo "   ❌ SSH端口 $port: 关闭或阻塞"
        return 1
    fi
    
    # 检查SSH版本
    echo "3. 检查SSH协议版本..."
    local ssh_version=$(ssh -o ConnectTimeout=5 -V 2>&1 | head -1)
    echo "   📋 SSH客户端版本: $ssh_version"
    
    # 详细连接测试
    echo "4. 详细连接测试..."
    ssh -o ConnectTimeout=10 -o BatchMode=yes -v -p $port $host exit 2>&1 | grep -E "(debug|Connection|Authenticat)" | tail -10
    
    echo "✅ 基本检查完成"
}

# 检查密钥权限
check_key_permissions() {
    echo "🔑 检查SSH密钥权限..."
    
    local key_files=(
        "$HOME/.ssh/id_rsa"
        "$HOME/.ssh/id_ed25519" 
        "$HOME/.ssh/config"
    )
    
    for key_file in "${key_files[@]}"; do
        if [[ -f "$key_file" ]]; then
            local permissions=$(stat -c "%a %U:%G" "$key_file")
            echo "   $key_file: $permissions"
            
            # 检查私钥权限
            if [[ "$key_file" =~ id_rsa$|id_ed25519$ ]] && [[ "$permissions" != 600* ]]; then
                echo "   ⚠️  警告: 私钥权限应为600,正在修复..."
                chmod 600 "$key_file"
            fi
        fi
    done
}

# 检查SSH代理
check_ssh_agent() {
    echo "🕵️  检查SSH代理..."
    
    if [[ -n "$SSH_AUTH_SOCK" ]]; then
        echo "   ✅ SSH代理运行中: $SSH_AUTH_SOCK"
        ssh-add -l
    else
        echo "   ❌ SSH代理未运行"
        echo "   💡 建议启动: eval \$(ssh-agent) && ssh-add"
    fi
}

# 主函数
main() {
    local host=$1
    local port=$2
    
    echo "开始SSH故障排查..."
    echo
    
    check_ssh_agent
    echo
    
    check_key_permissions  
    echo
    
    if [[ -n "$host" ]]; then
        check_ssh_connection "$host" "$port"
    else
        echo "💡 使用方法: $0 <主机> [端口]"
        echo "💡 示例: $0 example.com 22"
    fi
}

main "$@"

总结

通过本教程,你已经掌握了SSH的高级用法:

  1. 🔑 密钥认证 - 告别密码,拥抱安全
  2. 🔄 端口转发 - 搭建安全隧道,穿透网络限制
  3. 🚀 跳板机登录 - 一键连接内网服务器
  4. 🔧 批量管理 - 高效管理服务器集群
  5. 🔒 安全加固 - 保护你的SSH服务

这些技能就像给你的服务器管理装备上了瑞士军刀,无论是日常运维还是紧急故障处理,都能游刃有余。记住,熟练使用SSH是每个运维工程师和开发者的必备技能!

遇到问题时,记得使用故障排查工具,或者回头查看对应的配置步骤。Happy SSHing! 🎉