新站点架设过程

为何要重新建站?新站建在哪里?

在忍受了笨重的 Wordpress 三年后(以及 Bluehost 充满 2003 年设计感的管理面板),我终于决定放弃旧站另起炉灶。我对 Wordpress 博客主要有这些不满:

  • 体积庞大,占用很多不必要的服务器空间。
  • 没法使用我熟悉的工具来方便地备份。
  • 没有良好的命令行界面,而命令行正在成为我做任何事情的首选工具。
  • 缺少一些我所需要的基本功能,比如多语言支持。在部分人手里 WordPress 或许极为强大,但是我不想投入时间学习 CSS/js/php ,也不想从那些源头不明的 WordPress 插件商店里下载任何东西。
  • 这些 WordPress 主题/插件商店让我想起 Ubuntu 软件中心。
  • WordPress 有很多我用不到的功能,比如用户系统。这用在个人博客上显然是杀鸡用牛刀。

在我确定了自己的需求后,我很容易地就找到了替代品:一个用我所知道的编程语言(或者我愿意学习的编程语言)所实现的快而小巧并带有原生多语言支持的静态站点生成器,那就是 hugo

至于站点托管服务,我本来考虑使用 github pages 或 netlify 这种简单快速的解决办法,但是考虑到是个人站点,还是 VPS 这种功能强大一些的选择比较合适。而且 github pages 不支持自定义域名的 https ,这对我来说无法接受。我列出了所有比较出名的 VPS 服务提供商,筛出支持 Arch Linux 的部分,最后选择了 DigitalOcean 。由于我想要完全切断和 Bluehost 的联系,我把自己的域名也转移到了 Google Domains 。

安装 Arch Linux

注意 Arch Linux 其实并不适合用作服务器操作系统。如果一切以系统稳定性为优先,那么选择一个非滚动更新的 Linux 发行版比较合适。我在服务器上用 Arch Linux 主要是因为我在我的所有其他电脑上也都运行 Arch Linux。如果你选择使用 Arch Linux 作为服务器操作系统,最好勤于备份:虽然我还没遇到这种情况,但是常有人抱怨 Arch Linux 很容易被玩坏。

安装系统

显然我得到的关于 DigitalOcean 支持 Arch Linux 的情报已经过时了,他们已经停止支持 Arch Linux 有一阵子了。好在有 digitalocean-debian-to-arch ,使得在水滴( droplet , DigitalOcean 对每个服务器的称呼)上安装 Arch Linux 并不困难。我只需要 新建一个 droplet ,通过 ssh 登录服务器,并执行:

# wget https://raw.githubusercontent.com/gh2o/digitalocean-debian-to-arch/debian9/install.sh -O install.sh
# bash install.sh

系统设置

上述安装完成后,我的 droplet 上就有了带有网络的 Arch Linux 。绝大部分的额外设置都可以在 Arch Wiki 找到。我并没有想把这篇日志写成完整的教程,所以细节部分最好参考 Arch Wiki。记录在这篇日志里的指令只是做个人记录之用。

系统时钟

同步系统时钟并设置时区。

# timedatectl set-ntp true
# timedatectl settimezone <Region>/<City>

安装基础软件包

安装/升级 basebase-devel 软件包。

# pacman -S base base-devel

Fstab

生成 fstab

# genfstab -U / >> /etc/fstab

设置系统语言环境

/etc/locale.conf 里去掉 en_US.UTF-8 UTF-8 的注释,然后运行:

locale-gen

/etc/locale.conf 里设置 LANG=en_US.UTF-8

主机名

编辑 /etc/hosts 以加入水滴的主机名:

127.0.1.1    <hostname>.localdomain <hostname>

引导加载程序和 Initramfs

针对英特尔处理器的优化:

# pacman -S intel-ucode
# grub-mkconfig -o /boot/grub/grub.cfg

在 initramfs 里加入 crc32 模组,不然可能导致水滴无法启动。编辑 /etc/mkinitcpio.conf

MODULES= "crc32 libcrc32c crc32c_generic crc32c-intel crc32-pclmul"

重新生成 initramfs 镜像。

# mkinitcpio -p linux

