2018年6月15日星期五

iOS Workflow 分享 - Create QR Code

Workflow: Create QR Code

上次我分享了一个 Scan QR Code 的 Workflow,这次我分享一个正好相反的。如果我要分享一个 URL(或者是一段非常短的文本)给别人,我就可以用这个 Workflow 来生成 QR Code 图片然后发送给别人。

如果你还没有安装 Workflow,你可以先去免费下载安装上。然后打开 Create QR Code 并点击「GET WORKFLOW」,这个 Workflow 就会被自动导入到你的 Workflow 中去,之后你在分享菜单中就可以调用 Workflow 生成 QR Code 图片了。

原理

这是一个超级简单的 Workflow,我们只需要调用「Generate QR Code」这个 action 就能把字符串转为 QR Code 图片。我在分享 QR Code 图片前还加了一个「Quick Look」的 action,让我检查一下这个 QR Code。接着我们就可以把图片分享出去了。

Workflow: Create QR Code

我觉得这个 Workflow 虽然做了它该做的事情,但其实还有很大的优化空间。通常我们需要的不是简单地分享一个 QR Code 图片,而是把 QR Code 嵌入到一张更复杂的图片里去。Workflow 有一定的图片处理功能,需要的话肯定是可以把 QR Code 嵌入到我们挑选的另外一张(或一组)图片里去。

订阅

我之后还会分享更多 Workflow,不想错过的话欢迎通过邮件RSS/Atom 订阅我的博客。

2018年6月11日星期一

猜想:为什么 QR Code 在中日韩如此流行

为什么 QR Code 在中日韩的流行程度比在欧美地区高得多?我觉得要理解这个问题必须先理解 QR Code 本身解决的是什么问题。

难题 1:输入法

我觉得 QR Code 解决的第一个大问题源自中日韩对输入法的依赖。假设你刚刚认识了新的朋友,要加对方微信或 Facebook,这时候你如何找到对方呢?你可以问「你叫什么啊」,然后搜索对应的名字或 ID。(有些平台的用户更多选择使用真名,而另外一些则主要使用 ID,不同人的选择也不一样。)如果是中文名的话,你很可能就需要问对方具体是哪几个字,因为同音字太常见了,接着你需要正确地使用输入法输入这几个字,然后才能找到你要加的人。用英文的话,常见名字如果不是变体的话美国人一般都能拼出来,不需要问怎么拼。英文键盘直接输入就能进行搜索,不需要使用输入法。

中日韩输入成本之高,使得使用 QR Code 有明显优势。打开摄像头然后扫描对方屏幕其实并不快,如果好像英文那样听完自然能拼写正确,QR Code 并没有优势。但是需要区分同音字和使用输入法这两件事情使得 QR Code 的使用成本比输入中日韩要低。

当然,所有这些输入方式都在不停地得到改进,所以这并不一定是永恒不变的。中文输入法变得越来越智能,整词整句识别和模糊拼音提高了输入效率。中文搜索也在变得越来越智能,即使有个别错别字也能找到正确的结果。类似的改进在英文中同样出现了,而且不会比中文差。QR Code 扫描的性能也在得到提升,摄像头启动的速度和识别 QR Code 的速度都在变得越来越快。最终是否会出现一种输入方式能够超过 QR Code,这很难说。(我觉得语音有可能成为超越 QR Code 的输入方式。假设我对着你的手机说出我的名字就能让你搜索到我,这个过程比扫描 QR Code 还要快。)

难题 2:连接实体物品

QR Code 解决的第二大问题是如何连接那些没有具体名字的实体物品。一台打印机,甚至是一瓶水,在系统上都可以分配一个唯一 ID 给它,但让人输入这个唯一 ID 就不那么方便了。

人们想过用不同的方法来解决这个问题:

  • 蓝牙:这需要双方都有蓝牙芯片,要求每一瓶水上都有蓝牙芯片是不可能的,因为成本太高了。而且蓝牙的搜索范围太广,很容易搜索到周边的物品。
  • NFC:这解决了蓝牙搜索方位太广的问题,成本也稍微低一些,而且其中一方不需要主动供电,等待扫描就可以了。这其实能够解决一部分问题,但因为 Apple 一直拒绝在 iOS 设备上加入 NFC API 而难以得到推广。
  • QR Code:这是成本最低的方案,任何人都可以拿一张纸打印个 QR Code,或者在设备屏幕上显示 QR Code。因为现在的设备都用摄像头,并且提供 API 允许任何人调用,不会出现好像 Apple 拒绝开放 NFC API 这样的障碍。

