没有 https 加持的网站会逐渐地被浏览器标记为不安全的,所以为网站添加 https 已经变得刻不容缓。对于商业网站来说,花钱购买 ssl/tls 证书并不是什么问题。但对于个人用户来说,如果能有免费的 ssl/tls 证书可用将会是非常幸福的事情!let's encrypt 就是一个提供免费 ssl/tls 证书的网站,由于其证书期限只有三个月,所以需要我们用自动化的方式去更新证书。本文将介绍如何为通过 docker 运行的 nginx 中的站点添加 https 支持,并自动完成证书的更新。本文的演示环境为:运行在 azure 上的 ubuntu 16.04 主机(此图来自互联网):
准备环境
在 azure 上创建 ubuntu 类型的虚机事件非常容易的事情,安装 docker 也无须赘言。比较容易忽略的是配置合适的网络安全组规则,比如打开 80 和 443 端口:
还有就是配置 dns:
创建一个普通的 http 站点
简单起见,直接使用一个镜像中的 nodejs 应用作为 web 站点:
1
2
3
4
5
|
$ docker pull ljfpower /nodedemo $ docker network create -d bridge webnet $ docker run -d --restart=always --expose=3000 \ --network=webnet --name=myweb \ ljfpower /nodedemo |
在用户的家目录下创建 nginx 目录及其子目录 conf.d、conf.crt 和 html,创建 logs 目录及其子目录 nginx 和 letsencrypt:
1
2
|
$ mkdir -p nginx/{conf.d,conf.crt,html} $ mkdir -p logs/{nginx,letsencrypt} |
说明,本文演示的示例中需要我们手动创建的文件和目录结构如下:
创建 nginx/nginx.conf 文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
user nginx; worker_processes auto; error_log /var/log/nginx/error .log warn; pid /var/run/nginx .pid; events { worker_connections 2048; } http { include /etc/nginx/mime .types; default_type application /octet-stream ; sendfile on; keepalive_timeout 65; client_max_body_size 10m; include /etc/nginx/conf .d/*.conf; } |
然后创建 nginx/conf.d/default.conf 文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
upstream web{ server myweb:3000; } server { listen 80; listen [::]:80; server_name filterinto.com www.filterinto.com; location ^~ /.well-known /acme-challenge/ { default_type "text/plain" ; root /usr/share/nginx/html ; } location = /.well-known /acme-challenge/ { return 404; } location / { proxy_pass http: //web ; } } |
其中 /.well-known/acme-challenge/ 目录是 certbot 工具在生成证书时创建的。接下来创建文件 nginx/html/index.html 文件,内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!doctype html> < html > < head > < meta charset = "utf-8" /> < title >let's encrypt first time cert issue site</ title > </ head > < body > < h1 >hello https!</ h1 > < p > just used for the very first time ssl certificates are issued by let's encrypt's certbot. </ p > </ body > </ html > |
这个页面也是 certbot 在生成证书时需要用到的。最后让我们启动容器(在用户的家目录下执行下面的命令):
1
2
3
4
5
6
7
8
9
10
|
$ docker run -d \ -p 80:80 \ - v $( pwd ) /nginx/conf .d: /etc/nginx/conf .d:ro \ - v $( pwd ) /nginx/nginx .conf: /etc/nginx/nginx .conf:ro \ - v $( pwd ) /logs/nginx : /var/log/nginx \ - v $( pwd ) /nginx/html : /usr/share/nginx/html \ --restart=always \ --name=gateway \ --network=webnet \ nginx:1.14 |
注意:这时没有映射 443 端口,也没有挂载存放证书的目录。只能以 http 协议访问访问我们的站点:
为站点生成 ssl/tls 证书
let's encrypt 是一个提供免费 ssl/tls 证书的网站,它为用户提供了 certbot 工具用来生成 ssl/tls 证书。方便起见,我们把 certbot 简单的封装到容器中。在用户的家目录下创建 certbot 目录,进入 certbot 目录并把下面的内容保存到 dockerfile 文件中:
1
2
3
|
from alpine:3.4 run apk add --update bash certbot volume [ "/etc/letsencrypt" ] |
然后执行下面的命令创建 certbot 镜像:
1
|
$ docker build -t certbot:1.0 . |
然后在 certbot 目录下创建自动更新证书的脚本 renew_cert.sh,内容如下:
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
30
|
#!/bin/bash webdir= "$1" list=( 'filterinto.com' 'www.filterinto.com' ) led_list=() www_root= /usr/share/nginx/html for domain in ${list[@]}; do docker run \ -- rm \ - v ${webdir} /nginx/conf .crt: /etc/letsencrypt \ - v ${webdir} /logs/letsencrypt : /var/log/letsencrypt \ - v ${webdir} /nginx/html :${www_root} \ certbot:1.0 \ certbot certonly --verbose --noninteractive --quiet --agree-tos \ --webroot -w ${www_root} \ --email= "nick.li@grapecity.com" \ -d "$domain" code=$? if [ $code - ne 0 ]; then failed_list+=($domain) fi done # output failed domains if [ ${ #failed_list[@]} -ne 0 ];then echo 'failed domain:' for (( i=0; i<${ #failed_list[@]}; i++ )); do echo ${failed_list[$i]} done fi |
在用户的家目录中执行 ./renew_cert.sh /home/nick 命令就可以生成新的证书(/home/nick 为当前用户的家目录)。生成的证书被保存在 /home/nick/nginx/conf.crt/live 目录下,以域名命名的目录下保存着该域名的证书:
然后去检查下 nginx/html 目录,发现多了一个隐藏的 .well-known 目录,这个目录就是在生成证书时创建的:
有了 ssl/tls 证书,接下来我们就可以配置 https 站点了。
为站点配置 ssl/tls 证书
有了 ssl/tls 证书,接下来更新 nginx 的配置文件就可以了,更新 nginx/conf.d/default.conf 的内容如下:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
upstream web{ server myweb:3000; } server { listen 80; listen [::]:80; server_name filterinto.com www.filterinto.com; location ^~ /.well-known /acme-challenge/ { default_type "text/plain" ; root /usr/share/nginx/html ; } location = /.well-known /acme-challenge/ { return 404; } return 301 https: // $server_name$request_uri; } server { listen 443; listen [::]:443; server_name filterinto.com; # enable ssl ssl on; ssl_protocols tlsv1 tlsv1.1 tlsv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "eecdh+ecdsa+aesgcm eecdh+arsa+aesgcm eecdh+ecdsa+sha384 eecdh+ecdsa+sha256 eecdh+arsa+sha384 eecdh+arsa+sha256 eecdh edh+arsa !anull !enull !low !3des !md5 !exp !psk !srp !dss !rc4" ; # config ssl certificate ssl_certificate conf.crt /live/filterinto .com /fullchain .pem; ssl_certificate_key conf.crt /live/filterinto .com /privkey .pem; location ^~ /.well-known /acme-challenge/ { default_type "text/plain" ; root /usr/share/nginx/html ; } location = /.well-known /acme-challenge/ { return 404; } location / { proxy_pass http: //web ; } } server { listen 443; listen [::]:443; server_name www.filterinto.com; # enable ssl ssl on; ssl_protocols tlsv1 tlsv1.1 tlsv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "eecdh+ecdsa+aesgcm eecdh+arsa+aesgcm eecdh+ecdsa+sha384 eecdh+ecdsa+sha256 eecdh+arsa+sha384 eecdh+arsa+sha256 eecdh edh+arsa !anull !enull !low !3des !md5 !exp !psk !srp !dss !rc4" ; # config ssl certificate ssl_certificate conf.crt /live/www .filterinto.com /fullchain .pem; ssl_certificate_key conf.crt /live/www .filterinto.com /privkey .pem; location ^~ /.well-known /acme-challenge/ { default_type "text/plain" ; root /usr/share/nginx/html ; } location = /.well-known /acme-challenge/ { return 404; } location / { proxy_pass http: //web ; } } |
然后删除容器 gateway 并用下面的脚本重新创建:
1
2
3
4
5
6
7
8
9
10
11
12
|
$ docker run -d \ -p 80:80 \ -p 443:443 \ - v $( pwd ) /nginx/conf .d: /etc/nginx/conf .d:ro \ - v $( pwd ) /nginx/conf .crt: /etc/nginx/conf .crt:ro \ - v $( pwd ) /nginx/nginx .conf: /etc/nginx/nginx .conf:ro \ - v $( pwd ) /logs/nginx : /var/log/nginx \ - v $( pwd ) /nginx/html : /usr/share/nginx/html \ --restart=always \ --name=gateway \ --network=webnet \ nginx:1.14 |
现在就只能通过 https 来访问站点了:
自动更新证书
let's encrypt 提供的 ssl/tls 证书期限只有三个月,每过三个月要手动更新一次证书也够呛的,下面我们介绍自动更新证书的方法。
其实我们的配置已经为自动化更新证书提供了最大的便利(其实是使用 docker 带来的便利),在定时任务中添加下面两条记录就可以了:
1
2
|
0 0 1 * * /home/nick/certbot/renew_cert .sh /home/nick >> /home/nick/logs/cert .log 2>> /home/nick/logs/cert .error.log 0 1 1 * * docker exec gateway nginx -s reload |
每月 1 号的 0 点更新证书,一个小时后 reload nginx 的配置。
总结
let's encrypt 是一个非常棒的网站,对于初学者和个人来说,能够帮助我们轻松的实现 https 站点(还是免费的)!在方便的同时,其隐患也是显而易见的:既然谁都可以无门槛的获得 ssl/tls 证书,那么非法网站也可以通过它把自己伪装成看上去合法的站点。 所以千万不要片面的认为 https 站点就是安全的!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.cnblogs.com/sparkdev/p/9163162.html