本地数据库客户端只能自己用?用 cpolar 安全访问 NAS 上的 PostgreSQL 管理台

图:用 cpolar 安全访问 NAS 上的 PostgreSQL 管理台
我以前最怕这种验收:数据库跑在家里 NAS 或办公室小主机上,本地客户端连得好好的,同事一问"我能不能看一下表结构和几条测试数据",现场就开始尴尬。
直接把 PostgreSQL 的 5432 端口映射出去肯定不合适。数据库端口不是演示页面,公网裸露风险太高;更别说很多测试库里还混着调试数据,一不小心就把权限放大了。
这篇换个更稳的做法:在 NAS/Docker 上跑 PostgreSQL 测试库和 pgAdmin Web 管理台,只给同事一个临时 HTTPS 入口进管理台,用只读账号验收查询结果。验收结束后关掉 cpolar,入口消失,数据库端口一直不对公网开放。
1 这次要做成什么?
这套方案的重点不是"远程连接数据库",而是"临时验收 Web 管理台"。
最终链路长这样:
text
同事浏览器
↓ HTTPS 临时地址
cpolar HTTP 隧道
↓
NAS 上的 pgAdmin Web 管理台:5050
↓ Docker 内网
PostgreSQL 测试库:5432
划重点:外部访问的是 pgAdmin 的 Web 页面,不是 PostgreSQL 的 5432 端口。pgAdmin 再使用 Docker 内网地址连接 PostgreSQL。
这篇里我会用 PostgreSQL 官方镜像和 pgAdmin 官方镜像做演示。pgAdmin 官方文档要求容器启动时设置 PGADMIN_DEFAULT_EMAIL 和 PGADMIN_DEFAULT_PASSWORD,容器默认监听 80,我们把它映射到 NAS 的 5050 端口,方便本地浏览器访问。
安全边界先说清楚:
- 不公网暴露 PostgreSQL
5432。 - 不给同事超级管理员账号。
- 只使用脱敏测试库和只读数据库账号。
- cpolar 只用于临时验收,验收结束立刻关闭隧道。
- 生产环境走正式远程接入方案、堡垒机或统一网关,不拿临时隧道当长期入口。
2 环境准备:NAS 上先有 Docker
这篇默认你的 NAS、迷你主机或 Linux 服务器已经能运行 Docker 和 Docker Compose。群晖、威联通、Ubuntu 小主机都可以,命令写法以标准 Docker Compose 为准。
先在 NAS 上建一个单独目录,别把测试库文件散落到系统目录里:
bash
mkdir -p ~/pg-review-demo
cd ~/pg-review-demo
这里单独建目录有两个好处:后面清理简单,Compose 文件、初始化 SQL、数据卷都能按项目收口。临时验收结束后,如果确定不用了,整个目录也更容易归档。
再确认 Docker Compose 可用:
bash
docker version
docker compose version
如果 docker compose version 没有输出版本号,先把 NAS 的 Docker 套件或 Docker Compose 插件补齐。这里别急着往下跑,Compose 不通,后面的服务编排会直接卡住。
3 部署 PostgreSQL 测试库和 pgAdmin
先写一份 docker-compose.yml。这份配置包含两个服务:
postgres:测试用 PostgreSQL 数据库,容器内端口仍是5432,但不映射到宿主机公网端口。pgadmin:Web 管理台,对宿主机开放5050,后面 cpolar 只穿透这个端口。
bash
cat > docker-compose.yml <<'EOF'
services:
postgres:
image: postgres:16
container_name: pg-review-postgres
environment:
POSTGRES_DB: reviewdb
POSTGRES_USER: app_admin
POSTGRES_PASSWORD: AdminPass_ChangeMe_2026
volumes:
- pgdata:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d:ro
networks:
- pg-review-net
restart: unless-stopped
pgadmin:
image: dpage/pgadmin4:latest
container_name: pg-review-pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: PgAdmin_ChangeMe_2026
PGADMIN_DISABLE_POSTFIX: "1"
ports:
- "5050:80"
depends_on:
- postgres
networks:
- pg-review-net
restart: unless-stopped
volumes:
pgdata:
networks:
pg-review-net:
driver: bridge
EOF
提醒一下,示例密码是为了教程可复制。你自己跑的时候要改成更长的随机密码,尤其是 pgAdmin 登录密码。同事验收只需要 pgAdmin 的登录入口,不需要知道 app_admin 的密码。
这里没有写 5432:5432,是故意的。PostgreSQL 只在 Docker 网络里被 pgAdmin 访问,避免把数据库原始端口暴露到 NAS 宿主机和公网。
4 初始化测试表,并创建只读账号
为了让验收不只是"能打开页面",我们准备一张脱敏订单表,再创建一个只读账号 review_reader。同事后面用这个账号在 pgAdmin 里跑查询,能看到数据,但不能改表。
先创建初始化目录和 SQL 文件:
bash
mkdir -p init
cat > init/01-review-demo.sql <<'EOF'
CREATE SCHEMA IF NOT EXISTS review_app;
CREATE TABLE IF NOT EXISTS review_app.orders (
id BIGSERIAL PRIMARY KEY,
order_no TEXT NOT NULL UNIQUE,
customer_name TEXT NOT NULL,
amount NUMERIC(10, 2) NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO review_app.orders (order_no, customer_name, amount, status, created_at) VALUES
('T20260720001', '测试用户A', 128.50, 'PAID', '2026-07-20 10:20:00+08'),
('T20260720002', '测试用户B', 68.00, 'PENDING', '2026-07-20 10:35:00+08'),
('T20260720003', '测试用户C', 299.90, 'PAID', '2026-07-20 11:05:00+08')
ON CONFLICT (order_no) DO NOTHING;
CREATE ROLE review_reader LOGIN PASSWORD 'ReaderPass_ChangeMe_2026'
NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION;
GRANT CONNECT ON DATABASE reviewdb TO review_reader;
GRANT USAGE ON SCHEMA review_app TO review_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA review_app TO review_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA review_app GRANT SELECT ON TABLES TO review_reader;
EOF
PostgreSQL 官方文档里,CREATE ROLE 可以明确指定 LOGIN、NOSUPERUSER、NOCREATEDB、NOCREATEROLE 这些权限属性。这里把危险权限全部关掉,只保留登录和查询需要的权限。
注意:/docker-entrypoint-initdb.d 里的 SQL 只会在数据库数据目录首次初始化时执行。如果你已经启动过一次 PostgreSQL,再改初始化 SQL 不会自动重跑。测试阶段想重置环境,可以执行:
bash
docker compose down -v
docker compose up -d
down -v 会删除 Compose 创建的数据卷,测试数据也会一起清掉。别在真实库上这么干,这一步只适合当前这种临时演示库。
5 启动服务,本地先确认管理台能打开
现在启动两个容器:
bash
docker compose up -d
看一下状态:
bash
docker compose ps
正常情况下能看到 pg-review-postgres 和 pg-review-pgadmin 都处于运行状态。如果 pgAdmin 还在启动中,等十几秒再刷新页面,它第一次初始化会比普通静态页面慢一点。
在局域网电脑上打开:
text
http://NAS_IP:5050
把 NAS_IP 换成你的 NAS 地址,比如 192.168.1.20。登录账号使用刚才配置里的:
text
admin@example.com
PgAdmin_ChangeMe_2026

图:局域网先通过 pgAdmin :5050 验收数据库
这一步应该看到 pgAdmin 的登录页和主界面。图里适合截两块:浏览器地址栏里的 :5050,以及 pgAdmin 登录后的首页。这样读者能一眼看懂:现在只是局域网访问,还没对外开放。
如果打不开,先查三件事:
bash
docker compose ps
docker logs pg-review-pgadmin --tail=80
curl -I http://127.0.0.1:5050
curl 能返回 HTTP 响应,说明 pgAdmin 在 NAS 本机端口已经起来;局域网电脑打不开,多半是 NAS 防火墙、Docker 端口映射或访问地址填错。
6 在 pgAdmin 里添加 PostgreSQL 服务器
登录 pgAdmin 后,需要手动把 Docker 内网里的 PostgreSQL 加进去。
在左侧或首页找到添加服务器入口,填写这些信息:
text
General / Name: review-postgres
Connection / Host name/address: postgres
Connection / Port: 5432
Connection / Maintenance database: reviewdb
Connection / Username: review_reader
Connection / Password: ReaderPass_ChangeMe_2026
这里最容易填错的是 Host。不要填 127.0.0.1,也不要填 NAS 的局域网 IP。pgAdmin 和 PostgreSQL 在同一个 Docker Compose 网络里,服务名 postgres 就是可解析的内网主机名。
为什么这一步用 review_reader,而不是 app_admin?因为验收只需要看表结构和跑查询,不需要建库、删表、改权限。把只读账号提前接好,同事拿到 Web 页面后也只能做只读验证。
连接成功后,展开:
text
Servers → review-postgres → Databases → reviewdb → Schemas → review_app → Tables → orders
在 Query Tool 里执行:
sql
SELECT id, order_no, customer_name, amount, status, created_at
FROM review_app.orders
ORDER BY id;
能看到 3 条测试订单,说明数据库、权限和 Web 管理台链路都通了。
再试一条写入语句验证只读限制:
sql
UPDATE review_app.orders
SET status = 'CANCELLED'
WHERE order_no = 'T20260720002';
这条语句会被权限拒绝。这个报错是好事,说明只读账号没有被放大权限。验收前建议自己先跑一遍,别等同事打开页面才发现权限给多了或给少了。
7 用 cpolar 开临时 HTTPS 入口给同事验收
本地确认没问题后,再让外网同事访问。这里用 cpolar 开 HTTP 隧道,目标端口是 pgAdmin 的 5050。
Linux 环境可以使用官方一键安装脚本:
bash
curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash
安装完成后,先确认本机管理端口能打开:
bash
cpolar version
curl -s http://127.0.0.1:9200 >/dev/null && echo "cpolar web ui ok"
cpolar 支持通过 Web UI 登录完成账号绑定,也支持命令行手动写入 authtoken。纯命令行环境可以执行:
bash
cpolar authtoken YOUR_CPOLAR_AUTH_TOKEN
把 YOUR_CPOLAR_AUTH_TOKEN 换成你自己后台里的 token。这里不要把 token 发给同事,也不要写进公开仓库。
临时验收直接运行:
bash
cpolar http 5050
命令输出里会出现公网访问地址,选择 https:// 开头的地址发给同事。免费随机地址适合这种短时演示,cpolar 已核对规则里免费套餐的随机公网地址会在 24 小时内变化,所以不要把它写进长期文档。

图:cpolar HTTPS 临时入口交付同事验收
这张图适合截 cpolar 输出的 HTTPS 地址,以及同事浏览器打开 pgAdmin 登录页的状态。注意截图时遮住邮箱、token、真实公网地址里的敏感片段,只保留能说明"HTTPS 临时入口已生成"的部分。
发给同事时,只给这几项:
text
pgAdmin 临时地址:https://xxxx.cpolar.xxx
pgAdmin 登录账号:admin@example.com
pgAdmin 登录密码:PgAdmin_ChangeMe_2026
数据库连接名:review-postgres
可验收查询:SELECT * FROM review_app.orders ORDER BY id;
更稳一点的做法,是验收前把 pgAdmin 密码改成一次性的强密码,验收后再改掉或直接关闭容器。别把自己长期使用的 pgAdmin 管理员密码拿出来共享。
8 验收结束后怎么收口
同事确认能登录、能看表、能跑只读查询后,立刻关闭 cpolar 隧道。
如果你是在前台运行 cpolar http 5050,在终端按:
bash
Ctrl+C
再确认外网地址打不开,pgAdmin 页面就不会继续暴露在公网入口上。
如果这套 PostgreSQL 和 pgAdmin 只是本次验收使用,也可以停掉容器:
bash
docker compose down
如果连测试数据也不保留,执行:
bash
docker compose down -v
再次提醒,down -v 会删除 PostgreSQL 数据卷。只要里面有任何需要留存的数据,就不要加 -v。
这里还有几个收口动作我建议固定做:
- 删除或更换 pgAdmin 临时登录密码。
- 确认没有在路由器、防火墙、NAS 控制台里额外开放
5432。 - 如果测试库来自生产数据,验收前只保留脱敏字段,手机号、邮箱、地址、真实姓名不要原样放进去。
9 总结
这次我们做成的不是"把 PostgreSQL 放到公网",而是把 NAS 上的 pgAdmin Web 管理台临时拿出来给同事验收。数据库仍然待在 Docker 内网里,外部只通过 HTTPS 临时入口访问管理台,再用只读账号查看测试表。
关键步骤可以记成三件事:
- PostgreSQL 不映射
5432,pgAdmin 映射到 NAS 的5050。 - 数据库里提前创建
review_reader,只给CONNECT、USAGE和SELECT。 - cpolar 只穿透 pgAdmin 的 Web 端口,验收结束马上关闭。
安全边界也别省:同事验收用脱敏测试库,不给超级管理员,不共享长期密码;生产环境使用正式远程接入方案、堡垒机或统一网关。临时演示就用临时入口,做完收口,这样既能让协作顺畅,也不会把数据库安全押在侥幸上。