MENU

个人云服务架构升级实践

March 29, 2020 • Read: 1350 • 应用,安全

最近杂七杂八自己部署了好些应用,分布在几台机器上,分别管理各自的 HTTPS 证书、Nginx 配置非常繁琐,有些机器上装了 VeryNginx 当 WAF 用,有些机器又没装,显得很杂乱,决定改一下架构。

确定需求

第一个需求是可以比较方便地配置后端应用,这个需求比较简单,毕竟是基础功能。

第二个需求是可以方便地接入 HTTPS。

第三个需求是最好能提供细粒度的匹配规则,可以配一点 WAF 拦截规则上去。

选型

最开始看的是一些 API Gateway 类型的产品,比如 KongTYK,但是这些产品基本上只能满足第一个需求,有些产品还只提供 API 来修改配置,不能像 Nginx 那样读取文件,确实是不太适合我用。

后来看到一个安全网关产品,Janusec,支持像 VeryNginx 那样的 WAF 规则配置,它也内置了一些,也能正常支持后端配置的需求,看起来还不错,不过没有解决 HTTPS 的问题。而且,他的管理界面着实有点不好看,不过这是次要问题,无足轻重。

后来思考了一下,感觉能同时满足我这三个需求的产品几乎没有,只能退而求其次,优先满足比较痛点的 HTTPS 和后端配置的问题。这时候,刚好看到 Caddy 发布了 v2 的测试版,最大的变更是将以前的 Caddyfile 配置改成了 JSON 格式的配置。改成 JSON 格式的配置最大的好处就是如果需要自己写脚本生成配置文件,可以非常方便的处理格式问题,不需要考虑 Caddyfile 自己的格式。而且 Caddy 从一开始就是默认支持自动申请 HTTPS 证书的,免去了自己配置 acme 的麻烦。

迁移

做迁移的时候 Caddy v2 版本还处在测试阶段,功能都不稳定,更不可能有什么迁移工具之类的了,只能靠手工,好在应用也不太多,自己改改也还可以。

v2 版本也支持 Caddyfile,但是我其实还是冲着 JSON 配置去的,所以没有管 Caddyfile,直接手写 JSON。

基础部分

{
  "apps": {
    "http": {
      "http_port": 80,
      "https_port": 443,
      "servers": {
        "http": {
          "listen": [
            ":80"
          ],
          "routes": []
        },
        "https": {
          "listen": [
            ":443"
          ],
          "routes": [],
          "automatic_https": {
            "disable": false
          }
        }
      }
    },
    "tls": {
      "certificates": {
        "load_files": [
          {
            "certificate": "/var/lib/caddy/certs/40huo.cn.pem",
            "key": "/var/lib/caddy/certs/40huo.cn.key"
          }
        ]
      }
    }
  }
}

最外面的部分分为 apptls 两部分,分别对应应用部分和 HTTPS 部分,HTTPS 也支持自己导入证书。应用部分其实最关键的就是 routes 了,和 Nginx 的 server 概念类似。

路由部分

[
  {
    "match": [
      {
        "host": [
          "ntm.40huo.cn"
        ]
      }
    ],
    "handle": [
      {
        "handler": "subroute",
        "routes": [
          {
            "handle": [
              {
                "body": "Welcome to NanTianMen!",
                "close": true,
                "handler": "static_response",
                "status_code": 200
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "match": [
      {
        "host": [
          "40huo.cn",
          "www.40huo.cn",
          "blog.40huo.cn"
        ]
      }
    ],
    "handle": [
      {
        "handler": "subroute",
        "routes": [
          {
            "match": [
              {
                "path": [
                  "/*"
                ]
              }
            ],
            "handle": [
              {
                "handler": "reverse_proxy",
                "transport": {
                  "protocol": "http",
                  "read_buffer_size": 4096
                },
                "upstreams": [
                  {
                    "dial": "1.2.3.4:80"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

这些也没什么太多好说的,看看文档就明白了。

顺便还试验了一下 WAF 规则能不能匹配。

[
  {
    "match": [
      {
        "path_regexp": {
          "name": "sensitive files",
          "pattern": "/web-inf/|/\\.git/|/\\.svn/|/\\.idea/|/\\.DS_Store"
        }
      }
    ],
    "handle": [
      {
        "handler": "static_response",
        "status_code": 403,
        "body": "hacker!",
        "close": true
      }
    ]
  }
]

将 WAF 部分的规则放在 routes 的开头,效果还不错,勉强能用,也就支持那么几种比较简单的匹配了,更复杂的功能靠 Caddy 还是解决不了。

配置完成以后,将所有的域名都 CNAME 到 ntm.40huo.cn 网关的域名上,Caddy 自动处理 HTTPS 握手,打到后端应用的都是 HTTP 请求,后端就可以只靠一个 Nginx+PHP/Python/Go 处理业务,理论上讲后端不用 Nginx 也是没问题的,不过有些静态文件还是托管到 Nginx 会好一点,所以统一走了 Caddy-Nginx-应用服务的路径。

总结

基本上解决了开头的几个问题

  • HTTPS 证书由 Caddy 自己管理,自动申请和续期
  • HTTPS 流量降级,后端应用只需要处理 HTTP 即可
  • JSON 格式配置可以非常方便的接入自动化的脚本
  • 简单的匹配规则可以支持一些 WAF 的功能
Archives QR Code
QR Code for this page
Tipping QR Code