我们可以看到 QR Code 虽然是制作成本最低的方案,但不是使用起来最便捷的。NFC 的便利性可以比 QR Code 更好,但是因为 iOS 上的限制导致无法进一步普及,大家被迫使用 QR Code。如果有一天 Apple 开放 NFC API,NFC 有可能能够迅速普及开来。

跟输入法一样,解决这个问题的技术手段也在不停地进步。如果有一天设备的视觉识别能力足够好,QR Code 就不需要存在了,应用直接识别摄像头看到的实体物品就可以了。就算退一步来看,如果存在一种人手写写画画就能实现的 QR Code,例如说用我的笔迹写我的名字就能识别到是我,那也有可能能取代 QR Code。

总结

QR Code 解决的是标识输入(identification input)的问题。当我在应用内需要正确输入一个人(添加好友)、一家商店(支付)或一张优惠券(打折)的标识时,QR Code 是其中一种解决方案。其他替代解决方案的成本都更高,在中日韩尤其高,所以 QR Code 在中日韩迅速流行起来。

这意味着什么?

  1. 如果出现新的技术,能够使得标识输入的成本进一步降低,明显低于现在 QR Code 的成本,新技术可以逐步取代 QR Code。(Apple 开放 NFC API 也属于这种情况。)
  2. 如果新技术一直不出现,QR Code 是会逐渐在欧美和其他地区流行起来的,因为 QR Code 在某些应用场景还是比其他方案有优势,只是没有在中日韩那么明显而已。

最后,如果你喜欢我的文章的话,欢迎通过邮件RSS/Atom 订阅我的博客。

2018年6月10日星期日

iOS Workflow 分享 - Scan QR Code

Workflow: Scan Code

很多时候我们无意识地用微信扫描一个 QR Code,然后无论打开的是什么我们用就是了。我经常会好奇到底 QR Code 编码的是什么信息,到底是一个 ID 呢,还是一个 URL(可能是 deeplink)呢,还是一个 JSON 呢。所以我做了这个简单的 Workflow,先把 QR Code 的纯文本内容显示出来,然后再让我选择使用哪个 app 来接收这个字符串。如果这个字符串是 URL,Workflow 会提示我用浏览器打开,我也可以用 Opener 来打开 deeplink。

如果你还没有安装 Workflow,你可以先去免费下载安装上。然后打开 Scan QR Code 并点击「GET WORKFLOW」,这个 Workflow 就会被自动导入到你的 Workflow 中去,然后你就可以调用它来扫描 QR Code 了。

此外顺便推荐一下上面提到那个叫做 Opener 的 app。它做的事情很简单,你给它一个网站的 URL,如果那个网站有对应的 app 的话,它会尝试通过 deeplink 在 app 里面打开这个 URL 对应的内容。举个例子,我在 Mobile Safari 上打开了一个知乎的问题,然后我可以把这个网页的 URL 发送给 Opener 让它帮我打开知乎 app 且在知乎 app 内打开同一个问题。

原理

这个 Workflow 最重要的 action 是第一步,利用「Scan QR/Bar Code」把 QR Code 扫描出来。这一个 action 会返回 QR Code 本后编码的字符串,然后我们把它存到 Content 变量中去。

Workflow: Scan Code

接下来「Get URLs from Input」这个 action 可以把 Content 中看似 URL 的内容提取出来,因此如果 Content 是 URL 我们就会得到 URL 否则就没有内容。为了验证 URL 提取成功,检查提出出来的内容包含 ://,如果有的话那就一定是 URL 了,否则就是非 URL 的文本。

对于 URL,我提供 5 个操作的选项;对于非 URL,只有其中的 2 个。然后下面是根据用户作出的选择,执行 5 个操作之一:

  1. 打开(仅限 URL):使用 iOS 系统方式打开这个 URL。如果这是个 deeplink 或者 Universal Link,那就会在对应的 app 中打开。
  2. 在 Chrome 中打开(仅限 URL):因为我用 Chrome for iOS 而非 Mobile Safari,所以我会对普通网页使用这个选项。
  3. 在 Opener 中打开(仅限 URL):如果我想尝试用这个网页对应的 app 打开的话。我在 Opener 中设置了如果找不到对应的 app 就默认提示我用 Chrome 打开。
  4. 复制:复制字符串。
  5. 分享:调用系统的分享功能。

