适用于个体驾驶员的信息高速公路指南

越过长城,走向世界。
Across the Great Wall we can reach every corner in the world.

——中国第一封电子邮件

1987年9月14日,一封跨国电子邮件宣告了中国互联网的开端;1994年4月,中国正式接入国际互联网,成为地球村的一员。

时至今日,光纤走入千家万户,无线覆盖五湖四海。连接网络,访问信息已然不再困难。互联网以前所未有的力度改变着生活,也潜移默化地催生着新的价值观念。对成长在信息时代的人们而言,屏幕上跳动的信息不再只是惊喜和趣味,而是深植于内心的牵挂;虚拟的账号ID不再只是单纯的口令,而是区别于现实角色的另一重自我。

网络,是一种生活方式。


在自己所珍视的地方,无人甘愿成为单纯的过客。我们几乎本能地想要留下痕迹,想要与人交往,想要得到同属于这个世界的其他个体的认可。

一封信笺,一张邮票,千言万语便可传达。哪怕远在天涯海角,只要收件地址准确无误,那封承载着万千思绪的信纸,终会交给牵挂之人。

生活在现世的我们总会有归宿。即使几度变迁,即使并不起眼,也总有一串字符,一行号码,能将我们与这个连接。

而在光与电的世界里,我们又身在何方呢?

输入ipconfig,看到的是并不为外界所知晓的四段数字;重连网络,得到的是刚刚还不存在于这世界上的晦涩字符。

究竟如何,才能在嘈杂的电波之中,找到一片栖身之所呢?

让我们来注册一个域名吧。即使需要金钱维持,即使找不到“永远”的选项,它也是独属于自己的,是独一无二的。

接着,让我们凝望着信息高速公路上的车水马龙,然后自己也跃入其中。


一切的基础:域名

DNS服务器就像查号台,把自己的名字(域名)和号码(IP)加进去,就可供人查阅,就提供了被访问的可能。

坏消息是,这是要钱的。好消息是,不同的顶级域价格相当悬殊,而功能几乎一样,并没有什么特别重要的理由,让我们在这上面投资过多。比起这笔开销,将自己的域名分发到全球的DNS服务器中显得相当有吸引力。

那真正是“莫愁前路无知己,天下谁人不识君”。

个人用途的域名,不在于有无商业价值,也不在于顶级域是否优雅。简单好记,价格实惠,足矣。

个人在此推荐域名商Namesoil,提供免费的隐私保护服务,同时对部分顶级域有首年1美元的优惠活动,十分适合新手尝试。

拿到域名之后,可以注册Cloudflare账号,将域名的nameserver改为Cloudflare提供的服务器。原因有二:首先是cf的界面更加现代,其次是cf本身提供了公共DNS服务,搭配自家的nameserver可有效缩短解析更新延迟。将域名托管至Cloudflare可为后续的动态域名配置提供便利。

梦开始的地方:第一台服务器

第二春

显然,诸如手机和笔记本电脑等移动设备并不适合作为全天候运行的服务器设备。如果感觉现在就购买云服务显得为时过早,不妨先拿家中的旧设备试试水。

实际上,即使是家中的古董电脑,其(多核)性能也远超月付两位数的云服务器,如若配置得当,完全可以胜任服务器的职责。

已经给那台尘封的老家伙通上电了吗?那么,来给她装个新系统吧。

那个看起来很憨的企鹅

它不叫企鹅,叫Linux,准确地说,是一个叫做Linux的内核和若干外围软件组成的操作系统。不必过分关心它的历史渊源,我们只需选择时下流行的通用发行版即可,Debian

下载最新版镜像,制作启动介质,调整启动顺序,安装新的系统。注意要提前备份旧设备上可能存在的重要内容,数据无价。

陌生的新界面

也许还有很多人从没有接触过成熟的图形操作系统以外的电子设备,面对陌生的界面感到手足无措。然而界面简洁并不代表功能简陋,接下来,我们将采取一系列步骤,把这个系统设置为符合目标用途的状态:

