国内IP未备案 免费Cloudflare CDN的曲线小方案 (Deno&Cloudflare)和其它的一些小方案/记录

引子:由于大陆网络环境问题,有很多问题得绕“曲线”才能达成目的。亦或是通过“曲线”来节省成本。 

------------------------------------------------------------------------------------------------------------------------

这一段是20221023更新的,写这文章的功夫都超过调试的过程了。


最新更新大大的减少部署时间和难度,只要一个Deno项目和一个Cloudflare项目,就可以无限加域名。获取版本 GitHub

效果:
 Deno
这线路可是真复杂:(内网《=》阿里云)《=》Deno《=》Cloudflare workers《=》Cloudflare=》访问者。

httping Cloudflare代理后:正常中美线路个人感觉这延迟已经非常好了,何况还是这么得曲线


httping 电信公网IP转发到Deno再到Cloudflare的网页


httping 电信公网IP转发到Deno的网页
速度:由于阿里云只有5Mbps,所以只能跑到5Mbps,但是稳定性非常好。
不管是国内直连DenoCloudflare都是能跑满阿里云的带宽。



30Mbps上传/200Mbps下载的电信带公网IP转发通过Deno在国内访问的速度



30Mbps上传/200Mbps下载的电信带公网IP转发通过Deno在国外访问的速度


30Mbps上传/200Mbps下载的电信带公网IP转发通过DenoCloudflare在国内访问的速度




30Mbps上传/200Mbps下载的电信带公网IP转发通过DenoCloudflare在国外访问的速度

这样看来电信的瓶颈就在于上传带宽了
小问题的解释:
1. 为什么不直接用Deno:其实域名直接绑在Deno也可,但是还是喜欢Cloudflare的,因为这类域名比较容易被墙,但是目前Deno国内没什么人用,所以还没被墙。而且Deno有写不允许Proxy or VPN,所以用的人都很闷声,我blog写这也不会被收录。之前Cloudflare Workersworkers.dev)没被墙的时候,分享给别人订阅转换,后来被墙了影响非常大。
2. 为什么不能直接用Cloudflare WorkersCloudflare Workers不能fetch ip,所以需要Deno
3. 为什么不直接找台国外机器nginx反代下:要多花钱,如果线路好我还要阿里云干什么。领免费机器的事都是不稳定的,我需要搭好了就一直用就行。阿里云&Deno&Cloudflare都有这个保障。
4. 为什么不备案:就是不备案。
局限性:
1. 这个方法不是所有网站都可以反代,经过小小的测试,成功显示并能登录的页面是:nps管理界面&WebDAV&File Browser。失败的是:jellyfin不知道原因,无法正常显示,可能是前端框架的问题),Typecho(可以显示文章,但是登录/搜索框/评论无法使用,因为每次post完了都是返回302跳转,导致 Fetch failed: Can not redeliver a streaming request body after a redirect)
2. Requests的方法只写了get和post,已经够用了,Deno其它的好像也不支持。
3. Deno的限制:禁止Proxy or VPN,虽然这么写,但是感觉没审查。不过感觉要是一直下载可能会有问题。CPU Time per request是10 ms。官网写了:No uptime guarantees are provided during the initial public beta for Deno Deploy. Access to the service will be controlled by our fair use policy. Any user we deem to be in violation of this policy, runs the risk of having their account terminated.

During the initial public beta, the following hard limits apply. If any runtime limits are exceeded, all related requests will be immediately terminated, and a warning will be logged to the deployment's logs.
虽然这样了,先用着吧,大不了换个账号,减少用量。刚刚去看了下Log,即使在下载的时候,isolate start time都在5ms内。
4. 上传:文件大小限制,Cloudflare在100MB以内,超出就413了。CPU Time 上传下载大文件会超,这个需要注意。

测试NAS上传下载的时候,有看到一次超的

