Let's Encrypt,站点加密之旅

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。

Let’s Encrypt,是 2016 年 4 月 12 日成立的一家证书授权中心,提供免费的传输层安全(TLS)X.509 证书,通过自动化的过程消除目前安全网站证书需要手工创建,加密,签名,安装以及更新的复杂性。

一直以来都觉得浏览器网址开头的那把小绿锁很别致啊,现在 Let’s Encrypt 横空出世提供免费证书,说明 https 势在必行,那我也来动手给博客加把锁吧,看着就安全是吧。

Let’s Encrypt 的官网提供的脚本看起来更加自动化一些,但我没有亲自尝试,而是在 Github 上搜到了一个开源脚本 acme-tiny,用下来之后成功将博客加密完成。

特别提醒:请注意用户权限问题,下文都是使用的普通用户,而非 root ,请根据自己的情况调整。

file

根据 acme-tiny 提供的说明文档和我自己的实施过程列出以下几步:

### 克隆脚本

sudo git clone https://github.com/diafygi/acme-tiny.git  
cd acme-tiny

### 创建 Let’s Encrypt 私钥

openssl genrsa 4096 > account.key

### 创建 CSR (Certificate Signing Request,证书签名请求) 文件

ACME 协议 (Let’s Encrypt 所使用的) 需要一个 csr 文件,用来进行证书签名和证书更新。

将需要加密的域名加到下面的代码中,目前一张证书最多可以加密 100 个域名:

openssl genrsa 4096 > domain.key     
openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yoursite.com,DNS:www.yoursite.com")) > domain.csr

注意:openssl.cnf 文件的位置可能会因为 linux 版本的不同而有变

### 证明你拥有该域名

acme-tiny 脚本会生成验证文件并写入到你指定的目录下,然后通过 “.well-known/acme-challenge/“ 这个 URL 来访问到验证文件。注意: Let’s Encrypt 会对你的服务器做一次 http 请求来进行验证,因此你需要保证 80 端口能够访问.

  • 手动生成 challenges 目录,用来存放验证文件(路径可以根据需要修改)

    mkdir -p /var/www/challenges

  • 配置 nignx 的 80 端口

    server {

        listen 80;
        server_name yoursite.com www.yoursite.com;

        if ( $request_uri !~ "/.well-known/acme-challenge/*" ) { # 让 Let's Encrypt 成功访问到验证文件不受 301 影响
                  return 301 https://yoursite.com$request_uri; # 注意进行301重定向到https,否则通过http仍能访问你的站点
        }

        location /.well-known/acme-challenge/ {
            alias /var/www/challenges/;
            try_files $uri =404;
        }

        #...你的其他配置
    }

Apache 则需修改 .htaccess 文件配置 301 重定向:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

### 获取签名证书

sudo chmod +x acme_tiny.py  
sudo python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed.crt

### 安装证书

针对 nginx, 你还需要将 Let’s Encrypt 的中间件证书 intermediate.pem 内容附加在签名证书 signed.crt 之后:

    wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem  
    cat signed.crt intermediate.pem > chained.pem
    server {
        listen 443 ssl;
        server_name yoursite.com www.yoursite.com;

        ssl_certificate /path/to/chained.pem;
        ssl_certificate_key /path/to/domain.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
        ssl_session_cache shared:SSL:50m;
        ssl_prefer_server_ciphers on;

        #...你的其他配置
    }

### 证书自动更新定时任务

恭喜!你的网站已经使用上了 HTTPS。 但 Let’s Encrypt 证书有效期只有 90 天,所以需要定期更新。现在只需要写一个更新脚本并把它放到定时任务中即可。

脚本内容:

    #!/usr/bin/sh

    python /path/to/acme_tiny.py --account-key /path/to/account.key --csr /path/to/domain.csr --acme-dir /var/www/challenges/ > /tmp/signed.crt || exit

    wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem

    cat /tmp/signed.crt intermediate.pem > /path/to/chained.pem

    service nginx reload

