先上图,再说具体的内容

一直都在用腾讯云的机器,当时候也有优惠力度比较大活动,就买了一台4核16G内存的云服务器,因为自己做了一些APP啥的,这个配置也能带动需要用的服务,就用了2年左右吧,但是现在快到期了,续费又太贵了,也没有什么类似配置的活动了,直接买同规格的更买不起了,发愁的狠。

后来就想到了家里弄一台本地服务器,正好有个公网IP,于是说干就干,前面的 铭凡X500+5600G迷你主机搭建PVE+爱快+CentOS+网心云 这篇文章就是组的本地服务器的配置,加上网心云的收益正好抵了电费,就很划算。

因为用的东西比较多,此处记录下来方便以后维护使用

准备内容

  1. 能够装的爱快软路由
  2. 家庭服务器
  3. 公网IP服务器(随便一台即可,能装Nginx和PHP就行)

访问逻辑

分为两部分,一部分是内部业务程序主动检测IP进行上报公网IP服务器,另一部分时客户端访问服务器端的逻辑

1. 定时任务

在自己业务程序中增加一个定时任务,然后每隔三秒调用一次爱快的获取拨号IP的接口。
爱快登录后并获取拨号IP的接口请求内容如下
请求地址:http://192.168.123.1/Action/login , 此处的192.168.123.1为爱快的LAN地址,其实就是登录页面地址
http请求内容:

POST /Action/login HTTP/1.1
Host: 192.168.123.1
Content-Type: application/json
Content-Length: 129

{"username":"mrdong916","passwd":"6bbb75c7756b86ec9f69a8fb3ed46110","pass":"c2FsdF8xMURvbmczOTQzOTRA","remember_password":"true"}

passwd的计算逻辑:登录密码MD5后小写
pass的计算逻辑:salt_11+MD5后的密码进行Base64

登录后返回头中会返回一个Cookie,保存此Cookie即可

然后使用Cookie获取PPPOE拨号信息

POST /Action/call HTTP/1.1
Host: 192.168.123.1
Cookie: sess_key=6769ac2fae1be461c19dae8af8f62e36;
Content-Type: application/json
Content-Length: 68

{"func_name":"wan","action":"show","param":{"id":"1","TYPE":"data"}}

获取到的信息如下

{
    "Result": 30000,
    "ErrMsg": "Success",
    "Data": {
        "data": [
            {
                "pppoe_ip_addr": "222.xxx.xxx.125",
                "pppoe_netmask": "255.255.255.255",
                "pppoe_gateway": "222.xxx.xxx.1",
                "pppoe_updatetime": 1695151198,
                "pppoe_dns1": "202.xxx.xxx.68",
                "pppoe_dns2": "202.xxx.xxx.68",
                "pppoe_macremote": "14:23:0a:a5:49:4e",
                "dhcp_ip_addr": "",
                "dhcp_netmask": "",
                "dhcp_gateway": "",
                "dhcp_updatetime": 0,
                "dhcp_dns1": "",
                "dhcp_dns2": "",
                "dhcp_lease": 0,
                "bandmode": 0,
                "internet": 2,
                "mac": "",
                "speed": 0,
                "duplex": 0,
                "upload": 0,
                "download": 0,
                "qos_upload": 512000,
                "qos_download": 5120000,
                "vendorclass": "",
                "clientid": "",
                "hostname": "",
                "opt_type12": 0,
                "opt_type60": 0,
                "opt_type61": 0,
                "wifi_wisp": 1,
                "wifi_bssid": "",
                "wifi_ssid": "",
                "wifi_psk": "",
                "ip_mask": "",
                "gateway": "",
                "username": "03xxxxxxxx38",
                "passwd": "123456",
                "timing_rst_switch": 0,
                "timing_rst_week": "1234567",
                "timing_rst_time": "12:00",
                "cycle_rst_time": 0,
                "pppoe_service": "",
                "pppoe_ac": "",
                "mtu": 1480,
                "mru": 1480,
                "default_route": 1,
                "disc_auto_switch": 0,
                "link_time": "00:00-23:59",
                "check_link_mode": 3,
                "check_link_host": "www.baidu.com",
                "qos_switch": 1,
                "enable_ipv6": 0,
                "linkmode": 0,
                "policy": 1,
                "lte_service": "umts_gprs",
                "lte_mode": "auto",
                "lte_apn": "3gnet",
                "lte_dialnum": "*99#",
                "lte_pincode": "",
                "lte_antenna_switch": 0,
                "pppoe_ass_switch": 1,
                "ass_multi_total": 3,
                "ass_disc_rst_switch": 1,
                "ass_rst_check_week": "1234567",
                "ass_rst_check_time": "00:00-23:59",
                "ass_rst_check_interval": 10,
                "ass_rst_disc_num": 2,
                "ass_rst_disc_norestart": 0,
                "ass_check_errip_switch": 0,
                "ass_check_errip_list": "10,172,192.168",
                "dhcp_status": 0,
                "pppoe_check_errip_switch": 0,
                "pppoe_check_errip_list": "10,172,192.168",
                "id": 1,
                "modified_time": 1693749748,
                "comment": "",
                "name": "wan1",
                "bandif": "84:47:09:1d:76:c4",
                "pppoe_status": 2
            }
        ]
    }
}

返回内容中的pppoe_ip_addr就是拨号的IP,利用定时任务进行对比上次和获取的IP是否一致,如果不一致则请求公网服务器的PHP接口,来重写反向代理配置信息,然后重载配置。

2.PHP接口

先贴一下需要请求的网站的Nginx配置文件