订阅

我之后还会分享更多 Workflow,不想错过的话欢迎通过邮件RSS/Atom 订阅我的博客。

邀请链接(referral links)

中场休息,广告时间。以下是一些我的 referral links,如果你使用它们注册对应服务的话,那么你和我都能获得到一些好处。我会为每项服务做简单介绍,是否合适你还是要靠你自己去搜索别人的测评。

Wealthfront

Wealthfront 是一个 robo adviser,跟 Betterment 等服务相似。你把钱放进去,它帮你管理 portfolio,买进股票、债券等 ETF (Exchange-Traded Fund)。具体回报率跟你选择的风险等级相关,风险系数越高股票占 portfolio 的比例越大。

通过 Wealthfront 管理的资产,头 $10k 是免管理费的,之后是 0.25%。也就是说,如果有 $20k 资产在里面,头 $10k 免费然后之后的 $10k 每年收 $25($10k * 0.25%),然后分摊到 12 个月来收,每个月 $2 多。

如果你使用我的邀请链接的话:

  • 你得到的好处:免管理费资产额度增加 $5k,也就是说注册帐号就有 $15k 免管理费资产额度。
  • 我得到的好处:免管理费资产额度增加 $5k。

邀请链接:http://j.mp/catchen-wealthfront-invite

Acorns

如果你无法一次拿出 $500 存进 Wealthfront 进行投资,你可以选择通过 Acorns 慢慢一点一点存钱。Acorns 也是一个 robo adviser,但跟 Wealthfront 不一样的地方在于 Acorns 使用一些小技巧让你把零钱存下来。

在允许 Acorns 连接你的银行后,Acorns 会对你消费的「找零」进行统计,然后用作投资。例如说,你刷卡消费了 $12.45,Acorns 就会把这当作消费 $13 找零 $0.55,从你的银行转 $0.55 到你的投资账户。除此之外,Acorns 还跟一些商家有 cash back 的合作,通过 Acorns 在这些商家消费的话 cash back 也会进入投资账户。

如果你使用我的邀请链接的话:

  • 你得到的好处:获得 $5。
  • 我得到的好处:获得 $5。

邀请链接:https://chen.cat/acorns-invite

Stitch Fix

Stitch Fix 是一个帮你搭配衣服的服务。注册时它会让你填写众多衣服尺码和喜好的信息,多到简直让人想要放弃注册。然后它会根据这些信息帮你挑选 5 件单品并邮寄给你。(在它帮你挑选前,你可以留言说明一下有没有什么你特别想要的。)

在收到包裹后,你可以试穿然后决定 5 件中哪些你想要留下哪些你想要退回去。如果你 5 件都要了,全单 7.5 折;如果你 5 件都不要,你需要给 $20 的最低消费;否则你只需要对你要的那几件买单。我至今只下单过 3 个包裹,头两个我都只要了 1 件,第三个我想要 3 件,但发现全要的话打折之后更便宜,于是全要了。

如果你使用我的邀请链接的话:

  • 你得到的好处:$25,用于首次消费。
  • 我得到的好处:$25,用于下次消费。

邀请链接:http://chen.cat/catchen-stitchfix-invite

Chase Sapphire Reserve

Chase 最高端的旅行及饮食类信用卡:

  • 旅游和餐饮消费 3 倍积分。
  • 每年 $500 的旅游报销(自动 statement credit),可用于 Uber 甚至是 Uber Eats。
  • 免费附送 Priority Pass Select,可以免费使用全球上千个(美国 26 个)机场的贵宾室,持卡人可免费带两名同行旅客。(不能免费带同行旅客的 Priority Pass Prestige 价格为 $400 一年。)
  • 报销 Global Entry 或 TSA Precheck 费用。前者服务包含后者,费用分别是每 5 年 $100/$85。
  • 年费 $450。

如果你使用我的邀请链接的话:

  • 你得到的好处:没有。跟你自己申请开卡一样,头三个月消费满 $4k 就能获取 50k 点的开卡奖励。
  • 我得到的好处:获得 10k 点奖励。

https://chen.cat/chase-sapphire-reserve-invite–2018–06

2018年6月2日星期六

iOS Workflow 分享 - Debug Action

Workflow: Debug Action