找到终端,配置并确认SSH服务可用,这要求这台设备已经通过合适的方法接入了家庭网络;

新增非root的新用户,但这并不意味着密码可以随意填写,请设置足够强的密码并牢记;

为新用户赋予使用sudo提权运行的能力。

关于具体的操作方法,容我不再赘述。

现代Linux发行版通常已经具有了相当可用的图形界面,然而,对于服务器用途,这套图形界面几乎没有任何用处,所以从这一步开始,就可以把旧电脑放回不碍事的地方,转而用刚刚设置的root密码在其它设备上远程操作了。

守护者之谜:IPv6那些事

一夫当关

让我们暂时把目光投向默默无闻的光调制解调器。家庭网关几乎都配置了IPv6防火墙,阻断来自外部的访问,这无疑不利于服务器的建设。

解决这个问题有两种思路:关闭光猫防火墙,或是将光猫配置为桥接,由路由器拨号上网,再在路由器上进行设置。

无论哪种方法都需要光猫的管理员密码,而这串神秘字符通常并不容易获取。一些运营商的一些设备型号可能有便捷的破解方案,但并非总是如此。如果在这一步无论如何也无法解决防火墙问题的话,利用家庭宽带搭建服务器的路线就到此为止了。如果不幸遇到此类情况,还是阅读下面的章节,去物色一个合适的云服务产品吧。

嗯?IPv4公网地址?那是什么?知らない~

不安分的IPv6地址

如果成功关闭了IPv6防火墙,理论上这台Linux设备已经可以通过IPv6被外部网络访问(这里的“访问”并不仅限于能够ping通,还要能够顺畅访问运行在Linux上的网络服务,如SSH)。可以执行ip -6 addr show来查看设备当前的IPv6地址。

~ $ sudo ip -6 addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
    inet6 ::1/128 scope host proto kernel_lo
       valid_lft forever preferred_lft forever
2: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 fe80::c451:46ff:fe43:5b59/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
3: ifb0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 fe80::fcb7:a3ff:fea9:c18a/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
4: ifb1: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 fe80::582e:c3ff:fe05:206f/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
18: ifb2: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 fe80::58e8:d9ff:fe46:fd96/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
19: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 3000
    inet6 2409:891f:6862:ade7:fff8:a769:89d9:1839/64 scope global temporary dynamic
       valid_lft 6721sec preferred_lft 6721sec
    inet6 2409:891f:6862:ade7:1997:2cbf:cc3d:93a3/64 scope global dynamic mngtmpaddr stable-privacy proto kernel_ra
       valid_lft 6721sec preferred_lft 6721sec
    inet6 fe80::8ee8:80d1:5cc:7ee0/64 scope link stable-privacy proto kernel_ll
       valid_lft forever preferred_lft forever

可以注意到,IPv6地址显然不甚简洁,且不止一个。此外,如果重启设备或网卡,还会发现地址的后缀发生改变,如果重启网关设备,还可能导致前缀变化。哪怕什么都不做,ISP也可能定期强制更换分配的IPv6地址前缀,这都给我们的服务器搭建造成困难。

造成以上现象的原因有:

  • IPv6隐私扩展:由于用于对外上网的IPv6地址多为全球单播地址,对公网可访问,若前缀泄露则可能导致该网络下的其他设备地址暴露(根据设备MAC推断后缀),因此,大多数IPv6客户端设备都会启用IPv6隐私扩展,根据原始 IPv6 地址计算生成一个临时地址,并在连接远程服务器时优先使用这个临时地址。对应上述输出中的temporary标志。
  • DHCPv6:适用于IPv6的DHCP,用于从网关下发IPv6地址、路由和DNS信息。DHCP下发的地址具有有效时间(租期),过期或设备离线都可能导致地址后缀变更。然而,在ISP定期更换地址前缀的背景下,使用DHCPv6进行地址管理可能出现前缀更新不及时的情况。
  • 前缀动态分配:家庭网络的IPv6地址通常采用PD(前缀委托)模式,即ISP为用户网关分配IPv6前缀,由用户侧设备决定后缀生成方式。然而,出于成本和监管问题,ISP所分配的地址前缀会不定期更换,这为内网前缀的更新速度提出更高要求。

