ssh 命令详解#

ssh(Secure Shell)是 Linux 系统中用于安全远程登录和执行命令的协议和工具,是系统管理员和开发人员最常用的网络工具之一。它提供了加密的通信通道,确保数据传输的安全性。

入门#

基本用法#

# 远程登录到服务器
ssh user@hostname

# 使用 IP 地址登录
ssh user@192.168.1.100

# 指定端口登录
ssh -p 2222 user@hostname

# 执行远程命令
ssh user@hostname "command"

# 使用私钥登录
ssh -i /path/to/private_key user@hostname

常用选项#

选项说明
-p指定端口号
-i指定私钥文件
-l指定登录用户名
-v显示详细输出(调试模式)
-q静默模式
-C启用压缩
-X启用 X11 转发
-L本地端口转发
-R远程端口转发
-D动态端口转发(SOCKS 代理)

基本示例#

# 登录到远程服务器
ssh root@server.example.com

# 使用特定端口登录
ssh -p 2222 user@server.example.com

# 执行远程命令
ssh user@server.example.com "ls -la /tmp"

# 使用私钥登录
ssh -i ~/.ssh/id_rsa user@server.example.com

中级#

SSH 密钥管理#

# 生成 SSH 密钥对
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# 生成 ED25519 密钥(推荐)
ssh-keygen -t ed25519 -C "your_email@example.com"

# 查看公钥内容
cat ~/.ssh/id_rsa.pub

# 复制公钥到远程服务器
ssh-copy-id user@hostname

# 手动复制公钥
ssh user@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_rsa.pub

# 查看已保存的指纹
ssh-keygen -l -f ~/.ssh/known_hosts

SSH 配置文件#

# 编辑 SSH 配置文件
nano ~/.ssh/config

# 配置示例:
# Host server1
#     HostName server1.example.com
#     User admin
#     Port 2222
#     IdentityFile ~/.ssh/id_rsa_server1
#
# Host server2
#     HostName 192.168.1.100
#     User user
#     Port 22

# 使用配置文件登录
ssh server1

# 测试配置文件
ssh -F ~/.ssh/config server1

端口转发#

# 本地端口转发(将远程端口映射到本地)
ssh -L 8080:localhost:80 user@remote_host

# 远程端口转发(将本地端口映射到远程)
ssh -R 8080:localhost:80 user@remote_host

# 动态端口转发(SOCKS 代理)
ssh -D 1080 user@remote_host

# 绑定到所有接口
ssh -g -L 8080:localhost:80 user@remote_host

# 转发到其他主机
ssh -L 8080:internal_host:80 user@remote_host

高级#

SSH 隧道和代理#

# 创建 SSH 隧道访问内网服务
ssh -L 3306:internal_db:3306 user@jump_server

# 通过 SSH 隧道访问 Web 服务
ssh -L 8080:localhost:80 user@remote_host

# 使用 SSH 作为 SOCKS 代理
ssh -D 1080 -N user@remote_host

# 配置浏览器使用 SOCKS 代理
# 代理地址: localhost:1080
# 代理类型: SOCKS5

# 反向隧道(让远程服务器访问本地服务)
ssh -R 8080:localhost:80 user@remote_host

SSH 会话管理#

# 启动 SSH 会话并保持后台运行
ssh -f -N user@hostname

# 使用 autossh 自动重连
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" user@hostname

# 使用 tmux/screen 保持会话
ssh user@hostname "tmux new -s session_name"

# 附加到现有会话
ssh user@hostname "tmux attach -t session_name"

# 使用 ControlMaster 复用连接
# 在 ~/.ssh/config 中添加:
# ControlMaster auto
# ControlPath ~/.ssh/cm-%r@%h:%p
# ControlPersist 10m

SSH 安全配置#

# 编辑服务器 SSH 配置
sudo nano /etc/ssh/sshd_config