server
{
    listen 80;
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
    listen [::]:80;
    server_name xxx.xxx.cn;
    index index.php index.html index.htm default.php default.htm default.html;
    root /www/wwwroot/xxx.xxx.cn;

    #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
    #error_page 404/404.html;
    ssl_certificate    /www/server/panel/vhost/cert/xxx.xxx.cn/fullchain.pem;
    ssl_certificate_key    /www/server/panel/vhost/cert/xxx.xxx.cn/privkey.pem;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=31536000";
    error_page 497  https://$host$request_uri;

    #SSL-END

    #ERROR-PAGE-START  错误页配置,可以注释、删除或修改
    #error_page 404 /404.html;
    #error_page 502 /502.html;
    #ERROR-PAGE-END

    # 最重要的是这行 ↓
    include /www/wwwroot/127.0.0.1/ip.conf;
    # 最重要的是这行 ↑
    
    #PHP-INFO-START  PHP引用配置,可以注释或修改
    include enable-php-00.conf;
    #PHP-INFO-END

    #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
    include /www/server/panel/vhost/rewrite/6api.itooi.cn.conf;
    #REWRITE-END

    #禁止访问的文件或目录
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
    {
        return 404;
    }

    #一键申请SSL证书验证目录相关设置
    location ~ \.well-known{
        allow all;
    }

    #禁止在证书验证目录放入敏感文件
    if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
        return 403;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
    {
        expires      30d;
        error_log /dev/null;
        access_log /dev/null;
    }

    location ~ .*\.(js|css)?$
    {
        expires      12h;
        error_log /dev/null;
        access_log /dev/null;
    }
    access_log  /www/wwwlogs/xxx.xxx.cn.log;
    error_log  /www/wwwlogs/xxx.xxx.cn.error.log;
}

上面的配置文件中的这句include /www/wwwroot/127.0.0.1/ip.conf; 非常重要,这句是将PHP接口的网站的ip.conf文件引入,以便于后面利用PHP进行修改配置文件,文件内容如下

#PROXY-START/

location ^~ /
{
    proxy_pass http://222.xxx.xxx.125:xxx;
    proxy_set_header Host xxx.xxx.cn;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_http_version 1.1;
    # proxy_hide_header Upgrade;

    add_header X-Cache $upstream_cache_status;
    #Set Nginx Cache

    set $static_filepdwGFDzU 0;
    if ( $uri ~* "\.(gif|png|jpg|css|js|woff|woff2)$" )
    {
        set $static_filepdwGFDzU 1;
        expires 1m;
    }
    if ( $static_filepdwGFDzU = 0 )
    {
        add_header Cache-Control no-cache;
    }
}
#PROXY-END/

ip.conf配置中的proxy_set_header Host xxx.xxx.cnproxy_pass http://222.xxx.xxx.125:xxx;根据自己的情况进行修改,后续会动态修改proxy_pass参数的对应的IP地址

PHP默认是禁用shell_exec函数,需要开启该函数,具体的接口文件 nginx.php如下

<?php
    $key = $_GET['key'];
    $ip = $_GET['ip'];
    header('Content-Type: application/json');
    
    if ($key == 'nginx_reload') {
        // 重载nginx 配置
        $res = shell_exec("/www/server/nginx/sbin/nginx -s reload");
        echo '{"code":200,"msg":"ok"}';
        return;
    }else if ($key == 'nginx_update') {
        // 读取当前目录下的ip.conf
        $res = shell_exec("cat ip.conf");
        $pattern = '/proxy_pass http:\/\/(.*?):8223;/i';
        // 正则匹配需要替换的IP
        preg_match($pattern,$res, $matches);
        // var_dump($matches);
        // 替换IP为新的IP
        $res = str_replace('http://'.$matches[1],'http://'.$ip,$res);
        // echo $res;
        // 将替换过IP的内容覆盖旧的配置文件
        file_put_contents("ip.conf",$res);
        // 重载nginx 配置
        $res2 = shell_exec("/www/server/nginx/sbin/nginx -s reload");
        echo '{"code":200,"msg":"ok"}';
        return;
    }else if ($key == 'nginx_get') {
        // 读取当前目录下的ip.conf
        $res = shell_exec("cat ip.conf");
        $pattern = '/proxy_pass http:\/\/(.*?):8223;/i';
        // 正则匹配获取当前的IP
        preg_match($pattern,$res, $matches);
        echo '{"code":200,"msg":"ok","data":'.json_encode($matches[1]).'}';
        return;
    }
    echo '{"code":403,"msg":"error"}';
?>

在公网服务器上新建一个网站,里面放入nginx.php,然后通过定时任务的HTTP进行请求来更新和重载nginx

3. 客户端访问

假设我在公网上的域名为api.bzqll.com,那客户端就去请求api.bzqll.com,nginx读取对应的反向代理配置,请求反向代理配置文件中的IP,其实就是访问家里动态公网IP,然后到家庭服务器中去,也就实现了不暴露家宽IP还能使用家宽服务器的问题,nginx反向代理是可以抗住超大的访问的,这样也没有内网穿透软件的性能问题。

其他

其实之前是有考虑过跟公网组建一个局域网的,但是嫌弃麻烦还要搭建东西,有可能还会被运营商屏蔽的问题,就放弃了。另外就是也考虑过内网穿透的方案,但是见到网上说有连接数、并发性能以及稳定性的问题,也放弃了。最终还是采用nginx反代的方案,至少不会因为性能和连接,切记家宽端口用数值比较大的端口,还有不要带域名进行访问。

最后修改:2023 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