字节笔记本
2026年2月19日
cloudflare-worker-tunnel-mysql-example:安全连接 Worker 与自建 MySQL 的完整方案
本文介绍 cloudflare-worker-tunnel-mysql-example,一个通过 Cloudflare Tunnel 安全连接 Cloudflare Worker 与自建 MySQL 数据库的开源示例项目。该项目解决了将数据库暴露在互联网上的安全风险问题,提供了一套完整的隧道加密连接方案。
项目简介
cloudflare-worker-tunnel-mysql-example 是一个由 Brett Scott 开发的开源项目,旨在演示如何通过 Cloudflare Tunnel 建立 Cloudflare Worker 与自建 MySQL 数据库之间的安全连接。该项目在 GitHub 上已获得 66+ stars,是对 Cloudflare 官方 worker-mysql 模板的重要改进,解决了官方模板无法与 Cloudflare Tunnel 和 Cloudflare Access 配合使用的问题。
在传统的架构中,将数据库直接暴露在互联网上存在严重的安全隐患,恶意攻击者会扫描开放的数据库端口并尝试暴力破解。通过使用 Cloudflare Tunnel,可以将数据库隐藏在私有网络中,仅通过加密隧道对外提供服务,大大提升安全性。
核心特性
- Wrangler 2.0 支持:使用最新的 Cloudflare Workers CLI 工具
- TypeScript 编写:提供完整的类型支持
- 本地开发支持:支持在本地环境中进行开发和测试
- 自托管 MySQL 数据库:支持本地或远程 MySQL 数据库
- Docker 化部署:使用 Docker Compose 管理 cloudflared 和 MySQL 服务
- Cloudflare Access 集成:通过 Service Token 保护隧道访问
- ES Module 模式:采用最新的模块系统(替代 Service Workers)
- 环境变量加密:敏感信息通过 Wrangler Secrets 安全存储
技术栈
- Cloudflare Workers:边缘计算平台
- Cloudflare Tunnel (cloudflared):安全隧道服务
- Cloudflare Access:身份验证和访问控制
- MySQL 8.0:关系型数据库
- Docker & Docker Compose:容器化部署
- TypeScript:类型安全的 JavaScript 超集
- Wrangler:Cloudflare Workers 开发工具
架构原理
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Cloudflare │ ──────▶ │ Cloudflare │ ──────▶ │ Cloudflared │
│ Worker │ HTTPS │ Tunnel │ TCP │ (Docker) │
│ │ │ │ │ │
│ + Service │ │ + Access │ │ + MySQL │
│ Token Auth │ │ Token Verify │ │ (Docker) │
└─────────────────┘ └──────────────────┘ └─────────────────┘工作流程:
- Cloudflare Worker 携带 Service Token 发起请求
- Cloudflare Tunnel 验证 Token 并转发请求
- cloudflared 容器将请求转发到 MySQL 容器
- MySQL 执行查询并返回结果
前置要求
1. Docker
安装 Docker 用于本地运行 cloudflared 和 MySQL。Mac/Windows 用户推荐使用 Docker Desktop。
2. Node Version Manager (NVM)
nvm install
nvm use3. Cloudflare 账户
需要在 Cloudflare Dashboard 中添加一个网站域名。
安装指南
步骤 1:创建 Cloudflare Tunnel
- 访问 Cloudflare Zero Trust Dashboard
- 选择 "Access" > "Tunnels"
- 点击 "Create a Tunnel"
- 输入隧道名称,如 "Database Tunnel - Dev"
- 配置 Public Hostname:
- Subdomain:
db-tunnel-dev - Domain: 选择你的域名(如
example.com) - Service:
tcp://host.docker.internal:3306
- Subdomain:
如果 MySQL 不在 Docker 中运行,使用
tcp://127.0.0.1:3306
步骤 2:配置 cloudflared
- 登录 Cloudflare:
npm run cloudflared:login- 在
docker-compose.yml中配置 Tunnel Token:
cloudflared:
environment:
- TUNNEL_TOKEN="你的隧道令牌"- 启动服务:
npm run cloudflared步骤 3:创建 Service Token
- 在 Cloudflare Zero Trust Dashboard 中,进入 "Access" > "Service Auth"
- 点击 "Create Service Token"
- 记录生成的
CF_CLIENT_ID和CF_CLIENT_SECRET
步骤 4:配置 Cloudflare Application
- 进入 "Access" > "Applications"
- 创建 Self-Hosted 类型应用
- 配置应用域名(与隧道相同的域名)
- 添加 Policy:
- Action: "Service Auth"
- Include: "Service Token"(选择步骤3创建的 Token)
步骤 5:配置 Worker 密钥
npx wrangler secret put CF_CLIENT_ID
npx wrangler secret put CF_CLIENT_SECRET步骤 6:运行示例
- 安装依赖:
npm install- 更新
wrangler.toml中的TUNNEL_HOST:
[vars]
TUNNEL_HOST = "https://db-tunnel-dev.example.com"- 启动开发服务器:
npm run dev- 访问 http://localhost:8787 测试
核心代码示例
Worker 入口代码
import { Client } from './driver/mysql'
type Environment = {
readonly TUNNEL_HOST: string
readonly CF_CLIENT_ID: string
readonly CF_CLIENT_SECRET: string
}
export default {
async fetch(request: Request, env: Environment, ctx: ExecutionContext) {
let mysqlClient
try {
// 创建 MySQL 客户端
const mysql = new Client()
mysqlClient = await mysql.connect({
username: 'user',
db: 'appdb',
hostname: env.TUNNEL_HOST,
password: 'password',
cfClientId: env.CF_CLIENT_ID,
cfClientSecret: env.CF_CLIENT_SECRET,
})
// 执行查询
const param = 42
const result = await mysqlClient.query('SELECT ?;', [param])
// 关闭连接
await mysqlClient.close()
return new Response(JSON.stringify({ result }))
} catch (e) {
if (mysqlClient) {
await (mysqlClient as Client).close()
}
return new Response((e as Error).message)
}
},
}Docker Compose 配置
version: '3.3'
services:
mysql:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: 'appdb'
MYSQL_USER: 'user'
MYSQL_PASSWORD: 'password'
MYSQL_ROOT_PASSWORD: 'password'
ports:
- '3306:3306'
volumes:
- my-db:/var/lib/mysql
cloudflared:
image: cloudflare/cloudflared:1280-66d1f2750707
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=""
volumes:
- '~/.cloudflared:/etc/cloudflared'
depends_on:
- mysql
volumes:
my-db:常见问题与解决方案
1. globalThis 使用问题
错误:ERROR: CF_CLIENT_ID is not defined
解决:Module Workers 中不要使用 globalThis,直接使用 env 参数:
// ❌ 错误
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
globalThis.CF_CLIENT_ID = env.CF_CLIENT_ID
console.log(globalThis.CF_CLIENT_ID) // 会报错!
}
}
// ✅ 正确
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
console.log(env.CF_CLIENT_ID) // 直接使用 env
}
}2. Top-level Await 问题
错误:Top-level await is not available in the configured target environment
解决:将顶层 await 包装在立即执行函数中:
// ❌ 错误
await setup(DEFAULT_CONFIG);
// ✅ 正确
(async () => {
await setup(DEFAULT_CONFIG);
})();3. Docker 网络连接问题
错误:dial tcp 127.0.0.1:3306: connect: connection refused
解决:
- 如果 MySQL 在 Docker 中:使用
host.docker.internal:3306 - 如果 MySQL 在宿主机:在
docker-compose.yml中添加network_mode: host
安全建议
- 防火墙配置:确保 MySQL 端口(3306)不直接暴露在互联网上
- 强密码策略:使用复杂的密码并定期更换
- 最小权限原则:为 Worker 创建仅具有必要权限的数据库用户
- 定期轮换 Token:定期更新 Cloudflare Service Token
- 监控日志:定期检查 Cloudflare Access 日志以发现异常访问
生产环境部署
项目支持多环境配置(开发、预发布、生产):
# wrangler.toml
[env.staging]
name = "mysql-tunnel-example-staging"
[env.staging.vars]
TUNNEL_HOST = "https://db-tunnel-staging.example.com"
[env.prod]
name = "mysql-tunnel-example-prod"
[env.prod.vars]
TUNNEL_HOST = "https://db-tunnel-prod.example.com"部署命令:
# 部署到预发布环境
npm run publish:staging
# 部署到生产环境
npm run publish:prod项目链接
- GitHub 仓库:https://github.com/brettscott/cloudflare-worker-tunnel-mysql-example
- Cloudflare Tunnel 文档:https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/
- Cloudflare Workers 文档:https://developers.cloudflare.com/workers/
总结
cloudflare-worker-tunnel-mysql-example 为开发者提供了一套完整的解决方案,用于安全地连接 Cloudflare Worker 和自建数据库。通过 Cloudflare Tunnel 的加密通道和 Access 的身份验证,可以有效保护数据库免受互联网上的恶意攻击。该项目特别适合需要将无服务器函数与传统数据库集成的场景,是边缘计算与私有基础设施结合的优秀实践。