Mutual TLS (mTLS) on the Service Mesh (ASM) ingress gateway requires every client to present a valid certificate before establishing a connection. Each client certificate carries identity information that you can match in an authorization policy to deny specific clients access to certain paths -- giving you fine-grained, identity-based access control at the gateway level.
This guide walks through three tasks:
Generate root CA, server, and client certificates for mTLS.
Configure an mTLS listener on port 443 of the ASM ingress gateway.
Deploy an authorization policy that denies a specific client identity access to a path.
Prerequisites
Before you begin, make sure that you have:
Automatic sidecar injection enabled. For details, see Configure sidecar proxy injection policies
The ingress gateway and HTTPBin application deployed, with port 443 open on the ingress gateway. For details, see Deploy the HTTPBin application
Step 1: Generate mTLS certificates
This step produces three certificate pairs -- root CA, server, and client -- all signed by the same root CA.
When prompted for certificate fields during generation, accept the default values. The configuration files below pre-set all required fields.
Create a file named
ca.cnfwith the following content.Generate the root certificate.
openssl req -x509 -config ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEMThis produces
cacert.pem(the CA certificate) andcakey.pem(the CA private key).Create a file named
server.cnfwith the following content.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.comGenerate and sign the server certificate.
openssl req -config server.cnf -newkey rsa:2048 -sha256 -nodes -out server.csr -outform PEMtouch index.txt echo '01' > serial.txtopenssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles server.csrThis produces
servercert.pem(the server certificate) andserverkey.pem(the server private key).Create a file named
client.cnfwith the following content.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.clientThe client certificate's
CommonNameistest.client. The Subject Alternative Name (SAN) includesURI.1 = spiffe://test.client. Thespiffe://prefix is required because theprincipalsfield in an ASM authorization policy matches the portion afterspiffe://.Generate and sign the client certificate.
openssl req -config client.cnf -newkey rsa:2048 -sha256 -nodes -out clientcert.csr -outform PEMopenssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out clientcert.pem -infiles clientcert.csrThis produces
clientcert.pem(the client certificate) andclient.key.pem(the client private key).Import the mTLS certificate into ASM. Set the certificate name to
test.com. For details, see Use the certificate management feature of ASM.Alternatively, create a Kubernetes Secret directly with kubectl. Run this command with the kubeconfig of your data plane cluster:
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
The following table summarizes the certificate files generated in this step.
| Certificate | File | Key size |
|---|---|---|
| Root CA certificate | cacert.pem | RSA 4096-bit |
| Root CA private key | cakey.pem | RSA 4096-bit |
| Server certificate | servercert.pem | RSA 2048-bit |
| Server private key | serverkey.pem | RSA 2048-bit |
| Client certificate | clientcert.pem | RSA 2048-bit |
| Client private key | client.key.pem | RSA 2048-bit |
Step 2: Configure an mTLS listener on port 443
Add an mTLS listener on port 443 of the ASM ingress gateway so that external clients can access the HTTPBin service over mutual TLS.
Update the Gateway resource with the following configuration.
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.comKey fields:
Field Value Description tls.modeMUTUALRequires clients to present a valid certificate signed by the CA in the Secret credentialNametest.comReferences the Secret created in Step 1, which contains the server certificate, private key, and CA certificate Verify the mTLS connection by accessing the HTTPBin service with the client certificate.
curl --header "host:test.com" \ --resolve "test.com:443:${ASM gateway IP}" \ --cacert cacert.pem \ --cert clientcert.pem \ --key client.key.pem \ https://test.com/status/200 -IExpected output:
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: 6An
HTTP/2 200response confirms that the mTLS listener is working and the client certificate is accepted.
Step 3: Restrict client access with an authorization policy
Deploy an AuthorizationPolicy that denies the test.client identity access to a specific path on the HTTPBin application.
Apply the following AuthorizationPolicy. This policy denies requests from
test.clientto the/status/418path on the ingress gateway. For more information, see Configure authorization policies for HTTP requests.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: ingressgatewayKey fields:
Field Value Description source.principalstest.clientMatches the identity extracted from the client certificate's SAN ( spiffe://test.client). ASM strips thespiffe://prefix before matching.operation.paths/status/418The path to deny actionDENYRejects matching requests. All other requests are still allowed.
Verify the authorization policy
Access
/status/200with the client certificate. This path is not restricted, so the request succeeds.curl --header "host:test.com" \ --resolve "test.com:443:${ASM gateway IP}" \ --cacert cacert.pem \ --cert clientcert.pem \ --key client.key.pem \ https://test.com/status/200 -IExpected output:
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: 6Access
/status/418with the client certificate. The authorization policy blocks this request.curl --header "host:test.com" \ --resolve "test.com:443:${ASM gateway IP}" \ --cacert cacert.pem \ --cert clientcert.pem \ --key client.key.pem \ https://test.com/status/418Expected output:
RBAC: access denied%The
RBAC: access deniedresponse confirms that thetest.clientidentity is blocked from accessing/status/418.Access
/status/418with the server certificate to confirm that onlytest.clientis denied.curl --header "host:test.com" \ --resolve "test.com:443:${ASM gateway IP}" \ --cacert cacert.pem \ --cert servercert.pem \ --key serverkey.pem \ https://test.com/status/418Expected output:
-=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`A successful response confirms that the DENY policy applies only to the
test.clientidentity, not to other certificate holders.