基于以上讨论,我们可以明确要实现的目标:关闭隐私扩展,使用设备MAC生成地址后缀;停用DHCPv6,采用SLAAC分配地址;配置DDNS,将IP变化及时更新到域名解析结果中。

本文中,我们采用systemd-networkdsystemd-resolved来配置和管理设备网络,若设备上已经运行有NetworkManager,则需要先将其禁用:

Bash
sudo systemctl disable --now NetworkManager

启用systemd-networkdsystemd-resolved

Bash
sudo systemctl enable --now systemd-networkd
sudo systemctl enable --now systemd-resolved

/etc/resolv.conf 链接到 systemd-resolved 提供的解析服务:

Bash
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

创建配置文件/etc/systemd/network/10-default.network

INI
[Match]
Name=<interface> # 替换为实际的网络接口名称

[Network]
DHCP=ipv4 # 使用DHCP分配的IPv4地址
IPv6AcceptRA=yes # 接受RA(路由器通告)以无状态分配IPv6地址和路由

[DHCPv4]
UseDNS=false

顺带一提,在此处指定接受RA后,内核参数net.ipv6.conf.<interface>.accept_ra的值就不再反映系统真实行为,对该参数的修改也无效,原因是systemd会屏蔽内核相关实现,详细信息参见

创建或修改配置文件/etc/systemd/resolved.conf

INI
[Resolve]
DNS=223.5.5.5 2400:3200::1 # 阿里DNS
Domains=~.
LLMNR=no
MulticastDNS=no
DNSSEC=no # 禁用域名系统安全扩展,以防DNS无法解析
Cache=yes # 使用缓存加速解析
DNSStubListener=yes

配置DDNS(动态域名)的方案有多种,这里推荐使用Lucky,根据提示填入Cloudflare令牌,即可自动完成配置。

完成上述更改后,重启设备,等待新地址更新完成。在任意设备上执行nslookup <domain> 1.1.1.1,看看出现的是否是那串独一无二的字符吧。

“我是卖木梳的。”

“有桃木的吗?”

世界之窗:Web服务器

获得了独属于自己的域名,拿到了全球无二的IP地址,前面的辛勤准备,已经为后续的服务搭建打下了基础。

解决了联通性问题,我们接下来将着眼于本机的Web服务建设,由此打开一扇面向世界的窗口。

下文将首先演示在设备上搭建WordPress站点。

小身板大能量

本文使用Nginx作为Web服务器。可直接通过如下命令安装:

Bash
sudo apt install nginx

若需要自定义安装路径,或有额外模块需求(如rtmp直播),则可选择手动下载源码并编译安装。

正确安装并运行Nginx后,访问设备IP或域名即可看到Nginx的默认页面,若无法访问,则需要检查设备的防火墙设置,放行相关端口:

Bash
sudo ufw allow 80
sudo ufw allow 443

Nginx还可用于反向代理等场景,本文将在之后演示。

PHP是最……

要搭建WordPress这样的动态网站,PHP环境必不可少。

PHP亦可通过apt安装,或下载源码编译:

Bash
sudo apt install php php-fpm

为运行WordPress,还需要为PHP安装相关拓展:

Bash
sudo apt install php-mysql php-curl php-json php-mbstring php-xml php-zip php-gd

php.ini(通常位于/etc/php/)中启用这些拓展。

最后,在php-fpm.ini中配置监听:

INI
[global]
pid = /var/run/php-fpm.pid
error_log = /var/log/php-fpm.log
log_level = notice