Root 用户密码

你懂的。

# passwd

用户设置

这是一些让 Arch Linux 使用起来更加友好的设置。

创建用户帐号

显然只使用 root 帐号不是什么好主意:

# useradd -m -G wheel -s /bin/bash <username>
# passwd <username>

把用户加入 Sudoer

编辑 /etc/sudoers 并加入:

<username> ALL=(ALL) ALL

以用户身份登录

剩下的设置都会以用户身份执行:

# su <username>

软件包管理器

我一开始使用 packer 来同时使用 pacman 和安装 AUR 软件包。但是在我了解到其软件安装过程有 诸多安全隐患 后,我开始改用 trizenpacaur 是另一个较为稳妥的选择,而且在 reddit 上有一个机器人会在所有提到 yaourt 的帖子下面安利 pacaur ): trizen 会提示用户在安装前检查 PKGBUILD*.install 以及其他代码,而且 trizen 是用 Perl 而不是 Bash 写的。想要安装 trizen ,先根据 AUR 页面 通过 pacman 安装 trizen 所依赖的软件包,然后克隆其 git 仓库 到本地。进入包含 PKGBUILD 的文件夹并运行:

$ makepkg

来编译软件包,

$ pacman -U trizen-*.pkg.tzr.xz

来安装 trizen.

常用软件包

在设置完软件包管理器后,就可以大肆安装各种软件了!我的一些必备软件包括 emacs (在服务器上我只安装了命令行版本, emacs-nox), tmux (可以使用同一个命令行窗口来同时运行多个指令,非常有用), zshvim (作快速编辑之用)。

安全相关

Arch Linux 安装完成之后,我在把网站搬进去之前进行了一些安全方面的设置。

使用 ssh 安全登录

在本地机器上生成 ssh 密匙:

$ ssh-keygen -t rsa

把 ssh 密匙发送到服务器:

$ ssh-copy-id <username>@<server>

接下来再服务器上编辑 /etc/ssh/sshd_config

PermitRootLogin no
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no
AllowUsers <username>

这些改动会禁止 root 账户登录,禁止使用密码登录,并只允许特定用户通过 ssh 登录。

除此之外,把用于 ssh 的端口(默认为 22)改掉也是一个很棒的安全措施。继续编辑同一个文件(请记牢所选择的端口):

port <non-std-port>

为了让这些改动生效,重启 ssh 进程:

$ sudo systemctl restart sshd.service

接下来保留目前的 ssh 链接不变并在一个新窗口内尝试建立新链接以确认一切正常(保留原有链接以防设置出错):

$ ssh -p <non-std-port> <username>@<server>

防火墙设置

ufw 作为防火墙非常方便易用。使用 trizen 来安装 ufw 并开放允许连接的端口:

$ trizen -S ufw
$ sudo ufw allow <port>/<protocol>

如果想要允许 ssh 链接,开放 22/tcpssh (如果你改掉了默认端口,开放 <non-std-port>/tcp )。其他一些常用的端口有:

