sql执行环境不能直接跑在宿主机上,因易遭误操作或恶意sql攻击(如drop database、copy from '/etc/passwd'),而容器通过pid/user namespace隔离、只读文件系统、能力限制和临时存储实现强沙箱防护。

SQL执行环境为什么不能直接跑在宿主机上
容器化不是为了赶时髦,而是避免 SELECT pg_terminate_backend() 这类误操作直接杀掉生产数据库连接,或 CREATE EXTENSION 加载恶意共享库污染系统。宿主机上一个没设 readonly 的 PostgreSQL 实例,只要应用配置写错,就可能被注入 DROP DATABASE 或 COPY ... FROM '/etc/passwd'。
- 容器默认启用 PID namespace 和 user namespace(需显式配置),能天然阻断跨进程信号干扰
- 无法通过
/proc直接读取宿主机内核参数或其它容器内存映射 -
docker run --read-only --tmpfs /tmp:rw,size=64m可强制限制写入路径,比 chroot 更可控
如何用 Docker Compose 隔离 SQL 执行沙箱
核心不是“启动一个数据库”,而是让每次 SQL 提交都落在独立、可销毁的实例里。比如数据清洗脚本需要临时建表跑 ANALYZE,就不能复用长期运行的容器。
version: '3.8'
services:
sql-sandbox:
image: postgres:15-alpine
read_only: true
tmpfs:
- /var/lib/postgresql/data:rw,exec,size=256m
- /tmp:rw,size=32m
environment:
POSTGRES_PASSWORD: devonly
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
-
read_only: true阻止对镜像层的任何写入,所有变更必须落在tmpfs挂载点 -
cap_drop: [ALL]移除全部 Linux capability,连setuid都不可用,防止提权 - 不挂载
/var/lib/postgresql/data到宿主机,容器退出即数据清空,无残留风险
SQL执行时怎么防住危险语法和系统调用
PostgreSQL 本身不提供“禁用 COPY FROM”这类细粒度开关,得靠外围控制。真正起作用的是:连接前的身份隔离 + 运行时的资源围栏。
- 使用
pgbouncer做连接池层过滤,配合query_timeout = 30s和max_client_conn = 5限流 - 在容器启动时执行
ALTER ROLE app_user NOCREATEDB NOSUPERUSER;,确保连接用户无权限创建数据库或加载扩展 - 若用
psql -v ON_ERROR_STOP=1,遇到ERROR: permission denied for table pg_class会立即退出,不继续执行后续语句 - 禁用
postgres用户登录,只允许应用专用角色,角色密码通过secrets注入而非环境变量
为什么 bind mount /etc/passwd 是高危操作
有人图方便把宿主机 /etc/passwd 挂进容器,结果 SQL 里一句 COPY (SELECT * FROM pg_user) TO '/etc/passwd'; 就能覆盖系统用户表——这不是理论风险,是真实发生过的逃逸链起点。
-
docker run -v /etc/passwd:/etc/passwd:ro表面只读,但 PostgreSQL 的COPY仍可写入(因内核 VFS 层未拦截) - 正确做法是彻底不挂载敏感路径,用
useradd -r -u 1001 sandbox-user在镜像构建阶段固化非 root 用户 - 如果必须访问外部文件,改用
pg_read_file('/var/lib/postgresql/import.csv')并确保该路径在容器内为只读 tmpfs
容器不是银弹。真正关键的是:每次 SQL 执行是否对应一个生命周期明确、权限最小、存储隔离的实例;而不是“跑在容器里”这件事本身。