有时候我们想要知道别人的 app 在调用 Share Extension 时提供了什么类型的数据以及具体数据是什么,我们可以自己在 Xcode 里面写个 app 去接收别人 app 的数据,但我们也可以用 Workflow 内置的「View Content Graph」来展示数据。相对于自己写个 app 而言,显然是免费的 Workflow 要简单得多。

如果你还没有安装 Workflow,你可以先去免费下载安装上。然后打开 Debug Action 并点击「GET WORKFLOW」,这个 Workflow 就会被自动导入到你的 Workflow 中去,然后你就可以在其它 app 里分享数据给这个 Workflow 然后让它显示给你看了。

演示

我用 Foursquare 的 app 打开三藩 Union Square 这个地标,然后选择分享,接着就能在这个 Workflow 里看到 Foursqare 的分享功能都传递了什么数据:图片、地址、名称和 URL。

原理

这个 Workflow 做的事情超级简单:它只是把输入的数据传给「View Content Graph」这个步骤,让它显示调试信息,接着它会重新调用 Share Extension 让分享流程跑下去。之所以重新调用 Share Extension,是因为有时候我们需要先看看分享的是什么信息,再决定分享到哪个 Share Extension,这时候把这个 Workflow 插入在中间就很方便了。这个 Workflow 能够显示调试信息,之后又回到正常的分享流程。

Workflow: Debug Action

订阅

我之后还会分享更多 Workflow,不想错过的话欢迎通过邮件RSS/Atom 订阅我的博客。

2018年5月14日星期一

飞利浦 Hue 智能照明系统开发(Part 1 - API 入门)

Hue Bridge

我家里有超过 10 个的飞利浦 Hue 智能灯泡,我通常使用 Amazon Echo 和 iOS HomeKit 控制它们,例如说睡觉时对着 Echo 喊「Alexa, turn off bedroom.」,或者在 iPhone 上通过 Control Center 迅速开关一盏灯。在我买了一个 Raspberry Pi 架设加密 DNS 后,我就开始思考是否能在 Raspberry Pi 上跑一个程序控制 Hue 做更复杂的事情。在此之前,我先要自己搞明白 Hue 的 API 是如何工作的以及能做什么,于是我开始阅读 Hue API 文档

Hue API 是很典型的 REST API,我们可以通过 bridge 上面的 HTTP 服务器调用 Hue API 操作这个 bridge 连接的所有灯泡。Hue API 可以操作的对象包括灯泡、房间、传感器、规则等等,不过在我们能够调用这些 API 之前,首先我们必须找到 bridge 的 IP,然后才能连上它的 HTTP 服务器。

寻找 Hue Bridge

大多数人家里的网络都是用 DHCP 动态分配 IP,因此我们不知道 Hue bridge 的 IP 是什么,就算知道了将来还可能变。我们要如何连接 bridge 呢?最简单的方法是发一个请求到这个地址:

https://www.meethue.com/api/nupnp

这是飞利浦自己的服务器,不是我们家里 bridge 的服务器。它会根据我们的公网 IP 来查询我们家是否有 bridge 通过同一个公网 IP 连接着飞利浦的服务器,如果有的话它就会返回这个(或这些)bridge 的信息,包括内网 IP。例如说:

[
  {
    "id": "001788fffe000999",
    "internalipaddress": "10.0.0.9"
  }
]

这个 JSON 里面只有一个 bridge,这也是最常见的状况。(很少人会需要在家里装超过一个 bridge。)这一个 bridge 的 IP 是 10.0.0.9,也就是 internalipaddress 属性的值。那 id 属性的值是什么呢?这是这个 bridge 的序列号,如果这个 bridge 的 IP 将来发生了变化,我们可以通过这个序列号来确认这是同一个 bridge。

在找到 bridge 的 IP 后,我们就可以连接上去了。我这里提到的只是最容易上手的 bridge 寻找办法,更复杂的方法可以看官方的 Hue Bridge Discovery Guide

Hue Bridge 本地授权

能够连接到 bridge 的 IP 并不意味着能够操作 bridge,否则 bridge 就没有任何安全性可言了。任何新的客户端连接 bridge 之前,都需要有人去手动按一下 bridge 上面的物理按钮,然后新的客户端才能获取到授权。

首先,我们要按下 bridge 上的物理按钮,然后发送一个 POST 请求到这个地址:

http://<bridge_ip>/api

