如何在Nginx申请安装配置免费HTTPS证书+阿里云SLB如何配置HTTPS

Posted by Yancy on 2016-11-23

如何在Nginx申请安装配置免费HTTPS证书

HTTPS在各大互联网站已经成为标配,就连某度也在前不久全面启用HTTPS,很多小网站也配置了HTTPS,这是未来的一种趋势.HTTPS的好处多多,可以防止各种攻击劫持,运营商广告植入,客户传输信息泄露等问题。为了让HTTPS能够全面普及,让我们加密项目应运而生,它由互联网安全研究小组ISRG(互联网安全研究小组)提供服务,很早之前我就在关注 Let’s Encrypt这个免费、自动化、开放的证书签发服务。
ISRG是来自美国加利福尼亚州的一个公益组织.Let’s Encrypt得到了Mozilla,Cisco和Chrome等众多公司和机构的支持。

申请 Let's Encrypt 证书不但免费,还非常简单,虽然每次只有 90 天的有效期,但可以通过脚本定期更新,配好之后一劳永逸。本教程亲测有效,希望对正在寻找免费HTTPS方案的你有一定的帮助,本文记录本站申请过程和遇到的问题。

按照我们的加密官方提供的工具安装HTTPS的话与过于复杂,于是有好心人提供了更为轻巧的工具安装,极致纤巧诞生了,它的代码量在200行内,只需依赖的Python和OpenSSL的。

第一步:创建Let’s Encrypt加密账号

首先创建一个目录,例如 ssl用来存放各种临时文件和最后的证书文件。进入这个目录,创建一个RSA私钥用于 Let’s Encrypt 识别你的身份:

让我们加密使用一个私钥来进行账号的创建与登陆,因此我们需要使用openssl创建一个account.key

1
2
3
4
5
6
[root@ihaozhuo1 srv]# mkdir ssl && cd ssl
[root@ihaozhuo1 ssl]# openssl genrsa 4096 > account.key
Generating RSA private key, 4096 bit long modulus
...........................++
...........................++
e is 65537 (0x10001)

第二步:创建域名的CSR(证书签名请求)

让我们加密使用的ACME协议需要一个CSR文件,可以使用它来重新申请HTTPS证书,接下来我们就可以创建域名CSR,在创建CSR之前,我们需要给我们的域名创建一个私钥(这个和上面的账户私钥无关)。
接着就可以生成 CSR(Certificate Signing Request,证书签名请求)文件了。在这之前,还需要创建域名私钥(一定不要使用上面的账户私钥),根据证书不同类型,域名私钥也可以选择 RSA 和 ECC 两种不同类型。以下两种方式请根据实际情况二选一。

1)创建 RSA 私钥(兼容性好):

1
openssl genrsa 4096 > domain.key

2)注意:一定不要使用上面创建好的account.key 来当domain.key ❗️

有了私钥文件,就可以创建生成 CSR文件了。在 CSR中推荐至少把域名带 www 和不带www的两种情况都加进去,其它子域可以根据需要添加,替换下面的ihaozhuo.com即可 (目前一张证书最多可以包含 100 个域名)

3)注意,稍后会说到,每个域名都会涉及到验证)

1
2
3
4
5
6
7
#单个域名
openssl req -new -sha256 -key domain.key -subj“/CN=ihaozhuo.ccom”> domain.csr
#多个域名(如果你有多个域名,比如:www.ihaozhuo.com 和ihaozhuo.com,使用这种方式)
openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:ihaozhuo.com,DNS:www.ihaozhuo.com")) > domain.csr

4)说明:ihaozhuo.com,www.ihaozhuo.com 替换成你的域名执行这一步时,需要指定openssl.cnf文件,一般这个文件在你的openssl安装目录底下。 如果提示找不到 /etc/ssl/openssl.cnf文件,请看看/usr/local/openssl/ssl/openssl.cnf 和/etc/pki/tls/openssl.cnf是否存在。

1
2
3
4
5
[root@ihaozhuo1 ssl]# find / -name openssl.cnf ##查找openssl.cnf文件
/etc/pki/tls/openssl.cnf
[root@ihaozhuo1 ssl]# ln -s /etc/pki/tls/openssl.cnf /etc/ssl/openssl.cnf #做个软链接
[root@ihaozhuo1 ssl]# openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:ihaozhuo.com,DNS:www.ihaozhuo.com")) > domain.csr

如果还是不行,也可以使用交互方式创建CSR(需要注意 Common Name必须为你的域名):

1
openssl req -new -sha256 -key domain.key -out domain.csr

第三步:配置域名验证

CA在签发DV(域验证)证书时,需要验证域名所有权。传统CA的验证方式一般是往admin@ihaozhuo.com发验验邮件,而让我们加密是在你的服务器上生成一个随机验证文件,再通过创建CSR时指定的域名访问,如果可以访问则表明你对这个域名有控制权。

