Nanjing Linux User Group

December 05, 2016

alswl

教你在上海挑房子

201612/shanghai.jpg

点开的同学别失望,这并不是教你如何快速致富的 😂 ……


上海政府在 11-28 出了房屋新政 via,对房价进行进一步调控。 其核心思想是「认房又认贷」。3 月份的政策 via 是限制购房资格。 这次 11 月份调整则是提高二套房首付比例。 双管齐下,进一步给上海房市进行降温。

在这种严苛的政策下面,如何挑选一套自己满意,家人住得安心的房子就尤为关键了。 我对房市一直比较关注,也曾有几位朋友咨询我的一些经验。之前我是将 Evernote 笔记链接贴给别人,这次我就详细讲讲,如何在上海挑房子。

第一步,是建立有价值的 Indicator,并对具体房源进行计算。 Indicator 的含义是建立一系列评价指标,比如价格、户型、位置, 再根据这些 Indicator 给房源进行打分。 如果我是一个购房者,我挺希望我的置业顾问告诉我这套房有哪些优点、缺点,综合评价得几份。 (当然实际情况下这些置业顾问都是吹得天花乱坠,尽睁眼说瞎话。) 看房的过程可能长达数月,而人的记忆会随着时间逐步失真,只对最近看到的事物有深刻印象。 使用客观的数字评价房源,则可以进行精准的评价,避免产生主观臆断。

我使用的 Indicator 和评价细项罗列如下,供参考:

  • 价格
  • 面积
  • 地理位置:内环、中环、外环
  • 交通:离地铁距离,换乘便利性;多地铁线换乘;
  • 户型:南北通透;客厅朝南;全明;户型方正;无暗室
  • 装修:毛胚;简装;普通;精装;豪装
  • 商圈:购物中心;超市;菜场
  • 学区房
  • 产权:满五唯一
  • 楼层
  • 房龄:5 / 10 / 15 / 20
  • 政策发展前景
  • 停车

第二步,就是和家人达成一致的期望,避免潜在的纠纷。 每个人对未来的住房有自己的期望,即便是一家人, 各自看中的点也不会太一样。不管平时家庭决策是什么流程,我都建议坐下来一起讨论讨论。 将 Indicator 依次列出,大家讨论各自心中的优先级。

除了优先级的讨论,还要考虑一下各自对 Indicator 划分等级的理解。 比如对房型、装修这两项,评价标准就会很模糊,这一切都需要讨论清楚。

我个人最为看中价格、位置、交通,可以放弃的有学区房、停车、户型。

第三步,划定圈子,判断趋势。 众多 Indicator 里面,价格是固定的,户型、楼层等是和具体房源相关的, 我们能够撬动的最重要因素,其实就是位置。

作为上海这个拥有 2500w 人口的超大型城市,是不可能把各个位置的房源都扫一遍的, 所以必须重点规划自己准备实地考察的区域。 那么我们就在上海地图上面划几个圈了,数据可以通过链家、中原地产等多网站收集。 安居客有一个大颗粒度的圈子,展示了社区集中点 via, 下图是我筛选条件之后形成的几个簇:

201612/map_thumb.png

(数据爬取分析这一块,我只服一个人,他专门写了一堆爬虫捞数据然后分析, 如果对杭州的房地产投资感兴趣,可以私信我,我可以帮忙问问。)

从城市级别来看,其实不同区域的变化趋势基本是一致的,但是局部地区可能受外部因素而出现较大波动。 比如上海闸北并入静安,价格就大涨;大宁板块产生一个地王,也会让这片区域价格大幅波动; 甚至新开业一个 Mall,都会让周边价格波动。 这些外部因素,必须提前关注政府市政规划方案、房产网站、公众号、房产微博。 政策相关推荐阅读:「上海市城市总体规划(2015-2040)纲要概要」 via 规划了整体发展格调。 「上海市商业网点布局规划(2013-2020)」via 规划了 14 个市级商圈,之前只有 10 个,还有若干区级商圈。

第四步,快速行动,快速决策

还记得捡西瓜的故事么,小猴子一直想找个大西瓜,但一路看过去最后什么都没捡到。 看房不仅仅是一个体力活,而且非常讲究时效性的,必须能够快速响应,快速决策。 在上海这中刚需强需求旺盛的城市,一套评价都不错的房源,半天内就会被别人拿下。 所以前期观察一段时间之后,必须摸清楚自己的需求和承受能力,遇到合适的房源, 就速战速决。

速战速决还依赖实地看房源的效率。 从我的经验来看,一天至多可以看 10 套左右的房源。 出发前一天约好中介、房东,列一个清单,时间、地点、联系方式全部罗列下来,第二天依照清单行动。 另外为了提高速度,全程应该打车,对于上海这样的一线城市,出租车费用和房产价格相比,真的是小钱。

在看中合适的房源之后,有些人可能面对这笔巨额交易摇摆不定,而错失了良机。 收益于 Indicator + 前期数据准备,我们在看房时候应该具有了客观的评价能力。 一旦各方面满意,不要犹豫,快速决策。 但也不要因为几次擦肩而过就冲动决策,要相信客观的 Indicator 数据。

我推荐上班族看房时间安排是,频繁看房两个月,每周末两天出动。 前期一个月看房,不管看中多中意的,都不做决策, 但要计算出一个自己心理预期。一个月后,看到合适的立刻上手。


OK 写完了。回顾其实这四个步骤,其实就是选一个开源软件的流程嘛。 设定特性需求 -> 设定期望 -> 调研候选者 -> 快速测试上线。 嚯嚯嚯。

最后,如果你恰好在看房,希望你看完本文有些收获,收获自己中意的住房。


原文链接: https://blog.alswl.com/2016/12/house/
欢迎关注我的微信公众号:窥豹

3a1ff193cee606bd1e2ea554a16353ee

by alswl at December 05, 2016 01:29 PM

November 30, 2016

alswl

🔒 也谈 HTTPS - HTTPDNS + HTTPS

最近谈论 HTTPS 的文章很多,其原因之一是运营商作恶底线越来越低,动不动就插播广告, 前两天小米还联合几家公司发文 关于抵制流量劫持等违法行为的联合声明 痛斥某些运营商。 另一方面也是苹果 ATS 政策的大力推动,逼迫大家在 APP 中全部使用 HTTPS 通信。 上 HTTPS 的好处很多:保护用户的数据不外泄,避免中间人篡改数据, 对企业信息进行鉴权。

201611/https.png

关于 HTTPS 如何购买证书,如何部署,网上的教程已经太多了,实践起来没有太大的难处。 我们在部署 HTTPS 的时候,遇到了一些新问题,首当其冲的就是 HTTPS 部分网络不可访问的问题:

尽管使用了 HTTPS 技术,部分邪恶的运营商,仍然使用 DNS 污染技术,让域名指向的他们自己服务器 而这些服务器并没有部署 SSL 服务(就算部署了,也会触发 SSL 证书 Common name 不一致报警), 导致 443 端口直接被拒绝。

这个问题不解决,强行上 HTTPS 的话,会导致一部分用户出现无法访问网站 一旦用户不爽了,轻则对产品不信任,重则直接导致用户流失。

运营商为了赚广告钱、省网间结算是不择手段的。 他们普遍使用的劫持手段是通过 ISP提供的 DNS 伪造域名。 那有没有什么方法可以解决 DNS 劫持呢? 业界有一套解决这类场景的方案,即 HTTPDNS。

HTTPDNS 的原理很简单,将 DNS 这种容易被劫持的协议,转为使用 HTTP 协议请求 Domain <-> IP 映射。 获得正确 IP 之后,Client 自己组装 HTTP 协议,从而避免 ISP 篡改数据。

有两篇文章很清晰的讲解了 HTTPDNS 的细节:

201611/httpdnsjbyl.png

点击 https://dns.google.com/resolve?name=www.duitang.com / http://119.29.29.29/d?dn=www.duitang.com 感受一下 DNS-over-HTTPS / HTTPDNS。

单 IP 多域名支持

这个方案看似完美,但是在实际生产中,会遇到一个问题。

Android / iOS 在操作系统级别对 HTTPS 通信是提供了封装。 APP 无法在发起连接时候,也没有权限直接操作 socket。 所以尽管 APP 拿到了域名对应的 IP,却没有办法让这个 IP 在 HTTPS 里生效。

解决的思路很暴力:彻底放弃域名系统,完全使用基于 IP 系统的通讯。

原本请求 https://www.duitang.com 的 request, 调整为请求 https://221.228.82.181

OK,做到这一步,我们就可以跟运营商劫持说拜拜了。

不,还没结束。

完全搞定运营商之后,这 IP 方案给我们自己带来一个困扰: Nginx 服务器无法通过 Host 来识别不同域名下面的请求了!!! 在由于使用一个独立 IP,会导致所有域名请求混在一起,无法分别。 大公司可以 dedicated IP,小公司就玩不起了。

为了解决同一个 IP 下面多个域名的问题,我们引入了一个URL参数: __domain。 当请求 IP 域名时候,必须带着这个参数,服务器会将请求域名解析出来,再分发到对应的域名。

实现这个逻辑的 Nginx 核心代码:

set $query_domain $arg___domain;
if ($query_domain !~ '(www|a|b)\.example\.com') {
    rewrite ^ http://www.example.com/404/ redirect;
}
set $my_host $query_domain;
location / {
    proxy_set_header Host $my_host;
    proxy_set_header X-REAL-IP $remote_addr;
    proxy_pass http://127.0.0.1;
}

最后一个注意事项是,记得调整 Nginx 配置的 remote_addr,否则都变成了 127.0.0.1, 也许会导致其他一些策略失效。

完美收工,效果如下:https://221.228.82.181/?__domain=www.duitang.com

恭喜你,已经掌握核心科技了,再也不怕运营商瞎折腾了,从此走上了业务蓬勃发展的金光大道……☀️

下一篇文章,我会再谈谈如何做 HTTPS 的「内测」,避免将线上业务一次性切到 HTTPS 导致不少边边角角业务无法正常使用。



原文链接: https://blog.alswl.com/2016/11/https-1/
欢迎关注我的微信公众号:窥豹

3a1ff193cee606bd1e2ea554a16353ee

by alswl at November 30, 2016 02:02 PM

November 28, 2016

李凡希

关注2017维也纳新年音乐会

曾经关注过的那些维也纳新年音乐会:关注维也纳新年音乐会,2017年将是我第22次收看维也纳新年音乐会的直播。

2017维也纳新年音乐会CD封面

2017维也纳新年音乐会CD封面

  • 01 Franz Lehár – Nechledil Marsch aus der Operette Wiener Frauen – 内西雷迪尔进行曲(选自喜歌剧《维也纳的妇女》)
  • 02 Èmile Waldteufel – Les Patineurs; Walzer; op. 183 – 溜冰者圆舞曲
  • 03 Johann Strauss II – ‘s gibt nur Kaiserstadt, ‘s gibt nur Wien!; Polka; op. 291 – 只有帝国之都,只有维也纳(皇城)波尔卡 – 1997
  • 04 Josef Strauss – Winterlust; Polka schnell; op. 121 – 冬趣快速波尔卡 – 2005
  • 05 Johann Strauss II – Mephistos Hollenrufe; Walzer; op. 101 – 梅菲斯特的地狱圆舞曲 – 1995
  • 06 Johann Strauss II – So angstlich sind wir nicht!, op. 413 – 我们决不畏惧波尔卡 – 2009
  • 07 Franz von Suppé – Ouvertüre zu Pique Dame – 黑桃皇后序曲
  • 08 Carl Michael Ziehrer – Hereinspaziert!; Walzer; op. 518 – 闲庭信步圆舞曲 – 1979
  • 09 Otto Nicolai – Die lustigen Weiber von Windsor, Moon Choir – 月升小合唱(选自轻歌剧《愉快的温沙妇人》)
  • 10 Johann Strauss II – Pepita-Polka; op. 138 – 细花纹方格波尔卡
  • 11 Johann Strauss II – Rotunde-Quadrille; op. 360 – 圆形大厅四对舞
  • 12 Johann Strauss II – Die Extravaganten; Walzer; op. 205 – 奢靡圆舞曲
  • 13 Johann Strauss I – Indianer-Galopp; op. 111 – 印度人加洛普 – 2004
  • 14 Josef Strauss – Die Nasswalderin; Polka mazur; op. 267 – 纳斯瓦尔德的女孩玛祖卡波尔卡 – 1996
  • 15 Johann Strauss II – Auf zum Tanze!; Polka schnell; op. 436 – 跳舞吧快速波尔卡
  • 16 Johann Strauss II – Tausend und eine Nacht; Walzer; op. 346 – 一千零一夜圆舞曲 – 1992, 2005
  • 17 Johann Strauss II – Tik-Tak; Polka schnell; op. 365 – 提塔快速波尔卡 – 1979, 2002, 2012
  • 18 Eduard Strauss ? – ?
  • 19 Johann Strauss II – An der schönen blauen Donau, Walzer, op. 314 – 蓝色多瑙河圆舞
  • 20 Johann Strauss I – Radetzky-Marsch, op. 228 – 拉德茨基进行曲

