香港初创企业Web服务器上线前,如何按端口、认证与最小权限完成安全加固?
在一场典型的上线窗口中,工程师准备把一台香港节点的 Ubuntu Web 服务器交给业务团队使用。检查监听端口时,除了预期的 80 和 443,还发现了 22、3000、3306 和 6379;应用可以通过 root 启动,后台管理页面也直接暴露在公网。此时真正需要解决的不是“服务器能不能访问”,而是上线后公网究竟能触达到什么、谁可以登录、进程能够修改什么,以及异常操作能否被追溯。

判断香港初创企业怎么选 Web 服务器时,安全能力应当和 CPU、内存、带宽一起纳入采购条件:至少要有可用的控制台或恢复入口、可配置的入站访问控制、可回滚的快照或备份机制、明确的系统补丁维护方式,以及能够查看系统和登录日志的权限。服务器准备好这些前置条件后,再按照“端口清单、认证、最小权限、补丁、防火墙、审计、外部验证”的顺序加固,通常比上线后逐个封堵问题更稳妥。
先把服务器选型变成安全边界
上线前应先记录以下信息,并由业务负责人、开发和运维共同确认:
- 服务器操作系统及版本,是否仍在维护周期内。
- 公网 IPv4、IPv6 是否同时启用,域名是否会解析到两个地址族。
- 供应侧是否提供安全组、主机防火墙、快照和控制台恢复入口。
- 管理员的固定出口地址或允许管理的网络范围。
- Web 服务、应用服务、数据库和缓存分别监听哪些地址与端口。
- 出现 SSH 配置错误或防火墙误封时,是否可以通过控制台恢复。
- 补丁、日志、备份和恢复操作由谁负责,是否有明确的维护窗口。
如果一台服务器只有公网地址,没有控制台、快照或入站访问控制,那么即使配置成本较低,也不适合作为初创企业的首台生产 Web 服务器。原因很直接:一次错误的 SSH 配置或防火墙规则,就可能让管理员失去远程入口;没有恢复通道时,排障成本会超过节省的服务器费用。
在开始修改前,保留一个当前可用的管理会话,并完成应用配置、数据库和上传文件的备份。涉及防火墙、SSH 和权限变更时,最好准备一个维护窗口,同时确认控制台可以登录。不要在唯一的 SSH 会话中直接关闭密码登录或启用默认拒绝策略。
第一关:建立端口清单,而不是凭经验放行
服务器上的监听端口应当与业务架构一一对应。常见 Web 业务可以参考下面的边界:
| 端口 | 典型用途 | 公网建议 | 判断依据 |
|---|---|---|---|
| 80/TCP | HTTP 跳转、部分证书校验 | 按需开放 | 只提供跳转或必要的站点响应 |
| 443/TCP | HTTPS Web 服务 | 对公网开放 | 由 Nginx 或其他 Web 服务接收请求 |
| 22/TCP | SSH 管理 | 仅允许管理来源 | 不应对整个公网开放 |
| 3000、8000 等 | 应用进程 | 不对公网开放 | 通常只监听 127.0.0.1 或内网地址 |
| 3306、5432 | 数据库 | 不对公网开放 | 由应用主机或内网服务访问 |
| 6379、11211 | 缓存服务 | 不对公网开放 | 应限制在应用通信范围内 |
| 其他面板或调试端口 | 管理、测试 | 默认关闭 | 需要时临时放行并记录来源 |
先在 Ubuntu 22.04 或 24.04 服务器上查看监听情况:
sudo ss -lntup
sudo ss -lntup4
sudo ss -lntup6
重点看监听地址。127.0.0.1:8000 表示应用只接受本机连接,Nginx 可以通过本机转发;0.0.0.0:8000 表示所有 IPv4 网卡都可能接收连接;[::]:8000 则需要进一步确认 IPv6 是否也对外开放。数据库或缓存服务如果出现在 0.0.0.0 和 [::] 上,应优先修改服务监听地址,再依靠防火墙补充限制。
端口属于哪个进程,也要核对清楚:
sudo ss -lntup | grep -E ':(22|80|443|3000|3306|5432|6379)\b'
sudo systemctl --type=service --state=running
不要因为某个端口“暂时没有流量”就直接关闭服务。先确认它是否由应用依赖、是否属于旧版本部署,以及关闭后如何恢复。对于确认不再使用的服务,应先停止、设置为不自动启动,再观察业务状态:
sudo systemctl stop example-service
sudo systemctl disable example-service
上面的服务名只是示例,不能直接替换成任意名称执行。停用服务前应记录当前状态,并确认可以通过 sudo systemctl enable --now example-service 回滚。涉及生产数据库、队列或应用主进程时,应在维护窗口中操作。
从外部网络验证公网实际暴露面时,可以使用一台已获授权的测试主机:
nmap -Pn -sS -p 22,80,443,3000,3306,5432,6379 SERVER_IP
只扫描企业自己拥有或明确获准测试的地址。结果应与端口清单一致:80、443 按设计开放,22 只对管理来源开放,应用、数据库和缓存端口不应从公网访问。扫描只能说明端口是否可连接,不能证明应用认证安全,也不能证明端口背后的服务没有漏洞。
第二关:先稳住管理认证,再调整 SSH
管理账户是上线前最容易被忽略的入口。建议为每名管理员建立独立账号,使用 SSH 密钥登录,不共用 root,并在服务商控制台、服务器管理后台和 Web 管理后台启用多因素认证。
以下示例适用于 Ubuntu 22.04/24.04。执行前先确认当前管理员已经拥有可用的密钥登录方式,并保留控制台入口。
在本地管理电脑生成密钥:
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/startup_web_ed25519
通过控制台或现有管理会话,将公钥放入目标用户的 authorized_keys。如果系统已允许密码登录,也可以使用:
ssh-copy-id -i ~/.ssh/startup_web_ed25519.pub deploy@SERVER_IP
在服务器上确认权限:
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
如果用户或路径与示例不同,应先用 id deploy 和 getent passwd deploy 核实,不要盲目修改其他账号的目录。
在确认第二个终端可以使用密钥登录后,再创建 SSH 配置片段:
sudo install -m 600 /dev/null /etc/ssh/sshd_config.d/10-hardening.conf
sudo nano /etc/ssh/sshd_config.d/10-hardening.conf
内容可以从以下设置开始:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowGroups webops
AllowGroups webops 的前提是所有需要 SSH 登录的管理员都已加入该组,例如:
sudo groupadd --system webops
sudo usermod -aG webops deploy
如果 webops 已存在,groupadd 会报错,此时不需要重复创建。修改完成后,先检查配置语法,再重新加载 SSH:
sudo sshd -t
sudo systemctl reload ssh
sshd -t 没有输出通常表示语法检查通过;如果出现错误,不要执行 reload,先修正配置。重新加载后不要关闭旧会话,另开终端测试:
ssh -i ~/.ssh/startup_web_ed25519 deploy@SERVER_IP
验证失败时,优先通过控制台登录,恢复配置片段或临时恢复原认证方式,再执行:
sudo sshd -t
sudo systemctl reload ssh
如果企业使用基于 PAM 的额外认证,不能直接照搬 KbdInteractiveAuthentication no,应先确认认证链路,否则可能把管理员锁在服务器外。密码登录关闭后,也要检查旧账号的 authorized_keys,删除不再使用的公钥,并确认密钥文件没有被复制到其他账户。
Web 管理后台也要执行同样的原则:
- 每名管理员使用独立账号,不使用多人共用的超级管理员账号。
- 管理页面启用多因素认证和登录失败限制。
- 后台路径不能代替认证,隐藏路径不能代替访问控制。
- 如果业务允许,将后台限制在固定管理来源或专用管理入口。
- 测试账号、临时账号和离职人员账号上线前必须清理。
第三关:让进程和文件只拥有必要权限
最小权限不是简单执行一次 chmod 777。它要求不同角色只拥有完成工作所需的权限:

