CryptOChicken

Server

Table of Contents

本 doc 主要讲解两个服务的搭建: 静态博客proxy relay (江湖人称 "梯子/ladder").

静态博客和梯子都是很成熟的技术. 但是, 有博客需求的人和有梯子需求的人通常很不同.

本文档的贡献在于抓住皆有这两个需求的人士的市场: 需要搭梯子给自己用, 以及给家人朋友用, 而同时, 又会用同一个服务器来搭建静态博客. 自己在服务器上 DIY 一个博客的其中一个目的是为了能够全权支配这个服务器用来搭梯, 这是 GitHub Page 办不到的.

1. 写在前面

1.1. 序言 2023.09

这两个服务可以说是毫无关联的: 你可以只搭博客, 不管梯子, 或者只搭梯子不管博客. 从结构上来讲, 博客和梯子应该分两篇文章来讲才更对一点, 放一篇来讲就太大了. 然而有些必须澄清的概念对二者而言都是共用的. 并且实际上, 本网站的诞生历史就是二者一起完成的, 自己就是在反复折腾这两个东西的过程中加深了对更多东西的理解 (作为过来人, 我觉得搭博客还更折腾一点, 梯子反而没什么难度).

如果你觉得本文看起来像缝合怪, 没错, 本文在2019年10月的时候确实是两篇博文 — 那时候本站刚刚建立没多久, 我为此写了两篇博客介绍自己怎么搭博客和梯. 但那时候我的观点还很稚嫩, 发出去没几个月我就自己删了, 原稿留着作为自己的私人笔记. 后来居然有个小伙伴特地加我去要这两篇博文, 我就感觉这确实在一定程度上造福了不少人. 所以就趁合同还没开始, 赶紧把这篇博文 了出来, 其内容不反映我当前的认知程度.

另外我是重度Linux用户, 有不少命令我没有精力特地开Windows去查证是否这样用 (譬如说生成公钥那里), 但这些细节问题绝对可以百度找得到答案. 通常, 一篇介绍搭梯和搭博客的CSDN/cnblogs文都会顺便谈及这些细节的, 绝对不难查到. 如有不便请多多包涵.

如今的梯子已经支持在大陆地区使用ChatGPT, 只要你能自行解决一个境外电话卡即可 (TB/PDD有一大堆). 我在欧洲, 对梯子没有刚需, 梯子是搭了博客之后顺手搭的, 给家人朋友用, 没有借此盈利的考虑, 但是依然 欢迎打赏, 多少随意 😉

1.2. 序言 2024.12.19

一年过去了, 发现这篇博客居然帮了不少人, 包括几个医学朋友和法学朋友搭梯子客户端以及一个半专业的做生信的朋友做成了他自己的博客和梯子, 确实是帮到了不少人. 为此借圣诞之际再把没讲清的地方再完善一下.

本文谈及的确实是很成熟的技术, 都不一定会纳入计算机本科课程里, 甚至初高中生都可以做到. 因此:

  • 我会从很基本的命令行开始讲起, 不反映本人的真实水平.
  • 可以以直接 copy (commonly known, "抄") 本文并冠以你自己的名字来发表, 我不界意 (我希望这成为一种人人都知道的常识, 常识就是去中心化的)

搭梯子的难度相对来讲比博客更简单, 因为这很流程化, 经过一些基本的配置即可实现一个能够稳定使用的梯子 (实际上梯子界有很多协议, 但作为用户, 挑一个能用的即可), 但博客就涉及到很多个性化的东西, 把这写进文档就不太合适了1. 我会尽量补全一些比较通用的技术, 把个性化的设置留在后面.

2. Prerequisite

本节涉及到的东西是在搭博客和梯子的时候都需要频繁用到的命令. 出于照顾 Windows 用户的目的, 这里的命令都可以在 WSL 上实现.

2.1. 使用 WSL

WSL 将会用来生成公钥, 登录服务器等基本操作.

搭博客和梯子不可避免地要涉及到登录服务器. 通常, 如果你去找关于搭梯子的博客, 会发现他们用的是 PuTTY 去登录. 个人认为这个东西的界面很丑. 用 PuTTY 的那个年代, WSL (Windows Subsystem Linux) 还没出. 当我们有了 WSL 之后, 可以用一个好看点的界面去做这个事情.

使用 WSL 主要是为了用 Linux 命令行去操作服务器. 虽然客户端的图形化的界面应该会有不少, 但通常你的刚买的服务器是没有任何东西的, 就只能靠命令行一点一点来搭2.

大部份读者应该都是 Windows 用户. 这里指个关于如何搭 WSL 的路:

当然在有切鸡批梯 (ChatGPT) 的年代, 你会优先考虑问它. 关于 WSL 的手把手视频教程非常之多, 这里不细讲了.

Note: 通常在你装了一个 WSL 后的第一步是做这三个:

sudo apt update
sudo apt upgrade
sudo apt install build-essential

2.2. 基本命令行的使用 (Compulsory)

你需要懂的一些基本命令行的用法有 (我尽量使用 Windows 世界的词来讲):

  • 查看文件夹的内容: ls, ll, ll -h, ls MyDir, ll MyDir, …
  • 打开文件夹以及返回: cd, cd MyDir, cd MyDir0/MyDir1, cd -, cd .., cd ../../, cd ../MyDir1, cd ~/, cd $HOME/MyDir1, …
  • 当前的文件夹的路径: pwd or echo $PWD
  • Copy 和移动: 用 cpmv, resp. 如 cp file1 MyDir/, cp NyDir/file1 NyDir2/, mv file1 MyDir/; mv 的用法类似.
  • 重命名也可以用 mv: mv OldFileName NewFileName.
  • 查看文件夹的内容: cat file.txt (当你事先知道这个文件很小的时候, 可以这样做, 但如果文件内容很多, 要使用编辑器)
  • 更改权限: chmod 755 script.sh.
  • 切换用户: su OChicken, su root.

Note: 在 Windows 里, 我们会有分 "C 盘", "D 盘", "Documents", "Downloads" 这些用法, 这实际上是当作是 "用户目录" 来做的. 在 Linux 的世界里, "~/" 跟 "$HOME" 可以认为是一样的意思, 表示用户主目录.

2.3. 生成公钥私钥对 (Compulsory)

买VPS之前先使用如下命令生成公钥私钥对, 因为在生成你的云主机的时候会要求上传公钥的.

ssh-keygen -t rsa -C "[email protected]"

生成的 pk-sk pair 位于 ~/.ssh/id_rsa.ssh/id_rsa.pub.

2.4. 使用 Git 和 GitHub (Optional)

想把你对正在编写的文档的改动记录下来吗? 用 git!

首先, 在 WSL 上安装 git:

sudo apt install git

然后输入这两个命令做全局配置

git config --global user.email "[email protected]"
git config --global user.name "PigCatDog"

这会生成一个配置文件: ~/.gitconfig.

git 的最入门级的用法就这四条命令:

git status
git add file.txt
git commit -m "my modification to this file"
git push

使用这条命令, 来下载自己的或别人的项目:

git clone https://github.com/PigCatDog/project.git

上面这两个 block 其实就可以帮你实现一个基本的 文件传输 的功能: 你把你的目录内容 git push 上传 到你的 GitHub repository 中 (是 GitHub 的云主机), 然后在另一台计算机里 git clone 下载.

更多用法可以咨询切鸡批梯.

2.5. 登录服务器 (Compulsory)

如果觉得本节比较抽象, 可以先把云主机买了, 再来看.

当你在 VPS 供应商下单并生成了你的一台云主机之后, 他们会告诉你这个服务器的 IP. 假设这个 IP 是 134.xxx.xxx.xxx, 就可以在 WSL 这样登录:

