通过ASM网关启用HTTPS安全服务

当您需要对外提供加密的HTTPS访问时,可以通过ASM网关启用HTTPS安全服务。ASM网关支持证书的动态加载,您可以实时动态地配置私钥、服务器证书和根证书,无需重启网关或依赖Secret卷挂载,降低操作复杂性并消除因重启造成的服务中断风险。ASM网关能够监控并管理多个证书和私钥对,为不同的主机提供灵活且安全的通信能力,加强数据传输的安全性。

前提条件

背景信息

Istio支持动态加载证书。TLS(Transport Layer Security)所需的私钥、服务器证书以及根证书,都可以在网关不重启的条件下动态配置。ASM网关会监视所在命名空间中的Secret,通过Gateway CR实现动态加载。ASM网关的HTTPS协议支持有以下优势:

  • 入口网关可以在不需要重启的情况下,动态添加、删除或更新所需要的证书、私钥或者对应的根证书。

  • 不需要Secret卷挂载。创建Kubernetes Secret后,ASM网关会捕获该Secret,并将其包含的证书、私钥或根证书发送到入口网关。

  • ASM网关可以监视多个证书、私钥对,只需要为多个主机创建Secret并更新网关规则。

步骤一:为多个主机准备服务器证书和私钥

使用域名时需要备案才能正常访问。本示例中使用aliyun.com生成证书和私钥,并保存为Secret。

如果您已经拥有针对aliyun.com可用的证书和私钥,需要将密钥命名为aliyun.com.key,证书命名为aliyun.com.crt。如果没有,可以通过openssl,执行以下步骤来生成证书和密钥。

  1. 执行以下命令,创建根证书和私钥。

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=myexample Inc./CN=aliyun.com' -keyout aliyun.root.key -out aliyun.root.crt 
  2. 执行以下命令,为aliyun.com服务器生成证书和私钥。

    openssl req -out aliyun.com.csr -newkey rsa:2048 -nodes -keyout aliyun.com.key -subj "/CN=aliyun.com/O=myexample organization"
    openssl x509 -req -days 365 -CA aliyun.root.crt -CAkey aliyun.root.key -set_serial 0 -in aliyun.com.csr -out aliyun.com.crt
  3. 按照ASM实例版本,创建Secret或证书。

    • ASM实例为1.17以下

      在入口网关Pod所在的集群对应的KubeConfig环境下,执行以下命令,在istio-system命名空间中创建包含证书和私钥的Secret。

      kubectl create -n istio-system secret tls myexample-credential --key=aliyun.com.key --cert=aliyun.com.crt
      重要

      Secret名称不能以istio或prometheus开头,且不能包含token字段。

    • ASM实例为1.17及以上

      1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

      2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 证书管理

      3. 证书管理页面,单击创建,然后在证书信息面板,配置相关信息,单击确定

        配置项

        说明

        名称

        输入证书的名称,本示例为myexample-credential

        公钥证书

        步骤2生成的aliyun.com.crt内容。

        私钥

        步骤2生成的aliyun.com.key内容。

步骤二:为a.aliyun.com定义内部服务

示例中的内部服务是基于Nginx实现的,首先为Nginx服务器创建配置文件。以域名a.aliyun.com的内部服务为例,定义请求根路径直接返回字样Welcome to a.aliyun.com!及状态码200myexample-nginx.conf的具体内容如下。

events {
}
http {
  log_format main '$remote_addr - $remote_user [$time_local]  $status '
  '"$request" $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;
  error_log  /var/log/nginx/error.log;
  server {
    listen 80;
    location /hello {
        return 200 'Welcome to a.aliyun.com!';
        add_header Content-Type text/plain;
    }
  }
}
  1. 在入口网关Pod所在的集群对应的KubeConfig环境下,执行以下命令,创建Kubernetes ConfigMap(存储Nginx服务器的配置)。

    kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
  2. 为default命名空间启用Sidecar网格代理自动注入。具体操作,请参见启用自动注入

  3. 创建并拷贝以下内容到myexampleapp.yaml文件,执行kubectl apply -f myexampleapp.yaml命令,创建域名a.aliyun.com的内部服务。

    展开查看YAML

    apiVersion: v1
    kind: Service
    metadata:
      name: myexampleapp
      labels:
        app: myexampleapp
    spec:
      ports:
      - port: 80
        protocol: TCP
      selector:
        app: myexampleapp
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myexampleapp
    spec:
      selector:
        matchLabels:
          app: myexampleapp
      replicas: 1
      template:
        metadata:
          labels:
            app: myexampleapp
        spec:
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
            volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx
              readOnly: true
          volumes:
          - name: nginx-config
            configMap:
              name: myexample-nginx-configmap

步骤三:为b.aliyun.com定义内部服务

本示例中的内部服务基于httpbin实现。

  1. 使用以下内容,创建httpbin.example.yaml文件。

    展开查看YAML内容

    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin
      labels:
        app: httpbin
    spec:
      ports:
      - name: http
        port: 8000
      selector:
        app: httpbin
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: httpbin
          version: v1
      template:
        metadata:
          labels:
            app: httpbin
            version: v1
        spec:
          containers:
          - image: docker.io/citizenstig/httpbin
            imagePullPolicy: IfNotPresent
            name: httpbin
            ports:
            - containerPort: 80
  2. 在入口网关Pod所在的集群对应的KubeConfig环境下,执行以下命令,创建域名为b.aliyun.com的内部服务。

    kubectl apply -f httpbin.example.yaml