首先创建用于存放验证文件的目录,例如:

1
mkdir -p var/www/challenge

然后配置一个Nginx服务,以Nginx为例:(注意:这里的端口是80,不是443)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name www.ihaozhuo.com ihaozhuo.com;
location /.well-known/acme-challenge/ {
alias /var/www/challenges/;
index index.html index.php index.jsp index.htm;
try_files $uri =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

这是我在上面配置完成以后需要重启Nginx,先Nginx -t校验下是否有没有问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@ihaozhuo1 conf]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
```
如何验证上面设置是否成功? 我们只要在目录:/var/www/challenges/ 下面创建文件:test.html
然后通过http://www.ihaozhuo.com/.well-known/acme-challenge/test.html URL访问地址那个文件,如果能访问成功说明你配置没问题;否则就说明配置错误❌,这一步不能实现下面就不能成功了。
以上配置优先查找 `var/www/challenge`目录下的文件,如果找不到就重定向到` HTTPS` 地址。这个验证服务以后更新证书还要用到,建议一直保留。
### 第四步:获取网站证书
**1)上面配置好,现在我们使用acme-tiny.py来获取网站证书** acme-tiny脚本文件上Github下载:[acme-tiny.py](https://github.com/diafygi/acme-tiny)
**2)先把 acme-tiny 脚本保存到之前的ssl目录**
```bash
wget https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py

3)然后指定账户私钥、CSR 以及验证目录,执行脚本来获取网站证书

1
python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed.crt

4)如果一切正常,当前目录下就会生成一个signed.crt这就是申请好的证书文件,如果跟我一样报错了请看下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ihaozhuo1 ssl]# python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed.crt
Parsing account key...
Parsing CSR...
Registering account...
Already registered!
Verifying ihaozhuo.com...
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/fWyC6BvCCSlVunoaltJrnEV-ewj6FBpOv7Eu8LluBUg, but couldn't download http://ihaozhuo.com/.well-known/acme-challenge/fWyC6BvCCSlVunoaltJrnEV-ewj6FBpOv7Eu8LluBUg

5)脚本问题:

  1. 记得在网站记得域名解析要对,都是你现在申请证书这台外网解析地址: http://ihaozhuo.com/不通那解析就出现问题了。
  2. 网上也有很多人说域名DNS服务器解析导致域名不行,这个验证了以后不是的,这个是GitHub这个脚本acme_tiny.py问题导致的,需要编辑脚本注释掉下面全部内容即可 116行-123行:
1
2
3
4
5
6
7
8
# try:
# resp = urlopen(wellknown_url)
# resp_data = resp.read().decode('utf8').strip()
# assert resp_data == keyauthorization
# except (IOError, AssertionError):
# os.remove(wellknown_path)
# raise ValueError("Wrote file to {0}, but couldn't download {1}".format(
# wellknown_path, wellknown_url))

如果修改没有用,可以到我这里下载Github下载acme_tiny.py

6)然后保存再次获取网站证书

1
2
3
4
5
6
7
8
9
10
11
[root@ihaozhuo1 ssl]# python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /var/www/challenges/ > ./signed.crt
Parsing account key...
Parsing CSR...
Registering account...
Already registered!
Verifying ihaozhuo.com...
ihaozhuo.com verified!
Verifying www.ihaozhuo.com...
www.ihaozhuo.com verified!
Signing certificate...
Certificate signed!

㊗️ 如果看到如上内容,那么恭喜你,你的网站证书已经成功获取了。这时候查看目录下面有如下几个文件:

1
2
3
4
5
6
7
8
[root@ihaozhuo1 ssl]# ll
总用量 36
-rw-r--r-- 1 root root 3243 3月 30 16:42 account.key
drwxr-xr-x 4 root root 4096 3月 30 17:31 acme-tiny
-rw-r--r-- 1 root root 9159 3月 30 19:23 acme_tiny.py
-rw-r--r-- 1 root root 1639 3月 30 17:00 domain.csr
-rw-r--r-- 1 root root 3243 3月 30 16:53 domain.key
-rw-r--r-- 1 root root 2159 3月 30 22:11 signed.crt

到目前为止我们已经成功申请到Let’sENcrpyt的网站证书,对于Nginx用户我们还需要把Let’s ENcrpyt的中间证书加入到刚刚生成的signed.crt文件中。具体操作:

第五步:安装证书

证书生成后,就可以把它配置在web 服务器上了,需要注意的是,Nginx需要追加一个Let’s Encrypt的中间证书,在 Nginx 配置中,需要把中间证书和网站证书合在一起:

1
2
3
4
5
6
7
8
9
10
11
12
wget -O - https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > chained.pem
[root@ihaozhuo1 ssl]# ll
总用量 36
-rw-r--r-- 1 root root 3243 3月 30 22:06 account.key
-rw-r--r-- 1 root root 9159 3月 30 19:23 acme_tiny.py
-rw-r--r-- 1 root root 3834 3月 30 22:51 chained.pem
-rw-r--r-- 1 root root 1639 3月 30 22:07 domain.csr
-rw-r--r-- 1 root root 3243 3月 30 22:06 domain.key
-rw-r--r-- 1 root root 1675 3月 30 22:51 intermediate.pem
-rw-r--r-- 1 root root 2159 3月 30 22:51 signed.crt

1)现在我们可以在Nginx启动HTTPS,你可以直接在你之前网站Nginx.conf里面配置

注意⚠️ :为了不影响现在正在运行的网站,在配置HTTPS的时候最好将你现在配置好的文件全部备份起来,这样你在启动HTTPS出现问题可以能够快速恢复回来。

Nginx配置文件加入以下配置:

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
28
server {
listen 80;
server_name ihaozhuo.com, www.ihaozhuo.com;
location /.well-known/acme-challenge/ {
alias /var/www/challenges/;
try_files $uri =404;
}
...the rest of your config
}
server {
listen 443;
server_name ihaozhuo.com, www.ihaozhuo.com;
ssl on;
ssl_certificate /srv/ssl/chained.pem;
ssl_certificate_key /srv/ssl/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;
...the rest of your config
}

在Chrome浏览器里面你可以访问https开头的,但没有看到绿色锁标记,这是因为你网站有请求http://xxx资源,这个问题我遇到以后查了很多,因为我网站很早之前就有了,里面配置的地址都是http要修改过来非常麻烦,所以申请证书一开始域名下来就要申请这是最好的,如果你情况已经跟我一样,那你需要把所有的http://xxxx 资源全部切换成:https://xxx 的资源,这样绿色的锁才会出现。

第六步:定期更新

访问查看下网站详细信息:

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
28
29
[root@ihaozhuo1 conf]# curl -v https://www.ihaozhuo.com/home.html
* About to connect() to www.ihaozhuo.com port 443 (#0)
* Trying 120.27.185.86...
* Connected to www.ihaozhuo.com (120.27.185.86) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* Server certificate:
* subject: CN=ihaozhuo.com
* start date: 3月 30 13:51:00 2017 GMT
* expire date: 6月 28 13:51:00 2017 GMT
* common name: ihaozhuo.com
* issuer: CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US
* NSS error -8179 (SEC_ERROR_UNKNOWN_ISSUER)
* Peer's Certificate issuer is not recognized.
* Closing connection 0
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.

Let’s Encrypt 签发的证书只有90天有效期,但可以通过脚本定期更新。你可以创建了一个自动更新脚本renew_cert.sh,内容如下:

1
2
3
4
5
6
#!/usr/bin/sh
python /srv/ssl/acme_tiny.py --account-key /srv/ssl/account.key --csr /srv/ssl/domain.csr --acme-dir /var/www/challenges/ > /srv/ssl/signed.crt || exit
wget -O - https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > intermediate.pem
cat /srv/ssl/signed.crt intermediate.pem > /srv/ssl/chained.pem
service nginx reload

修改crontab配置,加入以下内容:

1
2
#每个月执行一次
0 0 1 * * /srv/ssl/renew_cert.sh 2>> /var/log/acme_tiny.log

大功告成,不过先别急,访问下自己的HTTPS网站是否正常,不出意外的话,网站正式启用HTTPS,但是网站如果有用CDN的话,那么需要CDN也支持HTTPS才行,否则无法正常加载CDN的资源,类似的错误如:

1
Mixed Content: The page at 'https://ihaozhuo.com/' was loaded over HTTPS, but requested an insecure script 'http://app.h5.ihaozhuo.combdimg.com/yjk/css/jquery.min.js'. This request has been blocked; the content must be served over HTTPS.

一种解决方法就是使用Upyun来解决这个问题。可参考:我写的一篇文章如何解决https跨站访问证书不安全访问。

一种解决方法就是使用七牛的云存储来解决这个问题。可参考:https://blog.blahgeek.com/qiniu-cdn-serve-static/

阿里云SLB如何配置HTTPS

使用阿里云很简单,不需要太多的配置不过个人还是喜欢Nginx去做代理访问。

公司后期更新了本地的Nginx去做负载均衡直接使用SLB ,这里记录下申请了证书以后只需要chained.pem和私钥domain.key

在SLB负载均衡左侧有证书管理-创建证书-选择服务器证书。

然后在负载均衡上面设置下监听端口选择下对应证书就可以了。

参考:

GitHub 安装教程