然后你现在就在云主机里了. 输入 exit 退出云主机, 回到你的 PC.

可以在 WSL 的 ~/.ssh/config 添加快捷方式:

Host PigCatDog
    HostName 134.xxx.xxx.xxx
    TCPkeepalive yes
    Serveraliveinterval 30

登录时用

ssh root@PigCatDog

即可. 后两行表示每30秒给服务器发一个心跳包, 以避免在本地连接服务器的时候因长时间 (即使只是几分钟) 无响应而断线.

2.6. scp 文件传输

假设你的云主机的 IP 是 134.xxx.xxx.xxx.

上传的例子: 把 file.txt 文件和 MyLocal/ 目录上传到云主机的 ~/MyDir/:

ssh file.txt 134.xxx.xxx.xxx:~/MyDir/
ssh MyLocal/ 134.xxx.xxx.xxx:~/MyDir/

然后你的云主机就会有这些内容: ~/MyDir/file.txt~/MyDir/MyLocal/.

下载的例子: 把云主机的这两个内容 ~/MyDir/file.txt~/MyDir/MyLocal/ 下载到当前文件夹 (末尾的 ./ 就是当前文件夹)

ssh 134.xxx.xxx.xxx:~/MyDir/file.txt ./
ssh 134.xxx.xxx.xxx:~/MyDir/MyLocal/ ./

2.7. 编辑器 (Compulsory)

"编辑器圣战" 是个流传甚广的老梗 (并不限于中文圈). 并且现代的编辑器特别是 VS Code 对切鸡批梯的支持特别地好. 但是当涉及到在服务器上操作的时候, 大家通常会用 Vim 或者 Emacs.

2.7.1. VS Code

在本地端, 你通常会使用 VS Code (Visual Studio Code). 直接在 Microsoft store 里下载安装即可. 它的支持特别的多, 用户群特别的广, 并不限于开发者圈.

但在云主机里你只能用非图形界面的编辑器: Vim 或 Emacs.

2.7.2. Vim

你需要在云主机上安装 Vim:

sudo apt install vim

这里有一些常用的命令:

  • :wq 保存和退出
  • :q 退出 (如果你没有更改文件)
  • :q! 强制退出
  • dd: 删除此行
  • gg: 到文件头 (相当于 "Ctrl+Home")
  • GG: 到文件尾 (相当于 "Ctrl+End")
  • /xxx: 搜索 "xxx". 输入了 "xxx" 后击 "Enter" 以搜索, 击 "n" 搜索下一个, 击 "N" 搜索上一个.
  • i 和 Esc: 进入和离开编辑模式

2.7.3. Emacs

你需要在云主机上安装 Emacs:

sudo apt install emacs-nox  # lite, or
sudo apt install emacs

emacs-nox 是不提供图形界面的那个版本的 Emacs, 比提供图形界面的 emacs 更轻量, 更适合服务器场合的使用.

这里有一些常用的命令 (这里的 C- 表示 "Ctrl-", M- 相当于 "Alt-"):

  • C-x C-s: 保存
  • C-x C-c: 退出
  • M-<: 到文件头 (相当于 "Ctrl+Home")
  • M->: 到文件尾 (相当于 "Ctrl+End")
  • C-p & C-n & C-b & C-f: 光标的上 ↑ 下 ↓ 左 ← 右 →.
  • C-SPC: 相当于按住鼠标 (然后可以用 C-p / C-n / C-b / C-f 移动光标选中区域)
  • M-w: 复制
  • C-y: 粘贴
  • C-a: 到行首 (相当于 "Home" 键)
  • C-e: 到行尾 (相当于 "End" 键)
  • C-l: 把光标所在行放到屏幕中央
  • C-d: 删除一个字符 (相当于 "Delete" 键)
  • C-z: 把 Emacs "最小化到系统托盘" (此时你在 WSL 的 bash shell 界面, 输入 fg 可以重新打开 Emacs 窗口 ("fg" 表示 front ground)) (在 Vim 你也可以这样做)

Note: Emacs 的快捷键跟 bash (也就是 WSL 的命令行) 有很多相似之处, 如 C-p & C-n & C-b & C-f & C-a & C-e & C-l, C-d.

可以下载我这个配置直接使用:

git clone https://github.com/OChicken/.emacs.d.git -b lite

3. 服务器 域名 证书

3.1. 概念区分

3.1.1. 主机 & 服务器

你的PC就是一个Linux主机. 一个主机会运行各种服务 (俗称后台服务). 通过 systemctl status 或Windows的任务管理器你可以看到各式各样的服务:

  • 提供网页浏览的 nginx.service;
  • 执行定时任务的 cronie.service;
  • 负责远程登录的 sshd.service;
  • 负责翻墙的 v2ray.service 和/或 xray.service;

甚至守护进程 emacs --daemon 本身也可以称作一个”服务”. 可见”服务器”这个词是个很泛化的概念. 你的一台不提供任何网页浏览服务的PC在上面所列的意义下其实也可以算作是”服务器”.

狭义的”服务器”是指在云端的提供网页浏览服务的主机. 其实如果单讲网页浏览服务的话, 这家伙甚至不需要部署在云, 你的PC就可以. 譬如说, 如果你给你的PC装了 Web服务 (Nginx 或 Apache2), 你就可以在浏览器里输入 localhost 看到它渲染出来的欢迎页; 接下来讲Hexo的时候, 也频繁用到 hexo s 作为本地预览你的博文

所以一个需要区分的地方就是, 云主机/云服务器, 跟本地主机(localhost)/本地服务器, 之间, 有什么不同, 前者多了什么?

3.1.2. 云主机 & 云服务器

加了”云”前缀的, 一言以蔽之, 就是 具有独立IP 的, 可被用户从世界上任何地方 访问 的主机/服务器. GitHub以及自购的VPS就是这种, PC就不是. 这里的”访问”, 可以指 通过浏览器进行Web访问 (需要安装 Web服务), 或者作为代理访问 (需要安装V2Ray或XRay) — 是的, 没装Web服务的云主机也可以称为云服务器, 只是它不提供网页浏览服务而已, 但可以提供翻墙服务.

没有独立IP的PC也当然可以装Web服务, 并把网页内容放在/var/www/html/, 但你只能在自己的电脑上打开, 自己阅读, 只为自己服务 (譬如说Hexo本地预览) — 但这也失去了”服务”的含义了. 其实PC并不是绝对不可以被外网登录的, 使用 内网穿透 这个功能即可. 但是我们一般不会拿自己的PC来被外网访问; instead, 作为一个toy model, 会拿 树莓派 作为内网穿透的尝试.

3.1.3. 配置文件 & 内容

Web服务的 配置文件 位于 /etc/nginx/ (for Nginx server) 或 /etc/apache2/ (for Apache2 server). 只需要修改 /etc/nginx/conf.d/index.conf, 其他文件尽量保持别动.

Web服务的 内容 默认位于 /var/www/html/. html或php等结尾的文件, 能被浏览器渲染出来成为我们在浏览器上看到的内容.

3.1.4. 域名

域名是跟IP地址绑定的较易识别的名字, 在浏览器里输入IP来访问网页很不方便因为IP是一串数字, 但是输入形如 "OChicken.net" 这样的英文名字就易记很多, 这就是域名. 可以理解域名为地名, 理解IP地址为地球上的地理位置. 地名有可能相同, 但在地球上的地理位置显然存在且唯一. 譬如说武汉有个柏林站, 显然不是德意志的柏林.

