运维中的安全实践备忘录
上线一套服务,从买云主机、跑 Docker,到接上数据库、GeoServer、FastAPI,中间哪一步没想清楚,日后都可能变成一颗“定时炸弹”。
这篇运维中的安全实践备忘录,不是教科书式的最佳实践大全,而是按我自己常用的技术栈, 把云服务器、Docker、Postgres、GeoServer、FastAPI 一路梳了一遍: 哪些配置一开始就该改掉,哪些权限必须收紧,哪些检查可以做成日常例行公事。 希望它更像一份给未来自己的“安全复查清单”,每次上新环境、开新服务前,花几分钟过一遍,少掉几个不必要的坑。
云服务器
用户与 SSH 登录
-
绝不直接用
root登录云服务器创建好之后: 新建一个普通用户,比如:
esc-user, 把这个用户加入sudo组,只在需要时用sudo提权好处是,哪怕应用被打穿,攻击者起手的权限也不会是系统最高的。
-
关闭密码登录,只允许 SSH Key
- 不给普通用户设置密码,统一用 SSH 公钥认证:
本地生成密钥对:
ssh-keygen, 把公钥写到服务器上用户的~/.ssh/authorized_keys-
在 SSH 配置里关闭密码登录:
PasswordAuthentication no PermitRootLogin no -
~/.ssh/known_hosts用来记录“我信任的远端主机”,方便下次连接时校验指纹,避免中间人攻击。
端口与协议
-
对公网只暴露 80 / 443
服务器防火墙 / 安全组策略: 只放行:
80/tcp、443/tcp, 其他端口一律内网访问或通过 VPN / SSH 隧道访问 -
强制所有 HTTP 跳 HTTPS
在 Nginx / Caddy 等反向代理里配置: 所有 80 端口请求直接 301 重定向到 443
-
一切对外流量走 HTTPS
API、管理面板、反向代理后端(只要能上公网)全部加 TLS: 正常申请证书(Let’s Encrypt 等), 禁用过时的 TLS 版本和弱密码套件(后面有空再细化)
定期漏洞扫描(nmap)
日常用 nmap 做简单的漏洞扫描和端口检查,看看自己是不是“暴露过头了”。
示例脚本备忘:
#!/bin/bash
host_scan='www.web-server.cn'
date_report=$(date '+%Y-%m-%d')
nmap_report=$(nmap -sV --script=vulscan/vulscan.nse $host_scan)
# TODO:
# 1. 把 $nmap_report 输出到以日期命名的报告文件
# 2. 配合 cron,每周或每月定期扫一次
以后可以考虑:
把报告存到 /var/log/security/,
简单做个 diff,看本次相比上次多了哪些开放端口或新漏洞。
Docker 应用安全
Docker 宿主机设置
-
当前用户加入
docker组避免满世界
sudo docker,但要警惕:能用 Docker 的人,本质上就相当于机器的管理员了。 -
修改 Docker 数据目录
默认目录改到:
/mnt/data/docker/root, 方便独立挂载磁盘、监控容量、做备份与迁移。 -
限制容器日志大小
日志驱动配置(daemon.json)设定单个容器 log 文件不超过
10m,避免磁盘被日志撑爆。
镜像与存储
- 优先用官方镜像
基础镜像优先选择官方维护版本,少踩“来路不明的复合镜像”的坑。
能用 alpine 优先选带 alpine 标签的镜像,体积小、攻击面更少。
-
数据通过 volumes 管理
使用
volumes存储应用数据,而不是直接映射整个宿主机目录: 方便权限管理, 降低误操作直接删宿主数据的风险如果确实需要挂载宿主机目录,尽量做到: 路径最小化, 权限受控(只读 / 限定目录)
编写 Dockerfile 时的习惯
-
容器里不用
root运行服务在 Dockerfile 中创建专用的非 root 用户,并切换:
RUN adduser -D appuser USER appuser -
避免安装不必要的软件包
减少镜像体积,也减少攻击面。只装应用真正在跑时需要的东西。
-
可执行文件归 root 所有、但无写权限
应用的二进制文件和关键脚本:
chown root:root, 对运行用户只开放“读 + 执行”,不给写权限 这样即使应用本身被利用,也不容易篡改可执行文件。
Portainer 使用
-
使用 Agent 模式管理云上 Docker
本地跑 Portainer,云服务器上只部署 Portainer Agent:
避免直接把 Portainer 管理界面暴露公网, 管理连接走 Agent 通道,相对安全可控
Postgres
-
管理员密码必须够复杂
避免简单词汇和常见组合,考虑使用密码管理器生成随机密码。
-
每个后台应用单独账号 + 数据库
为每个服务创建专属:
databaseuser+password
该账号只在自己的库里拥有读写权限。 一旦某个应用被攻破,至少不会“一把钥匙开所有门”。
-
定期备份(使用
pg_dump)约定频率:每月一次 起步,有条件可以每周:
pg_dump -U <user> -h <host> <dbname> > backup-YYYYMMDD.sql备忘:
- 备份文件不要跟数据库放在同一块盘上
- 偶尔做一次“恢复演练”,确认备份真能用
GeoServer 安全配置
管理账号与密码加密
-
改默认管理员 + 禁用原账户
修改默认管理员密码后,直接禁用默认管理员用户:
新建一个管理员,比如:
xxxx_admin, 强密码原则同上。 -
开启基于 PBE 的密码加密
对 GeoServer 中配置里的 URL、密码等敏感参数开启基于 PBE 的加密:
避免配置文件被直接读取时一览无余。
日志与暴力破解防护
-
启用生产级日志
方便问题排查,也有助于安全事件追踪。
-
暴力攻击防御(默认打开)
配置失败登录的延时:
- 最小延时:10 秒
- 最大延时:60 秒
这样可以显著拖慢暴力破解节奏。
服务级限流与控制
-
开启 URL 外部访问检查
限制一些敏感接口的访问,必要时仅在内网使用。
-
设定 WFS 服务
service limit比如限制为:
50000, 避免一次查询拉走过多数据导致服务卡死。 -
设置 WMS “Resource consumption limits”
限制单次地图请求的资源使用,比如:
- 最大像素数
- 最大输出尺寸
防止恶意或误操作的大图请求把 GeoServer 拖垮。
-
配置
control-flow防止过载# 超过 60 秒仍排队的请求直接放弃,客户端大概率已经不等了 timeout=60 # 全局并发请求上限 ows.global=128 # GetMap 并发上限 ows.wms.getmap=16 # Excel 输出(内存敏感)并发上限 ows.wfs.getfeature.application/msexcel=4 # 单个用户的并发请求上限(Firefox 默认并发 6) user=6 # GWC 切片请求并发上限(4 核机器经验值:4 × 核数) ows.gwc=16后续如果服务器性能升级(更多核心),记得对照实际压测结果调大这些值。
身份认证与匿名访问
-
接入 Keycloak OIDC
使用 Keycloak 做统一认证与权限管理:
为 GeoServer 配置 OIDC 客户端, 按角色、用户组划分可访问的工作区 / 图层。
-
禁用匿名用户预览图层
对任何“可查询真实业务数据”的服务,都尽量不要开放匿名预览。 必须公开的 demo 图层,另建工作区,确保与生产数据隔离。
FastAPI 服务
-
Rate Limit(访问频率限制)
对每个 IP / Token 在单位时间的请求数做限制:
防止恶意刷接口, 也能避免误伤自己(写错循环调用)。
-
合理的请求超时时间
对耗时长的接口设置上限:
超过一定时间直接中断,释放服务器资源, 后端耗时任务可以考虑放到队列里异步处理。
-
缓存常用请求
对一些读多写少、数据更新不频繁的接口:
加缓存(内存 / Redis),减轻数据库压力。 一定注意缓存失效策略。
-
正确配置 CORS
明确允许的:
- 域名
- 方法(GET/POST/PUT/DELETE…)
- 头部
不要一把梭
*,尤其是带上 Cookie、认证头时。 -
接入 Sentry 做错误与日志记录
所有异常统一打到 Sentry:
便于集中查看错误堆栈, 能看到错误趋势,及时发现新的安全问题或滥用模式
日常自检清单
每次上线新服务,最好过一遍:
- 云服务器有没有关掉 root 登录、密码登录?
- 防火墙是不是只放开必须的端口(通常 80/443)?
- Docker 容器里是不是还在用 root 跑?日志会不会无限长?
- Postgres 每个服务是不是有自己的库和账号?最近一次备份是什么时候?
- GeoServer 管理员是不是已经改名 + 强密码 + 禁用默认账号?
- GeoServer 的
control-flow、服务限额是否配置?匿名访问关了没? - FastAPI 有没有做 Rate Limit、超时控制、合理的 CORS?Sentry 正常收日志吗?
- 最近一次
nmap/ 漏洞扫描是什么时候?结果有没有处理完?