Cloudflare还算正常
程式(注意:阿里云http不能用域名访问,所以用ip+port即可,https不知道)
最初版
Deno  (Media Type 注意选JS)
const upstream = 'ip:port' //电脑端域名
const upstream_mobile = 'ip:port' //移动端域名
//下面的replace_dict里:xxx.deno,dev要改,其它的可以看需求添加
const replace_dict = {
    '$upstream': '$custom_domain',
    '//google.com': '',
    'http://xxx.deno.dev':'https://xxx.deno.dev',
}
addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
})
async function fetchAndApply(request) {
    const user_agent = request.headers.get('user-agent');
    let response = null;
    let url = new URL(request.url);
    let url_host = url.host;
 
    if (await device_status(user_agent)) {
        var upstream_domain = upstream
    } else {
        var upstream_domain = upstream_mobile
    }
 
    url.host = upstream_domain;
    let method = request.method;
    let request_headers = request.headers;
    let new_request_headers = new Headers(request_headers);
    url.href=url.href.replace('https://','http://');
    if (request.method === 'POST') {
        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Referer', url.href);
        new_request_headers.set('Origin', "http://"+upstream);
        var original_response = await fetch(url.href, {
            method: 'POST',
            headers: new_request_headers,
            body : request.body
        })
    }
    else{
    new_request_headers.set('Host', upstream_domain);
    new_request_headers.set('Referer', url.href);
    
    var original_response = await fetch(url.href, {
        method: method,
        headers: new_request_headers
    })
    }


    let original_response_clone = original_response.clone();
    let original_text = null;
    let response_headers = original_response.headers;
    let new_response_headers = new Headers(response_headers);
    let status = original_response.status;

    new_response_headers.set('access-control-allow-origin', '*');
    new_response_headers.set('access-control-allow-credentials', true);
    new_response_headers.delete('content-security-policy');
    new_response_headers.delete('content-security-policy-report-only');
    new_response_headers.delete('clear-site-data');

    const content_type = new_response_headers.get('content-type');
    if (content_type==null){original_text = original_response_clone.body}
    else if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
        original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);
    } else {
        original_text = original_response_clone.body
    }

    response = new Response(original_text, {
        status,
        headers: new_response_headers
    })
    
    return response;

 
}


async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()
    var i, j;
    for (i in replace_dict) {
        j = replace_dict[i]
        if (i == '$upstream') {
            i = upstream_domain
        } else if (i == '$custom_domain') {
            i = host_name
        }
 
        if (j == '$upstream') {
            j = upstream_domain
        } else if (j == '$custom_domain') {
            j = host_name
        }
 
        let re = new RegExp(i, 'g')
        text = text.replace(re, j);
    }
    return text;
}
 
async function device_status (user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    var flag = true;
    for (var v = 0; v < agents.length; v++) {
        if (user_agent_info.indexOf(agents[v]) > 0) {
            flag = false;
            break;
        }
    }
    return flag;
}

⚠️Deno弄完可以先测试下⚠️

Cloudflare workers

const upstream = 'ipconst upstream = 'xxx.deno.dev' //xxx.deno.dev的域名

const upstream_mobile = 'xxx.deno.dev' //xxx.deno.dev的域名

//下面的replace_dict里:自定义域名的话xxx.xxx.worker.dev要改成自己的,其它的可以看需求添加

const replace_dict = {

    '$upstream': '$custom_domain',

    '//google.com': '',

}

addEventListener('fetch', event => {

    event.respondWith(fetchAndApply(event.request));

})

async function fetchAndApply(request) {

    const user_agent = request.headers.get('user-agent');

    let response = null;

    let url = new URL(request.url);

    let url_host = url.host;

 

    if (await device_status(user_agent)) {

        var upstream_domain = upstream

    } else {

        var upstream_domain = upstream_mobile

    } 

    url.host = upstream_domain;

    let method = request.method;

    let request_headers = request.headers;

    let new_request_headers = new Headers(request_headers);

    //url.href=url.href.replace('https://','http://');

    if (request.method === 'POST') {

        new_request_headers.set('Host', upstream_domain);

        new_request_headers.set('Referer', url.href);

        new_request_headers.set('Origin', "https://"+upstream);

        var original_response = await fetch(url.href, {

            method: 'POST',

            headers: new_request_headers,

            body : request.body

        })

    }

    else{

    new_request_headers.set('Host', upstream_domain);

    new_request_headers.set('Referer', url.href);

        var original_response = await fetch(url.href, {

        method: method,

        headers: new_request_headers

    })

    }

    let original_response_clone = original_response.clone();

    let original_text = null;

    let response_headers = original_response.headers;

    let new_response_headers = new Headers(response_headers);

    let status = original_response.status;

    new_response_headers.set('access-control-allow-origin', '*');

    new_response_headers.set('access-control-allow-credentials', true);

    new_response_headers.delete('content-security-policy');

    new_response_headers.delete('content-security-policy-report-only');

    new_response_headers.delete('clear-site-data');

    const content_type = new_response_headers.get('content-type');

    if (content_type==null){original_text = original_response_clone.body}

    else if (content_type.includes('text/html') && content_type.includes('UTF-8')) {

        original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);

    } else {

        original_text = original_response_clone.body

    }

    response = new Response(original_text, {

        status,

        headers: new_response_headers

    })

        return response;

}