有各种各样的顶级域名: gov, com, cn, org, edu, 甚至 .monster… 域名抢注是很常见的事情.

3.1.5. 云服务器里的内容 & 域名地址

在浏览器地址栏里输入OChicken.net, 浏览器渲染出来的内容就是 /var/www/html/ 里的东西. 举例:

.
├── static
│   ├── Cola.png
│   ├── header.html            /* LAYOUT */
│   ├── postamble.html         /* LAYOUT */
│   ├── preamble.html          /* LAYOUT */
│   └── main.css               /* CSS    */
├── index.org
│── Makefile                   /* BUILD  */
│── blog.el                    /* BUILD  */
├── projects
│   ├── index.org
│   └── server
│       ├── Danke.gif
│       ├── index.org
│       └── path-to-files.org
└── research
    ├── GPQHE
    │   └── index.md
    └── index.html

若你打开的是目录 (如 OChicken.net/research, OChicken/projects) 且该目录里有一个名为index.html的文件, 那么浏览器就会渲染此文件的内容. 如果打开的是文件, 如 OChicken.net/static/Cola.png, 则浏览器就直接打开该文件. 文件类型也适用于html/php. 要想访问此目录内的其他html网页, 则就像打开文件那样去打开它. 这篇短小的 wiki 简要列举了几个例子.

3.1.6. 域名解析

域名解析就是将 域名 映射到 IP地址 的操作.

3.1.7. SSL 证书

SSL证书相当于一个网站的”身份证”, 证明这个网站是有一个真实存在的自然人所有的, 因为cyber space上有相当多的一部分网站并不是由自然人所有, 或者说是为不怀好意的黑客所有, 访问这些网站是不安全的. 没有”身份证”也不妨碍你的博客可以被访问, 但现在Google已经将没有证书的网站的搜索排序放到偏后的位置了.

GitHub本身就自带证书, 因为github/OChicken这个账号的所有者就是一个自然人. 自购的VPS需要自行搞定证书的申请.

3.2. 云主机的选择

GitHub服务器 是GitHub自带的, 你只需有一个GitHub账号即可在上面布局博客, 这个优势是显然的; 劣势在于不能被shell登录, 没法看到Linux的网页相关的配置, 所以不是一个很好的练习对象. 实际上, OChicken/OChicken.github.io (我已关闭此repo, 因为我自己不需要) 里面的内容就相当于 /var/www/html/ 里的内容, 只是GitHub把这些都隐藏起来了. 如果用GitHub服务器来搭博客的话, 跟它绑定的域名就是OChicken.github.io, 这是GitHub博客要求这样做的; 而用自购的云VPS或者树莓派来做的话则需要自行购买一个域名.

自购的云VPSGitHub 的区别在于, 前者可以通过 shell 以输入用户名和密码的方式登录, 但是后者不行, 所以 GitHub 并不是一个足够好玩的 toy model. 况且, 前者还能配置翻墙服务.

树莓派自购的云VPS 都可以通过shell输入账户密码登录, 进而可以看到Linux的网页相关的配置并可以DIY设置, 所以都是个不错的toy model, 况且树莓派比VPS更难的一处是要摆平内网穿透, 有点挑战价值. 但树莓派终究是不适合实战的. 因为要搞定内网穿透, 你终究还是得买一个云VPS作为一个中间跳板, 才能让托管在树莓派上的网页内容展示给公网. 虽然云VPS能做的事情树莓派都能做, 但在绝大多数场合, 你都需要一个云VPS作为中间跳板. 更何况, 树莓派的性能还很不行.

总结起来, 按配置复杂度从低到高: GitHub, 自购VPS服务器, 树莓派. 但比较有训练和实战价值的是自购的云VPS. 我们将在本文主要讨论它.

3.3. VPS

VPS服务器的选择非常多, Vultr, DO(DigitalOcean), 搬瓦工, Linode, 阿里云, 百度云, … flyzy小站 - Learning on the way有做相关测评.

我用的是DO. 借助GitHub提供的DigitalOcean学生优惠还能褥一波羊毛. 2019年The code is 2019GITHUB50-83f52d0d. 下面这些链接讲述了褥羊毛:

在买服务器的时候要上传公钥. 登录服务器成功之后立刻安装Nginx并启用服务

sudo apt install nginx-full
sudo systemctl enable --now nginx.service
systemctl status nginx.service

3.4. 域名

在Cloudflare上购买域名, 配置DNS, A记录 指向IPv4地址, 和 AAAA记录 指向IPv6地址. 提供域名出售服务的网站多如牛毛, 之所以推荐这家是因为它还能顺便搞定CDN.

完成以后, 在VPS的 /etc/nginx/conf.d/index.conf 上配置域名:

server {
    # ...
    server_name ochicken.net www.ochicken.net;
    root /var/www/html;
    error_page 404 = /404.html;
    # ...
}

使用如下命令检查语法 (无错则应弹出OK) 以及重启Nginx服务:

sudo nginx -t
sudo systemctl restart nginx.service
systemctl status nginx.service

3.5. 证书

证书是由通信双方 (站长我本人, 以及诸访客) 以外的第三方机构, 作为证书发布者, certificate authority, 简称 CA. 全世界有大大小小的CA. 面向个人建站的场合, 往往也使用免费的自签名的证书. 证书包括公钥和私钥, 但格式不尽相同: 常见公钥后缀是pem, crt, key, 常见私钥后缀是pfx, p12, pem, key (证书之间的转换(crt pem key) - 何惜戈 - CSDN博客).

3.5.1. 在Cloudflare上自己申请自己部署

在Cloudflare->SSL/TLS->Origin Server完成申请之后, 下载公钥pem和私钥key, 上传到VPS的 /etc/ssl/. 完成以后, 在 /etc/nginx/conf.d/index.conf 上配置证书:

server {
    # SSL configuration
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    ssl_certificate     /etc/ssl/ochicken.net.pem;
    ssl_certificate_key /etc/ssl/ochicken.net.key;
    # ...
}

3.5.2. 使用Certbot

Ref: 通过 Certbot 申请泛域名 HTTPS 证书及配置自动更新 - 简书

如果是个人建站, 使用自签名证书的话, 其实完全可以在VPS上解决, 只要有 域名和邮箱 即可, 也无需配置 /etc/nginx/conf.d/index.conf. 在服务器上

sudo apt install python3-certbot-nginx
sudo certbot --nginx  -d www.inhoge.com
sudo certbot --apache -d www.inhoge.com

然后启用以下两个服务:

sudo systemctl enable --now certbot.service
sudo systemctl enable --now certbot.timer

这将在/lib/systemd/system/下生成了两个文件: 服务certbot.service和定时器certbot.timer. 更改后者 OnCalendar=*-*-* 00,12:00:00OnCalendar=*-*-* 05:00:00, 每天凌晨5点更新证书. 然后如下重启服务, 即可定时更新自签名证书.

sudo systemctl daemon-reload
sudo systemctl restart certbot.timer

3.6. 安保措施

3.6.1. 防火墙

打开防火墙, 默认屏蔽所有浏览, 然后allow 22端口用于ssh登录, allow 443端口用于https访问. 而80端口因为用于不安全的http访问, 宜禁用.

sudo apt install ufw
sudo ufw enable
sudo ufw allow 22     # 登录专用端口, 必须设置
sudo ufw allow 443    # https 协议端口, 必须设置
sudo ufw status       # 查看服务器防火墙状态

3.6.2. 禁止 IP 访问