定时任务可以设置为每个月执行一次:
0 0 1 * * sudo bash /path/to/renew_cert.sh 2>> /var/log/acme_tiny.log

原文链接:macken.me/article/encrypt-your-web...

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 8年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 75
Summer

很酷炫,给你补了张图片。

8年前 评论

请问一个证书可以绑定多个域名吗?比如一个 a.example.com, b.example.com

openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:yoursite.com,DNS:www.yoursite.com")) > domain.csr

是在这个里面 nsubjectAltName 后面不断加吗?

8年前 评论

ValueError: Wrote file to /home/xxx/www/challenges/oJbvpIhkwkBGBAQUklWJXyC8VbWAdQqlgpwUJkgC1Vg, but couldn't download http://www.yoursite.com/.well-known/acme-c...

出现这个问题,楼主是怎么解决的?

8年前 评论

问题解决了,是因为我买的是腾讯云的新加坡 CVM,无法域名无法解析到造成的。在 CVM 上 ping 了一下,无法 ping 通。我试了 linode 主机后,发现可以了。

8年前 评论

@all
大家注意了昂。按照上面的配置,有可能会踹你 www.yourdomain.site 可以访问,但是 yourdomain.site 则会出现不安全链接,造成无法访问,原因是。nginx 中的配置文件中的逗号引起的。

file

去掉这个逗号就可以了。

8年前 评论

@江边望海

server {
    listen 443;
    server_name tanteng.me;
    return 301 $scheme://www.$host$request_uri;
}

server {
    listen 443;
    server_name *.tanteng.me;
    root   /usr/share/nginx/html/tanteng.me/public;
    index  index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

我的 server_name 这样写根域名无法访问,301 重定向加上去也没用。。

8年前 评论

@纸牌屋弗兰克 帖子一发我发现低级错误了。。。不能这样。。

8年前 评论

@纸牌屋弗兰克 我试了一下你的域名根域名没有办法访问。原因,我已经说过了,是逗号引起的。

8年前 评论

@江边望海
我的不是逗号的原因,你看我前面回复发的 nginx 配置,我新做了一个重定向,要注意的是 ssl 的重定向也需要带上验证信息。我看有没有更好的方法。现在 OK 了。

server {
    listen 443;
    server_name tanteng.me;
    return 301 https://www.tanteng.me$request_uri;

    ssl on;
    ssl_certificate /xxx/acme-tiny/chained.pem;
    ssl_certificate_key /xxx/acme-tiny/www.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_session_cache shared:SSL:50m;
    ssl_prefer_server_ciphers on;
}
8年前 评论
Summer

PHPHub 已经参照此教程部署上 :beers:

8年前 评论

开启了 HTTPS 网站有时候好慢啊,大家有同样的感觉吗,如何优化
https://www.tanteng.me

8年前 评论

@纸牌屋弗兰克 我访问了下你的 blog,pretty fast ! 你网速是不是有点 slow :smile:

8年前 评论

@Macken 我登录 wordpress 后台巨慢无比,然后主站和前台页面就会很卡。。

8年前 评论

<https:://www.h57.pw>

https://h57.pw

感谢分享,开心搞定了

8年前 评论

@M1racle 我一开始也用多说,但因为多说的头像资源是 http,导致没有心爱的小绿锁,所以还是选择使用 disqus 了~

8年前 评论

@Macken 恩,我的那些图片什么的还是 http 过来的,慢慢改,争取早日小绿锁,明天放假就都能搞定了

8年前 评论

@Macken @江边望海 请问下这个报错怎么解决,腾讯 CVM,部署了很多东西不想换服务器。
Traceback (most recent call last):
File "acme_tiny.py", line 198, in
main(sys.argv[1:])
File "acme_tiny.py", line 194, in main
signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca)
File "acme_tiny.py", line 123, in get_crt
wellknown_path, wellknown_url))
ValueError: Wrote file to /var/www/challenges/GeyNz3Z0Jig5aujuqc-XdaqqHsgnD98fXnG5JjitsMg, but couldn't download http://www.xxxxx.club/.well-known/acme-cha...