async function replace_response_text(response, upstream_domain, host_name) {

    let text = await response.text()

    var i, j;

    for (i in replace_dict) {

        j = replace_dict[i]

        if (i == '$upstream') {

            i = upstream_domain

        } else if (i == '$custom_domain') {

            i = host_name

        }

 

        if (j == '$upstream') {

            j = upstream_domain

        } else if (j == '$custom_domain') {

            j = host_name

        }

 

        let re = new RegExp(i, 'g')

        text = text.replace(re, j);

    }

    return text;

}

 

async function device_status (user_agent_info) {

    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];

    var flag = true;

    for (var v = 0; v < agents.length; v++) {

        if (user_agent_info.indexOf(agents[v]) > 0) {

            flag = false;

            break;

        }

    }

    return flag;

}


⚠️结束⚠️

最后,自定义域名,添加一条或多条解析并开启小云朵(A记录CNAME都无所谓瞎填填就行),再设置下域名的Workers Routes。


美中不足(安全性):

1. 从内网到阿里云:内网穿透:这个NPS协议估计是能被轻松识别了,数据在设置里加不加密都是看得见的,我有测试过。

2. 阿里云自身:这个估计流量会有几个月记录吧。

3. 阿里云到Deno:纯http通讯,这个也没办法加域名,阿里云拦截,https域名不知道是否拦截,可能以前测试过,https会发送SNI明文请求,那也是会拦截的。但是可以设置ip证书,有免费的来自zerossl,有效期3个月,acme申请证书可能是支持的,上次在阿里云上没能成功,当时是没IP域名的选项,读了zerossl的文档,没成功,记得文档里就写了域名证书的申请。

至于跨国传输,这个都心知肚明。

4. DenoCloudflare:至少是有SSL的。但是我使它更安全了,我在Cloudflare发送给Deno的请求头里加了Key值,这样的话如果Key值错误,Deno就不会返回任何内容,这里提到的更新可以在文章最上面的GitHub链接获取获取。甚至这个Key值是可以是个动态的,每几个小时变一次,从api fetch过来,这会增加CPU Time。其实从内网到Deno也可以这么实现,只不过改原程序确实不方便。

5. Cloudflare回来:那也是有SSL的,反正我没被墙过。

6. DenoCloudflare自身:感谢免费服务。

7. 让网站只能在Cloudflare的域名那访问:如上文更新所说DenoCloudflare这段有验证而且是https的,从Deno开始到用户全是https。Workers这里可以通过判断访问域名来隐藏内容。所以只剩阿里云NPS都还可以访问,这里就不要求了,毕竟SSL在这段都没有。

小感想:

0. 现在国内都转向小程序了,搞这些Web其实没啥用。但是我就是爱好,就是喜欢折腾,弄完有成就感。关于Deno/Workers这类至少我写完这个也不会再弄什么了。

1. 在国外访问国内的东西那是老快了,总感觉防火墙是单向的。

2. File Browser写的项目就是好,至少对于我的JS-Reverse-Proxy是好的。那几个Blog(WordPress&Typecho)估计是为了防盗。所以设计登录的时候想让别人难搞就多来几个Redirect,就像我现在上课的签到界面,有着这功夫还不如手点的,Redirect链接/参数随便改改,程序就要改,还需要重新抓包。虽然有playwright这种浏览器脚本,但是不能在命令行允许,我要它何用,难道还要给它docker出个桌面环境吗。

3. 发现国外现在好多免费的通过javascripttypescript在线部署的平台,多的都用不过来,但是每个平台都有略微不同。不过在国内弄这些网页不如弄小程序,因为大多数人只会用微信:前年ProjectBasedLearning,做网页,结果成果展示的时候,我在网页里嵌了统计工具,用的二维码,结果几乎所有人的User agent都是微信,那个例外可能是我测试的时候留下的。