[www]
listen = /tmp/php-cgi.sock
listen.backlog = 8192
listen.allowed_clients = 127.0.0.1
listen.owner = www
listen.group = www
listen.mode = 0600
user = www
group = www
pm = dynamic
pm.status_path = /phpfpm_status
pm.max_children = 150
pm.start_servers = 15
pm.min_spare_servers = 15
pm.max_spare_servers = 30
request_terminate_timeout = 100
request_slowlog_timeout = 30

其中各项参数可自行按需调整,尤其是用户权限部分。

可靠的扳手

在安装数据库之前,不妨先配置一套有用的工具:phpMyAdmin。它需要PHP和Nginx作为前置,所以要确保两者可用。

严格来说,phpMyAdmin并不是一个软件,它不能像可执行文件一样安装。我们需要从网络上下载最新的phpMyAdmin压缩包并移动到适当目录:

Bash
wget https://files.phpmyadmin.net/phpMyAdmin/5.2.3/phpMyAdmin-5.2.3-all-languages.zip # 可替换为最新版
unzip phpMyAdmin-5.2.3-all-languages.zip
mv phpMyAdmin-5.2.3-all-languages /www/wwwroot/phpmyadmin

配置Nginx(通常为/usr/local/nginx/conf/nginx.conf):

Nginx
worker_processes auto;
user www www;
events {
    worker_connections  768;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    client_max_body_size 100m;
    server_names_hash_bucket_size 64; 

    server {
        listen 8787; # 监听ipv4
        listen [::]:8787; # 监听ipv6

        location  /{
            root   /www/wwwroot/phpmyadmin/;
            index  index.php index.html index.htm;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            root           /www/wwwroot/phpmyadmin/;
            fastcgi_pass   unix:/tmp/php-cgi.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }
}

重启Nginx,现在理论上已经可以通过该设备的8787端口访问phpMyAdmin。别忘了防火墙。

我不认识MySQL

本文使用MariaDB来作为数据库软件。

依然可以使用apt安装:

Bash
apt install mariadb-server

运行配置脚本:

Bash
sudo mariadb_secure_installation

拿出刚刚配置好的phpMyAdmin,通过刚刚设置的root密码登录MariaDB,新建一个用户,为其新建数据库并授权。

WordPress,始動!

终于到了最后的环节。WordPress本质上和phpMyAdmin类似,都是依靠PHP和数据库工作的动态站点。

下载最新版WordPress压缩包,解压并移动到合适位置:

Bash
wget https://cn.wordpress.org/latest-zh_CN.zip
unzip latest-zh_CN.zip
mv latest-zh_CN /www/wwwroot/wp

也要在Nginx中配置对应的server块:

Nginx
    server {
        listen 80;
        listen [::]:80;
        location  /{
            root   /www/wwwroot/wp/;
            index  index.php index.html index.htm;
            try_files $uri $uri/ /index.php?$args;
        }
        location ~ \.php$ {
            root           /www/wwwroot/wp/;
            fastcgi_pass   unix:/tmp/php-cgi.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }

重载配置,访问网址,WordPress的安装界面就在眼前。

填入刚刚在MariaDB中新建的用户信息和数据库名,还没忘吧?

All in One:Host分流与反向代理

并不完美

通过之前的操作,我们成功把一个域名和一个特定的Web服务关联了起来,由此实现了对外访问。这么做最大的缺点莫过于无法多用IP:当多个不同的三级域名解析到同一个IP地址时,Web服务器只会为所有访问者提供相同的服务。

使用不同的端口号?域名后面还要加数字,多不优雅。

当通过http协议访问网站时,请求头中的Host字段会显示目标域名,可以基于这一特性,在Nginx中配置分流,来实现端口/IP重用。

修改此前对WordPress站点的配置:

Nginx
    server {
        listen 80;
        listen [::]:80;
        server_name www.hacinsl.top; # 添加此行
        location  /{
            root   /www/wwwroot/wp/;
            index  index.php index.html index.htm;
            try_files $uri $uri/ /index.php?$args;
        }
        location ~ \.php$ {
            root           /www/wwwroot/wp/;
            fastcgi_pass   unix:/tmp/php-cgi.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }

然后,还可以给phpMyAdmin“转正”,给它一个好看的三级域:

Nginx
    server {
        listen 80;
        listen [::]:80;
        server_name pma.hacinsl.top; # 不用再通过端口号访问了
        location  /{
            root   /www/wwwroot/phpmyadmin/;
            index  index.php index.html index.htm;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            root           /www/wwwroot/phpmyadmin/;
            fastcgi_pass   unix:/tmp/php-cgi.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }

大一统

对于本身就自带Web服务器的工具,如Lucky、宝塔面板之类,可以通过配置反向代理,为它们分配域名。

Nginx
    server {
        listen 80;
        listen [::]:80;
        server_name lucky.hacinsl.top;
        location / {
            proxy_pass http://127.0.0.1:11451; # 填写上游地址
        }
    }

注意,配置反向代理后,虽然用户侧并无感知,但在对应的服务看来,所有访问者的IP都变成了本地地址(经过Nginx转发),需要多加注意。特别是对于一些默认不对本机地址启用验证的服务,务必要小心配置权限。

通过上述配置,我们成功实现了端口重用的目标。别忘了在DDNS服务中为新增的三级域名配置解析。

安全第一:TLS加密

“不安全连接”

普通的http协议采用明文传输内容。无论是否有价值,通信存在被轻易截获甚至修改的可能性都不会令人愉快。本节将通过为Web服务启用TLS加密来增强安全性。

许多CA都提供了免费的TLS证书服务,例如Let’s Encrypt。该机构还支持签发泛域名证书,可将一套证书用于多个域名。

签发泛域名证书需要验证用户对域名DNS记录的所有权,如果此前采用Cloudflare管理DNS记录,可以通过Lucky快捷地签发和更新证书,具体操作不再赘述。

修改Nginx配置以启用TLS:

Nginx
    server {
        listen 443 ssl; # 修改监听端口为443
        listen [::]:443 ssl;
        server_name www.hacinsl.top;
        ssl_certificate     /www/cert/SSL.pem;  # pem文件的路径
        ssl_certificate_key  /www/cert/SSL.key; # key文件的路径;
        ssl_session_timeout  5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1.3;
        ssl_prefer_server_ciphers on;

        location  /{
            root   /www/wwwroot/wp/;
            index  index.php index.html index.htm;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            root           /www/wwwroot/wp/;
            fastcgi_pass   unix:/tmp/php-cgi-82.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }

以上内容以WordPress站点为例,其它服务亦可按需配置。

真的不是广告

除了自动续签外,Lucky还提供了证书更新后的映射和自动执行功能。可以将申请到的证书映射到相应目录,并在自动执行命令中填入systemctl restart nginx来使用新的证书。

“好用的很呐。”

Fly Me to the Moon:???

海的那边

也许在不知多少次遭遇Secure Connection Failed或Connection Timed out后,我们会突然觉得,是时候买一台海外服务器了。

也许动机不纯,也许心怀芥蒂,但是终究,那个订单还是被提交,一台远在大洋彼岸的服务器跃然屏上。

一台拥有IPv6地址的服务器也许会更好,这意味着其可以与位于家中的服务器直接通信。

新机器到手后的那套准备自然不能落下:权限配置,软件安装……也许新设备的性能孱弱到连PHP都无法支持,但总有某些东西必须要装上:

Bash
sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install

火星车票

Xray的配置文件位于/usr/local/etc/xray/config.json,先来进行简单且必要的配置:

JSONC
{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/xray/access.log", // 日志配置
    "error": "/var/log/xray/error.log",
    "dnsLog": true // 启用DNS日志以追踪耗时
  },
  "dns": {
    "servers": [
      "https+local://1.1.1.1/dns-query", // 使用Cloudflare的公共DNS,启用DoH确保隐私
      "localhost"
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field",
        "outboundTag": "direct"
      }
    ]
  },
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "<id>", // 可使用xray uuid生成随机id
            "flow": "xtls-rprx-vision",
            "level": 0,
            "email": "<email>" // 用户标识
          }
        ],
        "decryption": "none",
        "fallbacks": [ // 回落设置
          {
            "dest": 8080,
            "xver": 1
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "alpn": "http/1.1",
          "certificates": [
            {
              "certificateFile": "/www/cert/SSL.pem", // 证书设置
              "keyFile": "/www/cert/SSL.key"
            }
          ]
        }
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ]
}

理论上,上述配置可以在这台服务器上开放一个用于流量转发的服务。显然,为了让这一功能稳定安全地运行,这台机器也需要取得对应的TLS证书。解决证书问题的思路有两种:首先当然是通过前述方案再申请一套,然而,如果想另辟蹊径,也不是不可以定期从别的服务器拉取证书。这么做不太安全,尤其是在证书存放路径过于简单的情况下。

为了防止神秘力量的主动探测,可以在Xray的回落方向配置Nginx,将无法被识别的数据包(通常是正常的https流量)发送过去。

安装并配置Nginx:

Nginx
user www;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
events {
    worker_connections 768;
}

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    include /usr/local/nginx/conf/mime.types;
    client_max_body_size 5000m;
    gzip on;

    server {
        listen 127.0.0.1:8080;
        server_name misaka.hacinsl.top;
        location  /{
            root /www/wwwroot/wp/;
            absolute_redirect off; # 使用相对路径重定向,以解决重定向后主机名改变的问题
            index  index.html index.htm;
            try_files $uri $uri/ =404;
        }
    }
}