在完成上述设置后, 可以尝试在浏览器地址栏直接输入IP 134.xxx.xxx.xxx: 因为没有allow 80端口, 这是打不开的. 但是有一种做法可以依然做到通过IP访问: 输入https://134.xxx.xxx.xxx, 这时浏览器会提示说有风险, 但 只要手动跳过所有风险警示, 就能够以这样的方式通过IP访问到网站了. 为此须在VPS的 /etc/nginx/conf.d/index.conf 上设置

server {
    if ($host != $server_name) {
        return 404;
    }
}

3.6.3. 禁止非文文件访问

所谓”非文件访问” (我自己起的名字), 就是指在浏览器里输入的内容必须是对应于/var/www/html/目录里的一个文件, 否则就返回404.

server{
    # ...
    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
        if (-f $request_filename) {
            rewrite ^/(.*)$  /$1 break;
        }
    }
    # ...
}

4. Blog

如您在页脚处所见, 本站是由 Emacs Org mode 生成的, 虽然配色跟著名的 Hexo 博客框架的 NexT.Pisces 主题很像, 但并不是用 Hexo 生成的. 我会谈及如何做到这一点的.

4.1. 技术路线

4.1.1. 搭博客的方式

搭博客有很多种方式, 都可以在浏览器里输入IP地址看到发布的内容. 按易用程度来分, 大概分为图形界面的和依赖命令行的.

图形界面搭博客主要有 WordPress 和 Drupal. 你只需要在云服务器上安装它们指定的包, 就可以直接在 Chrome 上在线编辑博客, 就像写 QQ 空间那样简单. 这种方式完全 可以不去管Web网页服务器 的设置: 用shell进行最初一步的登录后, 直接把包的内容解压到 /var/www/html/ 里, 配置好域名和证书, 然后浏览器点开IP就有东西了. 你会看到无论哪个包, 解压出来的东西都含有 index.php 或者 index.html 等类似的文件. 这个文件显然很重要, 因为它是被网页服务器的配置文件定向指定的, 但里面写的啥并不必关心.

但这种方式不便于做 VC (版本控制). 我们通常会建议在 md 文件里面把博客写好, 再 通过命令行 一键发布. 代表作是 HexoHugo. 你可以发布到 GitHub Page, 也可以发布到自己的 VPS. md 是个很受非程序员欢迎的富文本笔记语言, 虽然用了一点命令行, 但你可以逐行 VC 做过什么改动, 这比基于图形界面的方式好很多. md 的优越性毋须多言, 用过都说好.

4.1.2. 动态 and/or 静态 博客

静态博客很”简单”: 因为访问者只能访问, 而不能作为用户登录这个网页并写comments. 这其实区分了读和写的两个权限, 访问者对网页的”写”过程要反馈到云服务器的 /var/www/html/ 中, 这涉及到比较多的权限问题. 要实现访问者能够”写”网页的目的, 还需要MySQL和PHP, 加上Linux, 合起来就是LAMP或者LNMP — 不少介绍搭博客的博客都说用”宝塔LAMP”搭博客, 就是指这个东西.

WordPress是可以实现动态博客这个功能的, 但它是个高度封装好了的产品, 不适合作为玩具; 且作为academic purpose的个人展示页, 静态博客即可.

这篇博文提及到动态和静态博客的区别:

动态博客有个 risk 是会被 DDOS. 虽然说这种个人博客类几乎没这风险, 但是当用作梯子的时候可能还是得注意一下.

4.1.3. Hexo, Hugo & Emacs Org mode

HexoHugo 是两个几乎平级的博客框架, 它们相比 WordPress 要说有什么更 "高级" 一点的话, 恐怕只有生成和发布那一步是要用一点点的命令行, 以及写 md 是纯文字的, 不像 WordPress 那样 WYSIWYG (所见即所得).

我之前用过很长一段时间的 Hexo. 工作方式是这样的: 本地写 md, 用 hexo g 生成 html 文件, 然后 hexo s, 在浏览器的 http://localhost:4000/ 预览, 明确无误就 hexo d 发布. 就这么简单的三连.

顺带一提: "博客" 这个词给人第一感觉是托管在 " 服务器" 里的. 实际上如前面提到的, "云" 这个前缀只不过是有一个独立IP可供世界各地的人访问而已; 如果你只想自个儿看, 那在一个甚至不需要联网的 PC 里都可以完成, 这个东西就是 http://localhost, 本地主机.

Emacs 的 Org mode 是比 md 更 "高级" 一点的笔记系统, 用过 Org mode 的都说它比 md 好. 所以一个考虑是, 能否像使用 "md + Hexo" 那样使用 "Org + Hexo"?

你确实可以在 Hexo 里使用 Org, 这需要安装 npm install --save hexo-renderer-org 这个插件, 之后就可以像使用md文件那样使用org文件并实时渲染网页了. Refs:

但是 Hexo 对 Org mode 的支持很落后. 这个插件的作者几乎不维护了 (看最后的更新时间). 著名 Emacs 用户刘家财先生也提及到, 该插件对 Org mode 的支持很糟, 他因此转粉了 Hugo.

很多 Emacs 用户都安利 Hugo, 因为这个博客框架原生就支持 Org. 我曾考虑过 "Org + Hugo" 的方案. 但是 Hugo 没有 NexT. 我对 NexT.Pisces 粘性很大, 对 Hugo 没有什么感情.

然而我当时想实现的 request 是, 我希望的是用 Emacs Org 强大的 export 和索引功能去生成博客, 就像 HexoHugo 所做的那样 (相比之下后两者其实算是第三方软件). 可是Emacs Org mode 生成的 TOC 总是位于文章开头, 而不像 Hexo & Hugo 那样可以放侧边. 这个 sidebar TOC 让我心心念念了好久.

所以当时想过这么些方案:

  1. 向 Hexo 低头, 发博客前把 Org 转为 md (但这样就不能像之前那样 md + Hexo 愉快地工作了)
  2. 投身 Hugo, 可是 Hugo 的 NexT 主题做得并不好 (外观主题之类的虽然不是个事儿, 但又确实是个事儿)
  3. 使用 Org 生成博客, 接受那个总是摆在前面的 TOC.

没有一个是好的选择.

后来我意识到一个突破点是, 一个网页之所以 "好看", CSS 文件起了 90% 的作用. 只要你意识到这一点, 就不难发现, Hexo 和 Hugo 其实只是通过一系列易用的接口, 帮你把 CSS 生成出来了而已. 当你不确定自己喜欢哪个主题以及这个主题的哪一个子主题的时候, 是可以通过他们给定的配置文件自己慢慢调的; 而 当你选定了一个主题样式之后, 就可以在这个已经生成好的 main.css 文件上面魔改了, 改好之后把这一行加入到 Org 文件的开头即可:

#+html_head: <link rel="stylesheet" type="text/css" href="path/to/main.css"/>

4.1.4. Your Choices of CSS

当时启发我意识到找到CSS等于完成任务一大半的是来源于这几个链接:

也就是说你只要在 Org 文件开头加上下面任意一条:

#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://fniessen.github.io/org-html-themes/src/readtheorg_theme/css/readtheorg.css"/>
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="fniessen.github.io_org-html-themes_src_readtheorg_theme_css_readtheorg.css"/>
#+html_head: <link rel="stylesheet" type="text/css" href="https://orgmode.org/worg/style/worg.css"/>
#+html_head: <link rel="stylesheet" type="text/css" href="/path/to/worg.css"/>

就可以使用 CSS. OlMon 分享了一系列用于 Org 文件的 CSS themes:

此外也有 Dr. gongzhitao 的

可见现成的选择是很多的.