连接地址 ngnix 报 403 Forbidden
nginx.conf
listen 80 default_server;

listen [::]:80 default_server ipv6only=on;

    server_name xxxxx.club www.xxxxxx.club;
    return 301 https;//xxxxx.club$request_uri;

    location /.well-known/acme-challenge/{
            alias /var/www/challenges/;
            try_files $uri = 404;
    }
    index index.html index.htm index.php;
    root  /home/xxxxxxx/xxxx;
8年前 评论

@GTYoung 你是用哪里的服务器?新加坡的还是香港的

8年前 评论
  1. 服务器的原因。新加坡或者香港的可能是由于网络原因引起的。
  2. 你配置的时候最好将 301 注释一下,等验证通过再开启 301
8年前 评论

@江边望海 广州三区的服务器。。。 301 注释了下还是 couldn't download。加了 301 访问这个链接会报此页面有重定向循环

8年前 评论

@GTYoung 出现这个问题是因为没有办法访问你的域名,是由于 dns 解析造成的,毕竟 encrypt 是在国外。

8年前 评论
叶落山城

@江边望海 我发现我也死在这一步了,总是提示没有。。。。而我的是阿里云的服务器,万网的域名。。。目前不知道怎么解决。。。。

Traceback (most recent call last):
  File "acme_tiny.py", line 198, in <module>
    main(sys.argv[1:])
  File "acme_tiny.py", line 194, in main
    signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca)
  File "acme_tiny.py", line 123, in get_crt
    wellknown_path, wellknown_url))
ValueError: Wrote file to /var/www/challenges/BEo7QjU4lE9pv5kExEJWcjYSjE-LrI5IIG3ZrWim0ag, but couldn't download http://yoursite.com/.well-known/acme-challenge/BEo7QjU4lE9pv5kExEJWcjYSjE-LrI5IIG3ZrWim0ag

@Macken 可有解决之法?

8年前 评论

@叶落山城 你要确定通过浏览器能访问到那个文件即可

8年前 评论
叶落山城

@Macken 然而并不能,我猜,应该是我 nginx 配置的问题。。。。

8年前 评论
叶落山城

@Macken 配置成功了,但是提示是 未授权证书, 红色的。。。。。

8年前 评论
叶落山城

@Macken 获取证书那,一直 download 失败,然后我把 301 跳转那个注释掉就可以了。

Parsing account key...
Parsing CSR...
Registering account...
Already registered!
Verifying iphpt.com...
iphpt.com verified!
Verifying www.iphpt.com...
www.iphpt.com verified!
Signing certificate...
Certificate signed!

到后面 再把 301 打开的

8年前 评论

@叶落山城 对的,就是要先关闭,验证成功再打开

8年前 评论
叶落山城

@Macken 然而 并没有绿色。。。。 心塞。。。

8年前 评论

@叶落山城 http://obq9881x1.bkt.clouddn.com/2e3cd3f85...
你这张图片是 http 资源,换成 https 的就 OK 了,把图片下载下来,放到你的项目下就 OK。

8年前 评论
叶落山城

@Macken ok 啦,确实是这个问题,谢谢亲,这篇文章其实是我昨天在 开发者头条看到的,不错不错
如果有人 down 失败了,且是国内服务器的,注意要把上面的 301 跳转记得注释掉哦。。。
吼吼吼~~~
叶落山城秋

8年前 评论

@江边望海 我还用了 DigitalOcean 的解析。
domain:gtblog.club

@Macken 请问还有没有什么解决方案....

8年前 评论
Lonexw

已收藏

8年前 评论

已成功开启哦。 独音电台:https://indie-music.cn

8年前 评论

能加 QQ 吗,我这边一直提示文件不存在:346127275,@Macken

8年前 评论

@ailulee 各位朋友随意加我 QQ:615170821

8年前 评论

CentOS6 的 openssl.cnf 文件在 /etc/pki/tls/openssl.cnf

8年前 评论

@Macken
如果 challenge.conf 这样写的话就既能跳转到 https 页面,也能在不注释掉 301 跳转的情况下完成验证

