Post

Nginx 反向代理 Apache SSL

笔者的 VPS 上运行了一个 Apache 的 PHP 服务,采用 HTTP 协议。现在打算为这个服务升级为 HTTPS,所以借用了 Nginx 反向代理实现目标。

环境

  • RHEL 7.4
  • Apache 2.4
  • Nginx 1.10.1

前提

  1. 安装 Apache Web Server,若没安装则参考安装指南
  2. 安装 Nginx(开启 SSL 模块)。

注:如果之前为 Apache Web Server 配置过 SSL,则需要通过移除 mod_ssl 的方式关闭 Apache 的 SSL 功能:

1
$ sudo yum -y remove mod_ssl

Apache 更改默认端口

Nginx 替代 Apache 作为最外层,所以需要腾出 Apache 的 80 端口,改为任意的空闲端口,仅供内网使用,如改为 7890。

取得 root 权限,修改配置文件 /etc/httpd/conf/httpd.conf,把 Listen 80 改为 Listen 7890

SELinux 规则开放新端口 7890

1
$ sudo semanage port -a -t http_port_t -p tcp 7890

确认端口 7890 已添加:

1
2
$ sudo semanage port -l | grep -w http_port_t
http_port_t        tcp      7890, 80, 81, 443, 488, 8008, 8009, 8443, 9000

重启 Apache:

1
$ sudo systemctl restart httpd

检查新端口是否生效:

1
2
$ sudo netstat -ntlp |grep httpd
tcp6       0      0 :::7890                 :::*                    LISTEN      18000/httpd

安装 certbot

对于 RHEL 7,需要下载存 EPEL 7 储包,才可提供 certbot 的依赖项:

1
$ sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/

安装 EPEL 7:

1
$ sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm

启用 EPEL:

1
$ sudo yum-config-manager --enable epel*

确认开启 EPEL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo yum repolist all

...

repo id                            repo name                                                                  status
epel/x86_64                        Extra Packages for Enterprise Linux 7 - x86_64                             enabled: 12607
epel-debuginfo/x86_64              Extra Packages for Enterprise Linux 7 - x86_64 - Debug                     enabled:  2791
epel-source/x86_64                 Extra Packages for Enterprise Linux 7 - x86_64 - Source                    enabled:     0
epel-testing/x86_64                Extra Packages for Enterprise Linux 7 - Testing - x86_64                   enabled:   702
epel-testing-debuginfo/x86_64      Extra Packages for Enterprise Linux 7 - Testing - x86_64 - Debug           enabled:   110
epel-testing-source/x86_64         Extra Packages for Enterprise Linux 7 - Testing - x86_64 - Source          enabled:     0

...

安装依赖及开启 channel:

1
2
$ sudo yum -y install yum-utils
$ sudo yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional

安装 certbot:

1
$ sudo yum -y install python2-certbot-nginx

获取 certbot 证书

确保 VPS 对外开放端口 80 及 443,为指定域名获取证书,如本例域名为 api.cotes.in

1
$ sudo certbot certonly --standalone -d api.cotes.in

获取成功的输出信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for api.cotes.in
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/api.cotes.in/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/api.cotes.in/privkey.pem
   Your cert will expire on 2018-09-27. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

其中包含了证书文件的目录:

  • /etc/letsencrypt/live/api.cotes.in/fullchain.pem
  • /etc/letsencrypt/live/api.cotes.in/privkey.pem

另外还有证书的到期时间 2018-09-27

修改 Nginx 配置

在配置文件 nginx.confhttp 模块里添加两个 server 模块:

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
http {
  ...

  # Https for Apache
  server {
    listen   443   ssl;

    ssl on;
    server_name   api.cotes.in;

    ssl_certificate     /etc/letsencrypt/live/api.cotes.in/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.cotes.in/privkey.pem;

    location / {
      proxy_pass         http://127.0.0.1:7890;
      proxy_redirect     off;
      proxy_set_header   Host                    api.cotes.in;
      proxy_set_header   X-Real-IP               $remote_addr;
      proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }

    location ~ /\.ht {
      deny  all;
    }

  }

  ## Apache http port 80
  server {
    listen       80;
    server_name  api.cotes.in;

    rewrite ^(.*) https://$server_name$1 permanent;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;

    location = /50x.html {
      root   html;
    }
  }

  # other settings ...
}

修改配置后执行语法检测:

1
$ nginx -t

通过检测后重新加载配置文件:

1
$ nginx -s reload

certbot 自动更新

更新前必须先确保申请机的 443 端口保持空闲状态,因为 Let's Encrypt 服务器会访问申请机器的 443 端口。上述运行着的 Nginx SSL/TLS 反代服务会占用 443 端口,所以必须暂时关闭 Nginx,再使用 certbot 命令执行更新,完毕后重新开启 Nginx 服务。

将上述原理写成 Shell 脚本,命名为 certbot-updater.sh。添加内容:

1
2
3
4
5
6
7
#!/bin/bash

if [[ $(certbot certificates |grep "INVALID: EXPIRED") ]]; then # Check if the certificaties has expired.
  nginx -s stop && \
  certbot renew --no-self-upgrade && \  # Update certificaties
  nginx
fi

if 中判断证书是否过期的逻辑,是通过 certbot 证书状态输出信息做判断,更加严谨点可以根据具体的过期日期转换为时间,和当前查询时间对比作为判断依据。

现在上述脚本文件放置到心仪的目录下,由 crontab 添加定时调用任务即可。

在系统文件 /etc/crontab 添加一行内容:

1
39      1,13    *       *       *       root       /usr/bin/bash /path/to/certbot-updater.sh

上述定时计划表示,每天 01:39 和 13:39,以 root 身份运行 certbot 的证书更新脚本。

最后,重启 cron 守护进程:

1
$ sudo systemctl restart crond

参考

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.