2017年维也纳新年音乐会即将迎来首次登上该音乐盛事指挥台的指挥家古斯塔沃·杜达梅尔。做为当代最为杰出的指挥家之一,80后的杜达梅尔也将成为有史以来新年音乐会上最年轻的指挥。

年轻的指挥家为这传统古老的音乐会带来了新的气息,这次音乐会正式演出的17个曲目中,有8首是第一次在新年音乐会上与乐迷见面,剩下的9首中也有7首是只在新年乐会上演出过一次的“冷门”曲目。加演的第一个曲目目前还没有公布,如果“路边社”消息属实,加演的是爱德华的一首快速波尔卡,那么这次音乐会的曲目单中就将史无前例地出现九位作曲家的名字,新年音乐会在演绎施特劳斯家族音乐的传统之上,越来越多的融入了更多其他作曲家的作品。

维也纳音乐之友协会合唱团将第一次加入新年音乐会的演出行列,在下半场与爱乐乐团一同演绎“月升小合唱”。

中央电视台从1987年开始转播维也纳新年音乐会,到现在已经有30年。我从1996年开始收看新年音乐会,到现在已经超过了20年。经历了刚开始的陌生和新奇以及中间的狂热,新年音乐会如今已经成为了一种习惯。我用上面的文字列举完了音乐会曲目单的各项“技术参数”以后,惊讶地发现,对于每一个具体的曲目,我竟然写不出任何文字再去深入的点评它们。那些曾经演出过的曲目,在脑海中的印象似乎也变得越来越模糊。不过我也不打算像以前那样把曲目单上的曲子都找出来复习+预习一遍了,我相信在新年到来的时候,乐团的演绎会让我回忆起那些曾经熟悉旋律,那种与老朋友相见的感觉想必是非常美好的。

期待新年音乐会,也期待新的一年。

by Li Fanxi at November 28, 2016 03:50 PM

November 13, 2016

Phoenix Nemo

使用 Unbound 搭建更好用的 DNS 服务器

用了好久的 DNSMasq 方案终于在大半年前彻底炸掉了。

原因不光是 DNSMasq 性能和安全性完全不足以撑起公网缓存/递归 DNS 的任务,也有想要做反污染和加速的时候确实太蛋疼的问题。

现在使用的方案是 Unbound+DNSCrypt,外带一份加速列表。这段时间看来,不管在我本机还是在公网服务的两台,效果和反馈都很不错。

准备工作

需要的程序:

  • unbound
  • dnscrypt-proxy
  • makefile
  • git

makefilegit 用于处理 dnsmasq-china-list

Unbound 配置

修改文件 /etc/unbound/unbound.conf。没有这个文件的话,一般需要找一下软件包里提供的配置 example 文件复制过去。这里列出的仅包含需要修改的部分,其他的按照默认配置一般没有问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
num-threads: 2 # 线程数可以修改为物理核心数
interface: 0.0.0.0 # 侦听所有 IPv4 地址
interface: ::0 # 侦听所有 IPv6 地址
# 如果只需要本机使用,则一个 interface: 127.0.0.1 即可
so-rcvbuf: 4m
so-sndbuf: 4m # 本机使用的话,这俩 buf 可以取消注释
so-reuseport: yes # 如果开了多线程,就写 yes
msg-cache-size: 64m # 本机可以设置 4m 或者更小
rrset-cache-size: 128m # 本机可以设置 4m 或者更小
cache-max-ttl: 3600 # 建议设置一个不太大的值...专治各种运营商 DNS 缓存不服
outgoing-num-tcp: 256 # 限制每个线程向上级查询的 TCP 并发数
incoming-num-tcp: 1024 # 限制每个线程接受查询的 TCP 并发数
# 下面这四个不需要解释了吧,不想用那个就写 no
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
tcp-upstream: no # 默认是 no,隧道状态比较稳的话也不需要写 yes。一些情况下强制使用 tcp 连上游的话写 yes
access-control: 0.0.0.0/0 allow # 本机用的话建议设置 127.0.0.0/8 allow,局域网用适当调整
root-hints: "/etc/unbound/root.hints" # 没有的话在 ftp://FTP.INTERNIC.NET/domain/named.cache 下载一份
hide-identity: yes # 不返回对 id.server 和 hostname.bind 的查询。
hide-version: yes # 不返回对 version.server 和 version.bind 的查询。
# 不过下面有 identity 和 version 的自定义选项,不隐藏这些的话,修改下选项还可以卖个萌(´・ω・`)
harden-glue: yes # 建议打开
module-config: "iterator" # 禁用 DNSSEC 检查,如果上游不支持 DNSSEC 就关掉。注意这个选项有可能在其他 include 的文件里
unwanted-reply-threshold: 10000000 # 针对各种网络不服,数值为建议值,具体可以自己修改看看效果
do-not-query-localhost: no # 一般是为了防止扯皮丢包开着,不过等下要用 DNSCrypt 所以关掉
prefetch: yes # 蛮好用的,开着吧
minimal-responses: yes # 省带宽,开着吧。本机用可以关掉
# 关键部分来了,把默认查询全部丢给 DNSCrypt。使用 [地址]@[端口] 指定查询地址和端口,默认端口 53。
# 然后把国内的地址丢给国内的缓存服务器。这两个选项的顺序不能错哟。
# 如果使用隧道查询,把这个地址改为隧道对端的地址,或者一个国外的 DNS 服务器都可以,例如 8.8.8.8。
# 具体看是在对端开 DNS 还是直接用国外的服务器。后者的话,前面 outgoing-interface 可以直接设置隧道本地端的地址,不过要配合 dnsmasq-china-list 的话,还是写路由表比较合适,否则不够灵活。
include: "/etc/unbound/accelerated-domains.china.unbound.conf"
forward-zone:
name: "."
forward-addr: 127.0.0.1@5353

DNSCrypt 配置

修改文件 /etc/default/dnscrypt-proxy

1
2
DNSCRYPT_PROXY_LOCAL_ADDRESS=127.0.0.1:5353 # 设置侦听在 127.0.0.1 端口 5353
DNSCRYPT_PROXY_RESOLVER_NAME=cisco # cisco 其实蛮快的,但是慢的话就去用个别的吧。d0wn 的那堆服务器真的不稳定,和名字一个样...

如果使用 systemd 的话这个文件就没用辣,看这里看这里

修改文件 /usr/lib/systemd/system/dnscrypt-proxy.socket

要修改的部分:

1
2
ListenStream=127.0.0.1:5353
ListenDatagram=127.0.0.1:5353

127.0.0.1:5353 就是上面 unbound 配置里 DNSCrypt 的监听地址。

dnsmasq-china-list 来加速

首先 clone 这个仓库到本地。

执行 make unbound 来生成一份 unbound 配置,然后放在上面 unbound 配置里写的地址,也就是 /etc/unbound/accelerated-domains.china.unbound.conf。默认的 DNS 是 114DNS,最不太近的一段时间都挺残的所以不建议用。如果要改为其他的,例如 DNSPod PublicDNS 的话使用 make SERVER=119.29.29.29 unbound 即可。

如果还需要一些特定的缓存上游设置,要放在 include: "/etc/unbound/accelerated-domains.china.unbound.conf" 这句前面。来举一只栗子。

举一只果子的栗子。

AppStore 的加载和下载速度一直在国内饱受一些极客们的诟病,然而同时饱受诟病的运营商 DNS 解析 AppStore 的下载地址反而是国内的 CDN 地址,速度会非常快。

需要使用 dig 工具,并且要在国内的网络环境下测试。Linux 发行版里都有包可以装,OS X 则自带。

第一条命令:dig +trace a100.phobos.apple.com.

递归追踪解析结果,这个记录被 CNAME 到了 a100.phobos-apple.com.akadns.net.

第二条命令:dig +trace a100.phobos-apple.com.akadns.net.,得到这条记录被 CNAME 到了 a1-a200.itunes-apple.com.akadns.net.

第三条命令:dig +trace a1-a200.itunes-apple.com.akadns.net.,看到了 CDN 的地址,a1-a200.phobos.apple.chinacache.net.

所以如果有国内的地址直接去跟踪解析 akadns.net 的 DNS 的话,一般是可以正确解析到国内 CDN 的。先找一个 akadns.net 的权威 NS 地址,比如 a1-128.akadns.net 对应的 IP 是 193.108.88.128。同时还得有国内的 CDN 地址也从国内地址去解析,那么配置里这样写:

1
2
3
4
5
6
forward-zone:
name: "akadns.net."
forward-addr: 193.108.88.128
forward-zone:
name: "chinacache.net."
forward-addr: 119.29.29.29 # DNSPod PublicDNS 的服务器地址

多次尝试之后,发现还有一个 CDN 地址 0gq2p7eckbs26f.mwcname.com,那么也把这个地址交给 unbound 通过国内网络解析。

1
2
3
forward-zone:
name: "mwcname.com."
forward-addr: 119.29.29.29

一并加到上面的配置里,然后来测试下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Phoenix-X1-Carbon :: ~ » dig @127.0.0.1 a100.phobos.apple.com

; <<>> DiG 9.10.3-P4 <<>> @127.0.0.1 a100.phobos.apple.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24454
;; flags: qr rd ra; QUERY: 1, ANSWER: 14, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;a100.phobos.apple.com. IN A

;; ANSWER SECTION:
a100.phobos.apple.com. 3596 IN CNAME a100.phobos-apple.com.akadns.net.
a100.phobos-apple.com.akadns.net. 117 IN CNAME a1-a200.itunes-apple.com.akadns.net.
a1-a200.itunes-apple.com.akadns.net. 297 IN CNAME a1-a200.phobos.apple.chinacache.net.
a1-a200.phobos.apple.chinacache.net. 1799 IN CNAME a1-a200.phobos.apple.cncssr.chinacache.net.
a1-a200.phobos.apple.cncssr.chinacache.net. 1799 IN CNAME cc00109.h.cncssr.chinacache.net.
cc00109.h.cncssr.chinacache.net. 120 IN A 223.99.228.87
cc00109.h.cncssr.chinacache.net. 120 IN A 113.207.33.15
cc00109.h.cncssr.chinacache.net. 120 IN A 113.207.33.12
cc00109.h.cncssr.chinacache.net. 120 IN A 202.110.80.14
cc00109.h.cncssr.chinacache.net. 120 IN A 61.179.105.154
cc00109.h.cncssr.chinacache.net. 120 IN A 61.179.105.7
cc00109.h.cncssr.chinacache.net. 120 IN A 119.188.138.172
cc00109.h.cncssr.chinacache.net. 120 IN A 120.192.248.195
cc00109.h.cncssr.chinacache.net. 120 IN A 221.181.39.76

;; Query time: 233 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Apr 28 00:34:02 CST 2016
;; MSG SIZE rcvd: 387

走国内 CDN 了吧~

重启相关服务,更改自己所用的 DNS 地址,完事儿收工(「・ω・)「

=== 2016-04-30 14:00 更新 ===

如果需要 edns-client-subnet 支持的话,需要手动编译源码安装。命令

