强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Dropbear SSH 完全指南 / 07 - SCP 与 SFTP

第七章:SCP 与 SFTP

7.1 文件传输方式概述

Dropbear 提供两种文件传输方式:

方式实现特点
SCPDropbear 内置简单高效,递归复制
SFTP需外部 sftp-server功能丰富,交互式操作

SCP vs SFTP 对比

对比维度SCPSFTP
实现方式Dropbear 自带需要外部 sftp-server
交互模式仅命令行交互式 + 命令行
断点续传
目录列表有限完整
文件操作仅复制删除、重命名、权限等
性能较高中等
适用场景脚本自动化交互式文件管理

7.2 Dropbear SCP

基本用法

# 上传文件到远程服务器
dbclient local-file.txt user@remote:/path/to/destination/

# 从远程服务器下载文件
dbclient user@remote:/path/to/file.txt ./local-dir/

# 递归复制目录
dbclient -r local-dir/ user@remote:/path/to/destination/

# 使用指定端口
dbclient -p 2222 file.txt user@remote:/tmp/

# 使用指定密钥
dbclient -i ~/.ssh/id_ed25519 file.txt user@remote:/tmp/

Dropbear SCP 的特点

Dropbear 的 scp 是基于 Dropbear 客户端库实现的独立工具:

# 如果编译时生成了 scp 符号链接
ls -la /usr/bin/scp
# lrwxrwxrwx 1 root root ... /usr/bin/scp -> dropbearmulti

# 或使用 dbclient 的 scp 模式
dbclient file.txt user@remote:/tmp/

SCP 命令行选项

选项说明
-r递归复制目录
-p保留文件权限和时间戳
-v详细输出(调试)
-P port指定端口(注意大小写)
-i keyfile指定私钥
-l limit限制带宽(Kbit/s)

SCP 实用示例

# 备份远程嵌入式设备的配置
dbclient -r root@device:/etc/config/ ./backup/config-$(date +%Y%m%d)/

# 部署固件到设备
dbclient firmware.bin root@device:/tmp/firmware.bin
dbclient root@device "sysupgrade /tmp/firmware.bin"

# 批量文件传输
for device in 192.168.1.{1..10}; do
    echo "传输到 $device..."
    dbclient config.txt admin@$device:/etc/config/app.conf
done

# 限制带宽(避免影响设备正常运行)
dbclient -l 1000 large-file.tar.gz root@device:/tmp/
# 限制为 1000 Kbit/s ≈ 125 KB/s

7.3 SFTP 子系统配置

Dropbear 本身不包含 SFTP 服务器实现,但可以通过配置子系统(subsystem)使用外部 SFTP 服务器。

安装 SFTP 服务器

# Debian/Ubuntu: 使用 OpenSSH 的 sftp-server
sudo apt-get install openssh-sftp-server

# 查找 sftp-server 路径
which sftp-server
# /usr/lib/openssh/sftp-server

# Alpine Linux
apk add openssh-sftp-server

# 或使用轻量级替代方案
apk add openssh-client  # 包含 sftp-server

配置 Dropbear 使用 SFTP

# 方法一:编译时配置(localoptions.h)
#define DROPBEAR_SFTP_SERVER 1
#define SFTPSERVER_PATH "/usr/lib/openssh/sftp-server"

# 方法二:通过启动脚本
# 使用 -c 选项强制执行 SFTP 子系统
dropbear -c /usr/lib/openssh/sftp-server
# 注意:-c 会强制所有连接使用该命令

更好的 SFTP 集成方案

由于 Dropbear 不支持 Subsystem 指令(像 OpenSSH 那样),需要通过其他方式集成 SFTP:

# 方案一:使用 shell 脚本包装
# /usr/local/bin/sftp-shell.sh
#!/bin/sh
case "$SSH_ORIGINAL_COMMAND" in
    sftp-server*)
        exec /usr/lib/openssh/sftp-server
        ;;
    *)
        exec /bin/sh -l
        ;;
esac
# authorized_keys 中指定 command
command="/usr/local/bin/sftp-shell.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...

SFTP 客户端连接

# 使用 OpenSSH 的 sftp 客户端
sftp -o "Port=22" user@remote

# 使用 Dropbear 的 dbclient 子系统模式
dbclient -s sftp-server user@remote

# 注意:dbclient 的 SFTP 支持需要编译时启用
# 如果不可用,使用 OpenSSH 的 sftp 客户端

7.4 chroot 环境配置

chroot(Change Root)可以将 SFTP 用户限制在指定目录中,增强安全性。

创建 chroot 环境

#!/bin/bash
# setup-sftp-chroot.sh - 创建 SFTP chroot 环境

CHROOT_DIR="/sftp/jail"
SFTP_USER="sftpuser"

