为 Envoy 开启 TLS 验证实战


通过 Envoy 反向代理 hugo 静态页面

为 Envoy 开启 TLS 验证实战

通过 Envoy 反向代理 hugo 静态页面

Wed Sep 26, 2018

2000 Words|Read in about 4 Min
Tags: envoy   service mesh   hugo  

我的博客之前是使用 Nginx 来反代的,由于 Nginx 性能优异,目前有很多国内网站采用 Nginx 作为 Web 服务器,而且参考文档比较丰富,无论是对于其部署,配置还是调优都更为有经验。但是还是会碰到几个绕不开的问题:

  • Nginx 的反向代理不支持 http2/grpc (好像今年 3 月份刚支持)
  • 不像 Envoy 几乎所有的网络配置都可以利用 xDS API 来实现动态变更,Nginx 缺乏有效的配置热变更机制(除非深入开发或者不断地 reload)。
  • Nginx 的很多微服务功能都是要买 Nginx Plus 才有的。

Envoy 是一款现代化的,高性能,小体积的边缘及服务代理,浑身散发出一股时尚潮流的气息。作为一名斜杠青年,在经过一定地了解后,我果断入了 Envoy 的坑。

关于如何为 Envoy 开启证书验证可以参考我之间的文章:为 Envoy 启用证书验证。本文将直接进入实战部分,通过 Envoy 来反向代理我的博客静态页面,并且加密客户端和 Envoy 代理之间的所有流量。

1. 方案架构


本方案涉及到两层 Envoy:

  • 首先会有一个前端代理在某个地方单独运行。前端代理的工作是给其他地方提供一个入口。来自外部的传入连接请求到这里,前端代理将会决定他们在内部的转发路径。
  • 其次,博客静态页面由 nginx 提供,同时运行一个 “服务 Envoy”,它与 nginx 容器共享 network nemspace(相当于 Kubernetes 的 Sidecar)。
  • 所有的 Envoy 形成一个网格,然后在他们之间共享路由信息。

注意,通常情况下你也可以只使用前端代理,然后去掉服务 Envoy 这一层。但是,使用完整网格的话,服务 Envoy 可以对应用服务进行健康监控等,让网格知道尝试联系一个挂掉的服务是否是毫无意义的。此外,Envoy 的统计数据收集最适合用在全网格上。

但本文需要开启 TLS 验证,如果前端代理开启了 TLS 验证,那么必须配合服务 Envoy 使用,否则验证将无法通过。

2. 部署服务 Envoy


我的博客是通过 hugo 生成的,其他生成静态页面的软件类似,都可以采用我的方案。由于我的 hugo 根目录是 /home/hugo,首先进入该目录,然后创建容器编排的 docker-compose.yml 文件。

version: '2'
services:

  hugo:
    image: nginx:alpine
    restart: always
    volumes:
      - /home/hugo/public:/usr/share/nginx/html ①
    networks:
      - default 
    expose:
      - "80"
      - "8080"

  service-envoy:
    image: envoyproxy/envoy-alpine:latest
    restart: always
    volumes:
      - ./service-envoy.yaml:/etc/envoy/envoy.yaml ②
    network_mode: "service:hugo" ③

networks:
  default:
    external:
      name: yang ④
  • ① : 将博客的静态页面挂载到 nginx 的 root 目录。
  • ② : 将服务 Envoy 的配置文件挂载到 Envoy 容器中。
  • ③ : 与 hugo 容器共享 network namespace
  • ④ : 这是我自定义的网络,你可以换成你自己的。

接下来需要创建服务 Envoy 的配置文件 service-envoy.yaml

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080 ①
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: service
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: local_service
          http_filters:
          - name: envoy.router
            config: {}
  clusters:
  - name: local_service
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    hosts:
    - socket_address:
        address: 127.0.0.1
        port_value: 80 ②
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8081
  • 8080 : 服务 Envoy 的监听端口。
  • 80 : hugo 静态页面的监听端口。

3. 部署前端代理


docker-compose.yml 文件中添加前端代理部分:

version: '2'
services:

  ...
  front-envoy:
    image: envoyproxy/envoy
    restart: always
    volumes:
      - ./front-envoy.yaml:/etc/envoy/envoy.yaml
      - /etc/letsencrypt:/etc/letsencrypt
    labels:
      EnvironmentName: "proxy"
      ServiceName: "envoy"
      ProxyMode: "tcp"
    networks:
      - default
    expose:
      - "80"
      - "443"
    ports:
      - "80:80"
      - "443:443"

创建前端代理需要的配置文件 front-envoy.yaml

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          codec_type: auto ①
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: backend
              domains: ②
              - "yangcs.net"
              - "www.yangcs.net"
              routes:
              - match:
                  prefix: "/"
                redirect:
                  path_redirect: "/"
                  https_redirect: true
          http_filters:
          - name: envoy.router
            config: {}
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    filter_chains:
    - tls_context:
        common_tls_context:
          alpn_protocols: h2,http/1.1 ③
          tls_certificates: ④
            - certificate_chain:
                filename: "/etc/letsencrypt/live/www.yangcs.net/fullchain.pem"
              private_key:
                filename: "/etc/letsencrypt/live/www.yangcs.net/privkey.pem"
      filters:
      - name: envoy.http_connection_manager
        config:
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: backend
              domains:
              - "yangcs.net"
              - "www.yangcs.net"
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: hugo
          http_filters:
          - name: envoy.router
            config: {}
  clusters:
  - name: hugo
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: hugo
        port_value: 8080
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001
  • ① : 编码/解码方式。参考:HttpConnectionManager.CodecType
  • ② : 允许访问的域名(这里使用公网可以访问的域名)。
  • ③ : TLS 监听器支持 ALPN。HTTP 连接管理器使用这个信息(以及协议接口)来确定客户端使用的是 HTTP/1.1 还是 HTTP/2
  • ④ : 网站使用的证书。可以通过 Let’s Encrypt 申请免费的证书。

其他配置详细说明请参考:为 Envoy 启用证书验证

准备好所有配置以后,我们就可以通过以下命令来启动所有服务了:

$ docker-compose up -d

Creating front-proxy_hugo_1        ... done
Creating front-proxy_front-envoy_1 ... done
Creating front-proxy_service-envoy_1 ... done

接下来就可以通过公网域名访问博客网站啦!没错,你现在浏览的我的博客就是通过 Envoy 反向代理的。

「真诚赞赏,手留余香」

杨传胜

真诚赞赏,手留余香

使用微信扫描二维码完成支付

See Also