以上配置可以实现简单的对8080端口的监听。可参照这篇文章,将前文建立的WordPress静态化部署在这台服务器上。若监听本地回落端口,无需配置TLS即可实现https访问支持。

互联世界

接着来看看Xray客户端的配置。

JSONC
{
  "log": {
    "access": "D:/Program Files/Xray/access.log", // 依然是日志记录
    "error": "D:/Program Files/Xray/error.log",
    "loglevel": "warning",
    "dnsLog": true
  },
  "inbounds": [ //设立两种情景模式
    {
      "tag": "default",
      "port": 49,
      "listen": "0.0.0.0",
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ],
        "routeOnly": false
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    },
    {
      "tag": "global",
      "port": 50,
      "listen": "0.0.0.0",
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ],
        "routeOnly": false
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct", // 直连出站
      "protocol": "freedom"
    },
    {
      "tag": "proxy-tky", // 代理出站
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "<domain>", // 填入远程服务器地址
            "port": 443,
            "users": [
              {
                "id": "<id>", // 使用在服务端配置好的合法id
                "security": "auto",
                "encryption": "none",
                "flow": "xtls-rprx-vision"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "allowInsecure": false,
          "serverName": "<domain>", // 使用与上相同的地址
          "fingerprint": "chrome"
        }
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    }
  ],
  "dns": {
    "servers": ["223.5.5.5"]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "domainMatcher": "mph",
    "rules": [
      {
        "outboundTag": "proxy-tky",
        "domain": [
          "geosite:google-cn"
        ]
      },
      {
        "outboundTag": "direct",
        "ip": [
          "geoip:cn",
          "geoip:private"
        ]
      },
      {
        "outboundTag": "direct",
        "domain":[
          "geosite:cn",
          "geosite:private"
        ]
      },
      {
        "outboundTag": "proxy-tky",
        "domain": [
          "geosite:google" // 按需扩充
        ]
      },
      {
        "inboundTag": "global",
        "outboundTag": "proxy-tky",
        "domain": [
          "regexp:."
        ]
      }
    ]
  }
}