- SSH 管理员可以维护系统,但不等于应用进程可以获得管理员权限。
- Nginx 负责读取 Web 文件,不应拥有修改整个站点目录的权限。
- 应用服务使用独立系统账号运行,不使用
root启动。 - 上传目录、缓存目录和临时目录与代码目录分开。
- 部署账号只拥有发布和重启应用所需的权限。
创建应用服务账号时,可以使用无登录权限的系统用户:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvc
如果应用账号已经存在,应先执行 id appsvc,不要重复创建。应用的 systemd 服务应设置 User=appsvc 和 Group=appsvc,而不是在启动脚本中使用 sudo 或直接以 root 运行。
对于纯静态站点,确认已经完成备份后,可以把代码目录设置为 root 拥有、Web 服务只读。下面的命令只适合静态文件目录,不能直接用于需要执行脚本或写入文件的动态应用:
sudo getfacl -R /srv/www/app > /root/app-acl-before.txt
sudo chown -R root:root /srv/www/app
sudo find /srv/www/app -type d -exec chmod 755 {} +
sudo find /srv/www/app -type f -exec chmod 644 {} +
这些命令会影响 /srv/www/app 下的所有文件和目录,可能导致应用无法写入上传文件、缓存或运行时文件。执行前应确认路径准确,并记录原权限。需要回滚时,可在确认备份文件完整后使用:
sudo setfacl --restore=/root/app-acl-before.txt
动态应用应将可写目录单独划分,例如只允许应用账号访问上传目录:
sudo install -d -o appsvc -g appsvc -m 750 /srv/app/shared/uploads
如果 Nginx 需要直接读取上传文件,还需根据实际目录组权限设计访问方式,不要为了快速解决 403 而把整个目录改成 777。
部署权限也应当收窄。一个只负责发布和重启指定服务的账号,不应拥有任意 root 命令权限。可以通过 visudo 创建精确规则:
sudo visudo -f /etc/sudoers.d/deploy-myapp
文件内容示例:
deploy ALL=(root) /usr/bin/systemctl restart myapp.service, /usr/bin/systemctl status myapp.service
保存后检查:
sudo chmod 440 /etc/sudoers.d/deploy-myapp
sudo visudo -cf /etc/sudoers.d/deploy-myapp
服务单元、启动脚本和部署目录必须由 root 或受控账号拥有,否则“只能重启指定服务”仍可能被利用来执行被篡改的程序。权限文件修改前应备份,回滚时删除对应的 /etc/sudoers.d/deploy-myapp 文件并再次使用 visudo -c 检查。
第四关:补丁和防火墙要配合使用
补丁不能被防火墙替代。公网 Web 服务器至少应优先关注内核、OpenSSH、Nginx、运行时、依赖库和应用自身的安全更新。
在 Ubuntu 22.04/24.04 上,维护窗口内可以先查看待更新内容:
sudo apt update
apt list --upgradable
确认备份或快照可恢复后,再执行常规升级:
sudo apt-get upgrade
升级过程可能重启服务,也可能要求重启服务器。完成后检查:
uname -r
sudo systemctl --failed
sudo systemctl status nginx --no-pager
sudo journalctl -p warning..alert -b --no-pager
如果内核、关键系统库或 OpenSSH 已更新,应安排重启并重新验证 Web、SSH 和应用。不要在没有备份的情况下执行可能删除依赖的清理命令,也不要在业务高峰期直接重启。补丁后若应用异常,应先查看本次升级涉及的包和服务日志,必要时通过快照或已验证的包版本回滚。
防火墙规则应与端口清单一致。Ubuntu 使用 UFW 时,先备份现有规则:
sudo cp -a /etc/ufw "/root/ufw-backup-$(date +%F-%H%M)"
sudo ufw status numbered
下面的规则仅供示例,203.0.113.10/32 是文档示例地址,必须替换成企业实际的管理出口地址。先放行管理来源,再设置默认拒绝,避免当前管理会话被意外切断:
ADMIN_CIDR="203.0.113.10/32"
sudo ufw allow from "$ADMIN_CIDR" to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
ufw enable 会改变主机入站访问策略。执行前应确保控制台可用,并在新终端测试 SSH。启用后查看实际规则:
sudo ufw status verbose
sudo ufw status numbered
如果管理出口地址经常变化,不要把 SSH 长期开放给整个公网。应结合服务商安全组、固定办公出口或控制台入口设计管理路径。上游安全组与 UFW 必须使用同一份端口矩阵,否则可能出现“主机规则允许但上游拦截”或“主机以为关闭但上游仍放行”的误判。
默认允许出站是一个常见的可用性折中,因为系统更新、域名解析、时间同步和日志上报都可能需要访问外部服务。如果业务已明确依赖的目标地址和端口,可以进一步收紧出站规则;但不能在不了解依赖的情况下直接拒绝所有出站流量,否则补丁、监控和故障恢复可能同时失效。
第五关:用日志证明“谁改了什么”
审计的目标不是收集所有日志,而是能够回答几个关键问题:谁登录过、谁使用过提权、谁修改了 SSH、防火墙和 sudo 配置、应用何时重启、异常请求是否持续增加。
先检查系统和 Web 日志:
sudo journalctl -u ssh --since "24 hours ago" --no-pager
sudo journalctl -u nginx --since "24 hours ago" --no-pager
sudo tail -n 100 /var/log/auth.log
sudo tail -n 100 /var/log/nginx/access.log
sudo tail -n 100 /var/log/nginx/error.log
Ubuntu 的日志文件是否存在,取决于系统日志组件和配置;如果某个路径不存在,应以 journalctl 和实际 Nginx 日志配置为准,不要为了“补齐路径”随意更改日志服务。
需要更强审计时,可以在维护窗口安装 auditd,并只监控关键配置文件:
sudo apt-get install auditd audispd-plugins
sudo systemctl enable --now auditd
创建规则文件:
sudo nano /etc/audit/rules.d/web-hardening.rules
示例内容:
-w /etc/ssh/sshd_config -p wa -k ssh-config
-w /etc/ssh/sshd_config.d -p wa -k ssh-config
-w /etc/sudoers -p wa -k privilege-config
-w /etc/sudoers.d -p wa -k privilege-config
-w /etc/ufw -p wa -k firewall-config
-w /etc/nginx -p wa -k nginx-config
加载并查询:
sudo augenrules --load
sudo auditctl -s
sudo ausearch -k ssh-config --start recent
sudo ausearch -k privilege-config --start recent
不要直接审计整个高频写入的上传目录,否则日志量可能迅速增大并占满磁盘。审计日志也要设置轮转、保留周期和告警;如果日志只存在于服务器本地,服务器被入侵或磁盘损坏后,证据可能一并丢失。企业可以将关键日志定期复制到权限隔离的日志存储位置,但必须限制写入端的权限,避免应用账号能够删除审计记录。
上线前的验证顺序和失败处理
加固完成后,不要只检查一条 ufw status。应从外到内验证:

- 从授权的外部测试主机扫描 22、80、443 和已知应用端口,确认结果与端口清单一致。
- 使用管理员密钥登录 SSH,确认 root 登录和密码登录已按预期关闭。
- 访问 HTTPS,检查页面、静态资源和应用接口是否正常。
- 在服务器本机执行
curl访问应用监听地址,区分应用故障与防火墙故障。 - 检查
systemctl --failed、Nginx 错误日志、应用日志和审计记录。 - 用一个非管理员账号测试文件读取、上传和部署操作,确认权限没有过宽或过窄。
例如,Nginx 返回 502 时,可以先执行:
sudo ss -lntup | grep -E ':(8000|3000)\b'
curl -I http://127.0.0.1:8000
sudo journalctl -u nginx -n 100 --no-pager
如果本机访问应用失败,问题通常在应用进程、监听地址或服务启动状态;如果本机正常、外部访问失败,再检查 Nginx 转发、防火墙和上游安全组。这样可以避免看到 502 就盲目开放应用端口。
如果 SSH 新终端无法登录,先不要关闭旧会话。通过旧会话执行:
sudo sshd -t
sudo ufw status numbered
sudo journalctl -u ssh -n 100 --no-pager
如果是规则误封,使用控制台恢复 UFW 备份或删除错误规则;如果是 SSH 配置错误,则恢复 /etc/ssh/sshd_config.d/10-hardening.conf 的上一版本,检查通过后再 reload。任何回滚操作都应记录修改时间和原因,避免恢复后再次被自动化部署覆盖。
上线后的盲区检查
现场加固经常遗漏的不是命令本身,而是边界条件:
- IPv6 是否同步加固:只扫描 IPv4 不代表 IPv6 没有暴露同样的服务。
- 供应商安全组是否与 UFW 一致:两层规则中任何一层放行,都可能改变实际结果。
- 旧密钥和旧账号是否清理:关闭密码登录后,遗留的管理员公钥仍然有效。
- 日志磁盘是否有容量上限和告警:日志写满可能影响应用、SSH 或数据库。
- 备份是否真的能恢复:快照存在不等于应用数据一致,至少要验证关键文件、配置和数据库恢复流程。
- 补丁后是否重新检查监听端口:升级或安装组件可能新增服务,端口清单需要重新比对。
- 临时放行是否按时撤销:测试端口、临时管理员和临时防火墙规则都应有负责人和失效时间。
对于初创企业,最小可行的安全加固并不意味着购买最复杂的架构,而是让公网入口足够少、管理认证足够明确、进程权限足够窄、补丁能够持续执行、异常操作能够留下记录,并且在配置失误时拥有可用的恢复路径。只有精品以这几项作为服务器选型和上线验收条件,后续业务增长时再扩展架构,安全边界才不会依赖某一名工程师的记忆。