SSH 的 ForceCommand 功能可以在用户连接时强制执行预设的命令,忽略客户端尝试自行执行的命令。这在需要限制用户只能进行特定操作(如只允许 SFTP、只允许 git 推送)的场景下非常有用。

ForceCommand 工作原理

ForceCommand 是 sshd_config 中的一个指令,它会:

  1. 强制执行预设的命令,忽略用户客户端传递的任何命令
  2. 同时忽略 ~/.ssh/rc 脚本
  3. 通过用户的登录 Shell 执行预设命令(shell -c "command"
  4. 原始命令仍可通过环境变量 SSH_ORIGINAL_COMMAND 获取(仅读取,不能执行)
1
2
3
# 查看当前默认配置
sshd -T | grep forcecommand
# 输出:forcecommand none

基础配置示例

1. 禁止执行任何命令,只允许端口转发

1
2
# /etc/ssh/sshd_config
ForceCommand /bin/true

这样配置后,用户无法执行任何远程命令,但可以正常使用端口转发(-L/-R)。

2. 记录用户尝试执行的命令

1
ForceCommand /usr/local/bin/audit_command.sh

审计脚本示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# /usr/local/bin/audit_command.sh
LOGFILE="/var/log/ssh_command_audit.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
USER=$(whoami)
ORIGINAL_CMD="${SSH_ORIGINAL_COMMAND:-none}"

echo "[$TIMESTAMP] User: $USER, Command: $ORIGINAL_CMD" >> "$LOGFILE"

# 允许某些安全命令
case "$ORIGINAL_CMD" in
    git-receive-pack|"git receive-pack"|git-upload-pack|"git upload-pack")
        exec git-shell -c "$ORIGINAL_CMD"
        ;;
    *)
        echo "Only port forwarding is allowed."
        ;;
esac

Match 块结合使用

ForceCommand 最强大的用法是结合 Match 块,针对特定用户、组或 IP 地址限制命令。

示例 1:只允许 SFTP,禁止 SSH 登录

1
2
3
4
5
6
7
8
# /etc/ssh/sshd_config

# SFTP 用户组配置
Match Group sftponly
    ForceCommand internal-sftp
    ChrootDirectory /home/%u
    AllowTcpForwarding no
    X11Forwarding no

解释:

  • Match Group sftponly:仅对 sftponly 组的成员生效
  • ForceCommand internal-sftp:强制使用内建的 SFTP 服务
  • ChrootDirectory /home/%u:将用户限制在自己的主目录
  • 禁止端口转发和 X11 转发

创建用户步骤:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 创建专用用户组
groupadd sftponly

# 创建用户并加入组
useradd -m -G sftponly sftpuser
# 设置密码
passwd sftpuser

# 确保目录权限正确
chown root:root /home/sftpuser
chmod 755 /home/sftpuser

# 测试配置
sshd -t

示例 2:只允许 Git 操作

1
2
3
4
5
6
7
# /etc/ssh/sshd_config

Match Group gitusers
    ForceCommand /usr/bin/git-shell-wrapper
    AllowTcpForwarding no
    X11Forwarding no
    PermitTTY no

Git Shell 包装脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# /usr/bin/git-shell-wrapper

# 允许的 git 命令
ALLOWED_CMDS="git-receive-pack git-upload-pack git upload-pack git-receive-pack"

if [ -z "$SSH_ORIGINAL_COMMAND" ]; then
    echo "Git only. No interactive shell."
    exit 1
fi

# 验证命令是否在白名单
CMD=$(echo "$SSH_ORIGINAL_COMMAND" | awk '{print $1}')
if echo "$ALLOWED_CMDS" | grep -qw "$CMD"; then
    exec $SSH_ORIGINAL_COMMAND
else
    echo "Command not allowed: $CMD"
    exit 1
fi

示例 3:只允许特定 IP 段执行备份命令

1
2
3
4
5
6
# /etc/ssh/sshd_config

Match Address 10.0.0.0/24
    ForceCommand /usr/local/bin/backup.sh
    PermitTTY no
    AllowTcpForwarding no

环境变量可用性

在 ForceCommand 执行的脚本中,可以访问以下 SSH 相关环境变量:

环境变量 说明
SSH_ORIGINAL_COMMAND 用户尝试执行的原始命令
SSH_CONNECTION 客户端连接信息(IP 端口 服务端IP 端口)
SSH_CLIENT 客户端信息(IP 端口 服务端端口)
USER 当前用户名
HOME 用户主目录
1
2
3
4
# 在 ForceCommand 脚本中查看可用环境变量
#!/bin/bash
env | grep SSH > /tmp/ssh_env.log
echo "Original command: $SSH_ORIGINAL_COMMAND"

安全注意事项

1. 环境变量可被客户端控制

SSH_ORIGINAL_COMMAND 虽然提供了原始命令,但它是客户端可控的(用户可以尝试传递任意命令)。因此不要依赖它做安全决策,而是用来审计允许特定白名单命令

1
2
3
4
5
# 不推荐:依赖 SSH_ORIGINAL_COMMAND 做安全判断
# 因为客户端可能伪造
if [ "$SSH_ORIGINAL_COMMAND" = "safe_command" ]; then
    exec /path/to/safe_command
fi

2. Match 块顺序

sshd 配置中 Match 块是从上到下匹配的,一旦匹配成功,后续的 Match 块不再生效。注意把更严格的规则放在前面。

3. 防止绕过

确保用户无法通过以下方式绕过限制:

  • 使用 -t 强制请求 PTY(配合 PermitTTY no
  • 使用端口转发(配合 AllowTcpForwarding no
  • 使用 Agent Forwarding(配合 AllowAgentForwarding no

完整的安全配置示例:

1
2
3
4
5
6
Match User backupuser
    ForceCommand /usr/local/bin/backup.sh
    PermitTTY no
    AllowTcpForwarding no
    AllowAgentForwarding no
    X11Forwarding no

调试技巧

1
2
3
4
5
6
7
8
9
# 测试配置语法
sshd -t

# 查看实时日志
journalctl -u sshd -f

# 或在另一终端查看
tail -f /var/log/auth.log  # Debian/Ubuntu
tail -f /var/log/secure    # RHEL/CentOS

配置生效后重启 SSH 服务:

1
2
3
4
5
6
7
8
# Debian/Ubuntu
systemctl restart sshd

# RHEL/CentOS
systemctl restart sshd

# 或仅重新加载配置(不断开现有连接)
systemctl reload sshd

总结

SSH ForceCommand 是一个强大的访问控制功能,结合 Match 块可以实现细粒度的用户权限控制:

  • SFTP 专用用户:限制只能使用 SFTP,不能 SSH 登录
  • Git 专用用户:仅允许 git push/pull 操作
  • 备份任务用户:只允许执行特定备份脚本
  • 审计场景:记录所有尝试执行的命令

合理使用 ForceCommand 可以显著提升 SSH 服务器的安全性,将用户权限限制在最小必要范围内。


参考来源