整体上采用的思路很简单:让流量根据域名和IP去它该去的地方。设置了default和global两种模式,以满足日常使用和特殊情况的需求。需要注意的是,为尽可能降低延时,本地DNS查询仍然通过传统方式,无隐私保护,仅在global模式下才会把未解析的域名发送给服务端处理。

先前对于服务端的配置也太过简单,让我们来增加一些功能:

JSONC
{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/xray/access.log", // 日志配置
    "error": "/var/log/xray/error.log",
    "dnsLog": true // 启用DNS日志以追踪耗时
  },
  "dns": {
    "servers": [
      "https+local://1.1.1.1/dns-query", // 使用Cloudflare的公共DNS,启用DoH确保隐私
      "localhost"
    ]
  },
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "type": "field", // 阻断对内网地址的访问
        "ip": [
          "geoip:private"
        ],
        "outboundTag": "block"
      },
      {
        "type": "field",
        "domain": ["geosite:google-cn"], // 明明并不cn(恼)
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "ip": ["geoip:cn"], // 防止直连国内
        "outboundTag": "wireguard"
      },
      {
        "type": "field",
        "domain": ["geosite:cn"],
        "outboundTag": "wireguard"
      },
      {
        "type": "field",
        "domain":[
          "dmm" // 后续再谈
        ],
        "outboundTag": "wireguard"
      }
    ]
  },
  "inbounds": [
    {
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "id": "<id>", // 可使用xray uuid生成随机id
            "flow": "xtls-rprx-vision",
            "level": 0,
            "email": "<email>" // 用户标识
          }
        ],
        "decryption": "none",
        "fallbacks": [ // 回落设置
          {
            "dest": 8080,
            "xver": 1
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "alpn": "http/1.1",
          "certificates": [
            {
              "certificateFile": "/www/cert/SSL.pem", // 证书设置
              "keyFile": "/www/cert/SSL.key"
            }
          ]
        }
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom"
    },
    {
      "tag": "wireguard", // 新增一个Cloudflare wireguard出站,获取key可参见:https://xtls.github.io/config/outbounds/wireguard.html
      "protocol": "wireguard",
      "settings": {
        "secretKey": "",
        "address": [],
        "peers": [
          {
            "publicKey": "",
            "endpoint": ""
          }
        ],
        "reserved": [0, 0, 0]
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole"
    }
  ]
}

