使用 AWS 云服务搭建 WordPress 博客 (中篇)- 全站SSL配置完成

点击这里阅读本文上篇

一、前言

全站SSL来啦!

这是用AWS搭建Wordpress的第二篇文章,重点讲述使用AWS的DNS服务(Route53)+ 证书服务(ACM)实现SSL防护主站和S3图床。配置过程也踩了不少坑,现在开始。

二、主要原理和架构

主要架构图如下所示。

在这张图中,红色表示未加密的HTTP流量,蓝色表示带有证书加密的HTTPS的流量。上一篇中我们提到过,使用SSL可以分别在ELB和Cloudfront上配置证书。为了简化配置,且另外我们的源站没有额外保护要求,因此,源站的ELB负载均衡的listener监听器只配置80,不配置443端口。

以上架构图可以从这里下载。解压缩后,需要用Draw.io编辑器修改。

当客户用浏览器访问网站时候,网页代码区分了主站域名和图床域名,分别引导进入两个不同的Cloudfront Distribution,每个分发器又有不同的源站设置和行为规则设置。域名设置上,用户的访问是CNAME域名,在DNS这一层,会翻译为Cloudfront自用的对外暴露的域名,即xxxxxxx.cloudfront.net,然后流量被引导到CDN。CDN对外暴露是加密的,因此这部分流量是加密的。

当CDN收到xxxxxxx.cloudfront.net的HTTPS请求后,会回到源站查找。这个时候,回源到主站用HTTP方式回源,访问地址是负载均衡器对外暴露的URL;回源到图床是HTTPS方式回源,访问地址是S3对外暴露的Public URL。

最后一步,在ELB负载均衡器上,再次查找后端的target group,以http(IP地址)方式回源访问。S3那边则是S3内部的响应和输出了。

由此,整个SSL过程跑通。

三、使用Route53提供DNS服务

本文上篇讲述了用Route53提供DNS服务,这里在补充下描述下配置方式,因为后续使用ACM签证书,要经过DNS验证。我尝试了Godaddy的DNS服务,生效时间太慢了,等了24小时都屡次不能验证成功。因此干脆该用Route53做DNS服务。

Route53在AWS大陆的北京和宁夏两个Region并未提供,因此本文使用海外DNS实现,主要跑在新加坡。

首先在Route53里边新建立一个host zone,然后进入这个zone,这个zone会默认就有几条存在的NS记录,如下截图。

上图的红色部分的NS记录,就是我们要去域名解析服务上(我的是Godaddy那里修改上去)。

登录Godaddy的DNS服务界面,找到NS记录的设置界面,如下截图,已经修改完毕。

修改NS记录一般全球生效要几个小时到24小时不能。过几个小时后,可以在命令行下,用dig命令查询ns记录确认下是否生效。

上图的红色框内表示NS生效了。那么接下来这个域名的解析权就由AWS Route53接过去了。

四、证书服务ACM的配置

注意,全球的Cloudfront服务的配置,要求必须把证书放在美东的北弗吉尼亚Region,即us-east-1,否则Cloudfront无法找到证书。

进入证书服务,点击申请新证书,然后选择用域名解析记录验证。这个时候,系统会生成一个随机字符串,需要把这个字符串作为CNAME填写到本域名的域名解析记录中,来验证申请证书的人对本域名是由完全的管辖权的。如下截图,遮盖了一部分的就是系统生成的验证字符串。

那么接下来,去上文配置好的AWS Route53 DNS解析服务上,把这个CNAME填写进去。如下图所示填写好。

填写完成后,回到ACM证书管理界面,点击验证,就等待就可以了。一般而言,几分钟内,ACM会提示验证失败,过一会再试。这个时候就休息下,不用管,等数分钟后就好了。

等一会再回来系统看,证书已经签发完毕。如下截图,Issued状态表示签发好了。

AWS的证书服务逻辑是,本证书签发好,是对所有浏览器CA有效的,在手机和PC端,浏览任何SSL都显示绿标安全的。但是,本证书不能被导出和下载,不能脱离AWS云环境自行配置。加载证书的地方是在CDN服务即Cloudfront上,或者在ELB负载均衡器上加载。对于一般的web server,例如起了一个Apache,是不用监听443的,不需要配置证书文件,因为也拿不到证书文件的。

五、为主站启用Cloudfront(CDN)的证书服务

在不启用全站SSL的情况下,配置CDN服务时候,网站自有域名的CNAME那里是必须留空不能填写的。同时,即便网站没有SSL,Cloudfront CDN服务也会自生成一个default SSL证书,绑定在https://xxxxxxxxxxx.cloudfront.net这个自有域名上。因此,在启用SSL时候,可以把原来的Distribution发布点直接编辑一下,把信息不全就可以了。

