阿里云服务网格(Service Mesh,简称ASM)提供一个全托管式的服务网格平台,兼容社区Istio开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。
ASM网关支持对外提供mTLS服务。mTLS要求客户端提供自身证书,该证书中包含了用户的身份信息。您可以在授权策略中配置只有特定的用户才可以成功访问该服务,进而为服务提供更加高级别的保护。
本文将介绍如何在ASM入口网关上配置mTLS服务,并且通过授权策略实现对特定用户的访问限制。
前提条件
- 已创建ASM实例。具体操作,请参见创建ASM实例。
- 已创建ACK集群。具体操作,请参见创建Kubernetes托管版集群。
- 添加集群到ASM实例。具体操作,请参见添加集群到ASM实例。
- 已经开启sidecar自动注入。具体操作,请参见多种方式灵活开启自动注入。
步骤一:在集群中部署入口网关和httpbin应用
参考https://help.aliyun.com/zh/asm/getting-started/deploy-the-httpbin-application?spm=a2c4g.11186623.0.i3在集群二中部署httpbin应用,并且通过80端口暴露在ASM网关上。
步骤二:生成mTLS通信证书
以下创建证书过程中,如果遇到需要填写证书信息的地方,直接回车,使用默认值即可。因为默认值已经在配置文件中手动指定过了。
- 将以下内容保存在
ca.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
- 执行以下命令,生成根证书:
openssl req -x509 -config ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM
执行完成后,可以看到输出了cacert.pem
和cakey.pem
文件。
- 将以下内容保存在
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
从配置文件中可以看出,此处生成的证书将会用于test.com
这个域名。
- 执行以下命令,生成服务器证书:
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.pem
和serverkey.pem
。
- 将以下内容保存在
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
客户端证书的CommonName为test.client,SAN信息中需要新增字段URI.1 = spiffe://test.client
,这里需要额外加上spiffe://
前缀,因为ASM的授权策略中principals字段会匹配spiffe://
之后的部分。
- 执行以下命令,生成客户端证书:
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
。
- 部署证书:
使用ASM证书管理导入mTLS证书,请确保此处导入的证书名称为test.com
,如何使用ASM证书管理_服务网格(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服务。请确保ASM网关开启了443端口。
使用ASM kubeconfig,修改网关规则,修改后的内容如下:
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
- 验证访问
执行以下命令,使用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访问
- 部署以下授权策略,限制test.client不能访问httpbin应用的
/status/418
路径。
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
- 验证访问结果
- 使用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
- 使用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%
- 使用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 ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`