我自己这几天在重构博客的时候重点参考过的只有两个项目的 CSS:

  1. HexoNexT.Pisces 生成的 CSS, 文件是 ./public/css/main.css. 原因无他, 就是喜欢其配色.
  2. Worg 官方指定的 "参考答案", 这个项目的 worg/style/worg.css 就能忠实复现出可以折叠显示的 sidebar TOC 的效果.

4.2. 用 Org mode 生成博客

我只推荐这两篇作为参考, 足够简单, 可以立刻上手

Worg 首页就提供了如何构建该网站的 repo (它们那个网站就是完全用 Org mode 生成的), 但项目很庞大, 个人不推荐.

你的本地博客目录应该有这些东西:

.
├── static
│   ├── Cola.png
│   ├── header.html            /* LAYOUT */
│   ├── postamble.html         /* LAYOUT */
│   ├── preamble.html          /* LAYOUT */
│   └── main.css               /* CSS    */
├── index.org
├── Makefile                   /* BUILD  */
├── blog.el                    /* BUILD  */
└── projects
    ├── index.org
    └── server
        ├── Danke.gif
        ├── index.org
        └── path-to-files.org

我分三个小节去介绍它们. 这个图基本上就是本站的 sitemap, 从浏览器地址栏按图索骥地输入路径即可查看. blog.elMakefile 是个人隐私, 不公开, 但我提供了 blog.el 的参考文件, 它跟我真正用的没有太大差别; Makefile 没有参考意义, 就不展示了.

4.2.1. Layout: header, preamble & postamble

Org mode 生成出来的 html 文件的结构如下所示. 把 head.html, preamble.htmlpostamble.html 范围所在的内容写到你自己的 layout 即可, 内容自行设计.

 1: <!-- head.html begins here (ends in line 7) -->
 2: <meta charset="UTF-8">
 3: <meta name="generator" content="Emacs 29.1, Org mode 9.6.6">
 4: <link rel="icon" type="image/png" href="/images/Cola.png">
 5: <link rel="stylesheet" href="/css/main.css">
 6: <link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
 7: <!-- head.html ends here (begins in line 1) -->
 8: <body>                                           <!-- body begins here (ends in line 39) -->
 9:   <div id="preamble" class="status">             <!-- preamble.html begins here (ends in line 20) -->
10:     <header class="header" itemscope itemtype="http://schema.org/WPHeader">
11:       Title
12:       <nav>
13:         <ul id="menu">
14:           <li><a href="/">Home</a></li>
15:           <li><a href="/research/">Research</a></li>
16:           <li><a href="/projects/">Personal Projects</a></li>
17:         </ul>
18:       </nav>
19:     </header>
20:   </div>                                         <!-- preamble.html ends here (begins in line 9) -->
21:   <div id="content" class="content">             <!-- contents begins here (ends in line 33) -->
22:     <h1 class="title">The title of the post</h1>
23:     <div id="table-of-contents" role="doc-toc">  <!-- TOC begins here (ends in line 31) -->
24:       <h2>Table of Contents</h2>
25:       <div id="text-table-of-contents" role="doc-toc">
26:         <ul>
27:           <li><a href="#第1节">第1节</a></li>
28:           <li><a href="#section-B">Section B</a></li>
29:         </ul>
30:       </div>
31:     </div>                                       <!-- TOC ends here (begins in line 23) -->
32:     <!-- main matter -->
33:   </div>                                         <!-- contents ends here (begins in line 21) -->
34:   <div id="postamble" class="status">            <!-- postamble.html begins here (ends in line 38) -->
35:     <footer>
36:       Powered by GNU Emacs 29.1 (Org mode 9.6.6)
37:     </footer>
38:   </div>                                         <!-- postamble.html ends here (begins in line 34) -->
39: </body>                                          <!-- body ends here (ends in line 8) -->

除了 shellcodes 提供的, 你还可以去找 Hexo ./public/ 目录里面的 html 文件, 这些文件是适配于 NexT.Pisces 主题的. 但我依然建议你参考前者, 以及任何一个由 Org 生成的 html 文件. 原因在于二者生成的 html 有很多 tags 和 class 是不一样的.

4.2.2. Build: Makefile 和 blog.el

hexo g, hexo s, hexo d 三连可以类比为用 Makefile 去做 project build 的 make g, make s, make d 三连.

hexo g 的等价命令是 emacs --script blog.el. blog.el 的核心内容如下, 这会把所有 Org 文件生成 html 在当前目录.

(package-initialize)
(require 'ox-html)
(require 'ox-publish)

(setq org-publish-project-alist
       '(("blog"
          :base-directory "."
          :publishing-directory "."
          :recursive t
          :publishing-function org-html-publish-to-html)))

(setq org-html-head      (with-temp-buffer (insert-file-contents "static/head.html")      (buffer-string))
      org-html-preamble  (with-temp-buffer (insert-file-contents "static/preamble.html")  (buffer-string))
      org-html-postamble (with-temp-buffer (insert-file-contents "static/postamble.html") (buffer-string)))

(add-to-list 'org-html-mathjax-options '(path "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"))

(org-publish-project "blog")

hexo s 的等价命令是 python -m http.server -d ./, 然后在浏览器里输入 http://localhost:8000/ 以预览.

hexo d, 也就是 deploy, 八仙过海各显神通, 本节开头提到的两个重点参考的博文就提及了 scp, rsync, Emacs Tramp, 甚至像 Hexo 党那样自建 git repo ([1]​, [2]​) 都可以.

我的 blog.el 就在当前目录里, 读者可以自行下载. 但是它写得比较复杂, 因为为了特地适配于我自己的目录结构, 我对它进行了很多功能上的扩充, 用到了一点点 elisp 初步, 有一定的参考价值.

Makefile 你可以暂且把它理解为一个执行一系列 shell 命令的集锦 (当然它的真实用途比这强大得多, 不然大家都靠 .sh 文件干活了), 示例如下

g:
        @date
        emacs --script blog.el g

然后直接 make g 即可. 所以用在本博客的 Makefile 没有参考意义, 不作为附件展示.

4.2.3. CSS

既然你都能打开我的博客了, 那肯定能知道我这个博客的 CSS 长什么样了, 并且可以顺便知道把前面讲的三个 layout 文件 (我虽然基本上参考了 NexT.Pisces, 但砍掉了很多). 正如前面所述, 我只参考过两个 CSS:

  1. NexT.Pisces 生成的 CSS 文件. 它非常大, 足有 2000+ 行.
  2. Worg, 近 1000 行, 但最最值钱的就 第 74-110 这几行的涉及 TOC 的配置, 其他的相比就不那么重要.

只要有这两个, 就可以动手魔改 CSS 了.

首先去处理一下我心心念念的 sidebar TOC 的问题, 这花了点小 trick. 回顾前面的 layout, 我在魔改的时候发现, 如果把 TOC 的那片代码移到 nav tag 里面, 放在 menu 的 ul (unordered list) 后面, 那么它的位置是刚刚好的!

所以接下来的问题就是怎么让这片代码在每次生成 html 文件的时候都做这一步. ChatGPT 教我:

<script>
  var toc = document.getElementById('table-of-contents');  /* get TOC */
  document.querySelector('nav').appendChild(toc);          /* Append the TOC to the nav */
  toc.querySelector("h2").innerHTML = "<i class=\"fa fa-list fa-fw\"></i> TOC";  /* change title */
</script>

把这个 js snippet 放到 footer tags 后面即可. 因为 footer 在 postamble.html 里面, 而 postamble 由也还在 body 里面, 所以这个 js 会检索文档里面的 TOC 和 nav tag, 做 nav.appendChild(toc);. 代码后两行是把 TOC 的 "Table of Contents" 这一长串改为 "TOC" 这三个字母, 就是你现在看到的左上角的那一小块.

