SSH 的 ForceCommand 功能可以在用户连接时强制执行预设的命令,忽略客户端尝试自行执行的命令。这在需要限制用户只能进行特定操作(如只允许 SFTP、只允许 git 推送)的场景下非常有用。
ForceCommand 工作原理#
ForceCommand 是 sshd_config 中的一个指令,它会:
- 强制执行预设的命令,忽略用户客户端传递的任何命令
- 同时忽略
~/.ssh/rc 脚本
- 通过用户的登录 Shell 执行预设命令(
shell -c "command")
- 原始命令仍可通过环境变量
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 服务器的安全性,将用户权限限制在最小必要范围内。
参考来源