server {

    listen 80;
    server_name foo.domain.com bar.domain.com;

    location /.well-known/acme-challenge/{
        alias /var/www/challenges/;
        try_files $uri =404;
    }

    location / {

        if ($host = "foo.domain.com"){
            return 301 https://foo.domain.com$request_uri;
        }

        if ($host = "bar.domain.com"){
            return 301 https://bar.domain.com$request_uri;
        }
    }

}

本人新手,如果有什么不妥之处,欢迎指正

8年前 评论

@NicholasStone 多谢指教,已做相应的修改:thumbsup:

8年前 评论

补充:需要在 location 中加入对 IP 的验证,否则会通过 80 端口访问到 nginx 的默认界面

location / {
        if ($host = "xxx.xxx.xxx.xxx"){
            return 301 https://bar.domain.com$request_uri;
        }
       #Others
}
8年前 评论

阿兰 2016/10/4 10:56:40
root@iZ23vqb2akbZ acme-tiny]# openssl req -new -sha256 -key domain.key -subj "/CN=www.jyhelper.com" > domain.csr
unable to load Private Key
3077805804:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: ANY PRIVATE KEY

8年前 评论

@xiao345 仔细看文章,有说明的:smile:

8年前 评论

@all

file

今天突然发现我的证书失效了,说明自动续约没起作用,研究了发现需要针对 301 做如上处理,其他同学有什么看法,可以留言。

8年前 评论

(SSL: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch)

8年前 评论

虽然安装成功了,但是假如 nginx 配置加上
if ($request_uri !~ "/.well-known/acme-challenge/*") { # 让 Let's Encrypt 成功访问到验证文件不受 301 影响
return 301 https://yoursite.com$request_uri; # 注意进行 301 重定向到 https,否则通过 http 仍能访问你的站点
}

    location /.well-known/acme-challenge/ {
        alias /var/www/challenges/;
        try_files $uri =404;
    }

的话会造成多次重定向,造成无法访问,所以没加,自然也就无法自动更新证书了,不知道 nginx 怎样写才不会多次重定向?

8年前 评论

厉害了 我的哥

8年前 评论
superwen

用官网的脚本的确简单些。推荐用官方脚本。

8年前 评论

这个月刚开启 https,也是用了 Let's encrypt 的证书,不过我的博客是又拍云赞助的免费 cdn,直接在又拍云申请的 let's encrypt 免费证书,自主配置还是很方便的。最主要的是不用自主更新,配置完后自动续期,一劳永逸,哈哈,五星好评!

8年前 评论

我的服务器是 Apache

file

这个文件什么时候生成的

8年前 评论

@496604841 在执行 acme_tiny.py 脚本的时候

8年前 评论
Destiny

亲,自动续费那个定时任务可以用?

8年前 评论

@Destiny 可以的,不放心的话可以手动执行一下。

8年前 评论
Destiny

@Macken 嗯嗯。。

8年前 评论
Destiny

不错不错,每月自动更新的脚步也生效了~~~!!!!

7年前 评论
jcc123

@Macken Already registered!
Verifying www.jc91715.top...
Traceback (most recent call last):
File "acme_tiny.py", line 198, in
main(sys.argv[1:])
File "acme_tiny.py", line 194, in main
signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca)
File "acme_tiny.py", line 123, in get_crt
wellknown_path, wellknown_url))
ValueError: Wrote file to /var/www/challenges/3Ub2MpiE01xEvtB5MOzHgQzoe5nYcnbHfVrGjPaiLKI, but couldn't download http://www.jc91715.top/.well-known/acme-ch...

apache2 vps 一直报这个错注释掉 301 也不管用 大神这是怎么回事

7年前 评论
jcc123

@Macken location /.well-known/acme-challenge/ {
alias /var/www/challenges/;
try_files $uri =404;
}

这是 Nginx 的配置 apache 上如何配置。

7年前 评论

需要注意 这个证书不被支付宝支持,支付宝异步通知时不认识这个证书 ,只能用 http 了 。

7年前 评论