Sky Watch

Sky Watch

马上订阅 Sky Watch RSS 更新: https://darksair.org/blog/feed.xml

联邦宇宙历险记

2022年12月27日 13:15

介绍

最近马一龙老板在 Twitter 搞了一系列事情,炒红了「联邦宇宙」这个概念,很多推友都申请了帐号。我一开始是拒绝的,因为我不怎么看好这个东西。分布式服务里用户最多的当属 email,可以说是失败的典型了⸺email 现在的状态可以说和「分布式」一点关系都没有,我认为这是所有分布式服务足够流行以后的必然归宿。但是后来马老板的政策过于光怪陆离,基本上是逼着我开始考虑其他平台了。

「联邦宇宙」这个词听起来神神叨叨的,其实它就是指网上有一票服务器,它们都使用同一个协议互相发消息,这个消息可以是一段文本(micro-blog),可以是图片(类 instagram 服务),也可以是视频(Peertube),而这个协议就是 ActivityPub。如果你注意看这个链接就会发现⋯⋯没错,这是个 W3C recommendation~~ 既然有协议,就要有实现这个协议的软件,其中最流行的当然就是现在烂大街的 Mastodon。本着「流行的一定不是最好的」的原则,我选择了比较小众的 Pleroma⋯⋯我在家里有自己的服务器硬件和环境,其中开了数个虚拟机,所以我直接就装在其中一台虚拟机上了。Pleroma 有官方编译好的二进制版本,可以直接运行,没有其它运行上的依赖,所以架起来十分简单,直接跟着官方文档走就可以了。我的虚拟机用的是 Arch Linux,所以用了 AUR(后来我自己升级到了 2.5)。整个安装过程用 Ansible 自动化,目前为止还比较无痛。

坑,好大一个

但是目前 Pleroma 有个惊天神坑,就是它对于「用户 ID 域名和服务域名不一样」这件事的支持。这要从 WebFinger 说起,WebFinger 的逻辑是这样的:你有个域名,很短很好看,你想用这个域名作为你 ID 的一部分,我们暂且把这个叫做你的公开域名。但是你想挂很多 HTTP 服务,每个服务都有自己的入口,一般这种情况有两种做法,

  • 所有的服务都挂在公开域名下面,但是在不同的路径下,比如 blog 在 /blog 下,Wiki 在 /wiki 下,等等。这种方法有很大的局限性,比如有些服务可能就不支持挂在非根路径下面;如果在根路径下挂服务还要保证 URI 不能重。

  • 在公开域名下开子域名,把服务挂在子域名下,在 HTTP 服务器里给每个子域名开虚拟服务器。这是比较好的做法。这里暂且把这个子域名叫做内部域名。

我家里的网络本来就在一个内部域名下面,所以我肯定是选择第二种方法,更何况 Pleroma 压根就不支持第一种方法。那么问题来了:内部域名很长,我就想用公开域名作为 ID,但是在内部域名下面架服务,怎么办呢?这里就需要一种机制,当外面看到我的 ID,认为我的服务在公开域名下,但是公开域名里有个东西告诉对方,我的服务其实是在内部域名下,让对方去那找。WebFinger 就是这样一种机制。Pleroma 用到的 WebFinger 其实是两个协议:

  • Web Host Metadata(RFC6415),这个协议说的是当外面想访问一个域名下的某个资源的时候,这个域名可以告诉对方关于这个资源的元信息。协议规定支持这个协议的程序在访问一个资源的时候,会先访问 /.well-known/host-meta 这个 URI,如果服务也支持这个协议,应该返回一个 XRD 文档。

  • WebFinger(RFC7033),这个协议说的是如何在一个服务里找一个人。

Host-meta

在 Pleroma 的语境下,这两个协议的用法是这样的: 每个人有自己的 ID,公开域名是这个 ID 的一部分。这个 ID 就是这个服务里的一个资源。所以如果有人想找到我,这个人应该先访问 host-meta,注意这时外面并不知道内部域名的存在,所以访问的是公开域名下的 /.well-known/host-meta。这时应该返回下面这个 XRD 文档:

<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  <Link rel="lrdd" template="https://internal.domain/.well-known/webfinger?resource={uri}" type="application/xrd+xml" />
</XRD>

简单地说这个文档的意思是你要找人的话就去这里看 WebFinger,其它的事我不管。LRDD link 是另一个 RFC,里面的 {uri} 就是要找的人的 ID(包含公有域名)。

注意这个请求发生在公有域名,所以 Pleroma 是看不到的。所以这时有两种方法:

  • 把这个请求重定向到内部域名,让 Pleroma 处理,这个 Pleroma 是支持的。

  • 这个文档是不用变的,所以可以手动在公有域名的目录里创建这个文件,或者把文件内容直接写在 HTTP 服务器的配置里。

对面在收到这个文档以后应该去访问里面提供的这个 URL,也就是 WebFinger 请求。

WebFinger

这时对面其实就已经知道了服务是在内部域名下。//internal.domain/.well-known/webfinger 这个 URL 完全是 Pleroma 负责的,回应里会包括用户的 profile URL(当然是内部域名)。如果 Pleroma 的版本是 2.4 或更老,这里还有一个二阶神坑。前面说到 host-meta 的回应里有 webfinger 的 URL,其中包括一个 {uri} 变量。对面看到这个以后应该把这个 {uri} 替换为用户的 ID,然后去访问 URL。所以整个 WebFinger URL 是 https://internal.domain/.well-known/webfinger?resource=name@public.domain。但是在 2.4 或更老的版本里,这个 URL 会返回 404,因为 Pleroma 期望对面会把这个 {uri} 替换为使用内部域名的 ID⋯⋯这根本就不对,也没有其它实现会这样干。所以当时我在 Apache proxy 里有几个重写规则来处理这个事:

RewriteCond %{REQUEST_URI} "/.well-known/webfinger"
RewriteCond %{QUERY_STRING} "resource=acct:(.*)@public.domain"
RewriteRule ^ "http://127.0.0.1:1234/.well-known/webfinger?resource=acct:%1@internal.domain**?**" [P,L]

RewriteCond %{REQUEST_URI} "/.well-known/webfinger"
RewriteCond %{QUERY_STRING} "resource=acct%3A(.*)%40public.domain"
RewriteRule ^ "http://127.0.0.1:1234/.well-known/webfinger?resource=acct:%1@internal.domain**?**" [P,L]

在 2.5 版里已经修复了这个 bug。

到此为止,整个查询用户...

剩余内容已隐藏

查看完整文章以阅读更多