在魔改 CSS 文件时还有这些事情可以值得注意:

  1. Org 文件里的一星表示 h1, 二星表示 h2, 依此类推. 然而在它导出的 html 文件里, h1 tag 包住的是博文的 title, 而 h2 tag 包住的是文件里标一星的那些节.
  2. CSS 文件的 ul (unordered list), ol (ordered list) 之类的对象, 在 Org 生成的 html 里分别是叫 org-ulorg-ol.

暂时就这两个重要的, 其他不那么重要的自行摸索.

一个题外话是, 我的设计不像 NexT.Pisces 那样那么宽. 用宽页或窄页各有所爱, 我选后者的一个考虑是, 窄页的宽度正好是跟一个 A4 pdf 在 (几乎任何一个) 阅读器里用 Fit Page view 以适应屏幕高度的方式缩放所呈现出来的宽度是几乎一致的, 从而 阅读博客页面跟阅读文献的 pdf 有近乎一样的体验.

虽然 NexT.Pisces 生成的 CSS 文件很大, 但 (至少是我想要有的) 核心的那些 (如你在本站所见的配色和效果) 其实 不到 300 行, 也不需要任何 js. 而从 NexT 成品的 2000 行代码砍剩 300 行累计只花了不到一周的时间.

​一想到这事我就想起了退钱哥的那句著名meme陈年老梗——███,退钱!

4.3. Empower your blog via Nginx config

声明: 因为本小节要频繁地对 /etc/nginx/conf.d/index.conf 进行改动, 每次改动之后都需要用以下命令重启Nginx Service:

sudo systemctl restart nginx.service

4.3.1. 对诸文件格式的log

设置文本, 图片, 视频的log的过期时间:

server {
    # ...
    location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
        access_log   off;
        expires      1d;
    }

    location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
        access_log   off;
        expires      10m;
    }

    location /nginx_status {
        stub_status on;
        access_log off;
    }
    # ...
}

4.3.2. 线上图书馆: autoindex & fancyindex

形如keylloIndex of /download/cling那样, 我希望搭建一个个人的线上图书馆, 用的就是Nginx的autoindex and/or fancyindex功能.

首先在/var/www/html/library/里放入你想放在线上图书馆展示的书.

对 autoindex, 编辑 /etc/nginx/conf.d/index.conf:

server {
    # ...
    location /library {
        autoindex on;                       # 开启目录文件列表
        autoindex_exact_size off;           # on: 显示出文件的确切大小, 单位是bytes; off: 显示出文件的大概大小, 单位是kB或者MB或者GB, human-readable
        autoindex_localtime on;             # 显示的文件时间为文件的服务器时间
        add_header Cache-Control no-store;  # 让浏览器不保存临时文件
    }
    # ...
}

autoindex是nginx自带的索引, 界面并不十分美观; 美观与否倒是其次, 更糟糕的是其配置的文件名限制在50个字符, 后面就以省略号代之, 作为线上图书馆你看不到全名当然是很不好的, 如openbsd - nginx: Long filenames in directory listing - Unix & Linux Stack Exchange这个问题提到, autoindex并不提供调整列宽的功能. 幸好 nginx 有个插件叫fancyindex. 在configuration - Stop nginx from trimming file name in directory list? - Server Fault提到了fancyindex的apt安装包的名字叫 libnginx-mod-http-fancyindex. 安装了这个包后, 检查软链接

/etc/nginx/modules-enabled/50-mod-http-fancyindex.conf -> /usr/share/nginx/modules-available/mod-http-fancyindex.conf

的存在有效性. 然后编辑 /etc/nginx/conf.d/index.conf:

server {
    # ...
    location /library {
        fancyindex on;
        fancyindex_name_length 255;
        fancyindex_exact_size off;
        fancyindex_localtime on;
        fancyindex_css_href "/library/.styles.css";
        add_header Cache-Control no-store;
    }
    # ...
}

Refs:

4.3.3. 默认编码

这是在使用fancyindex的时候发现的一个问题: 打开图书馆, 里面的txt文件只要是含有非英文字符的, 全都乱码. 为此, 只须在 /etc/nginx/conf.d/index.con f的server{}块外加上 charset utf-8 即可:

charset utf-8;

server {
    # ...
}

4.3.4. 特定目录的访问控制

所谓访问控制就是某些网站, 点开它, 有个弹窗, 要你输入用户名和密码, 以限制对该网站内的特定路径的访问. To this end,

sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd OChicken  # create passwd
sudo htpasswd    /etc/nginx/.htpasswd OChicken  # modify passwd

八卦: 虽然限制访问的功能通常与 HTTP服务器 (如Nginx、Apache等) 相关, 但是用于管理密码文件的工具 htpasswd 是由 Apache 软件基金会开发的, 因此它是在 apache2-utils 里面。这个工具可以在许多不同的HTTP服务器中使用, 不仅限于 Apache. 在 Nginx 等其他 HTTP服务器中, 也可以使用 htpasswd 工具来创建和管理密码文件, 以实现基本身份验证功能. 这是因为 htpasswd 工具提供了一种通用的方式来管理密码文件, 可以在多个 HTTP服务器中共享使用.

完成账号密码设置之后编辑 /etc/nginx/conf.d/index.conf:

server {
    # ...
    location /manuals {
        auth_basic           "Administrator's Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
    # ...
}

Refs:

5. Bypass GFW

London.jpg
(这个图是伦敦涂鸦墙, 详见报道12)

我在一月份的时候重置了本网站涉梯子的几乎所有功能, 包括

  • 基本使用
  • CDN保护
  • 借助Cloudflare WARP访问ChatGPT

proxy.svg

5.1. 基本使用: V2Ray / XRay

V2Ray 跟 XRay 是两个 (几乎可以说是兼容的) 项目, V2Ray是更出名一点, 因为它更早 (但比Shadowsocks晚一点), 而XRay是V2Ray的升级版, 我用的是XRay. 作为使用者其实不需要了解得太细; 而且就算你想了解关于这两个项目的更多信息, 那也得先出了墙才有可能.

5.1.1. Server

使用项目开发组提供的脚本进行安装和卸载 (注意是”安装”不是”配置”噢, 配置要自己动手.)

wget https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh  # V2Ray
wget https://github.com/XTLS/Xray-install/raw/main/install-release.sh                     # X-Ray
sudo bash install-release.sh
sudo bash install-release.sh remove                                                       # if you want to remove
sudo systemctl enable --now xray.service

最后一行表示开启 xray.service. 安装之后你的系统会多出这些文件 (script 会告诉你的):

  • /usr/local/bin/xray
  • /usr/local/share/xray/geoip.dat
  • /usr/local/share/xray/geosite.dat
  • /usr/local/etc/xray/config.json
  • /var/log/xray/
  • /var/log/xray/access.log
  • /var/log/xray/error.log
  • /etc/systemd/system/xray.service
  • /etc/systemd/system/[email protected]

V2Ray用户请自行更改为v2ray (反正二者在基本使用的层面是几乎兼容的). 下文我将以XRay为例讲解.

在Server端需要配置两个文件: XRay的 /usr/local/etc/xray/config.json 和Nginx的 /etc/nginx/conf.d/index.conf.

编辑XRay的配置文件 /usr/local/etc/xray/config.json. 本站用的是XRay, 但它同时支持 VMess (V2Ray首创) 和 VLess (XRay首创) 两种协议, 可以同时开启.

{
  "log": {
    "access": "/var/log/xray/access.log",
    "error": "/var/log/xray/error.log",
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 16388,
      "protocol": "vless",
      "settings": {
        "clients": [
          {
            "email": "XRay User",
            "id": "2428e3bf-fa3d-456e-abb2-57e7cb796090",
            "alterId": 0
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
          "path": "/6f1d804"
        }
      }
    },
    {
      "listen": "127.0.0.1",
      "port": 16389,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "email": "V2Ray User1",
            "id": "3b30d077-7c25-40af-ac15-bc210d2538e3",
            "alterId": 0
          },
          {
            "email": "V2Ray User2",
            "id": "2322c6f1-4e03-43a8-829e-df9c7398a0df",
            "alterid": 0
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
          "path": "/7831ee2"
        }
      }
    }
  ],
  "outbounds": [
    {
      "tag":"default",
      "protocol": "freedom"
    },
  ]
}