Port Usage
80/tcp http
443/tcp https
143 imap 通信
993 imap ssl 通信
25 收取外界来的邮件
587 smtp 通信 (不论有无 ssl

回顾已开放的端口并开启防火墙:

$ sudo ufw show added
$ sudo ufw enable

设置自动启动:

$ sudo systemctl enable ufw.service

同步服务器时间

使用 ntp 同步服务器时间:

$ trizen -S ntp
$ sudo systemctl enable ntpd.service

检查时间服务器的状态:

$ ntpq -p

设置 PTR 记录

DigitalOcean 会自动设置 PTR 记录,我唯一需要做的就是将水滴的名字改为绝对领域名称( FQDN ,帅气但是八成是机翻的译名取自 Wikipedia ),也就是 shimmy1996.com 。完成这一设置后,我可以通过以下命令来查看设置是否成功。

$ dig -x <ip_address>

正式启动服务器

在完成以上设置后,就可以为服务器托管网站做准备了。

建立网页文件夹

创建一个网页文件夹来放网页文件,一个比较普遍的选择是:

$ mkdir ~/public_html

确认该文件夹(以及用户的 home 文件夹)有合适的权限设置。权限设置可以用 chmod 修改(一般设成 755 就好)。可以在网页文件夹中放一个简单的 index.html 来方便测试。

安装 nginx

trizen 安装 nginx ,并编辑 /etc/nginx/nginx.conf 来设立 http 服务器(带有 listen 80 default_server 设置的部分):

server_name www.<domainname> <domainname>
root /path/to/public_html

server_name 这一行可以多列一些网址。如果你想一并架设邮箱服务器的话,一并将邮箱服务器地址列入网址就免去了生成额外的 ssl 证书的麻烦。在这些设置完成后,(重新)开始 nginx 并将其设为开机启动:

$ sudo systemctl restart nginx.service
$ sudo systemctl enable nginx.service

DNS 设置

下一步是为服务器完成 DNS 记录的设置。一开始必须设置的记录有三种: NSA ,和 CNAME 。我记下了一些比较常用的记录:

记录种类 主机名 数值 用法
NS @ DNS 服务器的地址 确认用于解析域名的服务器
A @ 水滴的 IPv4 地址 将主机名重定向到 IP 地址
CNAME www (可以是任何东西) @ www.<hostname> 设为主机名的
MX @ 邮箱服务器地址 指定邮箱服务器
CAA @ 标明 SSL 证书的授权机构 阻止其他授权机构为本站发行 SSL 证书

我的域名托管在 Google Domains ,但我的网站用的是 DigitalOcean 的 DNS ,所以我需要在 DigitalOcean 上完成设置并在 Google Domains 里加入 NS 记录。

在完成这些设置后,我就可以通过我的域名访问所架设的网站了,不过 DNS 记录通常需要数小时才会完全生效。

SSL 证书

Let's Encrypt 是个非常棒的项目。使用 certbot 这个工具就可以很方便的生成 SSL 证书。这里向 EFF 和 Linux 基金会的人们致以谢意。生成证书只需要运行 EFF 网站 上所记载的命令即可:

$ sudo pacman -S certbot-nginx
$ sudo certbot --nginx

为了给证书增加一点可信度,我还在 DNS 记录中加了一条 CAA 记录,标明 letsencrypt.org 是唯一允许给本站 SSL 证书授权的机构。目前 Let's Encrypt 还不支持通配符证书,不过会在 2018 年 1 月 添加这一支持。由于没有通配符证书,我只好在 nginx.config 里加上所有二级域名(这样生成证书的才能够为这些域名提供验证)。

下一步?

在鼓捣了几个小时后(其实大部分时间是在等 DNS 记录扩散),我的新站就上线了。既然选择了运行 VPS,我打算好好发挥它的潜能并架设了自己的电子邮箱。我正在考虑把架设邮箱过程中从各个网站七拼八凑其来的命令行笔记也整理成一篇日志。目前我仍在试图寻找使用 org-modehugo 里写多语言日志的最优工作流程。当我确信已经找到一套可以接受的解决方案的时候我会一并写成日志。

是 Spam 还是 Ham

遵循之前的计划,我打算将设置邮箱的过程记录下来。设置邮箱大部分地方都有教程,不过我需要添加不少额外的安全设置来避免邮件被扔进垃圾箱。

搭建邮件服务器

我八成是写不出比 这篇教程 更好的步骤说明的,所以我就在这里把我额外需要的一些设置记录下来。

设置 DNS 记录

在开始搭建邮箱之前,我需要设置 DNS 记录。我为我的邮件服务器地址设置了一条 A 记录,并用一条 MX 记录来标明所使用的邮件服务器。

创建 Maildir

在设置好 postfix 之后,我需要手动创建 Maildir 文件夹并赋予其合适的权限:

$ mkdir -p /home/<username>/Maildir/{cur,new,tmp}
$ chown <username> /home/<username>/Maildir/{,cur,new,tmp}
$ chmod 0755 /home/<username>/Maildir/{,cur,new,tmp}

SSL 证书

我没有使用 dovecot 自带的证书生成器,而是直接沿用了我网站的 SSL 证书。我将邮件服务器地址加入 /etc/nginx/nginx.conf 中的 server_name 下,并用 certbot 生成了合适的证书。在这之后,我修改了 dovecot 的设置文件 /etc/dovecot/conf.d/10-ssl.conf

use_ssl = yes
ssl_cert = </path/to/fullchain.pem
ssl_key = </path/to/privkey.pem

我也在 postfix 上用了这个证书。需要注意的是 dovecotpostfix 都需要用 root 账户运行才会有权限读取证书。

邮件客户端

我使用 Thunderbird 作为邮件客户端。收取邮件时,我使用 SSL/TLS,而发送邮件则使用 STARTTLS。

安全措施

设置好邮件服务器后,我试着发了几封邮件,不过发现都被 gmail 扔进了垃圾箱。似乎 gmail 最近添加了显示邮件安全检查状态的功能(在 gmail 中点击“查看原件”即可看到)。这些检查包括 SPF , DKIM ,和 DMARC 。由于我的邮件服务器没有通过最基本的 SPF 检查,所以我的头像显示为一个标着问号的八边形。为了避免邮件被扔进垃圾箱,我进行了一系列安全设置以保证我的邮件服务器能通过网上邮箱安全测试平台(我使用的是 intodnsmxtoolbox )的考验。

发件人策略框架( SPF )

SPF TXT 记录标明了某一域名所承认的邮件服务器地址。在我的情况下,我只会用到 MX TXT 记录中所提到的邮件服务器,所以我的 SPF TXT 记录就是:

v=spf1 mx -all

域名密匙邮件认证( DKIM )

我使用 opendkim 来为邮件署名,以验证邮件的确来自于我的服务器。在安装了 opendkim 软件包后,我遵循 Arch Wiki 里的步骤完成了设置。首先,复制设置文件模板 /etc/opendkim/opendkim.conf.sample/etc/opendkim/opendkim.conf 并编辑(端口可以随意选择):

Domain                  <domainname>
KeyFile                 /path/to/keys.private
Selector                <myselector>
Socket                  inet:<dkimsocket>@localhost
UserID                  opendkim
Canonicalization        relaxed/simple

接下来,在设置文件所指定的密匙文件路径下(默认为 /var/db/dkim/ ),生成密匙:

$ opendkim-genkey -r -s <myselector> -d <domainname> --bits=2048

执行指令后生成的不仅仅是 .private 密匙文件,还有装有完成 DKIM 设置所需 TXT 记录的 .txt 文件。这一记录的功能在于公布邮箱服务器的公共密匙。需要注意的是这条 TXT 记录有可能会被分成数条字符串,以满足每条字符串最长 255 字符的要求。为了查看 TXT 记录是否有设置成功,我运行了(需要安装 dnsutils 软件包):

$ host -t TXT <myselector>._domainkey.<domainname>

接下,重启 opendkim 服务并确保 postfix 在发出邮件之前执行加密。编辑 /etc/postfix/main.cf

non_smtpd_milters=inet:127.0.0.1:<dkimsocket>
smtpd_milters=inet:127.0.0.1:<dkimsocket>

在重启 postfix 后, DKIM 就开始运作了。

基于域名的邮件认证,报告和一致性检查( DMARC )

毫无意外,有一个软件包 opendmarc 可以用于部署 DMARC ,并且这一软件包也有对应的 Arch Wiki 页面。需要注意的是, DMARC 必须在 SPF 和 DKIM 都设置完成后才能生效。在安装软件包后,编辑 /etc/opendmarc/opendmarc.conf:

Socket inet:<dmarcsocket>@localhost

在启动 opendmarc 服务后,在 postfix 中启用 DMARC 邮件滤网(与 DKIM 滤网用逗号隔开):

non_smtpd_milters=inet:127.0.0.1:<dkimsocket>, inet:127.0.0.1:<dmarcsocket>
smtpd_milters=inet:127.0.0.1:<dkimsocket>, inet:127.0.0.1:<dmarcsocket>

最后,按照 Arch Wiki 所指示的步骤在 DNS 设置中加上一条 DMARC TXT 记录并重启 postfix 就大功告成了。

通过所有测试

check-auth@verifier.port25.com 发送测试邮件后,我收到了一份邮件服务器的测试报告。报告显示所有安全设置都在正常运作。除此之外, gmail 不再将我的邮件扔进垃圾箱了,而“查看原件”页面下也显示我的邮件通过了 SPF , DKIM , 和 DMARC 检查。 由于启用了 DMARC , gmail 还会每天向我的邮箱发送一份安全报告。折腾到这个地步后,我对我的新邮箱比较满意了,并准备废除我之前所使用的邮箱。我还在寻找可以自己架设的日历服务,希望不久的将来我可以完全摆脱在通讯方面对 Google 服务的依赖。

不再使用 Disqus

不久之前, Disqus 发生了一起 用户信息泄露事件 。这导致我开始重新考虑评论系统的选择:既然已经架设了自己的博客和电子邮件,也不差一个评论系统。

好家伙,坏家伙,丑家伙

我对 Disqus 将不同网站的评论统一成一个社交网络的主意抱有比较复杂的看法。我个人使用社交媒体时更多的是作为 “媒体” 而不是社交的工具:这些服务显然不适合发表长篇大论(所以大多社交网站帖子都充斥着链接和长图),而就算是作为发表随感的工具,我也觉得在这些网站上编辑文字不怎么理想(尤其是在手机上)。常年潜水的习惯使得我时常反过来审视自己的社交网站人格:我站在第三者的角度会怎么看这堆转推和赞所构成的怪物。

博客评论则比较不同。我在评论博客时更为放松:这种感觉更接近与博主一对一对话,而不是向整个网络广播自己的座标。社交媒体化的博客评论就不再给我这种感觉。使用 Disqus 的一大潜在好处大概在于能够吸引更多访客,不过我并不想让自己的博客评论区沦为又一个社交网络时间线:高价值的评论并不会因为我的网站不使用 Disqus 而消失(虽然相反的事情时有发生,并不是所有人都会愿意为了评论而注册新的社交网络帐号的)。

接下来就要说到 Disqus 比较丑陋的一面了。且不提在网站上嵌入我自己没有办法控制的 JavaScript 是一大安全隐患,我直到昨天才意识到 Disqus 的访客数据收集是默认启用的。由于我不打算通过博客获得收入,以牺牲访客隐私为代价换取 Disqus 的服务并不值得。除此之外,我还发现 Disqus 评论中的大部分“赞同”都来自于挂着交友网站的僵尸帐号。至于这些僵尸帐号的来源是被盗用的帐号还是水军机器人我就无从了解了。总之我可不想让这些僵尸帐号在我的博客上晃悠。

寻找替代品

我决定自己架设评论系统并尽量避开任何第三方服务。在细心搜寻后,issostaticman 成为了最终的候选者。 Isso 是一个使用 Python 写成的轻量评论服务器;而 staticman 则是一套将评论转换成文本文件并自动加入博客 Github 仓库的 API 。安装 isso 意味着我必须使用我之前一直尽力避免的数据库;使用 staticman 则可以让我的网站保持静态,但是必须依靠 Github 的 API (如果我不自己架设 staticman 的话,还需要 staticman.net 的官方 API )。虽然保持一个完全静态的站点很吸引人,不过我还是决定先尝试 isso ,看看脱离第三方服务是否值得我花时间鼓捣数据库。

我在寻找评论系统的过程中还发现了一个有趣的替代品:Echochamber.js

设置 isso

Isso 的官网有很详细的 说明文档 。我从 AUR 安装了 isso 并使用 systemctl 启用了它。设置过程出奇的顺利(包括数据库的部分),因为我的 isso 和博客共用一台服务器,我使用了与默认不同的 设置 。我所遇到的唯一问题在于 smtp 。在检查 postfix 的运行状态后,我很快发现问题在于 smtpd_helo_restrictions :在停用 reject_unknown_helo_hostname 后, isso 就能使用 smtp 发送通知邮件了。除此之外,我稍微花了点时间修改 isso 的 CSS 模板。新的评论区看起来不仅更契合博客主题,速度也比 Disqus 快多了。

祝评论愉快!

回顾 2017

2017 年再过几个小时就结束了,所以我觉得很有必要回顾一下过去这一年。但愿我在写完这篇日志的时候还能觉得 2017 过得没有后悔。

我都做了啥

我从六月份开始全职工作了。社畜生活其实比我想象的要轻松不少,我的日常生活比上学时反而有规律得多:至少 4 小时的睡眠时间不再显得那么奢侈,而且我总算能分配一部分时间来发展兴趣了。

在 2017 年里:

  • 使用 Emacs 三个月。
  • 使用自己架设的电子邮箱三个月。
  • 架设了新的站点并且更新了比 2015-2016 两年更多的内容。
  • 发现自己其实有点煮饭的隐藏才能
  • 我在后六个月中保持几乎每天跑步。我在 2 小时之内完成了我的第一个半程马拉松比赛。
  • 由于跑步,我的体重下降了 10 千克左右。

我觉得 2017 是很充实很开心的一年。

跟住去边度

最近常常听黄子华的栋笃笑,而 跟住去边度 给我留下的印象最深。我也打算问问自己这个问题。 2018 我会:

  • 以马拉松为目标继续训练,有机会的话完成一次正式比赛。
  • 写 20 篇以上的日志。
  • 获得第一个 PGP 密匙签名。
  • 安装 Gentoo 。
  • 少看,多阅读。

如果我有从过去那些我制定后没能执行的计划里得到任何经验的话,那就是定计划时最好稍微低估自己的能力,所以我的新年目标就先列到这里。在踏入 2018 后我会视情况添加新的内容。

新年快乐!希望 2018 也会是充满惊喜的一年!

自己画头像

我使用星球大战中旧共和国的标志作为头像已经有不短的时间了。不过最近,当我试图统一我所有的“社交网络人格”时,我意识到独特的头像往往是最为直接的在网上认人的方式(这大概就是为何 Gravatar 这类服务 如此受欢迎 会存在)。这么一想,拿 Wookiepedia 上的图片直接作为头像不是什么明智的行为。于是我决定:自己画头像!

由于我并不觉得自己有多少艺术细胞,我最初尝试的方法是在 GeoGebra 里用尺规作图复制原来的头像。这种作图方式很是让人上瘾,但遗憾的是:在荒废几个小时后,我得到的只是一堆纠缠不清的曲线。

GeoGebra 中的诸多失败作之一。

事已至此,我开始意识到我是画不出像旧共和国标志的那样复杂的头像的,所以我采取了另外一种策略:从这些失败作中抽取一个更加简练的图样。

我逐渐简化头像的过程。

我把原图里六芒星升起的部分提取出来,并舍去了碍事的翅膀形部分使得星形能够居中。经过少许修改后更接近流星的图样看起来(至少对我来说)非常赞。在短短几分钟后,我就完成了我的头像设计。我保留了旧头像的暗红的配色(实际用的是 #700000 而不是原图的 710100 ,因为我有个位数字强迫症)并加上了灰色的背景( #707070 ,白色背景有点太刺眼了)。

我推特资料上的完成版头像。

我还尝试了其他不同的配色,比如反色版本,以后可能会拿来作为我其他项目的图标。我所有还活跃的社交网站都已经换上了新头像,包括网站图标。这下大家应该可以更容易地在不同网站上认出我了 XD。

1 月 28 日:跑步 13 英里耗时 2:02。天气非常不错,不过太阳下山后有点冷。在 58°F/18°C 的阳光下跑步比我想象得要凉爽。之前我跑步过程中是不喝水的。这次我试着在第 9 英里处补充了一些运动饮料:显然当人精疲力竭的时候,经受一点甜味的刺激就会变成超级赛亚人。

1 月 31 号:跑步 3 英里耗时 00:30。天气一如既往地好,湿度不高。月亮特别大。跑到 3 英里后我感到左脚足弓疼痛所以停了下来。可能还得多休息几天才能恢复。

2 月 4 日:跑步 3 英里耗时 00:36。今天比平时更早开始跑步(下午 2:30)。我怀疑左脚有轻微的足底筋膜炎。这大概和我一直逆时针跑圈有关系?不过疼痛感比几天之前已经减轻不少了。下周天气会转冷,所以借这个机会我会休息一周,等疼痛缓解。