全站开启https总结(艰苦历程?)

Author Avatar
Reborn 7月 21, 2018
  • 在其它设备中阅读本文章

前言

在搭建完Hexo之后,我就开始思考要不要配置https呢?最后深思熟虑,还是决定上https吧!毕竟现在https已经开始普及,像Chrome、Firfox这些浏览器如果不是https协议的会提示如下图,警告“不安全”什么的。强迫症患者表示看着最不舒服了(逃~
http browser sho

于是我就开始了配置https之路……

2018-12-02更新:添加通配符证书申请方法

SSL证书

选取合适的ssl证书

https = http + ssl,所以要走https协议首先要申请ssl证书。
但是我第一次建站,对ssl证书一点都不懂啊!!哪个ssl证书更好我也不知道,去搜索一翻发现各种说法,最后还是去请教了各位前辈。
经过各位前辈的建议,我了解到了Let's Encrypt,详细介绍我就不说了,这是它在维基百科的资料,然后这是官网

除此之外还有DigicertSSL For FreeCA Cert。Digicert很贵,但是据某位前辈说似乎大企业喜欢用它的挺多,而且人家还支持签发 .onion 域名的证书(EV)。

看自己需求吧,我最后还是用了Let's Encrypt
老父亲说这是免费的,但验证过程要在服务器上做,有点复杂。我觉得复杂点没所谓,好用就行。
某群里的前辈说也可以不验证,自签名就好(其实我还没懂哈哈哈,尴尬.jpg);前辈们还说了一种DNS方式验证,不需要架设额外服务;在我问了“Let’s encrypt用standalone还是webroot模式更好”之后(其实这个我也不懂),终于决定用DNS模式,DNS 模式更方便,还可以签通配符证书。

开始获取SSL证书

参考:使用acme.sh脚本的DNS API方式申请及更新let’s encrypt证书

群里多位前辈介绍了一个在 Linux 上申请let’s encrypt证书的脚本——acme.sh

acme.sh脚本

Linux服务器下使用acme.sh脚本是申请let’s encrypt证书最便捷的方式,比官方推荐的certbot脚本工具要方便强大的多。好处是对环境要求较低,路由器上 busybox ash 都能跑,只需要使用到 curl 等少数几个外部程序

安装文档: https://github.com/Neilpang/acme.sh/wiki/说明

前提条件

  1. 拥有一个域名,例如 mydomain.com (在国内主机的用的话,还需要通过ICP备案)
  2. 确定二级域名,并且在域名服务器创建一条A记录,执行云主机的公网IP地址。www.mydomain.com指向xxx.xxx.xxx.xxx的IP地址
  3. 要等到新创建的域名解析能在公网上被解析到。
  4. 据说国内的域名提供商对letsencrypt的支持非常差,但是据说现阶段用dnspod解析的域名还没碰到问题。我用的是国外的Cloudflare解析我的域名,但是经常被墙(想骂人…),移动表示大多数都没有体验= =,联通情况好一点。

安装acme.sh脚本

执行一下命令后重新ssh登录服务器即可:

curl  https://get.acme.sh | sh

该命令会把 acme.sh 安装到你的 home 目录下: ~/.acme.sh/,一般会自动创建环境变量,后期直接命令行输入acme.sh即可。

使用DNS API申请证书

(推荐配置使用API的方式,否则每隔三个月,仍然需要重新验证DNS TXT记录)

创建DNS API的Key及Secret

参考:

  1. https://github.com/Neilpang/acme.sh/tree/master/dnsapi
  2. https://github.com/Neilpang/acme.sh/blob/master/dnsapi/README.md

acme.sh 目前支持 cloudflare, dnspod, cloudxns, godaddy 以及 ovh 等数十种解析商的自动集成.

【例子一】域名由Cloudflare提供解析的网站,使用CloudFlare域名API自动颁发证书

  1. 以 Cloudflare 为例, 你需要先登录到 Cloudflare 账号才能获得API密钥, 都是免费的。
    Cloudflare-Api-1

    Cloudflare-Api-2

  2. 然后在Linux下使用acme.sh命令申请证书:

    命令使用参考:Linux 下使用 acme.sh 配置 Let’s Encrypt 免费 SSL 证书 + 通配符证书
    先导入密钥和邮箱:

     export CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
     export CF_Email="[email protected]"
    

    我们现在发行一个证书:

      acme.sh --issue --dns dns_cf -d example.com -d www.example.com --accountemail [email protected]
    

    --accountemail是指定帐户电子邮件,仅对--install--update-account命令有效(待验证,因为我执行命令时并没有带上该参数)。

    输入命令后回车,需要等两分钟,终端会有倒计时。

    倒计时结束后,执行命令:

     acme.sh --renew -d example.com -d www.example.com
    
补充:获取 Let’s Encrypt 通配符证书

通配符证书,英文 Wildcard Certificate 国内黑话叫做野卡,经过一个月的跳票后,Let’s Encrypt 目前已经支持通配符的证书,同样 acme.sh 也是支持的,和多域名证书不同,通配符证书必须使用 DNS TXT 记录验证方式,我们以 example.com*.example.com 为例

acme.sh --issue -d example.com -d '*.example.com' --dns

记得*.example.com加单引号

如果你的 DNS 提供商支持 API,你也可以直接使用 API 而不需要手工修改 TXT 记录,详细用法请见这里

以 CloudFlare 为例:

acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'

生效后给证书续一秒

acme.sh --renew -d example.com -d '*.example.com'

然后后面你想要一个二级域名(如:abc.example.com)的时候就不用再申请了,只需要在DNS添加A记录等它生效就可以用了。当然,下面的安装证书配置站点(Nginx)步骤还是要做的😂

安装证书

# 创建安装目录
mkdir /etc/ssl/example.com/ -p

# reloadcmd参数里面填写的是在更新证书后需要重新启动的服务,例如重启nginx或者apache或者其它脚本。
acme.sh  --installcert  -d  example.com   \
        --key-file   /etc/ssl/example.com/example.com.key \
        --fullchain-file /etc/ssl/example.com/fullchain.cer \
        --reloadcmd  "systemctl reload nginx"

注意:

  1. /etc/ssl/example.com/的所有者以及用户组必须为当前用户,否则后面installcert的时候会提示没有权限,可执行如下命令,后面可更改回原来的所有者和用户组:
    #更改所有者和用户组
    sudo chown $USER:$USER -R /etc/ssl/example.com/
    #查看指定文件夹的权限、所有者和用户组
    ls -l /etc/ssl/
    ls -l /etc/ssl/example.com/
    
  2. (一个小提醒, 部分服务器系统用的是 service nginx force-reload, 不是 service nginx reload, 据测试, reload 并不会重新加载证书, 所以用的 force-reload)
  3. Nginx 的配置 ssl_certificate 使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer ,否则 SSL Labs的测试会报 Chain issues Incomplete 错误。
  4. --installcert命令可以携带很多参数, 来指定目标文件. 并且可以指定 reloadcmd, 当证书更新以后, reloadcmd 会被自动调用,让服务器生效.
  5. 值得注意的是, 这里指定的所有参数都会被自动记录下来, 并在将来证书自动更新以后, 被再次自动调用.

mkdir的-p参数解释:
-p, –parents 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录;
mkdir命令参考:https://www.linuxdaxue.com/linux-command-intro-mkdir.html

更新证书

目前证书在 60 天以后会自动更新, 你无需任何操作. 今后有可能会缩短这个时间, 不过都是自动的, 你不用关心.

更新 acme.sh

目前由于 acme 协议和 letsencrypt CA 都在频繁的更新, 因此 acme.sh 也经常更新以保持同步.

升级 acme.sh 到最新版 :

acme.sh --upgrade

如果你不想手动升级, 可以开启自动升级:

acme.sh  --upgrade  --auto-upgrade

之后, acme.sh 就会自动保持更新了
你也可以随时关闭自动更新:

acme.sh --upgrade  --auto-upgrade  0

应用实例

配置Nginx使用证书开通https站点

  1. 生成Perfect Forward Security(PFS)键值
    Perfect Forward Security(PFS)是个什么东西,我也不清楚,中文翻译成完美前向保密,反正是这几年才提倡的加强安全性的技术。如果本地还没有生成这个键值,需要先执行生成的命令。

    /etc/ssl/example.com/
    openssl dhparam -out dhparam.pem 2048
    

    生成的过程还挺花时间的,据我查到的资料,据说参数为2048的要4分钟左右,还受服务器性能影响,此时不妨做点其他东西。
    (时间一分一秒的过去…)
    好了,PFS键值生成了,我们用ls命令来确认一下目录下是否生成了dhparam.pem文件,确认完毕就可以执行下一步了。

    特别说明:
    一定要确认文件是否已经生成!!!在这一步我原本是执行openssl dhparam 2048 -out dhparam.pem,不知道为什么执行了多次都无法生成dhparam.pem文件,后来换了个目录执行了多次openssl dhparam -out dhparam.pem 2048才成功,这是后面配置完Nginx后无法重新启动Nginx服务了,我输出日志才发现的,具体后面会说。

  2. nginx配置使用证书。
    编辑你的Nginx配置文件,如:/etc/nginx/sites-available/<your-nginx-config>
    ☆☆修改配置文件前建议先备份☆☆
    可以通过Mozilla SSL Configuration Generator自动生成对应的参考配置

    修改http对应的server模块:

     server {
         listen 80;
         listen [::]:80;
         server_name example.com;
         #return 301 https://example.com$request_uri;
         if ( $scheme = http ){
           return 301 https://$server_name$request_uri;
         }
         root /var/www/html/example.com;
         index index.html index.htm;
     }
    

    return 301 https://$server_name$request_uri;是强制http跳转https

    在配置文件中添加https的server模块:

     server {
         listen 443 ssl;
         server_name example.com;
    
         #charset utf-8;
         location / {
            root /var/www/html/example.com;
            #index index.html index.htm;
         }
         #root /var/www/html/example.com;
         index index.html index.htm;
    
         access_log  /var/log/nginx/example.com_access.log;
         error_log  /var/log/nginx/example.com_error.log;
    
         ssl_certificate /etc/ssl/example.com/fullchain.cer;
         ssl_certificate_key /etc/ssl/example.com/example.com.key;
    
         ssl_session_timeout 1d;
         ssl_session_cache shared:SSL:50m;
         ssl_session_tickets off;
    
         ssl_dhparam /etc/ssl/example.com/dhparam.pem;
    
         ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
         ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK';
         ssl_prefer_server_ciphers on;
     }
    

    总的样例内容如下:

     server {
         listen 80;
         listen [::]:80;
         server_name example.com;
         #return 301 https://example.com$request_uri;
         if ( $scheme = http ){
           return 301 https://$server_name$request_uri;
         }
         root /var/www/html/example.com;
         index index.html index.htm;
     }
    
     server {
         listen 443 ssl;
         server_name example.com;
    
         #charset utf-8;
         location / {
            root /var/www/html/example.com;
            #index index.html index.htm;
         }
         #root /var/www/html/example.com;
         index index.html index.htm;
    
         access_log  /var/log/nginx/example.com_access.log;
         error_log  /var/log/nginx/example.com_error.log;
    
         ssl_certificate /etc/ssl/example.com/fullchain.cer;
         ssl_certificate_key /etc/ssl/example.com/example.com.key;
    
         ssl_session_timeout 1d;
         ssl_session_cache shared:SSL:50m;
         ssl_session_tickets off;
    
         ssl_dhparam /etc/ssl/example.com/dhparam.pem;
    
         ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
         ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK';
         ssl_prefer_server_ciphers on;
     }
    

    配置文件没有问题的话执行systemctl reload nginx,然后在浏览器打开http://example.com, 如果正常跳转到https://example.com,就算成功了。 如果是chrome浏览器,在地址栏点击绿色小锁的图标,可以查看证书的详情,如图。
    https browser sho

此步骤遇到的难题

  1. 这里要注意的是如果你是Nginx+Cloudflare,那么Cloudflare的”Crypto—SSL”要设置成Full(strict),否则会导致example.com将你重定向次数过多的情况,默认是Flexible。CF设置如图:
    cloudflare config of starting https with nginx

  2. 这里还有个坑就是前面说的无法生成dhparam.pem文件导致无法重载Nginx。一开始我还没发现,一直在以为Nginx配置文件哪里错了,后来sudo journalctl -xe输出日志才发现Nginx找不到dhparam.pem文件:
    nginx cannot found dhparam.pem-1
    ls命令可以发现目录下并不存在dhparam.pem文件:
    nginx cannot found dhparam.pem-2
    于是我又执行了几次openssl dhparam 2048 -out dhparam.pem,结果还是一样,而且过程竟然只需要1分钟不到。如图:
    nginx cannot found dhparam.pem-3
    察觉到不对劲之后我马上去找了跟dhparam的资料,后来在一个博客上发现他的命令顺序跟我不一样,这个文件存放目录也跟我不一样,于是我就把命令改成openssl dhparam -out dhparam.pem 2048和改目录到example.com(此时该目录权限还是$USER的)再试了几次,第一次是不行的,执行多几次后突然就成功了。如图:
    nginx cannot found dhparam.pem-4
    可以发现此次执行命令时间十分的长,我等了大概5分钟才执行完毕。
    然后我们再用ls来看看目录下的文件,可以发现文件已经生成:
    nginx cannot found dhparam.pem-5

测试证书

如果是网站的话,可以使用第三方网站工具测试自己网站的HTTPS配置的安全性

  1. SSL Lab就是个不错的选择:
    https://ssllabs.com/ssltest/analyze.html?d=example.com
    这是我的测试结果,都是A哦!!
    SSL Lab Server Test

  2. 也可以在Linux下使用openssl命令查看证书的过期时间

     openssl x509 -noout -dates -in /etc/ssl/example.com/example.com.key