个人网站实现方案更新:Traefik 反向代理、与 NAS 共用 Wildcard 证书等

最近在我的 VPS 服务器升级完操作系统之后,想体验一下使用 Docker Swarm Mode + Traefik 部署应用,这样还可以把自己电脑、NAS 等设备加入 Swarm 集群,方便本地的 Docker 服务通过 overlay 网络在 VPS 服务器上供外部访问。

但由于 Swarm 模式下使用 IPv6 有一些不方便的地方,所以暂时还是考虑使用普通的非 Swarm 模式,通过 docker-compose 部署应用,同时进行了如下几点修改:

  1. 停用 nginx-proxy, 反向代理改用 Traefik
  2. 子域名使用通配符证书
  3. 通过脚本,使自己的 NAS、无线路由器,能够自动从 VPS 服务器中获取 SSL 证书,从而使自己的服务器和设备能够共用同一套证书

之前的方案

之前,我主要使用 HyperApp 来安装、管理我的 VPS 服务器上的应用,包括我的 WordPress 博客。

HyperApp 是 @waylybaye 开发的一款 iOS App, 能够方便地在 Linux 服务器上自动化部署应用,同时提供了服务器资源监控、SSH 终端等功能。如果你在使用 Linux 服务器、NAS、Raspberry Pi、OpenWrt 无线路由器等设备,强烈推荐使用 HyperApp 进行远程管理。

HyperApp 在部署应用的时候,默认使用 nginx-proxy 提供反向代理,使用 docker-letsencrypt-nginx-proxy-companion 自动从 Let’s Encrypt 获取 SSL 证书,并在此基础上安装各种基于 Docker 的应用。通过 nginx-proxy,即可在外部以不同的域名进行访问。

由于 HyperApp 主要考虑到便利性,能够让没有技术背景的新用户也可以快速部署应用,所以在灵活程度上有一定的缺失。例如 HyperApp 中的 WordPress 直接使用了 Docker 官方的镜像,其中 PHP 的 display-errors 选项默认为打开状态,无法关闭。而根据 PHP 文档,该选项在生产环境中尽量应该关闭。

所以这次服务器升级操作系统后,打算仅使用 HyperApp 做为服务器监控工具,而安装和部署应用,则直接通过 docker-swarm 进行。

通过 Traefik 实现 HTTP 反向代理

Traefik 是一个用 go 语言编写的反向代理与负载均衡程序,对我来说主要有以下优点:

  1. 支持 Docker Swarm、Kubernetes 等,方便后续进一步折腾
  2. 有一个漂亮的 web 控制面板(虽然将来可能会被废弃),能够显示统计信息等
  3. 对于 Let’s Encrypt 的支持较为全面,通过几行配置,即可生成通配符证书
  4. 支持 HTTP Basic 认证,对于服务器上非公开的服务,可以设置密码,防止被其他人访问
  5. 支持将一个容器中的多个端口反向代理到不同的域名,例如在使用 frp 的时候,可能会在多个端口提供多个 HTTP 服务,这一功能就显得比较有用了

所以最终,我选择使用 Traefik 代替 nginx-proxy 做为 Web 服务的反向代理。 (虽然 Nginx 本身功能比较丰富,但 nginx-proxy 将 Nginx 经过了封装,功能就没那么多了。)

IPv6 支持

之前的一篇文章中,我通过 robbertkl/docker-ipv6nat 实现了 Docker 环境的 IPv6 支持。在改用 Traefik 和 docker-compose 之后,依旧可以使用类似的方法。

首先创建一个支持 IPv6 的 Docker 网络:

docker network create --ipv6 --subnet=fd00:dead:beef::/48 traefik-bridge

然后在 docker-compose.yml 中,将该网络指定为 default 网络,启动 traefik 和 ipv6nat 服务即可:

version: "3.2"

services:

  # traefik 反向代理
  traefik:
    restart: always
    image: traefik
    command:
      # traefik 命令,此处省略
    environment:
      # traefik 环境变量,此处省略
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./acme:/etc/traefik/acme
    ports:
      - target: 80
        published: 80
      - target: 443
        published: 443

  # IPv6 NAT
  ipv6nat:
    restart: always
    image: robbertkl/ipv6nat
    privileged: true
    network_mode: "host"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /lib/modules:/lib/modules:ro
    labels:
      - "traefik.enable=false"

networks:
  # 使用刚刚创建的支持 IPv6 的 bridge 网络
  default:
    external:
      name: traefik-bridge

由于自己的服务器上还需要运行其他服务,暂时不方便公开完整的 docker-compose.yml,详细的配置,请参考 Traefik 官方文档。

参考链接:https://github.com/containous/traefik/issues/977