这个 POST 请求需要带上一个简单的 JSON:

{
  "devicetype": "<app_name>#<device_name>"
}

这个 JSON 只有一个叫做 devicetype 的属性,用来命名这个新增的客户端,格式为应用名称加上 # 再加上设备名称。(一个应用可以安装在多台设备上,这样的命名格式要求可以帮助区分。)官方文档说这个字符串长度不能超过 40 个字符,其中应用名称不超过 19 个字符,设备名称不超过 20 个字符。实际上当前的 API 版本(1.24.0)只检查字符串长度是否超过 40,不检查应用名称和设备名称是否超过允许长度。

这个请求成功的话,返回的 JSON 会是这样子的:

[
  {
    "success": {
      "username": "83b7780291a6ceffbe0bd049104df"
    }
  }
]

我们获取到了一个叫做 username 的东西,这实际上是个密钥一样的东西,因为只要掌握了它就能操作 bridge。我们需要把这个密钥保存下来,然后我们就可以进行其他任意操作了。

测试请求

拿到 username 后,我们当然要测试一下能不能用。Hue API 传输 username 这个密钥的方式很有趣,我们需要把它放在 URL 里面,例如这样子:

http://<bridge_ip>/api/<username>/config

发一个 GET 请求到这个地址,我们就能获得这个 bridge 的所有配置信息。成功获取信息意味着 username 是有效的密钥,之后我们就能用它来获取其他信息了。以下是一些可以尝试 GET 请求获取的地址:

http://<bridge_ip>/api/<username>/lights
http://<bridge_ip>/api/<username>/groups
http://<bridge_ip>/api/<username>/schedules
http://<bridge_ip>/api/<username>/scenes
http://<bridge_ip>/api/<username>/sensors
http://<bridge_ip>/api/<username>/rules
http://<bridge_ip>/api/<username>/resourcelinks
http://<bridge_ip>/api/<username>/capabilities

总结

拥有上述知识后,我们就可以进一步探索 Hue API 了。为了方便方便我自己,我写了一个叫做 Hue Explorer 的开源项目用于连接我自己的 bridge 并查看上面的信息,如果你想要看源代码的话你可以到 GitHub 上查看。我暂时只做了一部分的 JSON 可视化,例如说灯可以可视化为这样子:

Hue Explorer v.0.2.0

如果你想要用第三方工具发送 REST 请求,可以试一下免费的 Postman。如果你懒得下载,可以直接访问 http://<bridge_ip>/debug/clip.html,这是一个每一个 bridge 都自带的调试工具,可以用来手工编写 REST 请求。

在下一篇文章里,我会开始讲述每一个具体的 Hue API 能做什么,不想错过的话欢迎通过邮件RSS/Atom 订阅我的博客。

2018年4月28日星期六

用 Raspberry Pi 架设加密 DNS 客户端

dig through DNS-over-HTTPS

最近 Cloudflare 宣布使用 1.1.1.1 作为 DNS,并且强调隐私保护。由于 Cloudflare DNS 支持 DNS-over-TLS 和 DNS-over-HTTPS,这使得加密 DNS 成为了热门话题

因为操作系统往往不支持加密 DNS,所以要使用加密 DNS 必须使用一个加密 DNS 的客户端,然后这个客户端同时作为一个明文 DNS 服务器向操作系统提供正常的 DNS 服务。我可以选择在每一台我使用的设备上安装一个加密 DNS 客户端(对于 iOS 来说则是 NetworkExtension),我也可以选择在家里假设一个加密 DNS 客户端然后把路由器 DNS 指向过去,之后家里所有设备的 DNS 都会跟着变。我选择了后者,因为这样做比较方便,也为我提供了一个折腾 Raspberry Pi 的借口——我需要把加密 DNS 客户端部署到 Raspberry Pi 上让它长期为家里的局域网提供 DNS 服务。

(为什么不用 OpenWRT 呢?因为我家里已经在用 Eero 来做路由器了,它可以通过 mesh Wi-Fi 来提供更好的覆盖。如果我要多买一个 OpenWRT 路由放在 Eero 前面,那我还不如买个 Raspberry Pi 来玩玩呢。)

Raspberry Pi

我买了这个 Raspberry Pi 套装,因为它自带盒子和电源。电源不重要,我家已经有很多 USB 电源,但是我总不能一块电路板随便一放吧,所以必须买个盒子。然后我还买了张 64GB 的 microSD。因为我所有 microSD 都是 64GB 的,所以我只买 64GB 的方便有需要时随意替换。