这个例子特地展现出多用户配置的特性: 可以某些用户使用 VLess, 某些用户使用 VMess, 井水不犯河水. "id"就相当于密钥; "path"则是跟”port”紧密关联的, 接下来配置Nginx就会讲到. 完成这个文件的配置后使用如下命令检查语法是否有错误:

sudo xray -test -c /usr/local/etc/xray/config.json

然后编辑Nginx的配置文件 /etc/nginx/conf.d/index.conf:

server {
    # ...
    location /6f1d804 {
        proxy_redirect off;
        proxy_pass http://127.0.0.1:16388;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        # Show realip in v2ray access.log
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /7831ee2 {
        proxy_redirect off;
        proxy_pass http://127.0.0.1:16389;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        # Show realip in v2ray access.log
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    # ...
}

端口16388和地址 6f1d804 都是随便设的. 虽然这里出现了port的概念, 初学者会以为这是跟22和443相似角色, 是对外的, 是要经过网卡的 — 实际上你 并不需要 到ufw那里开启16388端口和16389端口. Warum? 注意一下 http://127.0.0.1:16388 这种写法: 这表明端口16388是 本地端口, 表明Nginx会把对 OChicken.net/6f1d804 的访问做 本地端的端口转发, 转发给监听了16388这个端口的应用程序; 而监听 (listen) localhost (127.0.0.1) 端口16388的应用程序正是 xray.service. 这正是 进程间通信 的一个生动的例子.

完成好上面两个配置文件后 务必 用下面的命令重启对应的服务:

sudo systemctl restart  xray.service
sudo systemctl restart nginx.service

现在可以解释一下本节开头的那张图了, 以path 6f1d804 和16388端口为例. 从墙内访问Google的流量 (黑线) 经本地端的 xray.service 后, 伪装 成了访问 OChicken.net/6f1d804 的流量 (被灰线包裹的黑线). 该流量进入本站的443端口, 进入Nginx处理. 因为Nginx里有了上面这片代码, 它发现, 噢, 访问 OChicken.net/6f1d804 这个目录的流量应当转发给 本地端口 16388, 于是脱下灰线的大衣, 这些流量 (黑线) 就这么内部转发到XRay服务的inbound了. 经过其内部的身份验证 (验证id) 等操作, 就从outbound发出, 最终去到Google.

至此, Server上的基本配置就完成了, 你的云主机就成了一台”云服务器”, 即使你不安装博客, 浏览器里点开域名发现什么都没有, 它也是提供了服务: 翻墙服务.

5.1.2. Local

用于Windows和Android的客户端分别是 V2RayNV2RayNG, 读者可以到那里直接下载.

但是GFW对GitHub做了限流, 从项目repo下载是很慢的. 本站提供两个最新版的文件以便读者直接下载: V2RayN-With-Core release 6.27 (2023.06.18) 以及 V2RayNG release 1.8.6 (2023.07.30). 它们的 sha256 校验码如下. 这两个文件跟你从项目repo直接下载的文件是一模一样的, 童叟无欺.

5ec0066ddff21407b00fe9ca40408642051eb2a3ceb5fc50c92f94adfd302668  # V2RayN-With-Core release 6.27
f3cd3835916066be647c5c5c334e15a77c5e1ddc2947b4635e765a2331447da2  # V2RayNG release 1.8.6

需要声明: 我只是把文件搬运到这里而已, 没有给本站设置自动镜像去更新最新版本的客户端: 我没这个义务这么做, 且本文发布以后估计至少一年不会更新它. 但我相信, 一, GitHub就算被限流, 你也还是可以下载的, 有点慢而已; 二, 即使是旧版的客户端都能跑起来, 在这方面要相信开发组的实力.

Windows和Android的配置内容是几乎一样的, 我就仅以Windows为例截图展示好了. (印象中) V2RayN打开后会 "闪退" — 其实它不是闪退, 而是缩小到右下角的system tray去了. 如下两幅图我用GIMP修改过, 跟前面Server部分的配置是一样的, 只须保证 用户ID路径 这两个东西不填错即可. 记得左图下方的 "系统代理" 要选择 "自动配置系统代理".

有多年翻墙经验的老用户会问: 我是不是要在chrome下载Proxy SwitchySharp然后到浏览器里配置一番什么127.0.0.1啊端口号10808啊? 不需要的, V2RayN已经给你办好了.

到这一步, 你其实就已经出墙了, 可以用ifconfig.mewho.is来看看自己的IP, 应该就是VPS的IP. 先去喝杯可乐吧🎉

现在可以解释一下本节开头的那张图了, 刚才已经解释过服务器部分, 现在讲左边PC的部分. 开启梯子后, 你对Google的访问, 会先经过一番 本地端口转发, 默认端口都是10808 for socks和10809 for http. 而所谓 "开梯子" 就是指 xray.service 启动了, 该服务监听本地端口10808的消息, 收到你访问Google的流量后, 给这个流量套上一层灰色大衣 (图中灰线), 伪装 成了访问 OChicken.net/6f1d804 的流量 (被灰线包裹的黑线). OChicken.net是个人畜无害的个人博客, 所以很顺利地出墙了. 接下来就是上一小节服务器处理这段流量的故事.

本文应该也会有少部分Linux用户 (我本人就是, 重度的那种), 一定会很关心Linux上怎么翻墙. 第一步, 先像服务器配置那样, 下载 install-release.sh 安装XRay并开启 xray.service; 第二步就是编辑 /usr/local/etc/xray/config.json, 其内容怎么来呢? 很简单. Android或Windows上配置好之后, 点 "Export full configuration to clipboard", 把这些东西粘贴到此文件里去即可. 你最终看到的文件将会长这样. 最后restart xray.service.

5.2. CDN保护

到这一步, 如果你在terminal或Windows Power Shell里做 ping OChicken.net, 或你自己的域名, 会看到真实的IP, 就是那个在VPS供应商上面买的那个IP. 这种 "IP裸奔" 的做法是很不安全的. 并且更糟糕的是, 如果你的VPS供应商被墙掉, 那么你就没法打开OChicken.net, 也没法翻墙了, 因为DNS解析出来的IP是在被墙的IP段里面的. 在 Weißes Blatt Widerstand

I saw that there were so many people around the same age as me who took part in the white paper movement with me, who have been arrested and imprisoned. So I feel that I will always be in pain and have uncontrollable anxiety if I don't stand up and speak out on their behalf, even though there are great risks involved in doing so.

的时候这个IP就被墙了, 行业黑话叫 ping不通 了. 大陆无法访问我的博客, 也不能使用我的梯子. 我在 2023.01.05 的时候使用CDN修复了博客, 从大陆可以访问, 并在随后几天里从原来的Shadowsocks转为现在的{V2/X}Ray, 修复了梯子给家人朋友用. 但这时候我的IP在大陆还是不能ping通的. 2023.03.09 这一天, 我发现可以ping通了. 也就是说2023.{01.05-03.09}这段时间, 博客+梯子之所以都能用, 得归功于CDN.

事后复盘IP被墙这事, 我认为是那段时间gov.ccp拉起了非常高级别的警戒, 把所有大陆以外的VPS供应商的IP都墙掉了, 行业黑话叫 屏蔽IP段. DigitalOcean是主流的VPS供应商之一, 就自然地在被墙的列表上了.

CDN是个很成熟的技术, 以至于你顺手点一下开关, 就配置了CDN了. 登录Cloudflare, 点开你买的域名, 左侧sidebar有个DNS, 在A记录那里, 把Proxy status打开, 小云朵变成橙色, 就表明启用CDN了. 在1.4节配置域名的时候应该见过这个界面, 我觉得就不必截了.

怎么知道是否已经启用CDN了呢? 同样是ping域名, 这时候弹出的IP就应该不是VPS的域名了, 而是变成了别的. 把这个IP复制到ipinfo.iocip.cc, 就可以看到相应的信息, 会显示是Cloudflare节点.

现在可以解释一下CDN大致是怎么工作的. ping OChicken.net得到的是Cloudflare的节点IP这事表明, 披着灰色大衣的那个流量经大陆方面的DNS解析后, 会根据此IP先抵达Cloudflare的节点; Cloudflare 发现这个流量是要去OChicken.net的, 他们内部记录了OChicken.net的真实IP (这其实是你自己写在配置面板里的), 就把这个流量发到真实IP那里去. 疏理一下:

  • 0个proxy: 大陆 -> Google, 直连. 这是不可能的.
  • 1个proxy: 大陆 -> 你自己的梯子 -> Google.
  • 2个proxy: 大陆 -> Cloudflare CDN节点 -> 你自己的梯子 -> Google.

Cloudflare CDN节点和你自己的梯子都是proxies, 而且都是必要存在的: 自己的proxy是用来伪装成访问合法网站, 使得大陆方面的DNS能够放行; Cloudflare 的proxy用于保护自己的梯子的真实IP, 这样就算发生了 Weißes Blatt Widerstand 这种团灭VPS IP的事情, 照样能访问自己的博客+梯子.

5.3. 借助 Cloudflare WARP 访问 ChatGPT

OpenAI为了防止僵尸网络攻击以耗尽其计算资源, 认为所有的VPS的IP都是malicious的, 就把所有主流VPS供应商的IP段都列入黑名单里. 所以未配置此步时, 打开ChatGPT会发现access denied.

借助Cloudflare WARP可以解决这个问题. 大体思路跟用梯子类似: 在你的梯子跟ChatGPT之间, 又插入了一个proxy relay, 是Cloudflare的edge节点. 对比一下访问Google和ChatGPT:

  • 访问Google,   要2个proxies: 大陆 -> Cloudflare CDN节点 -> 你自己的梯子 -> Google.
  • 访问ChatGPT, 要3个proxies: 大陆 -> Cloudflare CDN节点 -> 你自己的梯子 -> Cloudflare edge节点 -> ChatGPT.

Cloudflare本身不提供VPS服务, 并不在ChatGPT的屏蔽列表当中, ChatGPT认为从Cloudflare edge发出的chat请求是innocuous的, 就不会拦截.

To this end, 你需要在你的梯子上配置WARP. 以下是三个比较有用的链接. 配置WARP只需要在VPS完成, 不用动PC的设置.

对这些连接稍微补充一点: 在涉及到WARP的tutorials里, 有一个令人一时半会转不过弯来的角色切换: 你的VPS, 对Cloudflare edge而言, 是客户端.

还有另一个可选的一步是对{V2/X}Ray的配置再细粒化. 你可以设置, 当你要访问ChatGPT时候, 就经WARP; 不访问ChatGPT的时候, 就不经WARP.

5.3.1. 配置WARP

登录VPS, 做如下命令安装WARP.

curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
sudo apt update
sudo apt install cloudflare-warp

接下来在你自己的服务器上, 注册客户端并配置, 这样才能使用Cloudflare的节点:

warp-cli register              # 注册客户端
warp-cli account               # 查看账号信息
warp-cli settings              # 查看设置信息

这些configurations位于 /var/lib/cloudflare-warp/. 然后开启socks5 proxy:

warp-cli set-mode proxy

再查一遍 warp-cli settings 会看到 Mode: WarpProxy on port 40000. 然后连接:

warp-cli connect
warp-cli enable-always-on

最后, 测试socks代理,理检查IP是否改变. 期望结果是, 显示的是Cloudflare的IP而不是VPS的IP.

systemctl status warp-svc.service
curl -x socks5://127.0.0.1:40000 ifconfig.me
curl -x socks5://127.0.0.1:40000 cloudflare.com/cdn-cgi/trace

如果我印象中没记错的话, 到这一步, 你其实已经可以访问ChatGPT了. 这个原理跟配置proxy, proxy内部怎么运作, 是很相似的, 就不赘述了. 现在如果你登录Google, 如果还给你登录提示, 同样会发现, 你的IP变了.

5.3.2. 细粒化配置{V2/X}Ray

根据是否访问ChatGPT (以及别的网站) 而决定是否经由40000端口转发给WARP的这个功能可以在{V2/X}Ray里配置outbound的规则. 编辑 /usr/local/etc/xray/config.json

{
  // 这是inbound等别的配置, 前面讲过
  "outbounds": [
    {
      "tag":"default",
      "protocol": "freedom"
    },
    {
      "tag":"ChatGPT",
      "protocol":"socks",
      "settings":{
        "servers": [
          {
            "address": "127.0.0.1",
            "port": 40000
          }
        ]
      }
    }
  ],
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "outboundTag": "ChatGPT",
        "domain": [
          "domain:chat.openai.com",
          "domain:ifconfig.co"
        ]
      }
    ]
  }
}

