上线一套服务,从买云主机、跑 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/tcp443/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

  • 管理员密码必须够复杂

    避免简单词汇和常见组合,考虑使用密码管理器生成随机密码。

  • 每个后台应用单独账号 + 数据库

    为每个服务创建专属:

    • database
    • user + 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:

    便于集中查看错误堆栈, 能看到错误趋势,及时发现新的安全问题或滥用模式

日常自检清单

每次上线新服务,最好过一遍:

  1. 云服务器有没有关掉 root 登录、密码登录?
  2. 防火墙是不是只放开必须的端口(通常 80/443)?
  3. Docker 容器里是不是还在用 root 跑?日志会不会无限长?
  4. Postgres 每个服务是不是有自己的库和账号?最近一次备份是什么时候?
  5. GeoServer 管理员是不是已经改名 + 强密码 + 禁用默认账号?
  6. GeoServer 的 control-flow、服务限额是否配置?匿名访问关了没?
  7. FastAPI 有没有做 Rate Limit、超时控制、合理的 CORS?Sentry 正常收日志吗?
  8. 最近一次 nmap / 漏洞扫描是什么时候?结果有没有处理完?