1
2
3
4
# 克隆源码
svn co http://unbound.nlnetlabs.nl/svn/branches/edns-subnet/
# 编译安装
./configure --enable-subnet --with-libevent && make && sudo make install

配置文件的格式

1
2
# 默认向所有服务器发送 edns-client-subnet
send-client-subnet: 0.0.0.0/0

如果只对特定权威 DNS 发送 edns-client-subnet 请求,则按照此格式写多行 IP。

November 13, 2016 04:54 AM

November 12, 2016

Phoenix Nemo

关于 SSL 和 IPKVM/VNC Applet 的笔记两则

虽然是没啥关系的两个东西不过最近没啥可写的所以放在一块做个笔记。

COMODO SSL CA 正确的串联姿势

有客户买了 COMODO Positive SSL 之后问我证书链变了要怎么串联。嘛确实文件变多了不过不至于这就不会了吧…

SSL 证书串联用 cat 命令的话顺序是自下而上,也就是 自己的证书 -> 二级 Intermediate CA -> 一级 Intermediate CA -> Root CA

所以不管文件有多少这个顺序总是有的。以 COMODO Positive SSL 为例,假设域名为 example.comcat 的串联命令:

1
cat example_com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > example_com.signed.crt

于是就可以拿着 example_com.signed.crt 和生成 CSR 时一并生成的 example_com.key 去给 NGINX 咯。

附一份 NGINX 配置样例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server {
listen 80;
listen [::]:80 ipv6only=on;
server_name example.com;
## redirect http to https ##
rewrite ^ https://example.com$request_uri? permanent;
}

server {
listen 443 ssl;
listen [::]:443 ssl ipv6only=on;
server_name example.com;

ssl on;
ssl_certificate /etc/nginx/ssl/example_com.signed.crt;
ssl_certificate_key /etc/nginx/ssl/example_com.key;
ssl_protocols SSLv3 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM;
keepalive_timeout 70;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

location / {
# ...
}
}