有证据表明,把本不需要转发的流量也发送出去,既耗费时间又不够安全,实在称不上是明智的选择。但是,假如真的因为各种原因把不该转发的流量发了出去,也应当设计相应的防护措施。

Cloudflare提供了免费的Wireguard服务,为用户提供更加安全的上网体验,我们可以将目标地址为大陆的流量通过wireguard出站,降低被检测的可能性。

如果购买的服务器并不拥有当地的原生IP(注册地为别国),且并不具备IPv6或目标服务器不支持IPv6,使用wireguard出站还可为解锁区域限制内容提供可能。虽然Cloudflare已经提供了向目标服务器暴露源IP的方法,但该策略仍可用于部分没有响应Cloudflare的服务。

给我过来

显然,有不少软件既不遵从系统代理,也不支持设置代理服务器。如果想让这类不听话的程序通过代理上网,就需要一些更为强力的代理工具,如Proxifier

让我们先忽略Proxifier是一个付费软件的事实,来看看怎么设置它:

Profile - Proxy Servers - Add,填入本机地址和Xray监听的端口。为两种模式对应的两个端口都建立对应的代理服务器。

Profile - Proxification Rules - Add,填入程序的进程名(文件名)以添加规则。对于日常使用,建议将本机应用流量全部代理至Xray的defaultTag上。

还有一点很重要,别忘了为Xray创建例外规则,不然就要死循环了。

物尽其用

既然这是一台功能完备的服务器,也许还能开发出别的用途。

例如,在这台服务器的nginx上也可配置基于主机名的分流和本地反向代理,为其它Web服务提供支持。