# 创建 chroot 目录结构
mkdir -p "$CHROOT_DIR"/{bin,lib,lib64,dev,etc,home/$SFTP_USER/upload}
mkdir -p "$CHROOT_DIR"/usr/lib

# 复制必要的库文件
# ldd /usr/lib/openssh/sftp-server 来确定依赖
for lib in $(ldd /usr/lib/openssh/sftp-server | awk '{print $3}' | grep -v '^$'); do
    dir=$(dirname "$lib")
    mkdir -p "$CHROOT_DIR$dir"
    cp "$lib" "$CHROOT_DIR$lib"
done

# 复制 sftp-server
cp /usr/lib/openssh/sftp-server "$CHROOT_DIR/usr/lib/sftp-server"

# 创建设备文件
mknod "$CHROOT_DIR/dev/null" c 1 3 2>/dev/null || true
mknod "$CHROOT_DIR/dev/zero" c 1 5 2>/dev/null || true
mknod "$CHROOT_DIR/dev/random" c 1 8 2>/dev/null || true
mknod "$CHROOT_DIR/dev/urandom" c 1 9 2>/dev/null || true

# 设置权限
chmod 755 "$CHROOT_DIR"
chmod 755 "$CHROOT_DIR/home/$SFTP_USER"
chmod 755 "$CHROOT_DIR/home/$SFTP_USER/upload"
chown root:root "$CHROOT_DIR"
chown "$SFTP_USER":"$SFTP_USER" "$CHROOT_DIR/home/$SFTP_USER/upload"

echo "chroot 环境已创建"

Dropbear 的 chroot 支持

Dropbear 对 chroot 的支持不如 OpenSSH 完善。以下是可行的方案:

# 方案一:通过用户 shell 实现
# 将用户的 shell 设置为 chroot wrapper
usermod -s /usr/local/bin/chroot-sftp-shell "$SFTP_USER"
#!/bin/sh
# /usr/local/bin/chroot-sftp-shell - chroot SFTP shell

CHROOT_DIR="/sftp/jail"

# 检查是否是 SFTP 子系统请求
case "$SSH_ORIGINAL_COMMAND" in
    sftp-server*)
        exec chroot "$CHROOT_DIR" /usr/lib/sftp-server
        ;;
    *)
        echo "此账户仅允许 SFTP 访问"
        exit 1
        ;;
esac

验证 chroot 环境

# 测试 SFTP 连接
sftp sftpuser@device
sftp> ls
sftp> pwd
sftp> cd /etc  # 应该无法访问
sftp> put localfile.txt upload/

7.5 文件权限管理

SSH 文件权限要求

SSH 对文件权限有严格要求,不正确的权限会导致认证失败:

文件/目录必需权限说明
~/.ssh/700只有所有者可访问
~/.ssh/authorized_keys600只有所有者可读写
~/.ssh/id_*(私钥)600只有所有者可读
~/.ssh/id_*.pub(公钥)644公钥可公开
~/.ssh/known_hosts644可读
~(主目录)755不能对组/其他可写
# 修复权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 644 ~/.ssh/known_hosts

嵌入式设备的权限策略

