在ASM入口网关上配置mTLS服务并限制特定客户端访问

ASM网关支持对外提供mTLS服务。mTLS协议要求客户端提供自身证书,证书中包含了用户的身份信息。您可以在授权策略中配置只有特定的用户才可以成功访问该服务,进而为服务提供更加高级别的保护。本文将介绍如何在ASM入口网关上配置mTLS服务,并且通过授权策略实现对特定用户的访问限制。

前提条件

步骤一:生成mTLS通信证书

说明

在创建证书时,如果需要填写证书信息,请使用默认值。这些默认值已在配置文件中预先设置。

  1. 使用以下内容创建ca.cnf文件,生成根证书。

    展开查看CNF文件内容

    HOME = .`
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ ca ]
    default_ca = CA_default # The default ca section
    [ CA_default ]
    default_days = 1000 # how long to certify for
    default_crl_days = 30 # how long before next CRL
    default_md = sha256 # use public key default MD
    preserve = no # keep passed DN ordering
    x509_extensions = ca_extensions # The extensions to add to the cert
    email_in_dn = no # Don't concat the email in the DN
    copy_extensions = copy # Required to copy SANs from CSR to cert
    
    #====Following 7 lines are for signing other certs, not for making the CA cert.====
    base_dir = .
    certificate = $base_dir/cacert.pem # The CA certifcate
    private_key = $base_dir/cakey.pem # The CA private key
    new_certs_dir = $base_dir # Location for new certs after signing
    database = $base_dir/index.txt # Database index file
    serial = $base_dir/serial.txt # The current serial number
    unique_subject = no # Set to 'no' to allow creation of several certificates with same subject.
    
    ####################################################################
    [ req ]
    default_bits = 4096
    default_keyfile = cakey.pem
    distinguished_name = ca_distinguished_name
    x509_extensions = ca_extensions
    string_mask = utf8only
    ####################################################################
    [ ca_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test-asm
    organizationalUnitName = Organizational Unit (eg, division)
    organizationalUnitName_default = R&D
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = Test CA
    emailAddress = Email Address
    emailAddress_default = test@example.com
    ####################################################################
    [ ca_extensions ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always, issuer
    basicConstraints = critical, CA:true
    keyUsage = keyCertSign, cRLSign
    
    
    #====All lines below are for signing other certs, not for making the CA cert.======
    
    ####################################################################
    [ signing_policy ]
    countryName = optional
    stateOrProvinceName = optional
    localityName = optional
    organizationName = optional
    organizationalUnitName = optional
    commonName = supplied
    emailAddress = optional
    ####################################################################
    [ signing_req ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
  2. 执行以下命令,生成根证书。

    openssl req -x509 -config ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM

    此命令会输出cacert.pemcakey.pem文件。

  3. 使用以下内容创建server.cnf文件,用于生成服务器证书。

    HOME = .
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ req ]
    default_bits = 2048
    default_keyfile = serverkey.pem
    distinguished_name = server_distinguished_name
    req_extensions = server_req_extensions
    string_mask = utf8only
    ####################################################################
    [ server_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = test.com
    emailAddress = Email Address
    emailAddress_default = test@example.com
    ####################################################################
    [ server_req_extensions ]
    subjectKeyIdentifier = hash
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"
    ####################################################################
    [ alternate_names ]
    DNS.1 = test.com
  4. 依次执行以下命令,生成服务器证书。

    openssl req -config server.cnf -newkey rsa:2048 -sha256 -nodes -out server.csr -outform PEM
    touch index.txt
    echo '01' > serial.txt
    openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles server.csr

    此命令会输出servercert.pemserverkey.pem

  5. 使用以下内容创建client.cnf文件,用于创建客户端证书。

    HOME = .
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ req ]
    default_bits = 2048
    default_keyfile = client.key.pem
    distinguished_name = server_distinguished_name
    req_extensions = server_req_extensions
    string_mask = utf8only
    ####################################################################
    [ server_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test.client
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = test.client
    emailAddress = Email Address
    emailAddress_default = test.client@example.com
    ####################################################################
    [ server_req_extensions ]
    subjectKeyIdentifier = hash
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"
    ####################################################################
    [ alternate_names ]
    URI.1 = spiffe://test.client

    客户端证书的CommonNametest.client,SAN信息中需要新增字段URI.1 = spiffe://test.client,这里需要额外加上spiffe://前缀,因为ASM的授权策略中principals字段会匹配spiffe://之后的部分。

  6. 依次执行以下命令,生成客户端证书。

    openssl req -config client.cnf -newkey rsa:2048 -sha256 -nodes -out clientcert.csr -outform PEM
    openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out clientcert.pem -infiles clientcert.csr

    此命令会输出clientcert.pem以及client.key.pem

  7. 使用ASM证书管理导入mTLS证书,请确保此处导入的证书名称为test.com。具体操作,请参见使用ASM证书管理

    您也可以使用kubectl直接创建secret来完成证书导入。使用数据面集群的kubeconfig,执行如下命令。

    kubectl create -n istio-system secret generic test.com \
      --from-file=tls.key=serverkey.pem \
      --from-file=tls.crt=servercert.pem \
      --from-file=ca.crt=cacert.pem

步骤二:在网关443端口上配置mTLS监听

本节将为ASM网关的443端口配置mTLS监听,让外部客户端可以通过mTLS访问httpbin服务。

  1. 使用以下内容,更新网关规则。

    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: httpbin
      namespace: default
    spec:
      selector:
        istio: ingressgateway
      servers:
        - hosts:
            - '*'
          port:
            name: test
            number: 80
            protocol: HTTP
        - hosts:
          - test.com
          port:
            number: 443
            name: https
            protocol: HTTPS
          tls:
            mode: MUTUAL
            credentialName: test.com
  2. 执行以下命令,使用client证书访问httpbin服务。

    curl --header "host:test.com" --resolve "test.com:443:${ASM网关IP}" --cacert cacert.pem --cert clientcert.pem --key client.key.pem  https://test.com/status/200 -I

    预期输出:

    HTTP/2 200
    server: istio-envoy
    date: Sun, 28 Jul 2024 7:30:30 GMT
    content-type: text/html; charset=utf-8
    access-control-allow-origin: *
    access-control-allow-credentials: true
    content-length: 0
    x-envoy-upstream-service-time: 6

步骤三:配置授权策略,限制test.client访问

  1. 使用以下内容,部署授权策略。限制test.client不能访问httpbin应用的/status/418路径。具体操作,请参见为HTTP流量设置授权策略

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: test
      namespace: istio-system
    spec:
      action: DENY
      rules:
        - from:
            - source:
                principals:
                  - test.client
          to:
            - operation:
                paths:
                  - /status/418
      selector:
        matchLabels:
          istio: ingressgateway
  2. 执行以下命令,使用client证书访问/status/200路径。

    curl --header "host:test.com" --resolve "test.com:443:${ASM网关IP}" --cacert cacert.pem --cert clientcert.pem --key client.key.pem  https://test.com/status/200 -I

    预期输出:

    HTTP/2 200
    server: istio-envoy
    date: Sun, 28 Jul 2024 7:33:30 GMT
    content-type: text/html; charset=utf-8
    access-control-allow-origin: *
    access-control-allow-credentials: true
    content-length: 0
    x-envoy-upstream-service-time: 6
  3. 执行以下命令,使用client证书访问/status/418路径。

    curl --header "host:test.com" --resolve "test.com:443:${ASM网关IP}" --cacert cacert.pem --cert clientcert.pem --key client.key.pem  https://test.com/status/418

    预期输出:

    RBAC: access denied%
  4. 执行以下命令,使用server证书访问/status/418路径。

    curl --header "host:test.com" --resolve "test.com:443:${ASM网关IP}" --cacert cacert.pem --cert servercert.pem --key serverkey.pem  https://test.com/status/418

    预期输出:

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