在外部安全地访问 Traefik 控制面板

Traefik 提供了一个 Web 控制面板,能够显示工作状态等信息。默认情况下,这个控制面板能够在 8080 端口以 HTTP 协议访问。

在 VPS 上,为了能够使自己在外部安全地访问 Traefik 控制面板,我为其增加了 HTTP Basic 认证,并限制只能通过 HTTPS 协议访问。

实现的的思路比较简单:通过 Traefik,反向代理其自身的 HTTP 控制面板。在 docker-compose.yml 的 Traefik 服务中添加相关的 labels 即可:

services:
  traefik:
    restart: always
    image: traefik
    # 省略 command、ports、environment、volumes
    expose:
      # Web 控制面板的端口
      - 8080
    labels:
      # 启用 traefik
      traefik.enable: true
      # 指定控制面板的子域名
      traefik.frontend.rule: "Host:traefik.example.com"
      # 指定 HTTP Basic 认证的用户名和密码,可使用 htpasswd 命令生成
      traefik.frontend.auth.basic: "user:password"
      # 指定 Web 控制面板的端口
      traefik.port: 8080

参考链接:Secure Traefik dashboard with https and password in docker

与 NAS 和路由器共用通配符证书

我的 VPS 上除了个人博客,还运行了其他多个服务,包括 OpenGrok,和一些仅供自己访问的私有服务。这些服务分别反向代理到不同的子域名。之前,每个子域名有一个单独的 SSL 证书。而有了 Traefik 之后,就可以方便地使用 Let’s Encrypt 通配符证书了。具体方法请参考官网文档,本文不再介绍。

对于我的 NAS 和无线路由器,也使用了 DDNS 使其能够通过子域名在外部访问。而且对于我的 RT1900ac 无线路由器,部分服务也需要通配符证书才能正常启动。所以,在这次更新个人网站架构后,同时打算通过脚本,在路由器和 NAS 上,自动从 VPS 服务器获取证书并更新。

将证书由 acme.json 转换为通用格式

对于 Traefik,在使用 Let’s Encrypt 的情况下,SSL 证书保存在 acme.json 文件中。如果需要和其他程序共用 Traefik 生成的证书,就需要通过程序从该文件中提取出证书,并保存为通用格式。

SvenDowideit/traefik-certdumper 镜像提供了这一功能,能够在 acme.json 文件变化时,将其保存为 .key, .crt.pem 格式。使用方法也比较简单,只需要在 docker-compose.yml 中增加如下服务:

  certdumper:
    restart: always
    image: svendowideit/traefik-certdumper:latest
    volumes:
      # ./acme 同时映射到 Traefik 容器,用于保存 acme.json 和生成的证书
      - ./acme:/traefik

NAS 和无线路由器通过脚本自动获取证书

我的 NAS 使用的是 DiskStation Manager (DSM) 操作系统,无线路由器使用的是 Synology Router Manager (SRM) 操作系统。两者均支持通过 HTTPS 访问 Web 管理页面。

这两个操作系统默认通过 Web 界面上传 SSL 证书。如果需要通过脚本自动更新证书,需要知道证书保存的位置。这一步可以参考 acme.sh 这个项目的文档中,文档中已经告诉了我们这两个操作系统中,证书的保存路径:

可知在 DSM 和 SRM 中,证书分别存放在如下位置:

# SRM
/usr/syno/etc/ssl/

# DSM
/usr/syno/etc/certificate/system/default/

在 DSM 中添加计划任务,通过脚本使用 scp 或 rsync 命令定期从 VPS 服务器上获取证书,替换 DSM 中原有的证书。然后通过 ssh 和 scp 命令将 DSM 中的证书传输到 SRM,替换 SRM 中的证书文件即可。

另外,参考 acme.sh 的文档,替换证书后,还需要在脚本中重启 Web 服务器进程:

# SRM
/usr/syno/sbin/synoservicecfg --restart httpd-sys

# DSM
/usr/syno/sbin/synoservicectl --reload nginx

这部分目前还没完全完成。后续完成后将在本文更新自己所使用的脚本。

Update (2019-11-10): 最后我还是直接在 NAS 上运行 acme.sh,为 NAS 单独申请证书的。其实 Let’s Encrypt 的 Rate Limit 非常宽松,没有必要使用前文中在 VPS 上申请,然后同步回 NAS 的方式。

“个人网站实现方案更新:Traefik 反向代理、与 NAS 共用 Wildcard 证书等”的2个回复

  1. DSM中简单替换system/default证书并重启nginx是不够的。
    /usr/syno/etc/certificate/和/usr/local/etc/certificate这两个目录下所有的子目录里的证书都要替换,并且需要分别重启每个涉及到的应用的服务。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

退出移动版