此外,还可利用这台服务器的IPv4公网访问能力,为之前仅具有IPv6公网地址的设备提供转发。

Nginx
user www;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
events {
    worker_connections 768;
}

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;
    include /usr/local/nginx/conf/mime.types;
    default_type application/octet-stream;
    client_max_body_size 5000m;
    log_format mylog '$proxy_protocol_addr - $remote_addr [$time_local] ' # 日志功能
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent"';
    access_log /var/log/nginx/access.log mylog;
    gzip on;
    
    server { # 提供80跳转443的功能
        listen 80;
        return 301 https://$http_host$request_uri;
    }
    
    server {
        listen 127.0.0.1:8080 proxy_protocol; # 支持proxy_protocol
        server_name misaka.hacinsl.top;
        location  /{
            root /www/wwwroot/wp/;
            absolute_redirect off;
            index  index.html index.htm;
            try_files $uri $uri/ =404;
        }
    }
    server {
        listen 127.0.0.1:8080 proxy_protocol;
        server_name www.hacinsl.top;
        location / {
            set $hacdomain "hacinsl.top";
            proxy_pass https://$hacdomain;
            proxy_set_header Host $host;
            resolver 1.1.1.1 valid=60s;
        } 
    }
}

以上配置作出了两点重要改动:

  • 使用proxy_protocol进行监听,解析Xray传递的客户端真实IP,便于日志分析等。
  • 对远程服务器进行反向代理,为确保DNS及时更新,采用“先设置变量再引用”和设置记录有效期的方式,避免转发时使用过期IP。

改善生活:更多服务

至此,基本的网络架构已经建成,不妨来做些有趣的事情。

网络存储/文件共享

移动存储价格居高不下,付费网盘难保服务质量。在此背景下,给服务器多塞几块淘来的硬盘,也许是扩容个人存储的不错选择。

HFS提供了便捷的文件存储与共享服务,支持精细的权限配置,使用虚拟文件夹简化目录结构,十分适合文件分享和简单的媒体播放。部署方法可参照官方说明。

频繁的媒体播放需求可由Samba胜任,别忘了仔细处理权限问题。

以下是一份可供参考的配置:

INI
[global]
security = user
log file = /var/log/samba/log.%m
max log size = 1000
logging = file

server role = standalone server
obey pam restrictions = yes
unix password sync = yes
passwd program = /usr/bin/passwd %u
passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
pam password change = yes
map to guest = never

read only = yes
create mask = 0700
directory mask = 0700

[Yanami]
comment = Yanami
path = /media/Yanami
public = yes
read only = yes
browseable = yes
directory mode = 0777
valid users = hacinsl

该配置文件会使用Linux用户体系进行身份验证,同时阻止任何匿名账户的访问。出于安全考虑,该配置没有启用文件读写。

RSS订阅/BitTorrent下载

这部分内容的典型应用场景就是动画下载,以下是简单的操作说明。

安装qbittorrent-nox,这是一个仅有WebUI的用户界面,适合服务器使用:

Bash
sudo apt install qbittorrent-nox

修改qbittorrent-nox的启动配置:

Bash
sudo nano /etc/systemd/system/qbittorrent-nox.service

可在其中修改qbittorrent使用的用户和组,以及WebUI的端口。

预设管理密码可能出现在运行日志中。

在合适的站点取得RSS订阅URL,填入qbittorrent中,设置自动下载规则即可。

可在BitTorrent - 做种限制中设置超过一定时间或分享率超过预设值后删除torrent,防止上传流量过高或种子积压过多。

若出现权限问题,可将qbittorrent使用的用户加入对应目录所有者的组。


想写的还有很多,但是在此告一段落吧。毕竟,我还才疏学浅,网络的魅力,也终究要自己去探索。

当 代 互 联 网 天 · 下 · 第 · 一 !!!

——レイン

她说得对吗?我觉得对。