收到 Raspberry Pi 之后,我就按照官方 NOOBS 的指引下载和准备安装。然而 NOOBS 复制到 SD 卡后无论如何 Raspberry Pi 都无法正常启动,只亮红灯没有视频输出。搜索之后发现绿灯不亮就是没有读取 SD 卡进行启动。我开头怀疑是我下载的 NOOBS 有问题,于是换成 NOOBS Lite 和 Raspbian,但都是不行。我也怀疑过是不是下载的 zip 数据有问题,但 sha256 checksum 正确。

实在找不到问题了,我就开始搜索到底 Raspberry Pi 是如何进行引导的,发现它必须从 FAT 分区进行引导。Raspberry Pi 自己的官方文档教大家使用一个叫做 SD Association’s Formatting Tool 的软件来格式化 SD 卡,但这个软件在面对超过 32GB 的卡时就会傻傻地使用 exFAT 来进行格式化。其实使用 Mac 内置的 Disk Utility 不就好咯,就算是超过 32GB 的 SD 卡也可以选择格式化为 FAT。

把 SD 格为 FAT 后,所有问题都解决了。NOOBS 能够正常启动,接着 Raspbian 也能够顺利装上。Raspberry Pi 安装好之后我尝试启用 VNC 以便我用 Mac 远程控制,结果那上面装的 VNC 和 Mac 自带的 Screen Sharing 客户端不兼容,我只好降级到用 SSH,不过也能完成绝大多数操作了。

启用 SSH 后 Raspbian 会提醒你改默认密码,没有改的话记得改掉,否则太不安全了。因为 Raspbian 连 dig 这么基本的命令都没有,需要通过 apt-get 来安装,所以我们需要先更新一下然后把 dig 装上:

sudo apt-get update
sudo apt-get install dnsutils

DNS-over-HTTPS

我基本上就是按照 Cloudflare 的 DNS-over-HTTPS 指引 来做的。一开始我觉得 Raspbian 既然是 Debian 系的就下载了 Debian 的安装包,结果发现安装不上去。接着尝试用 Linuxbrew 来装 homebrew 的版本,结果装上后发现不能执行。看到「exec format error」并且搜索后才突然明白到,Raspberry Pi 不是基于 x86/x64 架构的,而是基于 ARM 架构的。那到底 Raspberry Pi 是 32 位还是 64 位的呢?理论上 Raspberry Pi 3 B+ 是 64 位的 CPU,但在 Raspbian 上执行 uname -a 的话会显示:

Linux raspberrypi 4.9.80-v7+ #1098 SMP Fri Mar 9 19:11:42 GMT 2018 armv7l GNU/Linux

所以其实不是 64 位的,如果要选正确的版本那必须选 32 位的 ARM。只要选择正确的版本,Cloudflared 和 Dnscrypt-Proxy 都是可以用的。我两个都装了,都能在 localhost:53 上跑起来,最后选择了 Dnscrypt-Proxy 是因为配置方便。(Dnscrypt-Proxy 有配置文件模板,改改就可以用了,不需要对着文档写一个新的。)

Dnscrypt-Proxy 的安装跟着官方指引做就可以了,选择 Linux 版本 来下载。记得下载 Linux ARM 的版本,不要用 Android 或者 ARM64 的版本。(尽管 Dnscrypt-Proxy 是可以安装在 Pi-Hole 上面的,但我不想安装 Pi-Hole 来过滤广告所以选择了非 Pi-Hole 的版本。)尽管官方指引叫你检查一下是否有别的 DNS 服务正在使用 53 端口,但新装的 Raspbian 应该是不会有任何服务占用 53 端口的所以这一步可以略过。

Dnscrypt-Proxy 下载和解压好之后就可以开始配置了。假设我们已经在 Dnscrypt-Proxy 解压好的目录里:

cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml
sudo ./dnscrypt-proxy

这时候 Dnscrypt-Proxy 应该能够跑起来,在 Raspberry Pi 上用 dig 验证一下就知道了:

dig +short @127.0.0.1 cloudflare.com AAAA

