用Cloudflare Tunnel把FreshRSS搬回办公室电脑

阿里云服务器快到期了。上面跑着一个 FreshRSS,地址是:

https://rss.seis-jun.xyz/

怎么办?继续续费当然可以,但我又觉得有点亏。RSS 阅读器这种东西,本质上就是给自己用的,不需要一台公网 VPS 常年挂着。

于是这次试了一下:把 FreshRSS 搬到办公室 Fedora 电脑上,用 Podman 跑容器,再用 Cloudflare Tunnel 暴露到公网。

最后访问地址不变:

https://rss.seis-jun.xyz/

大概结构是这样:

浏览器 / RSS 客户端
    ↓
Cloudflare DNS / Tunnel
    ↓
办公室 Fedora 上的 cloudflared
    ↓
http://127.0.0.1:8080
    ↓
FreshRSS Podman 容器

这样有几个好处:

  • 不需要阿里云 VPS。
  • 办公室电脑不需要公网 IP。
  • 不用在办公室路由器上开 80/443/8080。
  • FreshRSS 还是完整自托管,不是静态 OPML 页面。

先把域名接到 Cloudflare

域名是:

seis-jun.xyz

在 Cloudflare 里添加站点,选择 Free plan。

原来博客是 GitHub Pages,所以 Cloudflare 扫出来的这两条记录先保留:

CNAME  seis-jun.xyz  junxie01.github.io
CNAME  www           junxie01.github.io

这里我把它们设成 DNS only。先不要让 Cloudflare 代理博客,不然 HTTPS、缓存、GitHub Pages 这些东西混在一起,容易把自己绕进去。

Cloudflare 给了两个 nameserver,然后去阿里云域名控制台把 DNS 服务器改过去。

检查一下:

dig NS seis-jun.xyz +short
dig @1.1.1.1 NS seis-jun.xyz +short

能看到 Cloudflare 的 nameserver,就说明 DNS 接管差不多好了。

Fedora 上跑 FreshRSS

办公室机器是 Fedora,用 Podman。

先装:

sudo dnf install -y podman
podman --version

然后建两个 volume:

sudo podman volume create freshrss_data
sudo podman volume create freshrss_extensions

一开始我想拉 Docker Hub 的镜像:

docker.io/freshrss/freshrss:latest

结果办公室网络访问 Docker Hub 超时。好吧,熟悉的味道。

于是换成 GHCR:

sudo podman pull ghcr.io/freshrss/freshrss:latest

启动容器:

sudo podman run -d --replace \
  --name freshrss \
  -p 127.0.0.1:8080:80 \
  -e TZ=Asia/Tokyo \
  -e CRON_MIN='1,31' \
  -v freshrss_data:/var/www/FreshRSS/data \
  -v freshrss_extensions:/var/www/FreshRSS/extensions \
  ghcr.io/freshrss/freshrss:latest

这里比较关键的是:

-p 127.0.0.1:8080:80

也就是说 FreshRSS 只监听本机 127.0.0.1:8080,不会直接暴露给局域网或公网。外面访问全部交给 Cloudflare Tunnel。

检查:

sudo podman ps --filter name=freshrss
curl -I http://127.0.0.1:8080/

如果返回类似:

HTTP/1.1 302 Found
Location: /i/?rid=...

就说明 FreshRSS 活着。302 不是坏事,是它在跳转到初始化或入口页面。

创建 Cloudflare Tunnel

安装 cloudflared:

curl -fsSL https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
sudo dnf install -y cloudflared
cloudflared --version

我本来想在 Cloudflare Zero Trust 网页后台创建 Tunnel,但 Free 激活时要绑定付款方式,中国卡不太顺利。

于是改走命令行 locally-managed tunnel。

登录:

sudo cloudflared tunnel login

它会给一个 URL,浏览器打开后选择 seis-jun.xyz。授权完成后会在 root 的 cloudflared 目录下生成证书。

注意:这个证书不要公开。

创建 Tunnel:

sudo cloudflared tunnel create freshrss-office

然后写配置:

sudo tee /root/.cloudflared/config.yml >/dev/null <<'EOF'
tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json

ingress:
  - hostname: rss.seis-jun.xyz
    service: http://127.0.0.1:8080
  - service: http_status:404
EOF

再把域名路由到 Tunnel:

sudo cloudflared tunnel route dns freshrss-office rss.seis-jun.xyz

如果看到类似:

Added CNAME rss.seis-jun.xyz which will route to this tunnel

就差不多了。

前台测试一下

先前台跑:

sudo cloudflared tunnel --config /root/.cloudflared/config.yml run freshrss-office

看到:

Registered tunnel connection
protocol=quic

说明连上了。

另开一个终端:

curl -I https://rss.seis-jun.xyz/

成功的话应该是:

HTTP/2 302
server: cloudflare
location: /i/?rid=...

如果本地 DNS 抽风,可以查公共 DNS:

dig @1.1.1.1 rss.seis-jun.xyz +short
dig @8.8.8.8 rss.seis-jun.xyz +short

Fedora 本地缓存没刷新时,也可以:

sudo resolvectl flush-caches
resolvectl query rss.seis-jun.xyz

初始化 FreshRSS

https://rss.seis-jun.xyz/ 能打开后,就用正式域名初始化 FreshRSS。

我这里选:

数据库:SQLite
Base URL:https://rss.seis-jun.xyz/

然后导入旧 FreshRSS 导出的 OPML。

OPML 一般能恢复:

订阅源
分类
Feed 地址

但不要指望它完整恢复历史文章、已读状态、收藏、插件配置和用户偏好。RSS 搬家嘛,能把订阅源搬回来就已经很不错了。

让它开机自动跑

Tunnel 做一个 systemd 服务:

sudo tee /etc/systemd/system/cloudflared-freshrss.service >/dev/null <<'EOF'
[Unit]
Description=Cloudflare Tunnel for FreshRSS
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/cloudflared tunnel --config /root/.cloudflared/config.yml run freshrss-office
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared-freshrss.service

FreshRSS 容器也交给 systemd:

sudo tee /etc/systemd/system/freshrss-container.service >/dev/null <<'EOF'
[Unit]
Description=FreshRSS Podman Container
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
Restart=always
RestartSec=10
ExecStart=/usr/bin/podman start -a freshrss
ExecStop=/usr/bin/podman stop -t 10 freshrss

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo podman stop freshrss
sudo systemctl enable --now freshrss-container.service

检查:

sudo systemctl status freshrss-container.service --no-pager
sudo systemctl status cloudflared-freshrss.service --no-pager

curl -I http://127.0.0.1:8080/
curl -I https://rss.seis-jun.xyz/

期望是:

FreshRSS: active
cloudflared: active
本地访问: HTTP/1.1 302
公网访问: HTTP/2 302

这就可以了。

日常维护

以后主要就这几个命令:

sudo systemctl status freshrss-container.service --no-pager
sudo systemctl status cloudflared-freshrss.service --no-pager

sudo systemctl restart freshrss-container.service
sudo systemctl restart cloudflared-freshrss.service

sudo journalctl -u freshrss-container.service -n 100 --no-pager
sudo journalctl -u cloudflared-freshrss.service -n 100 --no-pager

更新 FreshRSS 镜像时,因为 Docker Hub 访问不稳,继续用 GHCR:

sudo systemctl stop freshrss-container.service
sudo podman pull ghcr.io/freshrss/freshrss:latest
sudo podman rm freshrss

sudo podman run -d --replace \
  --name freshrss \
  -p 127.0.0.1:8080:80 \
  -e TZ=Asia/Tokyo \
  -e CRON_MIN='1,31' \
  -v freshrss_data:/var/www/FreshRSS/data \
  -v freshrss_extensions:/var/www/FreshRSS/extensions \
  ghcr.io/freshrss/freshrss:latest

sudo systemctl start freshrss-container.service

再检查:

curl -I http://127.0.0.1:8080/
curl -I https://rss.seis-jun.xyz/

安全上别偷懒

下面这些东西不要公开:

/root/.cloudflared/cert.pem
/root/.cloudflared/<TUNNEL_ID>.json
/root/.cloudflared/config.yml

特别是 .json 凭据文件,泄露后别人可能冒用这个 Tunnel。

FreshRSS 管理员密码也要设置强一点。毕竟现在 rss.seis-jun.xyz 是公网可访问的,不是自己电脑上随便玩的小服务。

如果以后 Cloudflare Zero Trust 的付款方式问题解决,也可以再加一层 Cloudflare Access。不过目前先跑起来,比什么都强。

最后

现在的状态是:

seis-jun.xyz 已接入 Cloudflare
GitHub Pages 博客记录保留为 DNS only
rss.seis-jun.xyz 通过 Tunnel 指向办公室 Fedora
FreshRSS 用 Podman 跑在 127.0.0.1:8080
OPML 已导入
公网访问返回 HTTP/2 302

等回办公室后,再做一次完整自检:

sudo systemctl is-enabled freshrss-container.service
sudo systemctl is-active freshrss-container.service

sudo systemctl is-enabled cloudflared-freshrss.service
sudo systemctl is-active cloudflared-freshrss.service

curl -I http://127.0.0.1:8080/
curl -I https://rss.seis-jun.xyz/

如果都正常,阿里云上的旧 FreshRSS 就可以不用续费了。

省下一台 VPS,RSS 继续活着。挺好。

对了,要是你想用的话给我说一下,我来给你建一个账户。

Comments

← Paper Reading (59) Blog