@Lenciel

增加ssl证书和对http2的支持

Google一直在致力于提醒用户更加安全的上网,并从Chrome 56版本开始,把使用HTTP的网站直接标记为“不安全的”。随后Firefox等浏览器也宣布加入了类似的功能,来逼迫网站开发者逐步废弃使用HTTP。

在过去,要支持SSL其实还是挺麻烦的,因为首先你需要一个证书。这东西申请起来麻烦,但只要给钱就特别好办:为这个,Google年初还以乱发了3万个证书为由宣布不再信任Symantec签发的证书

这也跟Google等一干巨头背后撑腰的免费证书发放机构Let’s Encrypt做大了有些关系。从它的官网上的数据可以看到,一年下来,证书发放量相当感人:

Vhost threshold

但作为一名上年纪的人,本座已经不会再时间紧跟业界潮流:所以Let’s Encrypt出来了很久,大概试过之后就一直在等待工具链成熟。这次正好搬迁到Jekyll,眼看着围绕着certbot构建的生态也非常完善,就做了切换。

把切换到HTTP2也一并做了。

HTTP2的介绍文章已经很多了,毕竟已经占据了超过8%的整体份额,感兴趣的同学可以去看看Ilya Grigorik的相关文章或者讲座

这里主要讲怎么开启。

因为国内访问github pages随时被墙的原因,这次迁移到纯Jekyll是在CentOS7上用Nginx来做的host。理论上来说,Nginx 1.9.5及其以上版本的开启非常简单:

1
2
3
4
5
6
7
8
9
10
server {
   listen 443 ssl http2;

   server_name lenciel.com www.lenciel.com;

   ssl_certificate /etc/letsencrypt/live/lenciel.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/lenciel.com/privkey.pem;

   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
   ssl_prefer_server_ciphers on;

注意到第二行里面的http2了吗?加上它就可以了。用浏览器看看请求是不是都是走HTTP2了:

Vhost threshold

咦?为什么没有起作用?查了一下,原来除开Nginx版本的要求,对OpenSSL和ALPN的版本也有要求。看了一下CentOS7通过yum安装的nginx的参数:

1
2
3
4
5
$ nginx -V
nginx version: nginx/1.10.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled

OpenSSL的版本果然是太低了,只好从源码来编译一个更新的版本:

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
yum -y groupinstall 'Development Tools'
yum -y install wget openssl-devel libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel rpmdevtools

OPENSSL="openssl-1.1.0-pre5"
NGINX_VERSION="1.11.13-1"
NJS_VERSION="1.11.13.0.1.10-1"

rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-$NGINX_VERSION.el7.ngx.src.rpm
rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-module-geoip-$NGINX_VERSION.el7.ngx.src.rpm
rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-module-image-filter-$NGINX_VERSION.el7.ngx.src.rpm
rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-module-njs-$NJS_VERSION.el7.ngx.src.rpm
rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-module-perl-$NGINX_VERSION.el7.ngx.src.rpm
rpm -ivh http://nginx.org/packages/mainline/centos/7/SRPMS/nginx-module-xslt-$NGINX_VERSION.el7.ngx.src.rpm

sed -i "/Source12: .*/a Source100: https://www.openssl.org/source/$OPENSSL.tar.gz" /root/rpmbuild/SPECS/nginx.spec
sed -i "s|--with-http_ssl_module|--with-http_ssl_module --with-openssl=$OPENSSL|g" /root/rpmbuild/SPECS/nginx.spec
sed -i '/%setup -q/a tar zxf %{SOURCE100}' /root/rpmbuild/SPECS/nginx.spec
sed -i '/.*Requires: openssl.*/d' /root/rpmbuild/SPECS/nginx.spec
sed -i 's|%define WITH_LD_OPT .*|%define WITH_LD_OPT ""|g' /root/rpmbuild/SPECS/nginx.spec
sed -i 's| -fPIC||g' /root/rpmbuild/SPECS/nginx.spec
spectool -g -R /root/rpmbuild/SPECS/nginx.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx-module-geoip.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx-module-image-filter.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx-module-njs.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx-module-perl.spec
rpmbuild -ba /root/rpmbuild/SPECS/nginx-module-xslt.spec

然后再次检查:

1
2
3
4
5
$ nginx -V
nginx version: nginx/1.11.13
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
built with OpenSSL 1.0.2k  26 Jan 2017
TLS SNI support enabled

完成后,重启nginx,再次检查访问的情况:

Vhost threshold

然后从Chrome的chrome://net-internals/#http2入口进去,可以看到lenciel.com已经在列了:

Vhost threshold

从Octopress转到纯Jekyll

离开Wordpress改用Octopress写blog已经好些年了,本座甚至还写了一个自己的Octopress模板。之所以要迁移主要是Octopress作为以Jekyll为基础的一套脚手架,自2011年发布以后进展非常缓慢,和Jekyll的快速更新很难对齐。

在近期跳票多次的Octopress3终于发布了之后,看了一下没有什么值得迁移的新功能。所以不管是从功能、性能还是开发活跃度上,继续跟Octopress都不太明智,就决定转用纯Jekyll。

迁移过程还算比较平滑,主要是:

  • Blog项目的迁移
  • 模板项目的迁移

Blog项目的迁移

本座主要的开发栈不是ruby,所以那套东西都放docker。配置好gem的镜像,安装最新的jekyll之后,创建一个干净的静态网站:

1
2
$ gem install jekyll
$ jekyll new my-site && cd my-site

然后把blog的源文件以及一些静态文件放到对应的目录,然后按照新老项目的_config.yml文件内容,挨个的排查之前的插件和配置情况。

比如之前Octopress因为有自己的代码高亮和引用插件,你的日志里面可能有codeblock这样的不是默认支持的tag,在jekyll build过程中导致构建失败。

要解决这种问题有两个思路:

  1. 把Octopress的插件移植一遍
  2. 把日志改成使用默认支持的语法

实际过程里面本座的做法大概是一半一半。比如像插入gist,blockquote这些现在默认也支持得挺好的功能插件,就去掉了它们,然后通过正则表达式对_posts目录下的文章进行全局替换。

而有些个人觉得Octopress确实解决得不错的功能,就按照新版Jekyll插件的语法进行了迁移,这部分包括对日期的处理,图片的插入等等。

这些大体修改完毕,然后安装相应的依赖(可以对比新旧的Gemfile,只需要安装自己的插件用到的依赖),这部分改动就大概完成了。

模板项目的迁移

因为Jekyll自己的模板是基于gem-based的,也就是说你首先得新建一个gem-based的项目:

  1. 注册rubygems的账号
  2. 新建项目,并按照gem的方式组织代码并发布模板
  3. 在Blog项目的_config.yml里面引用这个模板

最终的工作就是我又多了一个gem-based的Jekyll模板项目

这部分要特别注意的就是Jekyll的模板项目默认能够发布的目录(也就是包含在gemfile里面,能够被你的Blog项目在安装路径找到的目录)非常有限:

Jekyll will look first to your site’s content before looking to the theme’s defaults for any requested file in the following folders:

/assets /layouts /includes /_sass

如果你有很多其他目录希望一起发布,可以修改gemspec里面的相关选项:

1
spec.files = `git ls-files -z`.split("\x0").select { |f| f.match(%r{^(assets|_layouts|_includes|_sass|LICENSE|README|index)}i) }

还有一点就是这种发布和安装模板的方式,会破坏很多对静态资源的处理流程(如果有的话)。

比如我自己会对图片自动压缩,对css/js文件进行合并和uglify等等。这些工作的目标文件因为很多都在gem安装的模板里面,会变得比较tricky。最终本座只好对jekyll build之后的_site目录下的部署目标文件进行处理。

其他

虽然放弃了Octopress,但并不是说Octopress是个失败的项目。一方面,它的完成度很高,如果你不是有本座一些龟毛的要求(要自己搞模板,要对静态文件做优化,要支持各种国内才需要支持的定制),已经够用。作者一个人要跟上Jekyll一个社区的开发速度,本来就很难,是属于可以理解的不足。并且,通过看它的插件源代码,对我自己的模板实现也起到了很大帮助。

所以,感谢Octopress引进门,用了它,折腾Jekyll就容易多了。