这个验证必须在 Raspberry Pi 上做,因为 Dnscrypt-Proxy 的默认配置只监听 localhost:53 端口,从另外一台机器连上来 53 端口是不行的。如果 Dnscrypt-Proxy 正常工作了,我们就可以开始改配置了。打开 dnscrypt-proxy.toml,然后把 server_nameslisten_addresses 改掉。(在 SSH 上面,用 nanovi 都可以编辑 dnscrypt-proxy.toml。)

首先找到 server_names,把前面注释这一行的 # 删掉,然后把后面的内容改为你想要的服务。因为 Cloudflare 和 Google 都支持 DNS-over-HTTPS,而且都是可靠的大公司,所以我在这两家之间选。因为 Google 不强调隐私,有可能记录数据,所以我只用 Cloudflare 的,按照 Cloudflare 的文档把这一行改为这样子:

server_names = ['cloudflare', 'cloudflare-ipv6']

接着找到 listen_addresses,你会发现它只监听 IPv4 和 IPv6 的 localhost,所以其他机器不能用 Raspberry Pi 来做 DNS。这时候你要想办法把 Raspberry Pi 的 IP 绑上去。我的做法是这样子的:因为我家里路由器的 IP 是 192.168.0.1,然后 DHCP 范围是 192.168.0.10–192.168.0.199,所以 192.168.0.2–192.168.0.9 是不会被动态分配出去的。我把 Raspberry Pi 的有线网 IP 写死为 192.168.0.2,然后把它加到监听地址端口列表上:

listen_addresses = ['127.0.0.1:53', '[::1]:53', '192.168.0.2:53']

搞掂之后,可以再启动一下 Dnscrypt-Proxy:

sudo ./dnscrypt-proxy

然后从另外一台机器使用 dig 测试一下:

dig +short @192.168.0.2 cloudflare.com AAAA

如果没有问题的话,就可以把 Dnscrypt-Proxy 当装系统服务启动了:

sudo ./dnscrypt-proxy -service install
sudo ./dnscrypt-proxy -service start
sudo systemctl enable dnscrypt-proxy

之后登录到路由器,把路由器的 DNS 改为 192.168.0.2 就可以了,家里所有设备的 DNS 都会经过 Raspberry Pi 上的 Dnscrypt-Proxy 走 DNS-over-HTTPS 连接 Cloudflare 服务器。尽管 Dnscrypt-Proxy 的官方指引还说要把 Linux 上的 DNS 客户端指向 localhost,但因为我暂时不在 Raspberry Pi 上做别的事情所以不在意 Raspberry Pi 本身发出的 DNS 请求是否加密。只要它作为 DNS 服务器服务好我家里的其他设备就行。

已知问题

上述做法是有一些已知问题的。首先,如果我们请求使用 SNI 的 HTTPS 服务的话,我们还是会明文传输域名的,就算 DNS 加密了还是会存在域名泄漏的情况。如果多个不同证书的 HTTPS 域名要在一个 IP 上共处,那必须使用 SNI 否则 SSL 握手时无法决定用哪个证书的密钥。因此 SNI 常见于跑在云平台上的服务,因为云平台往往在多个服务之间共享 IP,但每一个服务来自不同的客户有不同的证书。对于大型网站来说这不常见,因为无论一个大型网站旗下有多少域名,它都可以选择把所有域名放在同一个证书里面。

其次,我没有做 IPv6 的配置,只让 Dnscrypt-Proxy 绑定了一个 IPv4 地址。这时候如果 IPv6 分配了不一样的 DNS,那使用 IPv6 DNS 查询时还是会走明文的。如果你所处的网络完全不使用 IPv6,那是没问题的。我知道 Comcast 是会分配 IPv6 地址和 IPv6 DNS 的,所以如果不在路由器上设置 IPv6 DNS(或者是不能设置)的话,那 IPv6 DNS 就有可能是 Comcast 分配下来的,也就是明文 DNS。(其他 ISP 也一样。)

最后,如果你喜欢我的文章,欢迎通过邮件订阅我的博客。

2018年4月27日星期五

网上吵架原则

在网上跟别人吵架辩论时,我有两条原则:

  1. 不能在三个回合内结束的架不吵。如果一件事情不能在三个回合内说清楚,那就是自己没搞清楚,不要浪费时间去跟别人吵。
  2. 拥有不能公开的信息时不跟别人吵架。如果一件事情自己觉得能够说清楚,但有部份信息不能够公开,那肯定说服不了别人,说了还不如不说,因为说了之后别人会觉得你缺乏证据强词夺理。