路由iStoreOSLuckyLucky STUN + Cloudflare Worker 实现内网服务双栈访问
Mus前置说明
阅读本文需具备一定的 Lucky 使用经验以及 Cloudflare 使用经验,若没有请自行查阅相关文章后再继续阅读。
本文所有操作均为文字描述,无图演示!
前置条件:
- Lucky 检测中检测出的 NAT 类型最好为 NAT1
- 路由器拨号且路由器开启了 UPnP 功能
Lucky 动态域名
假设你的域名为 example.com,推荐配置 A 记录为 *.ipv4.example.com,AAAA 记录为 *.ipv6.example.com。
当然你也可以配置为相同的,如 *.real.example.com。
具体操作步骤请参阅相关文章自行配置。
Lucky Web 服务配置
添加 Web 服务规则,下面介绍需要配置的项。
基础配置
- Web 服务规则名称:自定义
- 规则开关:开
- 操作模式:简易模式
- 监听类型:全选(IPV4 + IPV6 )
- 监听端口:100(这里可自定义为你自己的端口)
- 防火墙自动放行:开
- TLS:开
- TLS 最低版本限制:TLS 1.2(默认即可)
子规则
若配置的动态域名 A 记录为 *.ipv4.example.com,AAAA 记录为 *.ipv6.example.com。
添加子规则,下面介绍需要配置的项。
- 子规则名称:hi(假设这里提供名为 hi 的服务)
- 子规则开关:开
- 操作模式:简易模式
- 服务类型:反向代理
- 前端地址:
- hi.ipv4.example.com
- hi.ipv6.example.com
- 后端地址:http://127.0.0.1:8080
(填写为你真正提供服务的地址。可填写 Docker 容器或本地提供的服务)
- 万事大吉:开
- 忽略后端 TLS 证书验证:开
其他项可根据需求自行配置。
STUN 穿透配置
添加穿透规则,下面介绍需要配置的项。
基础配置
- 规则名称:自定义即可
- 操作模式:简易模式
- 穿透类型:IPv4-TCP
- 穿透通道本地端口:0
- 防火墙自动放行:关
- UPnP:开(默认不做任何设置,如有需要自行查阅相关文档后设置)
- NAT-PMP:关
- UPnP 内部端口自定义:指向你 Lucky Web 服务端口,如 100
- 不使用Lucky内置端口转发:开(这里推荐启用,方便服务获取来访者真实 IP 地址)
- 禁用自身转发检测:关
- 自定义脚本触发:开
自定义脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
| #!/bin/sh
CF_WORKER_API_TOKEN=""
CF_ACCOUNT_ID=""
WORKER_NAME="stun-redirect"
BASE_DOMAIN="example.com"
V4_SUFFIX="ipv4.example.com" V4_PORT=${port}
V6_SUFFIX="ipv6.example.com" V6_PORT=100
WORKER_CODE=$(cat <<EOF const CONFIG = { baseDomain: '${BASE_DOMAIN}', ipv4: { suffix: '${V4_SUFFIX}', port: ${V4_PORT} }, ipv6: { suffix: '${V6_SUFFIX}', port: ${V6_PORT} } };
addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); });
/** * 请求入口 */ async function handleRequest(request) { try { const corsHeaders = buildCorsHeaders(request);
// OPTIONS 预检 if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: corsHeaders }); }
const url = new URL(request.url); const subdomain = extractSubdomain(url.hostname); const networkConfig = getNetworkConfig(request); const targetUrl = buildRedirectUrl({ originalUrl: url, subdomain, networkConfig });
// 创建原始 307 const redirectResponse = Response.redirect(targetUrl, 307);
// 复制 Header const headers = new Headers(redirectResponse.headers);
// 添加 CORS for (const [key, value] of Object.entries(corsHeaders)) { headers.set(key, value); }
// 返回最终 307 return new Response(null, { status: 307, headers }); } catch (error) { return createErrorResponse(error, request); } }
/** * 构建 CORS Header */ function buildCorsHeaders(request) { const origin = request.headers.get('Origin') || '*';
return { 'Access-Control-Allow-Origin': origin, 'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': '*', 'Access-Control-Max-Age': '86400', 'Vary': 'Origin' }; }
/** * 提取子域名 */ function extractSubdomain(hostname) { const normalized = hostname.toLowerCase();
if (normalized === CONFIG.baseDomain) { return ''; }
const suffix = '.' + CONFIG.baseDomain;
if (!normalized.endsWith(suffix)) { return ''; }
return normalized.slice(0, -suffix.length); }
/** * 获取客户端 IP */ function getClientIP(request) { return request.headers.get('CF-Connecting-IP') || ''; }
/** * 判断是否为 IPv6 */ function isIPv6Address(ip) { return ip.includes(':'); }
/** * 根据客户端 IP 获取网络配置 */ function getNetworkConfig(request) { const clientIP = getClientIP(request);
if (isIPv6Address(clientIP)) { return CONFIG.ipv6; }
return CONFIG.ipv4; }
/** * 构建目标域名 */ function buildTargetDomain(subdomain, suffix) { if (!subdomain) { return suffix; }
return subdomain + '.' + suffix; }
/** * 构建跳转 URL */ function buildRedirectUrl({ originalUrl, subdomain, networkConfig }) { const targetDomain = buildTargetDomain(subdomain, networkConfig.suffix);
return 'https://' + targetDomain + ':' + networkConfig.port + originalUrl.pathname + originalUrl.search; }
/** * 创建错误响应 */ function createErrorResponse(error, request) { return new Response(error.stack || error.toString(), { status: 500, headers: { 'Content-Type': 'text/plain;charset=UTF-8', ...buildCorsHeaders(request) } }); } EOF )
RESULT=$(curl -s -X PUT \ "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/workers/scripts/${WORKER_NAME}" \ -H "Authorization: Bearer ${CF_WORKER_API_TOKEN}" \ -H "Content-Type: application/javascript" \ --data "$WORKER_CODE")
if echo "$RESULT" | grep -q '"success"[[:space:]]*:[[:space:]]*true'; then echo "✅ Worker 部署成功" else echo "❌ Worker 部署失败" echo "$RESULT" fi
|
Worker 路由
添加路由为:hi.example.com/*。
此时访问 hi.example.com 时,会自动将请求转发到 Lucky Web 服务,至于具体走 IPv4 还是 IPv6,会根据客户端的 IP 地址自动判断。