Istio supports MTLS to authenticate clients. This is configured using a Gateway
resource. There’s great documentation on the configuration steps here. However, what’s not quite clear is how to validate client identity using the subjectAltName
(SAN) presented in the client certificate. In the example from Istio documentation, any client who presents a certificate using a given CA is accepted which is not desired in certain cases e.g. server exposed to multiple clients in the same network but accepts traffic only from specific clients.
Starting from the example here, MTLS client validation is done by adding tls.subjectAltNames
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
... <snip> ...
tls:
mode: MUTUAL
credentialName: httpbin-credential
subjectAltNames:
- client.example.com
The caveat is, Istio validates only the SAN and not the Common Name (CN).
To create a CSR with SANs, do
openssl req -nodes -newkey rsa:2048 -subj "/CN=client.example.com/O=client organization" -out client.example.com.csr -pubkey -new -keyout client.example.com.key -sha256 -config ssl.cnf
where ssl.cnf
is
[ CA_default ]
copy_extensions = copy
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = client.example.com
To sign the client certificate, do
openssl x509 -req -in client.example.com.csr -CA example.com.crt -CAkey example.com.key -CAcreateserial -out client.example.com.crt -days 3650 -sha256 -extfile ssl-ext.cnf
where ssl-ext.cnf
is
basicConstraints = CA:FALSE
subjectAltName = client.example.com
and CA certificate/key are generated from
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
Server certificates installed installed in Gateway.tls.credentialName
are generated from
openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt
The credential contains
$ kubectl describe secrets/httpbin-credential -n istio-system
... <snip> ...
Data
====
ca.crt: 1046 bytes
tls.crt: 1054 bytes
tls.key: 1708 bytes
To test (against the httpbin server in the example), run
curl -vvv -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt --cert client.example.com.crt --key client.example.com.key \
"https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"