# 推荐配置:
# Port 2222                    # 更改默认端口
# PermitRootLogin no          # 禁止 root 登录
# PasswordAuthentication no    # 禁用密码认证
# PubkeyAuthentication yes    # 启用公钥认证
# AllowUsers user1 user2       # 限制允许的用户
# ClientAliveInterval 300      # 客户端存活间隔
# ClientAliveCountMax 2        # 最大存活次数

# 重启 SSH 服务
sudo systemctl restart sshd

# 限制登录尝试次数
sudo nano /etc/ssh/sshd_config
# MaxAuthTries 3

# 使用 fail2ban 防止暴力破解
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

大师#

SSH 批量管理#

#!/bin/bash
# SSH 批量管理脚本

SERVERS=("server1.example.com" "server2.example.com" "server3.example.com")
COMMAND="uptime"

# 在所有服务器上执行命令
for SERVER in "${SERVERS[@]}"; do
    echo "=== $SERVER ==="
    ssh user@$SERVER "$COMMAND"
    echo ""
done

# 批量上传文件
for SERVER in "${SERVERS[@]}"; do
    echo "Uploading to $SERVER..."
    scp local_file user@$SERVER:/remote/path/
done

# 批量下载文件
for SERVER in "${SERVERS[@]}"; do
    echo "Downloading from $SERVER..."
    scp user@$SERVER:/remote/path/file local_path/
done

SSH 自动化脚本#

#!/bin/bash
# SSH 自动化部署脚本

SERVER="user@server.example.com"
APP_DIR="/var/www/myapp"
BACKUP_DIR="/var/backups/myapp"

# 远程备份
backup_remote() {
    ssh $SERVER "mkdir -p $BACKUP_DIR && tar -czf $BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).tar.gz -C $APP_DIR ."
    echo "Backup completed"
}

