适用场景:
- Windows Server 2019 无外网环境
- 替代 freeSSHd,解决 Linux SFTP 中文文件名乱码
- 仅允许 SFTP 传输,禁止 SSH 交互登录
一、总体说明
- OpenSSH 不管理账号 ,直接使用 Windows 本地用户 / 域用户
- SFTP 文件名 原生 UTF-8,Linux / FileZilla 均不会乱码
- 通过
ForceCommand internal-sftp实现 SFTP-only
二、离线安装 OpenSSH(官方推荐方式)
1. 准备安装包(有网机器)
下载微软官方 OpenSSH:
- 地址:https://github.com/PowerShell/Win32-OpenSSH/releases
- 文件:
OpenSSH-Win64.zip
拷贝到目标服务器,例如:
text
C:\install\OpenSSH-Win64.zip
2. 解压到固定目录
text
C:\Program Files\OpenSSH
目录中应包含:
text
sshd.exe
ssh.exe
sftp-server.exe
install-sshd.ps1
3. 安装 OpenSSH 服务
以 管理员 PowerShell 执行:
powershell
cd "C:\Program Files\OpenSSH"
powershell -ExecutionPolicy Bypass -File install-sshd.ps1
成功后会生成两个服务:
- OpenSSH SSH Server (
sshd) - OpenSSH Authentication Agent (
ssh-agent)
4. 启动服务并设置开机自启
powershell
Start-Service sshd
Set-Service sshd -StartupType Automatic
Start-Service ssh-agent
验证:
powershell
Get-Service sshd
5. 放行防火墙端口(22)
5.1 入站规则(必需)
允许外部客户端连接到服务器:
powershell
New-NetFirewallRule \
-Name OpenSSH-Server-In-TCP \
-DisplayName "OpenSSH Server (sshd)" \
-Enabled True \
-Direction Inbound \
-Protocol TCP \
-Action Allow \
-LocalPort 22
5.2 出站规则(可选,严格安全环境推荐)
允许服务器响应客户端连接(通常 Windows 防火墙默认允许出站,但明确配置更安全):
powershell
New-NetFirewallRule \
-Name OpenSSH-Server-Out-TCP \
-DisplayName "OpenSSH Server (sshd) Outbound" \
-Enabled True \
-Direction Outbound \
-Protocol TCP \
-Action Allow \
-LocalPort 22
说明:
- 入站规则:必需,允许客户端连接到服务器
- 出站规则:可选,在严格安全策略环境中推荐配置
- Windows 防火墙默认允许出站连接,但明确配置可提高可审计性
三、创建 SFTP 专用账号与目录
1. 创建本地用户(示例)
powershell
net user sftpuser StrongP@ssw0rd! /add
说明:
- 用户名、密码可按公司规范调整
- 也可使用域账号:
DOMAIN\\username
2. 创建 SFTP 目录结构
示例目录:
text
D:\sftpdata\sftpuser
powershell
mkdir D:\sftpdata\sftpuser
3. 设置 NTFS 权限(非常关键)
3.1 Chroot 根目录权限(只允许管理员)
powershell
icacls D:\sftpdata /inheritance:r
icacls D:\sftpdata /grant Administrators:(OI)(CI)F
icacls D:\sftpdata /grant SYSTEM:(OI)(CI)F
3.2 用户子目录权限
powershell
icacls D:\sftpdata\sftpuser /inheritance:r
icacls D:\sftpdata\sftpuser /grant sftpuser:(OI)(CI)F
⚠ 注意:
- Chroot 根目录不能给用户写权限
- 否则 SFTP 会直接断开连接
四、配置仅允许 SFTP(禁用 SSH 登录)
1. 编辑 sshd_config
文件路径:
text
C:\ProgramData\ssh\sshd_config
2. 确认 SFTP 子系统
conf
Subsystem sftp sftp-server.exe
3. 添加 SFTP-only 配置(文件末尾)
conf
Match User sftpuser
ChrootDirectory D:\sftpdata
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
配置说明:
| 配置项 | 作用 |
|---|---|
| ForceCommand internal-sftp | 禁止 SSH Shell |
| ChrootDirectory | 限制用户根目录 |
| AllowTcpForwarding no | 禁用端口转发 |
| X11Forwarding no | 禁用图形转发 |
4. 重启 OpenSSH 服务
powershell
Restart-Service sshd
五、验证测试
1. 测试 SSH(应被禁止)
bash
ssh sftpuser@windows_ip
期望结果:
text
This service allows sftp connections only.
2. 测试 SFTP(应成功)
bash
sftp sftpuser@windows_ip
put 测试文件.txt
验证点:
- 中文文件名正常
- 只能访问指定目录
- 无法执行任何命令
六、常见问题排查
1. SFTP 连接即断开
- 检查 Chroot 根目录是否给了用户写权限
- 确认使用的是
internal-sftp
2. 修改配置未生效
- 忘记重启
sshd服务
3. 中文仍乱码
- 确认已弃用 freeSSHd
- 使用的是官方 OpenSSH
七、最终结论
- ✔ 账号 / 密码 = Windows 用户体系
- ✔ OpenSSH 原生支持 UTF-8
- ✔ SFTP-only 是企业级标准方案
- ✔ 适合生产、内网、审计环境
可扩展:
- 多用户批量配置
- 仅密钥登录、禁用密码
- 操作日志与审计
- freeSSHd 平滑迁移
很好,这两个点(限制 IP + 修改端口 )确实是生产环境必配项 👍
我刚才已经尝试直接把内容补进你右侧那份 Markdown 文档里 ,但由于编辑器在匹配标题时出现了格式冲突,本次更新没有成功写入 Canvas(我没有再强行重试,避免破坏原文档结构)。
下面我把你需要新增的完整 Markdown 内容 整理好,你可以直接整体复制,按推荐位置插入到文档中即可(我也会告诉你插到哪)。
📌 建议插入位置
👉 放在「仅允许 SFTP(禁用 SSH 登录)」之后、原「常见问题排查」之前
✅ 新增章节:IP 访问限制与端口修改(Markdown)
markdown
## 六、IP 访问限制与端口修改(安全加固)
### 1. 修改 OpenSSH 服务端口
#### 1.1 修改 sshd_config
编辑文件:
```text
C:\ProgramData\ssh\sshd_config
找到或新增端口配置(示例将端口改为 60022):
conf
Port 60022
说明:
- 可配置多个
Port,但生产环境建议只保留一个- 修改后需重启
sshd服务
1.2 放行新端口防火墙规则
入站规则(必需):
powershell
New-NetFirewallRule `
-Name OpenSSH-Server-In-TCP-60022 `
-DisplayName "OpenSSH Server (sshd) 60022" `
-Enabled True `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-Action Allow
出站规则(可选,严格安全环境推荐):
powershell
New-NetFirewallRule `
-Name OpenSSH-Server-Out-TCP-60022 `
-DisplayName "OpenSSH Server (sshd) 60022 Outbound" `
-Enabled True `
-Direction Outbound `
-Protocol TCP `
-LocalPort 60022 `
-Action Allow
如需关闭默认 22 端口:
powershell
# 关闭入站规则
Get-NetFirewallRule -DisplayName "OpenSSH Server (sshd)" | Disable-NetFirewallRule
# 关闭出站规则(如果已配置)
Get-NetFirewallRule -DisplayName "OpenSSH Server (sshd) Outbound" | Disable-NetFirewallRule
1.3 重启服务
powershell
Restart-Service sshd
2. 限制允许访问的客户端 IP(推荐方式)
2.1 使用 Windows 防火墙限制 IP(最稳妥)
示例:只允许 192.168.10.100 访问 SFTP 端口 60022:
入站规则(允许指定 IP):
powershell
New-NetFirewallRule `
-Name OpenSSH-Allow-192.168.10.100 `
-DisplayName "OpenSSH Allow 192.168.10.100" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress 192.168.10.100 `
-Action Allow
出站规则(响应指定 IP,可选):
powershell
New-NetFirewallRule `
-Name OpenSSH-Allow-192.168.10.100-Out `
-DisplayName "OpenSSH Allow 192.168.10.100 Outbound" `
-Direction Outbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress 192.168.10.100 `
-Action Allow
阻断其他来源(入站):
powershell
New-NetFirewallRule `
-Name OpenSSH-Deny-Other-IP `
-DisplayName "OpenSSH Deny Other IP" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress Any `
-Action Block
⚠ 注意
- 防火墙规则优先级:允许 > 阻断
- 入站规则:控制谁可以连接到服务器
- 出站规则:控制服务器可以响应哪些客户端(可选,但推荐在严格环境中配置)
- 推荐用于生产 / 内网隔离 / 审计场景
2.2 限制 IP 后无法连接的故障排查
问题现象:
- 通用规则(无 RemoteAddress)可以连接
- 限制 IP 的规则(有 RemoteAddress)无法连接
原因分析:
- 存在多个规则冲突(通用规则和限制规则同时存在)
- 规则优先级问题
- 之前的通用规则未删除或禁用
解决方法:
步骤 1:查看当前所有相关规则
powershell
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*OpenSSH*60022*" -or $_.LocalPort -eq 60022 } | Format-Table DisplayName, Direction, Action, Enabled, RemoteAddress
步骤 2:删除或禁用之前的通用规则
如果之前创建了通用规则(无 RemoteAddress),需要先删除或禁用:
powershell
# 方法 1:禁用通用规则(推荐,保留规则记录)
Get-NetFirewallRule -DisplayName "Allow OpenSSH Port 60022" | Disable-NetFirewallRule
# 方法 2:删除通用规则
Remove-NetFirewallRule -DisplayName "Allow OpenSSH Port 60022" -ErrorAction SilentlyContinue
步骤 3:确保限制 IP 的规则已正确创建
powershell
# 创建限制 IP 的规则(确保 Enabled=True)
New-NetFirewallRule `
-Name OpenSSH-Allow-192.168.34.18 `
-DisplayName "OpenSSH Allow 192.168.34.18" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress 192.168.34.18 `
-Action Allow `
-Enabled True
步骤 4:验证规则优先级
Windows 防火墙规则优先级(从高到低):
- 明确允许规则(Allow + 特定 RemoteAddress)
- 明确阻止规则(Block + 特定 RemoteAddress)
- 通用允许规则(Allow + Any)
- 通用阻止规则(Block + Any)
步骤 5:测试连接
bash
# 从允许的 IP 测试
telnet 192.168.31.107 60022
# 或使用 SFTP
sftp -P 60022 test@192.168.31.107
完整配置示例(推荐顺序):
powershell
# 1. 先删除或禁用所有旧规则
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*OpenSSH*60022*" } | Remove-NetFirewallRule -ErrorAction SilentlyContinue
# 2. 创建限制 IP 的允许规则
New-NetFirewallRule `
-Name OpenSSH-Allow-192.168.34.18 `
-DisplayName "OpenSSH Allow 192.168.34.18" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress 192.168.34.18 `
-Action Allow `
-Enabled True
# 3. 创建阻止其他 IP 的规则(可选,更严格)
New-NetFirewallRule `
-Name OpenSSH-Deny-Other-IP `
-DisplayName "OpenSSH Deny Other IP" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 60022 `
-RemoteAddress Any `
-Action Block `
-Enabled True
3. 通过 sshd_config 限制 IP(可选补充)
在 sshd_config 中添加:
conf
AllowUsers sftpuser@192.168.10.100
限制整个网段示例:
conf
AllowUsers sftpuser@192.168.10.*
修改后重启服务:
powershell
Restart-Service sshd
建议:
- IP 控制优先用防火墙
sshd_config作为逻辑层补充
八、验证测试(端口 & IP)
1. 指定端口连接
bash
sftp -P 60022 sftpuser@windows_ip
2. 非允许 IP 测试
-
连接应被拒绝或超时
✅ 给你的最终建议(运维视角)
生产推荐组合:
- ✔ 修改默认 22 → 自定义端口
- ✔ Windows 防火墙限制来源 IP
- ✔
ForceCommand internal-sftp - ✔ Chroot + 最小权限目录
👉 这套配置已经是企业级 SFTP 基线
如果你愿意,下一步我还能帮你把整份文档再升级一版,例如:
- 🔐 禁用密码,仅允许密钥登录
- 📜 SFTP 上传 / 下载审计日志
- 🧩 多用户统一模板(复制即用)
- 🔄 freeSSHd → OpenSSH 迁移 checklist