4. 20220907:关于备案:我看了看备案流程有点复杂。这个国内玩网站服务器完全没办法和欧美比。假设一个欧美学生想要做个网站,可以到eu.org申请个免费域名,免费用Cloudflare,用GitHub等挂网站内容,不要半小时网站就可以上线。也有其它很多的免费项目。假设收费,那速度可更快了,只要用一个汉堡的钱可以开通一个月,服务器的价格和物价比那真是九牛一毛,哪有备案这种东西。DDNS那可更简单了 路由器直接自带,比如华硕,免费的一堆,没有墙的风险。DDNS方案多的,国内就是麻烦多,代理+cloudflare ddns还是很稳的,IP更新挺及时的。

------------------------------------------------------------------------------------------------------------------------

之前这些东西写的零零散散,直到现在才发现其实我自己有时候也可能会用到这些出于无奈的方案。就记录下而已。

以下是我在彻底解决这个问题前,试过的所有方案:

有的是从别人的博客看来的就直接放链接了,有的内容过于复杂,我也懒得尝试了。

1. 2022九月左右:国内机器的不带备案访问。https://www.v2ex.com/t/837913 (Web Archive

我要解决的问题:我之前用的cloudflare workers来fetch api的,但是现在网站限制了Cloudflare ip的访问,所以得换机器fetch,其它的比如vue我不会,感觉类别很多,不想弄。所以还是PHP方便,但是我不打算续国外机器了,用处不大。提供1和2的两种方案。



用国外服务反代,cfworker 不支持直接解析域名,所以还要嵌套一层代理服务,大致流程:
User browser < = > Cloudflare dns < = > Cloudflare worker < = > Deno < = > Aliyun
deno 代码:
import { serve } from "https://deno.land/std@0.114.0/http/server.ts";
async function handler(req: Request): Promise<Response> {
// console.log(req.headers);
// console.log(req.method, req.url);
const url = req.url;
const path = url.substring(url.indexOf('.dev') + 4);
return await fetch(`http://your VPS ip:8080${path}`);
}
console.log("Listening on http://localhost:8000");
serve(handler);
    但是Deno不允许VPN&Proxy的。

2. 20220925: 免费的PHP网站建设,(待观察) www.000webhostapp.com

这个可以免费建WordPrees,绑上域名。这玩意太好用了,省得买机器部署PHP了。


(这算是点限制吧,我看他们要求每周要登录下,我到时候看看不登录会怎么样。)不登录也没事。移除000webhost右下角Powered by 000webhost字样。( Web Archive


3. 很久以前:PHP 转发,可以结合方案1。如果不行的话就用国内机器+方案一吧。

    <?php

    $contents = file_get_contents('https://api.example.com/');

    echo $contents;

    ?>


4. 20220919: https://ssnhd.com/2022/01/08/blog/ Web Archive

    javascript的blog,甚至不需要PHP,直接可以无服务器托管,通用的md格式文章,可以迁移到任何地方。文件可以托管在GitHub,(然后用netlify部署)可以Cloudflare Pages,最后Cloudflare加上cdn和tls,稳定性没得说,域名可以无限添加。


5. Blogger国内访问

    打造一个可国内访问的BloggerBlogspot)方法 ( Web Archive)由于懒惰加上国内搜索引擎不收录,暂时搁置。


6. gcore.com CDN

    这是纯记录下,这个CDN有1T每月的免费流量。


7. 20221015: CasaOS,一个docker可视化网页部署工具,最近很多人在推,其实和这个文章没什么关系。

但是国内机器基本上连不上官方仓库的,如果内网有代理的话可以玩玩。


8. 20221015: 💡有个想法:突然想到阿里云可以搭虚拟机中机,但是是要换系统吗,最好装软件能直接虚拟,现在内存就用10%,都装了nginx+php+nps+docker了,最简单就是装个winserver+破解的vmware+许多的虚拟机。虽然linux下也有vmware那类软件,一是要先装图形界面,没有web的,二是不好破解。还是现在的方法最好,占用小就小吧,就算装了vmware虚拟机里也不知道装什么。

    PVE:

        Debian 10安装Proxmox VE(PVE)虚拟化管理软件教程

        腾讯云248上海 安装PVE 7.1-5 成功, NAT小鸡批量端口转发也搞定

😂另外,用阿里云转发的ss 在墙外效果非常好,nas里的视频看起来非常顺畅,可能群晖系统自身有压缩。


Comments

Popular posts from this blog

海外回国代理分流

openwrt设置定时任务-远程唤醒计算机

从Blogger转移