# 远程部署
deploy_remote() {
    echo "Deploying to $SERVER..."
    
    # 上传文件
    scp -r ./dist/* $SERVER:$APP_DIR/
    
    # 执行远程命令
    ssh $SERVER "cd $APP_DIR && npm install && pm2 restart myapp"
    
    echo "Deployment completed"
}

# 远程监控
monitor_remote() {
    while true; do
        ssh $SERVER "pm2 status"
        sleep 10
    done
}

case "$1" in
    backup)
        backup_remote
        ;;
    deploy)
        deploy_remote
        ;;
    monitor)
        monitor_remote
        ;;
    *)
        echo "Usage: $0 {backup|deploy|monitor}"
        exit 1
        ;;
esac

SSH 密钥轮换#

#!/bin/bash
# SSH 密钥轮换脚本

OLD_KEY="$HOME/.ssh/id_rsa_old"
NEW_KEY="$HOME/.ssh/id_rsa_new"
SERVERS=("server1.example.com" "server2.example.com")

# 生成新密钥
generate_new_key() {
    echo "Generating new SSH key..."
    ssh-keygen -t ed25519 -f $NEW_KEY -N "" -C "new_key_$(date +%Y%m%d)"
    echo "New key generated"
}

# 分发新密钥
distribute_new_key() {
    echo "Distributing new key to servers..."
    
    for SERVER in "${SERVERS[@]}"; do
        echo "Adding new key to $SERVER..."
        ssh-copy-id -i ${NEW_KEY}.pub user@$SERVER
    done
}

# 测试新密钥
test_new_key() {
    echo "Testing new key..."
    
    for SERVER in "${SERVERS[@]}"; do
        if ssh -i $NEW_KEY -o PasswordAuthentication=no user@$SERVER "echo 'Success'" > /dev/null 2>&1; then
            echo "✓ New key works on $SERVER"
        else
            echo "✗ New key failed on $SERVER"
            return 1
        fi
    done
}

# 移除旧密钥
remove_old_key() {
    echo "Removing old key from servers..."
    
    for SERVER in "${SERVERS[@]}"; do
        echo "Removing old key from $SERVER..."
        ssh user@$SERVER "sed -i '/$(cat ${OLD_KEY}.pub | awk '{print $2}')/d' ~/.ssh/authorized_keys"
    done
}

# 主函数
main() {
    generate_new_key
    distribute_new_key
    
    if test_new_key; then
        read -p "Remove old key? (y/n) " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            remove_old_key
        fi
    else
        echo "Key rotation failed. Old key still active."
    fi
}

main

无敌#

企业级 SSH 管理系统#

#!/bin/bash
# 企业级 SSH 管理系统

CONFIG_FILE="/etc/ssh_manager/config.conf"
LOG_FILE="/var/log/ssh_manager.log"
KEY_DIR="/etc/ssh_manager/keys"
SERVER_DB="/etc/ssh_manager/servers.db"

mkdir -p $KEY_DIR

# 初始化服务器数据库
init_server_db() {
    if [ ! -f "$SERVER_DB" ]; then
        echo "hostname,user,port,key_file" > $SERVER_DB
        echo "server1.example.com,admin,22,id_rsa_server1" >> $SERVER_DB
        echo "server2.example.com,admin,2222,id_rsa_server2" >> $SERVER_DB
        echo "Server database initialized"
    fi
}

# 添加服务器
add_server() {
    local hostname=$1
    local user=$2
    local port=$3
    local key_name=$4
    
    echo "$hostname,$user,$port,$key_name" >> $SERVER_DB
    echo "Server $hostname added"
}

# 列出所有服务器
list_servers() {
    echo "=== Server List ==="
    tail -n +2 $SERVER_DB | while IFS=, read -r hostname user port key_file; do
        echo "Host: $hostname, User: $user, Port: $port, Key: $key_file"
    done
}

# 在所有服务器上执行命令
execute_on_all() {
    local command=$1
    
    tail -n +2 $SERVER_DB | while IFS=, read -r hostname user port key_file; do
        echo "=== $hostname ==="
        ssh -p $port -i $KEY_DIR/$key_file $user@$hostname "$command"
        echo ""
    done
}

# 批量部署
batch_deploy() {
    local source_dir=$1
    local target_dir=$2
    
    tail -n +2 $SERVER_DB | while IFS=, read -r hostname user port key_file; do
        echo "Deploying to $hostname..."
        scp -P $port -i $KEY_DIR/$key_file -r $source_dir/* $user@$hostname:$target_dir/
        ssh -p $port -i $KEY_DIR/$key_file $user@$hostname "cd $target_dir && ./deploy.sh"
    done
}

# 生成管理报告
generate_report() {
    local report_file="/tmp/ssh_report_$(date +%Y%m%d_%H%M%S).txt"
    
    echo "SSH Management Report - $(date)" > $report_file
    echo "============================" >> $report_file
    echo "" >> $report_file
    
    echo "Server Status:" >> $report_file
    tail -n +2 $SERVER_DB | while IFS=, read -r hostname user port key_file; do
        if ssh -p $port -i $KEY_DIR/$key_file -o ConnectTimeout=5 $user@$hostname "echo 'OK'" > /dev/null 2>&1; then
            echo "✓ $hostname - Online" >> $report_file
        else
            echo "✗ $hostname - Offline" >> $report_file
        fi
    done
    
    echo "" >> $report_file
    echo "Report saved to: $report_file"
}

# 主函数
main() {
    init_server_db
    
    case "$1" in
        add)
            add_server "$2" "$3" "$4" "$5"
            ;;
        list)
            list_servers
            ;;
        exec)
            execute_on_all "$2"
            ;;
        deploy)
            batch_deploy "$2" "$3"
            ;;
        report)
            generate_report
            ;;
        *)
            echo "Usage: $0 {add|list|exec|deploy|report}"
            exit 1
            ;;
    esac
}

main "$@"

SSH 审计和监控#

#!/bin/bash
# SSH 审计和监控系统

AUTH_LOG="/var/log/auth.log"
AUDIT_REPORT="/var/log/ssh_audit_$(date +%Y%m%d).log"

# 分析 SSH 登录尝试
analyze_login_attempts() {
    echo "=== SSH Login Analysis ===" >> $AUDIT_REPORT
    echo "Date: $(date)" >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    # 成功登录
    echo "Successful Logins:" >> $AUDIT_REPORT
    grep "Accepted" $AUTH_LOG | tail -20 >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    # 失败登录
    echo "Failed Login Attempts:" >> $AUDIT_REPORT
    grep "Failed" $AUTH_LOG | tail -20 >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    # 按用户统计
    echo "Logins by User:" >> $AUDIT_REPORT
    grep "Accepted" $AUTH_LOG | awk '{print $9}' | sort | uniq -c | sort -rn >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    # 按IP统计
    echo "Logins by IP:" >> $AUDIT_REPORT
    grep "Accepted" $AUTH_LOG | awk '{print $11}' | sort | uniq -c | sort -rn | head -10 >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    # 暴力破解检测
    echo "Potential Brute Force Attacks:" >> $AUDIT_REPORT
    grep "Failed" $AUTH_LOG | awk '{print $9}' | sort | uniq -c | sort -rn | head -10 >> $AUDIT_REPORT
}

# 监控活跃 SSH 会话
monitor_active_sessions() {
    echo "=== Active SSH Sessions ===" >> $AUDIT_REPORT
    who >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
    
    echo "SSH Processes:" >> $AUDIT_REPORT
    ps aux | grep sshd | grep -v grep >> $AUDIT_REPORT
    echo "" >> $AUDIT_REPORT
}

# 检查 SSH 配置安全性
check_ssh_config() {
    echo "=== SSH Configuration Security Check ===" >> $AUDIT_REPORT
    
    CONFIG_FILE="/etc/ssh/sshd_config"
    
    if [ -f "$CONFIG_FILE" ]; then
        echo "Checking $CONFIG_FILE..." >> $AUDIT_REPORT
        
        # 检查 root 登录
        if grep -q "^PermitRootLogin yes" $CONFIG_FILE; then
            echo "⚠ WARNING: Root login is enabled" >> $AUDIT_REPORT
        else
            echo "✓ Root login is disabled" >> $AUDIT_REPORT
        fi
        
        # 检查密码认证
        if grep -q "^PasswordAuthentication yes" $CONFIG_FILE; then
            echo "⚠ WARNING: Password authentication is enabled" >> $AUDIT_REPORT
        else
            echo "✓ Password authentication is disabled" >> $AUDIT_REPORT
        fi
        
        # 检查默认端口
        if grep -q "^Port 22" $CONFIG_FILE; then
            echo "⚠ WARNING: Default port 22 is in use" >> $AUDIT_REPORT
        else
            echo "✓ Non-default port is configured" >> $AUDIT_REPORT
        fi
    fi
}

# 生成安全报告
generate_security_report() {
    analyze_login_attempts
    monitor_active_sessions
    check_ssh_config
    
    echo "Security report generated: $AUDIT_REPORT"
}

# 实时监控
realtime_monitor() {
    echo "Starting real-time SSH monitoring..."
    tail -f $AUTH_LOG | grep --line-buffered -E "Accepted|Failed" | while read line; do
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $line"
    done
}

# 主函数
main() {
    case "$1" in
        audit)
            generate_security_report
            ;;
        monitor)
            realtime_monitor
            ;;
        *)
            echo "Usage: $0 {audit|monitor}"
            exit 1
            ;;
    esac
}

main "$@"

SSH 灾难恢复系统#

#!/bin/bash
# SSH 灾难恢复系统

BACKUP_DIR="/var/backups/ssh_recovery"
RECOVERY_LOG="/var/log/ssh_recovery.log"

# 备份 SSH 配置和密钥
backup_ssh_config() {
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_file="$BACKUP_DIR/ssh_backup_$timestamp.tar.gz"
    
    mkdir -p $BACKUP_DIR
    
    echo "Backing up SSH configuration..."
    tar -czf $backup_file \
        /etc/ssh/ \
        ~/.ssh/ \
        /var/log/auth.log
    
    echo "Backup created: $backup_file"
}

# 恢复 SSH 配置
restore_ssh_config() {
    local backup_file=$1
    
    if [ ! -f "$backup_file" ]; then
        echo "Backup file not found: $backup_file"
        return 1
    fi
    
    echo "Restoring SSH configuration from $backup_file..."
    
    # 停止 SSH 服务
    sudo systemctl stop sshd
    
    # 恢复文件
    sudo tar -xzf $backup_file -C /
    
    # 重启 SSH 服务
    sudo systemctl start sshd
    
    echo "SSH configuration restored"
}

# 紧急访问设置
setup_emergency_access() {
    echo "Setting up emergency access..."
    
    # 创建临时密钥
    local emergency_key="$HOME/.ssh/emergency_key"
    ssh-keygen -t ed25519 -f $emergency_key -N "" -C "emergency_$(date +%Y%m%d)"
    
    # 添加到 authorized_keys
    cat ${emergency_key}.pub >> ~/.ssh/authorized_keys
    
    echo "Emergency access configured"
    echo "Private key: $emergency_key"
    echo "Remember to remove this key after emergency!"
}

# 测试 SSH 连接
test_ssh_connection() {
    local server=$1
    
    echo "Testing SSH connection to $server..."
    
    if ssh -o ConnectTimeout=5 -o BatchMode=yes $server "echo 'Connection successful'" > /dev/null 2>&1; then
        echo "✓ SSH connection successful"
        return 0
    else
        echo "✗ SSH connection failed"
        return 1
    fi
}

# 自动恢复
auto_recovery() {
    local server=$1
    
    echo "Attempting automatic recovery for $server..."
    
    # 尝试使用备用密钥
    if [ -f "$HOME/.ssh/emergency_key" ]; then
        echo "Trying emergency key..."
        if ssh -i $HOME/.ssh/emergency_key $server "echo 'Success'" > /dev/null 2>&1; then
            echo "Emergency access successful"
            return 0
        fi
    fi
    
    # 尝试恢复配置
    local latest_backup=$(ls -t $BACKUP_DIR/ssh_backup_*.tar.gz | head -1)
    if [ -n "$latest_backup" ]; then
        echo "Restoring from latest backup..."
        restore_ssh_config $latest_backup
        test_ssh_connection $server
    fi
}

# 主函数
main() {
    case "$1" in
        backup)
            backup_ssh_config
            ;;
        restore)
            restore_ssh_config "$2"
            ;;
        emergency)
            setup_emergency_access
            ;;
        test)
            test_ssh_connection "$2"
            ;;
        recover)
            auto_recovery "$2"
            ;;
        *)
            echo "Usage: $0 {backup|restore|emergency|test|recover}"
            exit 1
            ;;
    esac
}

main "$@"

最佳实践#

  1. 使用密钥认证:优先使用 SSH 密钥而非密码认证
  2. 禁用 root 登录:禁止直接使用 root 账户登录
  3. 更改默认端口:使用非标准端口减少攻击面
  4. 定期更新密钥:定期轮换 SSH 密钥
  5. 使用配置文件:使用 ~/.ssh/config 简化连接管理
  6. 启用日志审计:记录和监控 SSH 登录活动
  7. 使用 fail2ban:防止暴力破解攻击
  8. 限制访问:使用防火墙和 AllowUsers 限制访问

注意事项#

  • SSH 私钥必须妥善保管,不要泄露给他人
  • 在生产环境操作前先在测试环境验证
  • 修改 SSH 配置后确保能正常登录再断开连接
  • 使用 -v 选项调试连接问题时注意保护敏感信息
  • 端口转发可能带来安全风险,谨慎使用
  • 定期检查 ~/.ssh/known_hosts 文件
  • 注意 SSH 协议版本,优先使用 SSH-2
  • 在自动化脚本中使用 SSH 时考虑错误处理