最后不忘打广告: [https://c.phoenixlzx.com]

=== 2014-10-15 更新 ===

SSLv3 再次爆出漏洞,在 Google 的新草案 TLS_FALLBACK_SCSV 还未明朗的情况下,目前禁用 SSLv3 是一个 不考虑 IE6 的 workaround。

1
2
3
4
5
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

IPKVM/VNC Applet 的使用

从 7u45 版本之后的 Java Webstart 就不能再直接执行 Ubersmith/SolusVM/Proxmox 等等的 IPKVM/VNC Applet 了。虽然实际上是这些商业软件的错不过还是很想让人骂 Java。

解决方案:下载一个旧版的 Java,比如 jdk6。

但是直接安装也是不能运行的,因为实际上调用的 java executable 是最新版 Java 的,所以一样报错。

如果是在 Linux 下,jre6 的目录在 $HOME/jre6/jre1.6.0_38,那么下载到 Applet 之后用下面的命令启动:

1
2
applet_path=/path/to/your/applet.jnlp
~/jre6/jre1.6.0_38/bin/java -verbose -Xbootclasspath/a:~/jre6/jre1.6.0_38/lib/javaws.jar:~/jre6/jre1.6.0_38/lib/deploy.jar:~/jre6/jre1.6.0_38/lib/plugin.jar -classpath ~/jre6/jre1.6.0_38/lib/deploy.jar -Djava.security.policy=file:~/jre6/jre1.6.0_38/lib/security/javaws.policy -DtrustProxy=true -Xverify:remote -Djnlpx.home=~/jre6/jre1.6.0_38/bin -Dsun.awt.warmup=true -Djnlpx.origFilenameArg=$applet_path -Djnlpx.remove=false -esa -Xnoclassgc -Djnlpx.splashport=59999 -Djnlpx.jvm=~/jre6/jre1.6.0_38/bin/java -Djnlpx.vmargs="-esa -Xnoclassgc" com.sun.javaws.Main $applet_path

November 12, 2016 01:47 AM

2014·夏

2014年的夏天应该是我一直以来度过的最繁忙和劳累的一段时光。多方面的压力、打击、困扰,加上自身各种情况条件不利,这个暑假真的让人感觉非常憋屈窝囊。

实习

最值得一提的当然是我的第一份实习。大三下学期那会也就是想找个实习锻炼下自己,也可以给家里减轻一些经济负担。在 Twitter / V2EX 发布求职贴之后陆续有不少公司找上门来,大部分公司都给出了非常不错的条件,有些甚至在我明确答应之前就问我什么时候过去…虽然简历写得比较糟糕,不过仅仅通过和各种公司打交道还是见识到了一点互联网行业的竞争状态。也算是头一回知道原来北京的创业公司工资可以给这么高

最终选择爱奇艺的原因大概就是 iven 前辈所说可以玩集群和虚拟化。虽然公司给的待遇实在让我不知道说啥比较好(除去房租几乎不够生活费,回学校一趟花掉一半工资在路费上…),但是在经济状况还没有糟糕到不得不去待遇最高的公司的时候,我还是想多见识点东西学学经验。说实话爱奇艺没怎么让我失望,工位上就有质量非常好的千兆公网带宽,配备23寸 Dell 显示器(虽然主机配置不怎么样…),弹性工作制,且几乎没有看到员工加班(虽然听说很多人回家后还是会挂上 VPN 连回公司继续工作),办公环境有物业定时打扫,茶水间的阿姨遇到自动售货机有优惠活动就会通知大家,看我打了几个喷嚏还专门去帮我拿了药,公司对于新员工和实习生的成长也算是费了脑筋…

感受颇深的是我所在的团队。在校期间经常和已经参加工作的前辈们闲聊,前辈们吐槽和抱怨比较多的基本上就是一些比较奇葩的同事,其次是公司的种种不合理。我一直认为在公司遇到奇葩同事属于及其正常甚至一定会发生的现象,在决定动身去帝都的时候也做了很多心理准备和各种应对处理办法。不过来到之后发现我想太多… 团队成员友好和睦程度完全超出我的想像,完全没有遇到之前朋友们吐槽的种种情况。每天中午都是一起出去吃饭,路上一边挑餐厅挑半天一遍各种调侃…互相尊重、理解、关心、帮助几乎是整个团队互动的原则。

说到这里其实我有些愧疚。因为自身的很多原因,每天工作的效率都非常低,心情也相当不好,写代码自然也极其浮躁。iven 前辈每次 review 代码的时候都会提出一大堆完美主义的问题,要我一遍一遍修改重构,甚至有时候让我感觉「为什么要把一大堆坑死人不偿命的框架揉在一起,为什么我有一只处女座的 mentor」… 不过事实是我发现自己在写代码的时候越来越想着更优化的解决方案,框架内提供了解决方案的尽量不去用 hack 的方法。在面对一堆完全陌生的框架揉在一起的坑,且自己整个人都很不好的情况下,还是发挥出了平日 1/3 的开发效率。韧性应该有不少提高吧…

跑路

曾经津津乐道卖 VPS 的某某某又跑路了~ (为何是「又」…) 可是现在我也要干这种事。想想真是可笑。

其实跑路并不是一方原因直接决定,也算是很多方面的共同作用结果。一直以来 UltraKVM 项目都处于亏损状态且耗费了我大量的精力,但是由于整个虚拟化平台上还有很多朋友在跑的应用,我也还是一直坚持着做下去。8月份的攻击事件之后我把机房寄发的账单转给了吸引攻击流量的客户,却被喷小公司服务不靠谱。客户修改了自己的联系信息之后不见踪影(其实修改客户信息,财务面板是会发邮件的… 所有历史信息都会留底,所以不要觉得自己改了信息就找不到了)。无奈之中找朋友诉苦,意想不到地得到了帮助。于是向机房发邮件请求下架机器,并向他们一直以来的高质量服务表示感谢(MultaCOM 机房是我目前见过的最好的机房没有之一,不管是网络还是服务)。机房帮我取消了攻击流量的账单,在我指定的日期帮我下架机器并寄送到另外的机房。因此对 MC 机房也抱有很深的歉意。

为了这件事情,我三天没吃下饭,睡不着觉,通知客户备份数据的邮件发了一遍又一遍,担心用 sendmail 发会被丢到垃圾箱还用自己的个人邮箱发了两遍,承诺会在新机房上线并为所有有效客户提供为期至少一年的免费 VPS 服务。(吐槽:就这样还有人在下架之后找我说 VPS 为啥不能访问了云云让我感觉非常不爽,同时感谢朋友的帮助才让我有偿还客户的机会)。一直到给机房发送了最后要付的所有费用之后才算是舒坦了一些。

权限狗

暑假里另外一件闹得比较欢乐的事情是喵窝服务器。应该是敖厂长的原因(?) 整个姬家都可以看到很多人在玩 Minecraft。一边在姬家宣传,管理组也是费尽了心思来准备向墙内玩家开放申请。

其实向墙内玩家开放也是我主要在推,因为墙内确实很多高素质的玩家(先不说游戏技术如何),我很希望能吸引到这些玩家来玩。17精心策划了宣传帖,二小姐把之前用的服务器寄到了帝都,我和壕桥手忙脚乱地把服务器送到机房调试了一个晚上,我还花了两个小时写了申请表单的程序,都是在为了迎接新玩家所做的准备。

不过事实上情况并没有我们想像得那么好。前三天来申请的玩家里,过审率只有不到1/12,接下来的两天里更是掉到了 1/20。从开放申请到开学之前,几乎每天都是好几十封白名单申请邮件。前几天去大概看了下,过审率依旧是 1/15 的样子。不过就算如此严格的审核,我们依然遇到了漏网之鱼…

事件经过详细说的话估计今晚我就不用睡觉了。大致就是先后有三个熊孩子在服里发飙被封,一个是要在毫无干系的百度贴吧到处发帖把我和其他几个管理骂了一通,要和我「正面刚」(…错字?),另一个则恐吓我说要去 mcbbs 「好好说说这件事」。再后面则是一个熊孩子根本没过审核,在群里刷屏伸手被拒绝后骂我们对新手不友好,是「虐待他」…..于是目前还记得的就这仨。

熊孩子被封么无非就是到处发帖子骂,是不是相关论坛也不管(素质…),然后拼命要求加群加好友然后在附言里破口大骂权限狗一类的词,或者是各种无理取闹要求入群入服或者挑衅之类。这么看咱这开源社区的管理员都算是非常好当的了233,对付熊孩子咱也只有放置 play 咯。”Argue with idiots, and you become an idiot.” – Hackers and Painters

客户中心

从大一开始我就在做各种域名/主机/数字证书的代理商,来找我买的都是姬家或者其他 SNS 的朋友,我也没怎么去宣传,权当是自己用也方便下朋友用吧。不过麻烦在于一个域名后缀在几家注册商都可以注册这样的问题,客户为了拿到更低的价格光在我这里就要注册好几个帐户。因此整合自己的业务,做统一的客户中心算是早就有的想法。在返校前两天的时候终于抽时间搭了起来,顺便用掉了两张阿里云的抵用券。广告:地址在 [https://c.phoenixlzx.com]

于是麻烦又来了——

坑:换了域名,支付宝的接口要重新审核。一连三次审核失败:

  • 第一次:网站无法访问——泥煤啊我是 https 好吗!
  • 第二次:网站非中文——泥煤啊我有多语言支持的好吗!另外狗爹那样的专业坑爹英文站怎么过审的?!
  • 第三次:网站未备案——泥煤啊我之前一样不备案怎么过审的?!一个二级域名你给我备案试试看啊!

于是干脆不提交审核了,我想其他办法用。

坑:Hostbill 默认货币选择的 USD,于是支付宝接口愣是不显示。不要那么智能好吗?敢不敢在结帐的时候自动转换?!

然后我批量更新了下产品价格,然后手残误操作把价格提太高了,想再降回去,于是玩完:所有产品的价格配置坏掉了。200多个产品啊!我一个个改得心脏都早搏了………

坑:Hostbill 自己的 whois 文件有问题,很多域名后缀无法正确查询可用性。Google 了半天找到一个论坛的帖子有解决方案,却没有提供 patch 过的文件。没办法自己一点点按照修改步骤打了 patch。由于在公司被 mentor 带出来的习惯,每一处独立的变更都会 commit 一次,然后创了一晚上80个 commit 的记录…..壕桥我超过你的一天50个记录了!

剩下没有的域名自己按照类似的方法也修了,文件丢在 Github

返校

学校的事情一直想等毕业以后再写,写本文的时候也纠结了很久,最后还是稍微写一点点。本来公司的项目第一个版本就要上线,还差最后的几个功能,突然接到母上大人的电话要求我返校报到。直接结果是——我下个月生活费不够了。

然后在学校便是各种跑宿舍分配、联系任课老师等等等…

比较让我提起精神的事情是发现社团办公室整洁了不少,书架上也放满了各种著名的计算机类书籍。新任主席也不像以前那么傲娇,而且一个月写出了一套很不错的前端页面(blueed 老伙计你可以安心的去了…

归京

明天就回帝都了。今晚好好休息… 虽然还有很多憋屈的事情根本都没提,不过也算是吐出来不少。希望回去公司可以有更好的状态吧。

晚安,好梦。

November 12, 2016 01:46 AM

为国内用户加速 Apple 服务

偶然看到雅诗的po 才发觉自己 DNS 的 Apple 服务加速已经几乎不能用了,速度非常慢。倒是挺奇怪的,因为自己做的 DNS 服务器对于整个 Apple 域的上游都是 V2EX DNS,难道是 V2EX DNS 出问题了?

总之先不管那些。现在来简单做一份 Apple 服务加速的配置。

稍微 Google 下 Apple 的服务地址,目前获得的地址如下:

1
2
3
4
a{1-2000}.phobos.apple.com
swdist.apple.com
itunes.apple.com
contentdelivery.itunes.apple.com

对应在国内的 CDN 地址是

1
2
3
4
a1-a200.phobos.apple.chinacache.net
swdist.apple.ccgslb.net
itunes.apple.ccgslb.com.cn
(最后一个没找到CDN地址..)

然后根据 114DNS 和 Google DNS 综合查询以上地址的 IP。顺便发现地址里有 cnc, cncssr 这样的关键字,于是正好尝试把 cnc 改成 tel 于是得到了电信节点的 IP。

现在写一个两句话的 js 脚本来生成一份针对 a{1-2000}.phobos.apple.com 的加速列表(废话这种东西能手写吗要累死啦),以达到尽可能将电信、联通的节点混在一起,保证各个 ISP 用户的下载速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Accelerate IPs for Apple download service.
// apple-ips.js
var ips = [
'221.192.144.12',
'182.118.46.137',
'124.95.150.148',
'123.235.32.2',
'119.84.69.17',
'122.228.85.196',
'115.231.150.15',
'61.164.153.7',
'183.57.28.18'
]

for (var i = 1; i <= 2000; i++) {
console.log(ips[Math.floor(Math.random()*ips.length)] + ' a' + i + '.phobos.apple.com');
}

然后

1
~> node apple-ips.js > apple.conf

就得到一份可以直接用的加速列表啦。

November 12, 2016 01:46 AM

Sierra 4G LTE EM7345 with Arch Linux

X1 Carbon 2015 内置 Sierra EM7345 4G LTE 适配器,系统里看到是这样:

1
2
$ lsusb
Bus 001 Device 002: ID 1199:a001 Sierra Wireless, Inc.

根据 ThinkPad EM7345 4G LTE Mobile Broadband - Overview and Service Parts 这块 EM7345 基本完美支持联通的 3G/4G 制式。

插入 microSIM 卡,然后安装需要的包:

1
# pacman -S usbutils usb_modeswitch modemmanager mobile-broadband-provider-info

启用 modemmanager 服务

1
systemctl enable modemmanager.service

然后加载内核模块 cdc_mbim,文件 /etc/mkinitcpio.conf

1
MODULES="... cdc_mbim"

重新生成内核镜像

1
# mkinitcpio -p linux # or change to your kernel preset here

重启即可在 NetworkManager 中新建 GSM 设备的 broadband (移动宽带) 连接。

顺便上一张图,老家小城市还是郊区所以 4G 信号几乎没有,还是达到了这个速度,点个赞(๑•̀ㅂ•́)و✧

Sierra EM7345 4G LTE Speedtest on China Unicom

参考文档:

November 12, 2016 01:46 AM

公共场合请使用指纹识别器

前几天带本本去导师办公室修改论文,虽然开机启动速度快,但是还是在老师的注视之下紧张敲错了三遍密码…得,本来没在意的也记住密码了(ry

所以为啥有指纹识别器的本子偏偏要输密码(:з」∠)

检查设备

总之先来看下指纹识别器是哪家生产的好了。

1
2
Phoenix-X1-Carbon :: ~ » lsusb | grep -i 'finger'
Bus 001 Device 003: ID 138a:0017 Validity Sensors, Inc. Fingerprint Reader

搜索一下就发现 X1 Carbon Gen3 这么受欢迎的本子早已上了 ArchWiki https://wiki.archlinux.org/index.php/Lenovo_ThinkPad_X1_Carbon_(Gen_3)

安装驱动

安装 fprintd 包,这个包会依赖 libfprint,但是官方仓库中的 libfprint 目前的版本在此指纹识别器上无法正常工作,表现为 enroll 只扫描一次,并且无法验证指纹。

Note that recent versions of fprint are broken for this model : One is able to enroll a finger but recognition always fails.

GitHub 上已经有了 vfs5011 相关的 issue 和 fix,但是还没有发布新的稳定版。所以暂时安装 aur/libfprint-git 包。

配置指纹验证

Fprint - ArchWiki 上已经给出了配置方法。将 auth sufficient pam_fprintd.so 加入 /etc/pam.d/system-local-login 中的第一位置即可。同样方法可以用于 /etc/pam.d/ 下的其他文件。例如 KDE 登陆使用的 sddm

扫描指纹,以普通用户身份命令:

1
$ fprint-enroll

默认是右手食指。可以使用 -f 选项指定手指。正常的表现为成功扫描五次即可保存指纹,并且在需要验证的时候自动启动指纹识别器。不过还有个小问题,如果保存多个指纹,似乎只能识别保存指纹列表中的第一个。

然而我懒得折腾了就这样吧

November 12, 2016 01:45 AM

在异步方法中使用 for 循环,以及循环中包含异步方法的几点坑

又是熬夜干活。

之前在 StackOverflow 上看到过 Array.forEach 是同步(阻塞)的,但是实际使用的时候,特别是我在写 Node.js 程序的时候几乎没感觉到过它的阻塞—— forEach 或者其他 for 循环之后的 callback 从来都是被立即调用,没有等待整个循环执行完。<== 这个命题是个假命题,下面会说明。

今天又一次掉进异步的坑里才发现 for 循环确实是阻塞的。至于为何它后面的回调函数会立即被调用,原因是 for 循环里有异步方法。整个循环不会等待异步方法结束,而是直接进入下一个循环,所以它 看起来 像是异步的,后面的回调函数「立即」被调用了。StackOverflow 上有更详细的例子和说明。

不过如果是数组,那么直接使用 async 替代原生的 forEach 和其他 for 语句即可简单解决。但是问题出在我这里的情况是要用 for in 语句处理 Object,虽说 Javascript 里 Object 和 Array 非常相似但是毕竟没有 Array 那些好用的方法,在这种情况下也不方便使用 prototype 来给我的 object 新增类似方法。二小姐给出的解决方案是手动设置计数器,获得 object 长度的办法是使用 Object.keys(obj).length

注意 Object.keys(obj).length 并不一定是要循环的次数,具体需要看 Object 的结构。我这里就是解析 DOM 得到的 Object 所以有额外的东西,length - 3 才是需要的结果。

代码写出来大概是这个样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function(obj, callback) {

var tasks = Object.keys(obj).length;

var arr = ['one', 'two', 'three'];

for (key in obj) {

tasks--;

async.eachSeries(arr, someFunc(n, cb) {

// do stuff here.

cb();

}, function(err) {

// operations after array iteration completed.

if (tasks === 0) {

// All tasks done, return to callback
return callback();

}

});
}
}

判断的语句其实也可以放在 async.eachSeries 之后,这样可以明显减少判断的计算(虽然那点计算不算啥) 但是也有可能会在没等待最后一次 async 执行结束的时候就返回了,所以保险起见还是多花点处理器来保证全部任务执行完。以及在最后 callback 的时候如果不写 return 就会失效。我还没查原因,因为已经累得不行了… 所以(当回伸手党)如果哪位童鞋知道的麻烦告知… 有灵梦的节操相送哦 >///<

==== 2014-05-20 更新 ====

感谢 Xuetian Weng 菊苣为了灵梦的节操不远万里给我纠错 /w\ 上面的代码在 async.eachSeries 里有异步方法时存在多次 tasks === 0 的情况,因为同样的因为 for in 循环不会等待 async 结束,async 的回调不一定会在 tasks 下一次自减之前被调用。解决办法是 tasks-- 放在 async.eachSeries 的回调函数里。更好些的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function(obj, callback) {

var tasks = Object.keys(obj).length;

var arr = ['one', 'two', 'three'];

for (key in obj) {

async.eachSeries(arr, someFunc(n, cb) {

// do stuff here.

cb();

}, function(err) {

// operations after array iteration completed.

// tasks self-decrease should be here to ensure every async.eachSeries call has ended.
tasks--;

if (tasks === 0) {

// All tasks done, return to callback
return callback();

}

});
}
}

不过菊苣说最后的 return 是可有可无,我这里的测试是不加 return (不管是在前面还是后面) 这个 callback 都不会被正确调用… 所以 return 的问题并没有算是解决,于是放到后面有时间的话来满满折腾吧~看 Xuetian Weng 菊苣如此渴求节操的表情咱就给一点表示感谢好了呜(双手奉上

November 12, 2016 01:45 AM

使用 IPSec 连接带有 chnroutes 的隧道网络配置小记

标题似乎不是很易懂,于是解释下先。

位于大陆的服务器 A 和位于海外的服务器 B,A 与 B 之间使用隧道互联成内网,服务器 A 配置 chnroutes 以在必要的时候通过海外服务器访问网络,并在服务器 A 上配置 IPSec 服务器,从而使终端用户能够在任何网络环境下安全接入内网。大致描述如下:

需要加速海外访问时:终端用户 <- IPSec VPN -> 服务器 A <- 隧道 -> 服务器 B <-> 互联网

需要访问大陆网域时:终端用户 <- IPSec VPN -> 服务器 A <-> 互联网

此场景适用于互联网公司为员工提供快速、安全、便捷的工作网络环境,对于个人用户来说负担较大,不建议使用。

配置隧道

最简单的方案在服务器 A 和 B 上配置 GRE 隧道即可,步骤简单不再赘述。需要注意的是为了能让客户端的内网地址能够访问到隧道的对端,也就是服务器 B 端,服务器 B 上配置隧道时 peer 的参数应当是包含服务器 A 和 VPN 客户端 IP 的 IP 段,例如 ip addr add 10.7.0.1 peer 10.7.0.2/24 dev gre0

隧道打通后,服务器 A 的路由配置为:

  • 到服务器 B 的公网 IP 路由经服务器 A 的公网网关出站
  • 默认出口路由为隧道对端
  • 注意设置内网之间的路由
  • chnroutes 配置到大陆的流量经由服务器 A 的公网网关出站

配置 IPSec

安装 strongswan

1
# apt-get install strongswan

修改 /etc/ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
config setup
charonstart=yes
nat_traversal=yes
uniqueids=never # 修改为 yes 可以限制为单个设备连接

conn ios
keyexchange=ikev1
authby=xauthpsk
xauth=server
left=%defaultroute
leftsubnet=0.0.0.0/0
leftfirewall=yes
right=%any
rightsubnet=10.7.0.64/26 # 在配置的隧道网段中选择一个小段以避免地址冲突
rightsourceip=10.7.0.65/26 # rightsubnet 和 rightsourceip 按情况
pfs=no
auto=add

修改 /etc/ipsec.secrets

1
2
3
: PSK "Your pre-shared key" # 预共享密钥,注意修改引号内部分

username : XAUTH "userP@ss" # 用户名和密码,按格式每行一个

配置 iptables 转发

1
# iptables -t nat -A POSTROUTING -o <公网网卡> -s <VPN 网段> -j SNAT --to-source <公网地址>

在本例中,iptables 的命令为

1
iptables -t nat -A POSTROUTING -o eth0 -s 10.7.0.64/26 -j SNAT --to-source xxx.xxx.xxx.xxx

DNS 加速优化

既然是服务器中转分流,那么需要在服务器上进行 DNS 解析方可获得最佳效果。

安装 dnsmasq 和 git

1
# apt-get install dnsmasq git

修改 /etc/dnsmasq.conf 部分:

1
2
server=8.8.8.8 # 默认上游服务器通过隧道交由 Google DNS 解析以获得最佳海外站点效果
conf-dir=/etc/dnsmasq.d # 启用配置文件目录

获取 dnsmasq-china-list,将其中的配置文件软连接到 /etc/dnsmasq.d 下即可使用 114DNS 直接解析大部分需要加速的国内站点。

屏蔽公网对本机 DNS 服务的直接访问:

1
2
# iptables -A INPUT -p udp -d <服务器 A 公网 IP> --dport 53 -j DROP
# iptables -A INPUT -p tcp -d <服务器 A 公网 IP> --dport 53 -j DROP

修改 /etc/strongswan.confcharon { ... } 块内添加:

1
dns1 = 10.7.0.2  # 此处可填服务器 A 上除公网 IP 和 localhost 之外的任意可绑定 IP 地址,例如隧道的本地端地址

测试

一切就绪后重启 strongswan 服务:

1
# service strongswan restart

客户端的 IPSec VPN 配置:

  • 服务器地址为服务器 A 的公网地址
  • 用户名、密码、预共享密钥为服务器 A 中配置文件中的值

连接上之后在客户端 ping 任意公网 IP、服务器 A 和服务器 B 的隧道端 IP 应该都是通的。至此,客户端连接 IPSec VPN 后所有流量都会加密经由服务器 A 中转,在必要时会经由海外服务器 B 转发,保证了访问工作必需站点的速度和在任何公共网络环境下的数据安全。

November 12, 2016 01:45 AM

使用 iptables 过滤 DNS 放大攻击

昨晚开始我们的 DNS 日志中出现大量的针对wradish.comANY记录请求,导致大量正常解析请求超时。今天上午到中午时分再次出现流量更大、针对ping.zong.co.uaANY记录请求,导致整个 DNS 服务瘫痪,完全无法响应正常解析请求。

检查后发现 wradish.com 早已因多次 DNS 放大攻击而被列入开放 DNS 服务器黑名单,网络上也有大量的针对该域名攻击请求包的过滤方法。ping.zong.co.ua 却完全没有任何搜索记录。

DNS 放大攻击 (DNS Amplification Attack) 即通过向多台 DNS 开放服务器发送无意义的 recursive 解析请求,从而使得根域名服务器遭到超大流量的解析请求包,无暇顾及正常的解析请求,由此实现分布式拒绝服务攻击 (Distributed Denial of Service)。我们的 DNS 服务器在这里既是攻击者,也是受害者。

DNSMasq 默认允许 recursive 请求,且似乎本身就没有设计为开放 DNS 服务,所以貌似也没有办法禁用掉 recursive 请求。因此我们使用 iptables 规则来过滤掉这些请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Create a chain to store block rules in
iptables -N BADDNS

# Match all "IN ANY" DNS Queries, and run them past the BADDNS chain.
iptables -A INPUT -p udp --dport 53 -m string --hex-string "|00 00 ff 00 01|" --to 255 --algo bm -m comment --comment "IN ANY?" -j BADDNS
iptables -A FORWARD -p udp --dport 53 -m string --hex-string "|00 00 ff 00 01|" --to 255 --algo bm -m comment --comment "IN ANY?" -j BADDNS

# Block domains that are being used for DNS Amplification...
iptables -A BADDNS -m string --hex-string "|04 72 69 70 65 03 6e 65 74 00|" --algo bm -j DROP --to 255 -m comment --comment "ripe.net"
iptables -A BADDNS -m string --hex-string "|03 69 73 63 03 6f 72 67 00|" --algo bm -j DROP --to 255 -m comment --comment "isc.org"
iptables -A BADDNS -m string --hex-string "|04 73 65 6d 61 02 63 7a 00|" --algo bm -j DROP --to 255 -m comment --comment "sema.cz"
iptables -A BADDNS -m string --hex-string "|09 68 69 7a 62 75 6c 6c 61 68 02 6d 65 00|" --algo bm -j DROP --to 255 -m comment --comment "hizbullah.me"

# Rate limit the rest.
iptables -A BADDNS -m recent --set --name DNSQF --rsource
iptables -A BADDNS -m recent --update --seconds 10 --hitcount 1 --name DNSQF --rsource -j DROP

添加以上规则后执行 iptables-save 来使更改生效。

目前为止似乎没有报告出现了明显规模的 DNS 放大攻击征兆,攻击者针对我们防火墙封锁的反应速度、攻击所用的记录以及从日志中发现的大量不同请求源也暗示着我们的服务器可能是最终攻击目标,用攻击流量消耗尽服务器带宽而导致服务瘫痪。至于原因,我也没有想明白为何会有攻击者对一个小型私用服务器感兴趣。

PS. 到本文发表时,攻击依旧在继续,且流量越来越大。目前阿里云的云盾设置可以说无力,只能依靠 iptables 暂时缓解。后续的应对计划仍在制定中。

=== 2014-05-24 更新 ===

Aveline 菊苣给出了一个DNS Amplification IPTABLES block lists 给点128个赞!

November 12, 2016 01:45 AM

Kancolle Broker - 舰娘直连游戏!

废话:啊……我终于治好了自己的拖延症。

事情的起因,无非就是 2015 年的第一场大戏——舰娘国服。嘛。不多评论,大家心里都明白。之后用 nginx + SNI Proxy 配置了一个代理服务器方便大家在日本地区外玩舰娘。但是这种代理服务有一个很大的问题就是会引起恶意攻击,被用作 DDoS 攻击的肉鸡使用,因此日本法律也是禁止 VPN 服务器的。

这个 idea 出现的时候还在帝都,房间里没开空调没有暖气,4M带宽20人用还有人开迅雷,无奈只好烧朋友送的 4G 流量卡来维持网络需求。完全没有力气做事情的时候偏偏看到 DMM 那个变态到极点的登录验证……

于是一直拖到回家之后碰上一大堆喵窝的活动… bangumi.moe 那边也在一直催着干活,直到今天我才良心发现用力让自己头脑清醒一点,认真分析了下 DMM 的登录验证。

整个过程大概是这样:客户端请求登录页面,页面中包含两个 token,加载完毕后一个 token 作为 header 数据,一个作为 XHR POST 请求数据发送给服务器,获得另外两个 token。这两个 token 将作为用户登录 email 和 password 的 key。用户登录时 post 给服务器的数据大概是下面这样:

1
2
3
4
5
6
7
8
9
{
"token": "11dde99d39af2287fc6eed02632ccbee",
"login_id": "user@example.com",
"save_login_id": 0,
"password": "P@ssw0rd",
"use_auto_login": 0,
"dfdfa8466f24710893d99529acaaeef0": "user@example.com",
"2760cb37702b03d10d92caf9daaaf675": "P@ssw0rd"
}

… 好了,看晕了吧。咱就不吐槽日本人脑子里到底在想啥了。总之来看代码 -> Github

原理就是登录后用 cookie 拿到游戏 link 的 apptoken,而这个游戏的 link 是不会验证日本 IP 的。

想测试效果的话,那么先关闭本地的各种舰娘代理,移除所有相关的 hosts,然后访问这里。使用 DMM 帐号登录,成功的话会跳到舰娘的游戏页面。适用于所有在日本境外没有日本代理的情况下使用。

问:会不会有安全问题?

必须的。而且比 SNI 代理更不安全。但是反过来说,也是比 SNI 更安全的。因为 DMM 登录发送密码完全没有加密,只是靠 HTTPS。所以用这个程序的话:

  1. 密码都是可以在服务端记录的。虽然程序里完全没有记录密码相关的代码,但是要加也不是难事。
  2. 没有 HTTPS 的情况下,你发送的所有数据都是明文的。所以我做的这个 demo 用了 HTTPS 加密链接。

所以如果是有 HTTPS 加密且 deploy 这个程序的人比较靠谱的话,安全性还是有保障的。

至于大家是不是相信我……嗯这个看各位如何考量啦。

最后:我真的没玩过舰娘,我也不会去玩。

November 12, 2016 01:44 AM

懒(烂)办法制作 Telegram Sticker Pack

Telegram 支持表情包 (Stickers Pack) 之后各个群里就开始出现各种奇怪的聊天表情。嘛,虽然有些做得不错但是终究是别人分享的,自己有很多好看的表情图片就不便使用了。

所以今天折腾了下,用现成的图片集来制作 stickers pack。本文使用的 sips 命令内置于 OS X 操作系统,Linux 则可以更方便地使用 ImageMagick 来操作。

Telegram 要求 Sticker 图片为 PNG 格式,并且要有透明层,至少一边为 512 像素,另一边则不超过 512 像素。最大文件大小为 350KB。透明层就算了,我不会玩 PS。那么直接偷懒(死)来批量把当前目录下 JPG 和 PNG 混杂的图片们统一转换为 PNG 好了。

命令:

1
2
3
4
5
6
# 创建 output 目录以存放输出图片...
mkdir -p output
# 把当前目录下的所有 JPG 文件转换为 PNG 丢进 output 目录里。
for i in *.jpg; do sips -s format png $i --out output/$i.png;done
# 把剩下的 PNG 图片丢进 output 准备下一步的批量操作以及简单清理... 没有的话可以跳过。
mv *.png output/ && rm *.jpg

接下来把所有图片缩放为宽或高最大为 512 像素,并且保持比例。

1
2
cd output
sips -Z 512 *.png

搞定。最后一步就是添加 Stickers Pack。需要敲这位机器人

几条命令发给 stickers bot:

  1. /newstickerpack
  2. 发送表情包的名字…
  3. 在内置的 emoji 中发送一个最符合你要发送图片的表情…
  4. 然后把对应的图片作为文件发送
  5. 如果还有其他图片的话重复 3-4
  6. 全部表情图片设置完毕,发送 /publish 命令
  7. 为你的 stickers pack 取一个短名字 (用于 URL)

KABOOM! 新的 Stickers Pack 制作完成~ 快拿链接去和大家分享吧~

最后贴我的芙兰酱的表情包
这些图片木有高清的所以如果谁能做一份高清的表情来请务必告诉我_(:3」

=== 2015.06.07 更新 ===

Mika 菊苣制作了一份芙兰酱的高清表情包

November 12, 2016 01:44 AM

迁移到了 uWSGI

虽然 PHP FPM 一直用得还行… 唔,大概已经不能叫做「还行」了,因为它总是莫名其妙地一个请求不响应然后阻塞掉了其他所有的请求。然后就是无止境的 504 Gateway Timeout… 唯一的解决方案是重启 FPM。

然后负载一高就%@T$#$#%@#$%什么的不说了。

无缝切换 PHP FPM 到 uWSGI

安装必要的包

1
apt-get install uwsgi uwsgi-plugin-php supervisor

如果是全新安装的话,php 的各种库也要一起装上

1
apt-get install php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imap php5-mcrypt php5-mhash php5-pspell php5-recode php5-sqlite php5-tidy php5-xmlrpc php5-xsl

创建文件 /etc/uwsgi/apps-available/uwsgi-php.ini 并软连接到 /etc/uwsgi/apps-enabled/uwsgi-php.ini

1
ln -s /etc/uwsgi/apps-available/uwsgi-php.ini /etc/uwsgi/apps-enabled/uwsgi-php.ini

文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[uwsgi]
plugins = php

; leave the master running as root (to allows bind on unix socket or low ports)
master = true
master-as-root = true

; listening on unix socket
socket = /var/run/uwsgi-php.sock
chown-socket = www-data:www-data

; drop privileges
uid = www-data
gid = www-data

; our working dir
project_dir = /var/www

; load additional PHP module ini files
env = PHP_INI_SCAN_DIR=/etc/php5/embed/conf.d

; ; or if you need to specify another PHP.ini
; php-ini = /etc/php5/embed/php.ini
; ; then load additional php module ini files manually
; for-glob = /etc/php5/embed/conf.d/*.ini
; php-ini-append = %(_)
; endfor =

; allow .php and .inc extensions
php-allowed-ext = .php
php-allowed-ext = .inc

; set php timezone
php-set = date.timezone=UTC

; disable uWSGI request logging
disable-logging = true
; use a max of 10 processes
processes = 10
; ...but start with only 2 and spawn the others on demand
cheaper = 2

如果要指定 php.ini 的话,env 会失效所以单独用一个循环来读入所有的额外模块配置。envphp-ini/for-glob 二选一。不知道有没有更优雅一些的方法呢…

创建 Supervisor 配置文件 /etc/supervisor/conf.d/uwsgi-php.conf

1
2
3
4
5
6
7
[program:uwsgi-php]
command = uwsgi --ini /etc/uwsgi/apps-enabled/uwsgi-php.ini
autorestart = true
user = root
stderr_logfile = /var/log/supervisor/uwsgi-php.log
logfile_maxbytes = 1048576
logfile_backups = 10

user 一定是 root,因为需要权限创建 unix socket。之后 uWSGI 会自动降权(参见前面的配置文件)。

然后在 NGINX PHP 的部分修改为

1
2
3
4
5
location ~ \.php$ {
include uwsgi_params;
uwsgi_modifier1 14;
uwsgi_pass unix:/var/run/uwsgi-php.sock;
}

最后重启所有服务并清理——

1
2
3
service supervisor restart
service nginx reload
apt-get purge php5-fpm

搞定(๑•̀ㅂ•́)و✧

November 12, 2016 01:44 AM

想要导出 Telegram 贴图

Telegram 上出现了越来越多的优质贴纸,想要把这些贴纸用到其他 IM 平台上的时候就会比较麻烦,所以一直想要一键导出一个贴纸包的功能。

可惜的是,Telegram bot API 的限制,并没有任何简单的办法通过贴纸消息获得贴纸包的信息。寻找另外的途径,例如 telegram.me 的贴纸链接会定向到 tg://addstickers?set=[StickerSet]。搜索了一下现成客户端的源码,都是交给 MTProto 的 API 处理,也没有明确的解析过程。而这些客户端所调用的 messages.getStickerSet 也没有在官方的文档中列出。(吐槽:Telegram 的协议、文档和代码真是糟糕,查阅的时候我的表情一直是 黑人问号.gif

由于最近状况不是很好,所以只好暂时放弃继续读 webogram 的源码。因为读 Angular 的东西实在是折磨…

所以依然是选择直接发 sticker 再转为图片发给用户的模式。这样的已经有了相关的 bot,于是改为多个 sticker 打包、支持多语言、支持 jpg 和 png 以及批量缩放功能的 bot。需要安装 Node.js v4.0 及以上版本和支持 webp 的 ImageMagick。

虽然实现效果看起来还可以,但是并未实现最初希望的功能,所以只能是练手用的轮子而已。不过,这个轮子稍微尝试了一些新的东西。例如超简陋的内存数据库,而且很多细节考量更加周到,例如任务锁虽然不是写过最麻烦的,不过应该算是相对完善的。当然也考虑了内存数据库的手动释放以防内存爆炸为此还特地在群里讨论了 object children 被 undefine 而 object 其他 children 还在被引用的状态下是否可以回收部分内存的问题

源码的实现非常简单,但是好久不写代码还是手生,折腾了一下午写功能加一晚上和朋友们 debug。读源码戳 GitHub

这里有一只 bot 跑在测试环境,所以可以尝试一下。如果没理你说明沙盒没开,那么就请自己去跑源码来使用辣ᕕ(ᐛ)ᕗ

有几点坑,比如这个 node-telegram-bot-apionText 方法无法正确匹配 Negative Lookahead 的正则表达式(不应该啊…然而没深究),adm-zip 非常非常不好用,jszip 文档表述不清 API 调用复杂然而用起来了就还不错。

但是最坑的是,只为实现这么一个简单功能的 bot,我的 node_modules 目录下居然有

1
2
Phoenix-X1-Carbon :: js/telegram-stickerimagebot/node_modules ‹master› » ll | wc -l                                                                                               1
104

WHAT??? 104 个依赖包!!!

真是可怕…明明我已经尽可能减少不必要的依赖了…

November 12, 2016 01:44 AM

搭建一套权威 DNS 服务架构

萌 DNS 已经年久失修。尽管一直有计划完全重写出一套应用目前各种 DNS 特性和优化的完整平台,但是目前的精力不允许。所以为了先让萌 DNS 的用户们至少先有一个能支持 Let’s Encrypt 的 DNS 服务,决定暂时舍弃 GeoDNS 功能,使用一套更加成熟的解决方案提供服务。

搭配方案如下:

服务器部署:

  • 管理服务器 x1
    • MySQL Master
    • PowerDNS
    • PowerDNS-Admin, supervisor, virtualenv, gunicorn…
    • NGINX, Let’s Encrypt
  • DNS 服务器 x4
    • MySQL Slave
    • PowerDNS

在管理服务器上安装 PowerDNS 和 MySQL Master 的考量是由于 PowerDNS-Admin 使用 PowerDNS HTTP API,在管理服务器(或管理私网中)启动一个仅用于提供 API 和操作主数据库的 PowerDNS 实例能够减轻 Primary NS Server 的压力并提升安全性。整套架构使用 Ansible 进行自动化部署,不过好久没用了各种生疏,照着文档折腾好久的配置…

于是这里暂且记录下整个过程。有些坑只是作者一时疏忽或者有别的考量但没有明确记录,也许在未来的版本中会修复。

安装 PowerDNS

所有服务器均使用 Ubuntu 16.04,需要 PowerDNS 4.0 以上的版本。按照此页面的说明添加 PowerDNS 官方的仓库即可。

1
# apt install pdns-server pdns-backend-mysql mysql-server

由 dpkg 自动配置 PowerDNS 的数据库,然后删除 /etc/powerdns/pdns.d无关的配置文件。

1
2
# rm /etc/powerdns/pdns.d/pdns.local.conf
# rm /etc/powerdns/pdns.d/pdns.simplebind.conf

配置 MySQL Replication,管理服务器作为 Master,其他 DNS 服务器作为 Slave。细节不多讲,官方文档 或者 DigitalOcean Tutorial

管理服务器 (MySQL Master) PowerDNS 配置文件 /etc/powerdns/pdns.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
api=yes
api-key=yourapisecretkey
api-logfile=/var/log/pdns-api.log
config-dir=/etc/powerdns
guardian=yes
include-dir=/etc/powerdns/pdns.d
launch=
local-address=127.0.0.1 # 不对外提供服务
local-ipv6=::1
security-poll-suffix=
setgid=pdns
setuid=pdns
webserver=yes
webserver-address=127.0.0.1 # 仅向本机的 PowerDNS-Admin 调用。如果配置在内网,请使用内网 IP
webserver-allow-from=127.0.0.1/32 # 同上,如果使用内网则写 PowerDNS-Admin 在内网的 IP
webserver-port=8081
default-soa-name=ns1.example.com # 改为 Primary NS 的地址
default-soa-edit=INCEPTION-INCREMENT
default-soa-mail=hostmaster.example.com # 改为默认服务器管理员的邮箱地址,并将 '@' 替换为 '.'
default-ttl=3600

DNS 服务器 (MySQL Slaves) PowerDNS 配置文件 /etc/powerdns/pdns.conf

1
2
3
4
5
6
7
8
9
10
11
config-dir=/etc/powerdns
daemon=yes
disable-axfr=yes
guardian=yes
include-dir=/etc/powerdns/pdns.d
launch=
security-poll-suffix=
server-id=ns1.example.com # 改为当前服务器的 ID,ns1/ns2/ns3/etc...
setgid=pdns
setuid=pdns
version-string=anonymous # 可以写任意字符串恶搞_(:з」∠)_

安装 PowerDNS-Admin

作者有提供详细的教程但是还是有坑

安装依赖:

1
# apt install git python-pip supervisor virtualenv python-dev libmysqlclient-dev libsasl2-dev libldap2-dev libssl-dev letsencrypt

创建数据库,切换到普通用户权限,clone 仓库到本地,然后一步一步操作即可。

1
2
3
4
5
6
7
8
$ git clone https://github.com/ngoduykhanh/PowerDNS-Admin.git
$ cd PowerDNS-Admin
$ virtualenv flask
$ source ./flask/bin/activate
$ pip install -r requirements.txt
$ pip install mysql gunicorn
$ cp config_template.py config.py
$ vim config.py

配置文件 config.py 中需要更改的地方:

1
2
3
4
5
6
7
8
9
SECRET_KEY = 'yoursessionencryptkey'
SQLA_DB_USER = 'yourdbusername'
SQLA_DB_PASSWORD = 'yourdbpassword'
SQLA_DB_HOST = 'localhost'
SQLA_DB_NAME = 'yourdbname'
PDNS_STATS_URL = 'http://localhost:8081/'
PDNS_API_KEY = 'yourapisecretkey'
PDNS_VERSION = '4.0.0'
RECORDS_ALLOW_EDIT = ['A', 'AAAA', 'CNAME', 'SPF', 'PTR', 'MX', 'TXT', 'SRV', 'NS', 'SOA']

然后执行 ./create_db.py。如果没有报错说明数据库安装成功,执行 ./run.py 即可访问 http://127.0.0.1:9393 看到登陆页面了。

部署 Web 服务

直接跑 run.py 当然不科学。Supervisor 配置文件 /etc/supervisor/conf.d/pdnsadmin.conf

1
2
3
4
5
6
7
8
9
10
11
[program:pdnsadmin]
command=/home/pdns/PowerDNS-Admin/flask/bin/gunicorn run:app
directory=/home/pdns/PowerDNS-Admin/
user=pdns
autostart=true
stdout_logfile=/var/log/supervisor/pdns-stdout.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=2
stderr_logfile=/var/log/supervisor/pdns-stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=2

创建 DHParam

1
2
# cd /etc/ssl/certs
# openssl dhparam -out dhparam.pem 4096 # 如果性能不够请使用 2048

NGINX 配置文件 /etc/nginx/site-enabled/pdnsadmin.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
server {
listen 80;
server_name dns.example.com;

location /.well-known {
default_type "text/plain";
root /var/www/html;
}

location / {
return 301 https://dns.example.com$request_uri;
}
}

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name dns.example.com;

ssl on;
ssl_certificate /etc/letsencrypt/live/dns.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/dns.example.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";
keepalive_timeout 70;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;


access_log /var/log/nginx/dns.example.com.access.log;
error_log /var/log/nginx/dns.example.com.error.log;

location /.well-known {
default_type "text/plain";
root /var/www/html;
}

location /static {
alias /home/pdns/PowerDNS-Admin/app/static;
}

location / {
proxy_pass http://127.0.0.1:8000;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-IP $remote_addr;
port_in_redirect on;
server_name_in_redirect off;
proxy_connect_timeout 300;
}
}

记得把 dns.example.com 换成自己的域名。

签发 Let’s Encrypt。也不多讲。NGINX 配置中已经有了针对 Let’s Encrypt 的续期设置。

然后重启各项服务

1
2
# systemctl restart supervisor
# systemctl restart nginx

查看 PowerDNS-Admin 的运行状态,使用 supervisorctl status

添加 GLUE 记录

要使自己的 NS 生效,必须有保存在上级 NS 中的记录。很多域名注册商都提供了配置 GLUE 记录的功能,例如 Hexonet (1API):

Glue Records

简言之,需要把自己的 NS 服务器及对应的 IP 记录到上级 NS。完成之后,通过 PowerDNS-Admin 添加自己的域名,zone 类型为 NATIVE。然后添加所有 NS 服务器的 A/AAAA 以及所有的 NS 记录——你没听错,要自己写 NS 记录。其他域名也需要添加这些 NS 记录,否则不会托管。

Glue Records

收尾

全部完成之后就是一个完整功能的 DNS 服务了。如果希望启用 DNSSEC,需要在管理服务器中通过 pdnsutil 来添加 key。

由于目前 PowerDNS-Admin 没有限制不能添加提供的 NS 之外的名称服务器,所以其他域名按照添加 GLUE 记录的方法,也可以将这些 NS 服务器「变成」自己的 NS。

好了,不会说话了。讲效果——

一般来说,DNS 服务都会提供多台 NS 服务器域名,将域名的 DNS 改为这些 NS 服务器才能托管到该 DNS 服务上。但是现在只需要知道这套 DNS 的服务器 IP 地址,即可给自己的域名添加 GLUE 记录、NS 记录和 NS 对应的 A/AAAA 记录进而使用自己的域名作为 NS,而不需要用 DNS 服务的 NS 域名。当然一般就是看起来会比较厉害而已…

November 12, 2016 01:43 AM

修复 Arch Rollback Machine 磁盘占用

最近发现 downgrade 失效了,降级啥包都是直接错误退出。然后排查的时候发现是 Arch Rollback Machine 挂了,原因是——磁盘不足。

检查后发现 pm2 的日志有 7G,850天前的包有5000多个,365天内的包有10万余,磁盘已经被吃得满满当当。

日志好说,因为没啥特别有用的东西也不用拿来做分析,直接删掉就好。Felix 也把 850 天前的包全部删干净了。不过再次检查的时候 df -h 显示依旧是磁盘被吃满的,而 df -ih 显示 inode 占用没有问题。猫怀疑是有文件句柄还开着,但是把所有可能的进程杀掉之后依旧没有变化,排除。另外一个现象是文件删除进程完成后过了一段时间磁盘开始有少量空闲空间了,可能是磁盘缓存…?

于是靠着这空出来的 900M 多点的空间执行了几个操作:把有 HTTP flood 漏洞的 Node.js 0.10.16 升级到最新版 0.10.28,将包同步的 cronjob 降低到每天执行一次,压缩旧的 NGINX 日志。

以及用户一直要求的包归档功能,实际上是已经有了的,但是并没通过 NGINX 配置来实现(Pia!<(=o ‵-′)ノ☆猫) 所以准备把归档功能写到 API 服务端里。因为之前很有预见性的同步的时候把每天的包数据库按照 /year/month/day/repo 的结构保存起来(然后这部分的数据库文件就有 30G!),所以用户按照这个路径来请求包数据库的时候直接把对应的数据库文件丢给客户端,请求包文件的时候则直接从包目录里丢过去。这样的话其实从一个较早的时间结构最后请求一个较新的包文件也能拿到….不过这个不是问题,因为包归档给 pacman 用的话,有哪些包还是 pacman 要读数据库的。

包归档的 index 确实花了不少时间,目前的实现貌似也不很好,虽然貌似还挺快的,但是也挺吃硬盘的…而且一连好几次手残敲错了东西、少加了分隔符、加了冗余的配置变量…. 总之就是爆肝熬夜的效率超级差,4点多基本上没问题的时候已经能感觉到身体很不舒服了…

然后是一觉睡到第二天中午。觅食归来给 A.R.M 的 KVM 虚拟机硬盘增加了 100G。执行 resize2fs 的时候报错说已经达到最大空间,删掉 swap 之后依旧。接下来干了一件很作死的事情——把所有分区删掉了,然后按照原来的分区方案重建了新的分区。Reboot 之后 lsblk 是对了,但是 df -h 依旧是原来的分区。一叶说是 resize 没成功,要进 LiveCD 里面 resize2fs。不过我没镜像… 按照一叶的建议挂上 VNC 进 recovery 然后 fsck,再正常进系统执行 resize2fs,成功。

回过神来的时候发现这种作死的行为我居然手都没抖一下 = = 几百G的数据呐…

晚上还要做字幕,再去休息下好了…

November 12, 2016 01:43 AM

ThinkPad X1 Carbon 2015 与久违的 Arch Linux

对 X1 Carbon 2015 垂涎已久。然而中国市场不提供定制配置、没有 16G 内存的版本、512G PCIe SSD 的型号配置触控屏。这些都不能忍…于是折腾了好久,打扰了至少三位在美国的朋友,最终在黑色星期五当天下单,前天顺丰到手。

先晒一下单。这个配置在联想美国官网购买的原价是 2622.60 美元,黑色星期五的各种优惠后价格是 1630 美元。嘛… 多出来的预算败了一块 1TB SSD 当移动硬盘,真是麻烦帮忙带回国的朋友了(っ‘ω’c)

X1 Carbon 2015 20BSCTO1WW

  • Intel Core i7 5600U 2.6GHz / 3.6GHz
  • 16G (2x8G) DDR3 1600MHz On-board RAM
  • 512G PCIe SSD
  • 2560x1440 QWHD IPS
  • Fingerprint Reader
  • Sierra LTE EM7345

其中 SSD 是 SAMSUNG SM951,测试读写速度分别为 1546MB/s 和 1260MB/sdmidecode --type memory 显示有两条镁光制造的内存,这个倒是和网络上流传的单根内存不太一样。不过两根都是 on-board,所以是无法更换的。

屏幕颜色偏暖,大概是为了长时间工作不会太劳累设定的。ThinkScope 提供了一份 ICC 文件,效果有点偏蓝(紫红?)。不过屏幕型号并不是页面上写的 LG LP140GH1-SPA2,而是 LG LP140QH1-SPB1。总之雾面屏加 86% sRGB coverage 导致它的显示效果比旁边的 MacBook Pro Retina 差了一大截,而且 HiDPI 带来软件上的问题… 嘛这个后面再说。

X1 Carbon 2015 整个机身做工很好,拿着很有份量(不是沉!)给人一种很结实的感觉,完全没有其他 ThinkPad 的塑料感。唯一略心颤的是屏幕没办法像 MacBook Pro 一样稳,和其他笔记本一样调整屏幕角度还是会晃动几下,大概是铰链设计的差异导致。键盘仍然是那个熟悉的手感——哪怕是悬浮式键帽,仍然和老式 ThinkPad 键盘手感不相上下。换到这么舒服的键盘之后打字速度已经快到让 fcitx 分不清键序啦( ´∀ )σ)Д`)其实是 GTK 程序的 bug。ThinkLight 改到键盘下方,可以更好地看清键帽更高大上了,但是没办法当作光源看其他东西… 不过本来也不怎么亮还是爱护下眼睛吧。触控板很舒服,虽然大概还是不够和 MacBook Pro 相比,毕竟人家有底层驱动优化,系统层还支持很多手势。单从手感上来讲还是很不错的。

新本本送到后开机检查硬件,然后当然是要删掉预装的 Windows 10 然后换上 Arch Linux 啦。之前 T420 的 UEFI 安装 Arch Linux 总是启动不能,然而 X1 Carbon 就很顺利。当然是关掉了 Secure Boot 的。

续航感觉还不错,中午送到开箱检查的时候就还有 60% 的电量,然后从下午开始到晚上一直在安装配置系统,到半夜的时候报告还剩 15% 电量。中间有几次编译,所以顺便看了下散热。室内温度大概 10 摄氏度,sensors 检测高负载时最高温度 57 摄氏度,一般工作温度 35-40 摄氏度。这是有点出乎意料的,本来以为超极本的散热都会很悲剧。不过据说其他人也有到过 80 度的,所以要再用用看。

Sierra LTE EM7345 是 microSIM 槽,然而手头只有 nanoSIM 所以还没用上。回去学校把流量卡补成 microSIM 再玩玩看。

安装过程完全不折腾。硬件兼容性报告只有一句话:Everything works out of box.

如果一定要在兼容性上挑刺儿的话,大概是 GPS 只有在 Windows 下才有办法打开,打开之后就可以直接在 Linux 下用了。

回到 Arch Linux 上。一年没用,KDE 已经快不认识了。通过更改 DPI、字体大小和图标大小,Qt5 的程序算是很好的支持了 HiDPI。然而一些 GTK / Qt4 程序就没那么好说话了… 总之用各种奇怪的办法让这些跟不上时代的程序变得勉强能看。

刚开始用 MacBook Pro 不久的时候,除去再也无法接受 retina 之下的显示效果(retina 有毒啊)之外,由于键盘、输入法(!!!)、快捷键、工具链、UNIX 生态等等问题上,OS X 上的工作效率只有 Linux 的 1/3 左右(是真的,很早的时候解决一个问题大概花了一个多小时,后来在 OS X 上再次遇到却花了一个下午)。

总之用来这几天,可以说是非常满意的。没有任何生产商设置的门槛,OS-Specific 的东西通过各种 折腾 Google、折腾 IRC 频道里的各位、折腾 ArchWiki… 等基本都解决了。现在看到它就有想摸一摸、想用它工作的冲动(๑•̀ㅂ•́)و✧然后上个图~

Desktop

KInfoCenter

最后的一点牢骚。

一年多没怎么碰 Linux 桌面,虽然看起来变化很大,但是实际上的提升基本没有感觉到。倒不如说,由于硬件技术一直在革新,Linux 桌面却没有了当年的势头。前段时间去 KDE 主页看的时候发现他们把捐赠放到了首页上,而且赞助页看起来好粗糙,完全不像是 KDE 一贯精致的设计风格。从对 HiDPI 的支持来看更是如此。KDE/High-dpi issues 页面从半年前到现在基本没有太大的变化…

嘛。当然了就算是钱多得没处花的某有点软的公司也没让自己的操作系统好好的支持 HiDPI ( ´∀ )σ)Д`)

开源界仍然有大量被广泛使用的应用保持着粗制滥造、works for me 等等的心态。虽然绝大多数不会有人为之付费,但是这并不是不认真的理由。而且这样下去也并不会有多少杀手级的应用出现。

虽然作为 DevOps 的话只要有好用的命令行工具链就万事大吉了。 更何况现在厨子整天瞎搞,果子就算还能继续赚无脑追捧的大众的钱,它还能制作多少 developer-friendly 的产品呢…
(´・ω・`)

November 12, 2016 01:43 AM

用优雅的方式在 OS X 中为单个应用设置语言

买到 CLIP STUDIO PAINT Pro,激活之后发现不给启动,显示 Unsupported OS 并退出。搜索了下发现是因为语言设置的问题导致,需要将系统环境设置为应用所支持的语言才能运行。

嘛。日本人做事情也是让人无话可说,那么多应用都没有多语言支持的… 用能支持的语言来显示就好了嘛。

话说回来,我一开始设置的系统语言是简体中文,虽然后备语言加了 English 和日本語,不过有些不能自动变更语言的应用在更换系统语言为 English 之后变得很别扭。于是寻找可以单独设置应用语言的方法。

用惯了 Linux 再用 OS X 其实并没有那么容易的改变习惯… 当我准备尝试单独 export 一份 locale 再运行 app 的时候 OS X 直接告诉我不适用我的表情简直和伊莉雅一样。

睡了一觉起来继续 Google。找到了可以单独为应用设置语言并且 launch 的 app Language Switcher,看起来不错,但是每次启动 CLIP STUDIO PAINT 都要先打开这货,这不是我想要的效果。

于是最终找到一个合适的解决方案:用 defaults 命令。

原帖在这里

设置 CLIP STUDIO PAINT Pro 语言环境命令:

1
defaults write jp.co.celsys.CLIPSTUDIOPAINT.lip AppleLanguages '("en-US")'

其中 jp.co.celsys.CLIPSTUDIOPAINT.lip 可以在应用的显示包信息 -> Contents -> Info.plist 中找到。

执行后就可以直接双击启动啦~

最后献丑一张w

Kira( > ◡╹)~ ◡╹)~" /> ◡╹)~" />

November 12, 2016 01:43 AM

在 Ubuntu 服务器上搭建 OpenConnect 服务器小记

最近猫给推荐的 Cisco AnyConnect,面基的时候也看到 Aveline 菊苣在用 AnyConnect(插嘴:正太菊苣果然好可爱!!!) 于是自己折腾了下。作为少有的能让我折腾起来的 VPN,暂且把搭建步骤简单记录下。

AnyConnect 的好处是基于 HTTPS,证书可以申请 StartSSL 的,而且配置也不很复杂。另外配置文件里发现了很多专门为商业化/企业服务定制的选项,例如最大同时在线客户端数量,同账户最大在线设备数量等等。

OpenConnect 是 AnyConnect 的开源实现。目前 0.8.0 版本需要 GnuTLS 3.1 以上版本,所以我们就直接在 Ubuntu 14.04 中搭建。另外 就算是基于 HTTPS,这货也是要用 TUN 设备的,所以 OpenVZ 用户们请注意。

准备

命令

1
2
3
apt-get install build-essential libwrap0-dev libpam0g-dev libdbus-1-dev \
libreadline-dev libnl-route-3-dev libprotobuf-c0-dev libpcl1-dev libopts25-dev \
autogen libgnutls28 libgnutls28-dev libseccomp-dev libhttp-parser-dev

安装

下载源码

1
2
3
wget -c ftp://ftp.infradead.org/pub/ocserv/ocserv-0.8.0.tar.xz
tar xvf ocserv-0.8.0.tar.xz
cd ocserv-0.8.0

编译安装

1
2
3
4
5
./configure --prefix=/opt/ocserv 
make
make install
mkdir /opt/ocserv/etc/
cp doc/sample.config /opt/ocserv/etc/config

配置

这里只记录需要修改的重点配置,其他配置请参照样例配置文件的注释按需修改。

文件 /opt/ocserv/etc/config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
auth = "plain[/opt/ocserv/etc/passwd]"
listen-host = connect.your.domain
max-clients = 128
max-same-clients = 4
server-cert = /opt/ocserv/etc/ssl/server-cert.pem
server-key = /opt/ocserv/etc/ssl/server-key.pem
ca-cert = /opt/ocserv/etc/ssl/ca.pem
mobile-idle-timeout = 2400
ipv4-network = 192.168.1.0
ipv4-netmask = 255.255.255.0
dns = 8.8.8.8
dns = 8.8.4.4
route = 192.168.1.0/255.255.255.0
route-add-cmd = "ip route add 192.168.1.0 dev tun0"
route-del-cmd = "ip route delete 192.168.1.0 dev tun0"

创建用户,命令

1
/opt/ocserv/bin/ocpasswd -c /opt/ocserv/etc/passwd username

按提示输入两次密码。

iptables 规则

1
iptables -t nat -A POSTROUTING -j SNAT --to-source <server ip> -o <nic>

记得把 <server ip><nic> 改为服务器公网 IP 和对应网卡的名称。

搞定

折腾完毕,AnyConnect 客户端可以成功使用了。

不过呢折腾完之后发现这货其实被干扰得很厉害啊…

November 12, 2016 01:43 AM

末字幕组时代,我们何去何从

日本のアニメやドラマを勝手に翻訳して字幕を付け、違法配信する中国のアマチュア集団「字幕組」。北京在住のメンバーを取材してその実態や動機、違法性を伝えたところ、中国のネットメディアが本紙記事を翻訳、「字幕組がなくなれば抗日ドラマに洗脳される」などと擁護の声が殺到した。

这是好久之前出现在日本各大新闻站上的文章。今天偶然和朋友闲聊时再次聊到了版权问题。

前些时间开始,射手网、人人影视、极影等字幕发布站相继被 takedown,相关律师函甚至恐吓信开始出现在站长们的邮箱中。在中国,「版权」第一次闹得这么凶。然而可悲的是,这并不是因为中国人开始有了版权意识,而仅仅是影响了一些商业集团的利益。

在近期某字幕组发布的作品中,发布者这样写到:

現今,版權的世代到來
觀眾也都紛紛的看版權去了
這對我們來說不是什麼問題,因為我們本來就不在意什麼載量
做字幕本來就是自爽的,與其說是做給別人看,不如說是做給自己看
對字幕界比較大的問題其實是新人減少這件事

從這幾天MANGLOBE(動畫公司)倒閉事件來看
看似盛況的MAG(漫画、アニメ、ゲーム)也明確的顯示出,其實是正在走下坡的
但是國外購買版權這情況,是有助於動畫公司轉虧為盈,不再只是依靠BD賺錢
我們理所當然的是要感到高興
這絕對絕對不是壞事,有愛支持正版是對的

但同時我也希望各位,如果真的很喜歡某片的話
在經濟允許的情況下,買個BD原盤支持一下吧,因為並不是每個片子都有版權購買(AMAZON用郵局的VISA卡就能買囉)
請把口中的支持化作實際的支持吧,良好的作品不該讓它沈入水中

另一方面
由於版權大量崛起,似乎可以望見字幕組的數量會越來越少
有所謂的版權可以看後,也會大幅降低新人願意加入字幕組的意願,因此製作字幕的人會越來越少
最後,支撐著字幕組的人只剩下老人
工作,結婚,後宮,家庭,最後連老人們都各奔東西(現在越來越坑就是因為現實的干擾越來越多
接著就化作了時代的眼淚

曾经在早些年,在大陆的视频站大部分仍然只是互联网上用于大家互相上传视频自娱自乐的时候,并没有某相关部门令人无力吐槽的审查和胡搅蛮缠,日本动画也并没有那么大规模的影响力和市场。字幕组的相继出现,让国内的观众们能够看到制作精良的日本动画,也促进了国内 ACG 圈子的发展。更重要的,字幕组将日本的二次元文化、包含其中的人的素养,人性的光辉和思考,带给了越来越多的人。

在这个时候应运而生的发布站,整合了多个字幕组的作品给观众挑选,也直接加剧了字幕组之间的竞争。经过一段时间的发展,字幕组的工作流程越来越优化,翻译和字幕的质量越来越高,甚至做到了日本电视台首播后两三个小时就可以制作出精美字幕的程度。

字幕组的百家争鸣慢慢引起了商界的注意。好像就在一夜之间,各大视频站都开始争相购买日本动画的版权。这本来是一件好事,但是相关部门的审查、过长的广告等等问题引发了观众们的不满,他们开始将发布站和视频站推向一个对立面,微博上甚至出现了「视频站不给看,我们去xxx下载!」类似激进的言论。然而与此同时,某圈内朋友表示:

国内所有视频网站我都是VIP
我觉得体验还不错
就翻译有待加强

字幕组的建立,有些是因为兴趣爱好,也有些只是随口说出的玩笑。但是共有的一点,都对二次元文化充满了热爱。很多组员看到了喜欢的作品会攒下生活费购买蓝光盘用于收藏,根本不会拆封,只是为了支持一下制作公司而已。但是从法律上来讲,字幕组的行为依然不能被接受。

我们推动了动画在国内的推广,但是并没有实力给制作公司分钱。
所以是我们退场的时候了,让专业的来吧。

所有人都已经看到了残酷的现实。曾经为了兴趣抑或只是一句玩笑建立起的小组,一路走来承载着众多组员们的羁绊,转瞬间已经到了并不华丽的谢幕。

也许会有字幕组和发布站继续存在下去,但是更多人会选择在线观看视频站购买的正版动画。虽然并不是中国人的版权意识提高了,但是至少,视频公司不管是通过广告还是销售会员获得的利润,可以用来继续购买更多的版权动画给大家看。对动画制作公司,对观众们,都不是坏事。

说到底,大部分字幕组,只是为了大家能在一起玩得开心罢了。字幕只是让大家聚到一起的载体,就算没有再继续做字幕,这些「字幕组」之名也可以让组员们继续走在一起吧。想必,这段可能仅仅存在于近几代人中的记忆,会被小心翼翼地保存一生。

R.I.P

November 12, 2016 01:42 AM

尝试迁移到 AWS Cloud

发了个帖子抱怨服务器一大堆却没个放自己个人站的地儿。其实是服务器都是生产环境要跑各种业务,不能放自己的东西。其实个人站就一个博客一个知识库,还都是静态的,连买个 Linode 都觉得资源浪费。放在 GCE f1.micro 上吧,是便宜了不过网络抽死。如果不是因为要用 SSL 防运营商劫持和中间人攻击,真的直接扔 GitHub 了。

感谢 @sparanoid 的建议,花了一下午尝试将自己的静态网站部署在 AWS 云上。

AWS 准备工作

S3 文件桶

创建一个 S3 文件桶,并在「属性」 -> 「静态网站托管」页选择「启用网站托管」,然后索引文件填写 index.html

在此页面会得到一个网站终端节点,例如 example.com.s3-website-ap-northeast-1.amazonaws.com 这样。先复制备用。

上传 SSL 证书

其实之前签发了一张三年的 ShinoSaki ECC 证书,然而 AWS 不认(ノ=Д=)ノ┻━┻ 只好去签了 AlphaSSL 的泛域名。

需要安装 awscli,可以直接 sudo pip install awscli

上传证书命令如下(示例名称记得改成自己的哦)

1
aws iam upload-server-certificate --server-certificate-name cert-name --certificate-body file://example_com.pem --private-key file://example_com.key --certificate-chain file://ca.pem --path /cloudfront/yoursite/

前面的 file:// 也不能移去,否则会报错 A client error (MalformedCertificate) occurred when calling the UploadServerCertificate operation: Unable to parse certificate. Please ensure the certificate is in PEM format.

创建 CloudFront Distribution

如果是 Jekyll 一类会创建「文件名.html」的程序,origin 直接下拉菜单中选择 S3 的文件桶地址即可。但是 Hexo 和 MinoriWiki 都是目录名下的 index.html,所以只能使用之前得到的网站终端节点。填入地址后,下面的配置随自己的想法选择即可。Alternate Domain Names (CNAMEs) 填写自己的域名,然后下面的 SSL 选择 Custom SSL Certificate (example.com) 和一个对应的证书。Default Root Object 填写 index.html

为什么只能使用网站终端节点而不是 S3 地址的原因是 CloudFront 和 S3 的 Default Root Object 工作原理不同,如果是请求子目录,那么 CloudFront 不会给默认加上 index.html 导致 403。

创建之后 CloudFront 需要(很长一段)时间来部署。于是先把分配的 CDN 地址 xxxxxxxxxxxxx.cloudfront.net. 复制备用。

设置 Route 53

建立一个 Route53 Hosted Zone,然后导入以前的 zonefile 或者什么的。网站的地址 Type 选择 A - IPv4 address,然后 Alias 选择 Yes。Alias Target 是刚才复制的 CloudFront 的 CDN 地址。Routing Policy 选择 Simple——GeoDNS 将交由 CloudFront 完成。

网站程序准备

Hexo

Hexo 已经有了现成的 S3 deployer,不过主页上的那个不工作也不管别人给提交的 PR。所以推荐使用 hexo-deployer-aws-s3

不过貌似是会强制覆盖每个文件的,上传花了好久啊比 Git 慢多了… S3 啥时候支持 Git (ノ=Д=)ノ┻━┻

MinoriWiki

MinoriWiki 从一开始就是面向 UNIX 系使用者的一套超简单个人知识库,所以压根没考虑用 S3 啊(ノ=Д=)ノ┻━┻

不过自己写的东西好处就是可以各种改(死)所以半个下午发布了一个带有 S3 支持的新版。使用了 node-s3-client 库,可以只上传修改了的文件。

搞定

一路搞下来,CloudFront 也就差不多了,域名解析应该也更新了。试试看效果吧~

没啥访问量的个人站,用 Route53 + S3 + CloudFront 的开支一个月可以控制在 2 美元左右,但是性能、网络和可用性远远比一个月 2 美元的 VPS 好得多。

然而在国内… AWS 被滥用还是挺凶的。所以各种服务被干扰,于是成了现在这惨样。

其实还不如直接托管在 GitHub 然后上个支持自定义 SSL 和 PAYG 的 CDN

November 12, 2016 01:40 AM