步骤四:创建网关规则

  1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

  2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 网关规则,然后单击使用YAML创建

  3. 创建页面,选择目标命名空间(本文以default为例)和任意场景模版,配置如下YAML,然后单击创建

    展开查看YAML内容

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: mysdsgateway
    spec:
      selector:
        istio: ingressgateway # use istio default ingress gateway
      servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        tls:
          mode: SIMPLE
          credentialName: myexample-credential # must be the same as secret
        hosts:
        - '*.aliyun.com'
                                    

    创建完成后,您可以在网关规则页面,看到新建的网关。

步骤五:创建虚拟服务

  1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

  2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择流量管理中心 > 虚拟服务,然后单击使用YAML创建

  3. 创建页面,选择目标命名空间和任意场景模版,配置以下YAML,然后单击创建

    展开查看YAML内容

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: mysdsgateway-myexamplevs
    spec:
      hosts:
      - "a.aliyun.com"
      gateways:
      - mysdsgateway
      http:
      - match:
        - uri:
            exact: /hello
        route:
        - destination:
            host: myexampleapp.default.svc.cluster.local
            port:
              number: 80      

    重复此步骤,为b.aliyun.com定义相应的虚拟服务。

    展开查看YAML内容

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: mysdsgateway-httpbinvs
    spec:
      hosts:
      - "b.aliyun.com"
      gateways:
      - mysdsgateway
      http:
      - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
        route:
        - destination:
            port:
              number: 8000
            host: httpbin.default.svc.cluster.local        

    创建完成后,在虚拟服务页面,可以看到新建的虚拟服务。

执行结果

获取入口网关服务的地址

通过命令行访问入口网关服务

  • 执行以下命令,通过HTTPS协议访问a.aliyun.com服务。

    curl -k -H Host:a.aliyun.com --resolve a.aliyun.com:443:{替换成真实的入口网关IP地址}  https://a.aliyun.com/hello

    预期输出:

    Welcome to aliyun.com!
  • 执行以下命令,通过HTTPS协议访问b.aliyun.com服务。

    curl -k -H Host:b.aliyun.com --resolve b.aliyun.com:443:{替换成真实的入口网关IP地址}  https://b.aliyun.com/status/418  

    预期输出:

        -=[ teapot ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`

相关操作

更新网关证书

如果您要更新网关挂载的证书,需要在数据面新建一个Secret,然后修改Gateway资源的credentialName字段值为Secret的名称,网关将自动挂载新的Secret。本文以为example.com服务器创建名为new-istio-ingressgateway-certs的Secret为例。

  1. 创建颁发者为myexample的证书。

    1. OpenSSL工具中执行以下命令,创建根证书和私钥。

      openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=myexample Inc./CN=example.com' -keyout example.root.key -out example.root.crt
    2. 执行以下命令,为example.com服务器生成证书和私钥。

      openssl req -out example.com.csr -newkey rsa:2048 -nodes -keyout example.com.key -subj "/CN=example.com/O=myexample organization"
      openssl x509 -req -days 365 -CA example.root.crt -CAkey example.root.key -set_serial 0 -in example.com.csr -out example.com.crt
    3. 获取集群KubeConfig并通过kubectl工具连接集群

    4. 执行以下命令,创建名为new-istio-ingressgateway-certs的Secret。

      kubectl create -n istio-system secret tls new-istio-ingressgateway-certs --key example.com.key --cert example.com.crt
  2. 在集群中执行以下命令,删除旧的证书Secret。

    kubectl delete secret  istio-ingressgateway-certs -n istio-system
  3. 更新网关规则。

    1. 登录ASM控制台,在左侧导航栏,选择服务网格 > 网格管理

    2. 网格管理页面,单击目标实例名称,然后在左侧导航栏,选择ASM网关 > 网关规则

    3. 网关规则页面,单击目标规则右侧操作列下的查看YAML

    4. 编辑对话框,修改credentialName参数值为新的证书Secret名称new-istio-ingressgateway-certs,然后单击确定

  4. 验证更新网关证书是否成功。

    1. 执行以下命令,在集群中查看当前证书信息。

      kubectl exec istio-ingressgateway-xxxx -n istio-system -- curl localhost:15000/config_dump  > ingressgateway_dump.yaml
    2. 执行以下命令,打印并搜索new-istio-ingressgateway-certs证书。

      cat ingressgateway_dump.yaml | grep new-istio-ingressgateway-certs -A 3

      预期输出:

      证书

      复制以上inline_bytes参数后的内容,获取Base64编码后的证书。

    3. 在本地命令行工具中执行以下命令,将证书进行Base64解码。

      echo <Base64编码后的证书内容> | base64 --decode
    4. 将Base64解码内容保存为test.com.crt文件。

    5. 在OpenSSL工具中执行以下命令,查看证书的组织。

      openssl x509 -in test.com.crt -text -noout

      预期输出:

      组织

      可以看到组织成功更换为myexample,说明网关证书更新成功。