如下截图,CNAME部分填写本网站的主域名 blog.bitipcman.com (这其实是一个CNAME,解析到CDN对外暴露的xxxxxxxxxxx.cloudfront.net域名上。证书选项选自定义,然后下拉框就可以看到ACM上配置好的证书了。

然后去设置源站。

进入Cloudfront本发布的源站设置,第一行就是源站对外暴露的ELB负载均衡入口地址(HTTP80访问),然后把选项选择为HTTP ONLY。如下截图。

然后就是确认下发布和跳转规则是否正确。在如下截图中,选择当用户访问http时候,自动重定向到https。

至此主站部分配置完成。

先不要着急用浏览器去访问,因为一会wordpress还需要安装全站SSL插件,否则代码中输出的JS和CSS都是绑定在HTTP服务上的,浏览器会拒绝混合加载HTTP和HTTPS。

六、为图床启用Cloudfront(CDN)的证书服务

接下来搞图床。

WordPress默认上传附件都是写入EC2本地磁盘的,例如/var/www/html/wp-upload目录下。为了更好的扩展,在本文第一篇也讲述了应该用S3的原因。这里需要安装Wordpress第三方的Plugin,通过插件支持上传到S3。

WordPress有多个支持S3上传的插件,我选择的是“WP Offload Media Lite”这款。通过Wordpress左侧的plugins按钮,点击“Add New”,在点击Search,就可以找到。

然后在配置界面中,启用S3。同时这个插件也支持别的一些国外的主流的对象存储。S3的授权方式有两种,在上一篇中提到过,如果网站是跑在AWS以外的云上,那么来连接S3就需要API Access Key了。如果网站是跑在AWS EC2上,那么最佳安全时间是不使用API Access Key,而是创建一个IAM角色,赋予这个角色能读写特定S3 Bucket的权限,再把这个权限绑定到Wordpress所在的虚拟机上。由此,就可以拉出来S3 Bukcet清单了,并可以选定一个要使用的。如下截图。

在下一步是配置Wordpress使用的S3图库的别名。这个别名,可以分别是:

  • S3 Bucket对外Public域名。这种配置是当直接拿S3对外提供服务时候,就填写S3 Bucket的URL。这意味着如果别人盗用图片,是直接复制走S3域名的。
  • Cloudfront CDN的xxxxxx.cloudfront.net域名。这种配置是当CDN没有启用SSL时候,也就是没有自己的证书,CDN是不允许绑定第三方域名的,因此没办法用自己的域名,只好绑定CDN。这意味着如果别人盗用图片,是直接复制走CDN域名的。
  • 客户自有图床域名,例如本文的blogimg.bitipcman.com。这种配置是必须全站SSL启用后,CDN才允许绑定第三方域名,然后图床才可以用第三方域名去引用素材。这意味着别人如果盗用图片,是直接复制走图床专用域名的。

以后专门开一帖,写通过WAF检查refer头,做防盗链。下边的截图是为wordpress插件设置好第三方图床域名。

好了现在该去配置Cloudfront了。配置过程和主站一样,把CNAME图床对应的域名填写上,把证书选择为自定义证书。如下截图。

在源的设置方面,针对S3的发布点,选项少很多,因为S3后台是固定的HTTPS对外发布,可以调整的比较少。如下截图。

最后,发布行为策略方面,S3就没有必要强制HTTP跳转HTTPS了,这个选项是否切换都可以。如下截图。

这样,对S3的CDN和证书设置就好了。

七、打开Wordpress的全站SSL

接下来打开Wordpress的全站SSL。使用Wordpress的人第一时间想到的,就是把Wordpress系统全局设置里边,如下的两个参数从不带S的http改成https。如下截图。

然而事实上,这样是不work的!因为wordpress本身的关系,它只把主站和图床改成了在https上,所有静态的CSS还是走的http的链接,没了CSS,页面就飞掉了。

这个错误在浏览器内看不出问题点,除非走浏览器的查看网页源代码。如果在命令行下curl访问网站,获得index.html后,然后编辑这个文件,就发现里边大量链接还是http的资源链接。

解决方法,装Wordpress插件。支持启用SSL的插件也有很多个,wordpress的社区和第三方插件真是完善。我装了这个名为Really Simple SSL的。如下截图。

安装好之后,真的是一键点击启动。如下截图。

完工!

八、修改HTTP/2协议设置解决Safari不能访问的问题

这样在用浏览器访问过去,一切正常,默认全局SSL了。主站和图床都是自己的域名,也都带SSL了。

1、问题

不过在测试中,发现Google Chrome和Firefox正常,MacOS上的Safari报错。错误信息是:

"Safari can't open the page. The error is "The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)"

有Google访问就是方便,Google基本就是信息的保障。查询了一下,主要反馈是Apache2默认启用了HTTP/2协议,但是发送了一个升级到HTTP2 with TLS的Upgrade Header,导致Safari报错。解决方法是在Apache上禁用发送这个Upgrade header功能。

参考详细描述(英文)

解决方案,二选一。推荐改Apache配置,而不是粗暴的禁用HTTP/2协议。

2、从Apache层面解决

登录到服务器上,看了下apache的配置文件,果然是默认启用了h2。如下截图。

为了禁用它,改成如下。

执行如下命令重启apache服务:(本EC2为Amazon Linux 2,可使用systemctl)

systemctl restart httpd

DONE!问题解决。

因为是二选一,推荐这样改就可以了,不用动CDN。

3、从CDN和ELB层面解决

如果不想改Apache的conf,那么就要在CDN和ELB层面,禁用HTTP/2协议。

进入Cloudfront配置界面,对主站的发布点编辑配置,找到如下选项。

通过以上截图看到,确实默认是打开了HTTP2支持的。那么将其关掉就可以了,因为普通的Web网站的浏览器访问对于HTTP/2意义不是特别大,所以直接关没啥大问题。

另外,在ELB层面,也把HTTP2关掉。进入ELB,把页面滚动到最下方,可以看到HTTP2的设置,关闭之。

测试通过,Safari显示正常!

不过在CDN上禁用的方法不推荐,最好还是改Apache配置文件吧。

九、结束语

至此整个Blog完成全站SSL,主站和图床都是用了自有域名和自有SSL证书,CDN正常发挥效力。

下篇预告,配置Wordpress过程中的一点分享。

BTW,好了,F1夏休期结束,比利时斯帕赛道,1小时后排位赛即将到来!@2019-08-31 20:00