# /etc/init.d/S40permissions - 启动时修复权限
#!/bin/sh
case "$1" in
    start)
        # 修复 SSH 相关权限
        chmod 700 /root/.ssh 2>/dev/null
        chmod 600 /root/.ssh/authorized_keys 2>/dev/null
        chmod 600 /etc/dropbear/*_host_key 2>/dev/null
        
        # 确保关键目录权限正确
        chmod 755 /tmp
        chmod 1777 /var/tmp 2>/dev/null
        ;;
esac

7.6 SCP/SFTP 安全配置

限制文件传输权限

通过 authorized_keys 选项限制 SFTP 用户的权限:

# 仅允许 SFTP,禁止 shell 访问
command="/usr/lib/openssh/sftp-server",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... sftp-user

# 限制只能访问特定目录
permitopen="127.0.0.1:0",command="/usr/local/bin/restricted-sftp.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... sftp-user

限制 SCP 命令

# 只允许下载,禁止上传(通过 command 包装)
# ~/.ssh/authorized_keys
command="/usr/local/bin/scp-download-only.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
#!/bin/sh
# /usr/local/bin/scp-download-only.sh - SCP 只读访问

case "$SSH_ORIGINAL_COMMAND" in
    scp\ -f\ *)
        # -f 表示从服务器发送文件(下载),允许
        exec $SSH_ORIGINAL_COMMAND
        ;;
    scp\ -t\ *)
        # -t 表示向服务器发送文件(上传),禁止
        echo "上传权限被拒绝"
        exit 1
        ;;
    *)
        echo "不支持的命令"
        exit 1
        ;;
esac

7.7 传输性能优化

SCP 性能调优

# 选择更快的加密算法
dbclient -c aes128-ctr file.tar.gz user@remote:/tmp/
# 或
dbclient -c chacha20-poly1305@openssh.com file.tar.gz user@remote:/tmp/

# 压缩传输(适用于文本文件)
# 注意:Dropbear 不支持 -C 选项
# 替代方案:传输前压缩
tar czf - large-directory/ | dbclient user@remote "tar xzf - -C /destination/"

# 并行传输(适用于多个文件)
ls *.tar.gz | xargs -P 4 -I {} dbclient {} user@remote:/tmp/

带宽限制

# 限制 SCP 带宽,避免影响设备正常运行
# Dropbear scp: -l 选项(Kbit/s)
dbclient -l 500 large-file.tar.gz user@device:/tmp/
# 500 Kbit/s ≈ 62.5 KB/s

大文件传输策略

#!/bin/sh
# safe-transfer.sh - 安全的大文件传输脚本

SOURCE="$1"
DEST="$2"
DEVICE="$3"
USER="${4:-root}"
MAX_RETRIES=3
CHECKSUM_CMD="md5sum"

if [ -z "$SOURCE" ] || [ -z "$DEST" ] || [ -z "$DEVICE" ]; then
    echo "用法: $0 <源文件> <目标路径> <设备IP> [用户名]"
    exit 1
fi

# 计算本地文件校验和
LOCAL_MD5=$($CHECKSUM_CMD "$SOURCE" | awk '{print $1}')
echo "本地文件: $SOURCE (MD5: $LOCAL_MD5)"

# 传输文件
attempt=0
while [ $attempt -lt $MAX_RETRIES ]; do
    attempt=$((attempt + 1))
    echo "传输尝试 $attempt/$MAX_RETRIES..."
    
    if dbclient "$SOURCE" "$USER@$DEVICE:$DEST"; then
        # 验证远程校验和
        REMOTE_MD5=$(dbclient "$USER@$DEVICE" "$CHECKSUM_CMD $DEST" | awk '{print $1}')
        
        if [ "$LOCAL_MD5" = "$REMOTE_MD5" ]; then
            echo "✅ 传输成功,校验和匹配: $LOCAL_MD5"
            exit 0
        else
            echo "❌ 校验和不匹配 (本地: $LOCAL_MD5, 远程: $REMOTE_MD5)"
        fi
    else
        echo "❌ 传输失败"
    fi
done

echo "❌ 超过最大重试次数"
exit 1

7.8 业务场景:嵌入式设备固件更新

#!/bin/sh
# firmware-update.sh - 通过 SCP 更新嵌入式设备固件

DEVICE_IP="$1"
FIRMWARE="$2"
BACKUP_DIR="/tmp/device-backup/$(date +%Y%m%d_%H%M%S)"
USER="root"

if [ -z "$DEVICE_IP" ] || [ -z "$FIRMWARE" ]; then
    echo "用法: $0 <设备IP> <固件文件>"
    exit 1
fi

echo "=== 嵌入式设备固件更新 ==="
echo "设备: $DEVICE_IP"
echo "固件: $FIRMWARE"
echo ""

# 1. 备份当前配置
echo "[1/4] 备份当前配置..."
mkdir -p "$BACKUP_DIR"
dbclient -r "$USER@$DEVICE_IP:/etc/config/" "$BACKUP_DIR/"
echo "  配置已备份到 $BACKUP_DIR"

# 2. 传输固件
echo "[2/4] 传输固件..."
FIRMWARE_SIZE=$(du -h "$FIRMWARE" | awk '{print $1}')
echo "  固件大小: $FIRMWARE_SIZE"
dbclient "$FIRMWARE" "$USER@$DEVICE_IP:/tmp/firmware.bin"

# 3. 验证传输
echo "[3/4] 验证传输完整性..."
LOCAL_MD5=$(md5sum "$FIRMWARE" | awk '{print $1}')
REMOTE_MD5=$(dbclient "$USER@$DEVICE_IP" "md5sum /tmp/firmware.bin" | awk '{print $1}')

if [ "$LOCAL_MD5" != "$REMOTE_MD5" ]; then
    echo "❌ 校验和不匹配,中止更新"
    exit 1
fi
echo "  校验和匹配: $LOCAL_MD5"

# 4. 执行更新
echo "[4/4] 执行固件更新..."
read -p "确认更新? (yes/no): " confirm
if [ "$confirm" = "yes" ]; then
    dbclient "$USER@$DEVICE_IP" "sysupgrade /tmp/firmware.bin"
    echo "固件更新已启动,设备将重启..."
else
    echo "更新已取消"
fi

7.9 本章小结

要点说明
SCPDropbear 内置,简单高效
SFTP需要外部 sftp-server,功能丰富
chroot限制用户在指定目录
权限SSH 对文件权限有严格要求
安全通过 authorized_keys 选项限制传输权限
性能选择合适加密算法,限制带宽避免影响设备

扩展阅读


上一章:端口转发 | 下一章:dropbearkey 工具