我这里还顺手加入了个ifconfig.co, 它跟ifconfig.mewho.is的角色是一样的. 这样配置时, 当打开ifconfig.me, 显示的IP是你VPS的IP; 当打开ifconfig.co, 显示的是Cloudflare节点的IP.

Remember, 因为你动过了 /usr/local/etc/xray/config.json, 可别忘了restart xray.service 使配置生效.

6. Vielen Dank

Danke.gif

请我一瓶可乐 (1.3EUR)/一支雪糕 (1.5EUR)/一杯喜茶 (30CNY)/一顿鑫隆 (5.9EUR)吧😋

PayPal: [email protected]; WeChat Pay

Footnotes:

1

就拿本地写博客然后上传这事来讲, 就涉及到编辑器偏好 (please refer "编辑器圣战", 这个梗出来的那个年代 VS Code 应该还没出生, 所以现在应该有三方在竞争) 以及在你对应的操作系统上的配置. 如果你是完全的小白, 那可能会花大量的时间在折腾这个你自己用得顺手的个性化配置上面. 然后, 博客用什么框架也是个圣战: 你可以写 md, 然后用 Hexo 去发布; 如果你想像本文作者那样手动魔改 css 并用 Emacs Org Mode 来写 … Org Mode 是很好用, 但它是 Emacs 的招牌产品, 应该不便于在别的编辑器上用, 如果你说想用 VS Code 或 Vim 来做 Org Mode 的工作, 这就又回到开头的编辑器偏好了 … 作为一个忠实的 Emacs 用户, 我很难在这方面帮到你…

2

如果你说能否完全用 Windows 的 Power Shell 的命令来操作, 而完全不使用 Linux command line 来做, 那也不是不行, 但你要是有这个技术那就不是本文的读者了.