Istio service mesh ingress with HTTP over TLS
This article shows the following ways to reach HTTP services inside Istio service mesh in Kubernetes over TLS:
-
Client in the mesh → Server in the mesh (
Client 1
andServer 1
in the diagram below) -
Client outside the mesh → Server in the mesh, both in the same Kubernetes cluster (
Client 2
,Client 3
andServer 1
in the diagram below) -
Client outside the Kubernetes cluster → Ingress gateway → Server in the mesh (
Client 4
,Client 5
,Client 6
,Client 7
andServer 2
in the diagram below)
1. Acronyms
Acronym | Meaning |
---|---|
AES |
|
ALPN |
|
CA |
|
CRD |
Kubernetes CustomResourceDefinitions |
DNS |
|
HTTP |
|
HTTPS |
|
IP |
|
mTLS |
|
PEM |
|
RSA |
|
TCP |
|
TLS |
|
WSL |
2. Prerequisites
-
A Kubernetes cluster.
-
Helm CLI. See Installing Helm.
-
OpenSSL CLI.
I was using the following at the time of writing this article:
|
3. Install Istio
I used Istio 1.17.1 for this article. |
Create a namespace for Istio control plane.
kubectl create namespace istio-system
Configure Istio Helm repository.
helm repo add \
istio https://istio-release.storage.googleapis.com/charts
helm repo update
Install Istio cluster-wide CRDs that must be installed prior to the deployment of Istio control plane.
helm install \
istio-base \
istio/base --version 1.17.1 \
--namespace istio-system
Verify installation.
$ helm ls --namespace istio-system NAME NAMESPACE REVISION STATUS CHART APP VERSION istio-base istio-system 1 deployed base-1.17.1 1.17.1
Deploy istiod
service.
helm install \
istiod \
istio/istiod --version 1.17.1 \
--namespace istio-system \
--wait \
--values <(echo '
pilot:
env:
ENABLE_TLS_ON_SIDECAR_INGRESS: "true" (1)
telemetry: (2)
enabled: false
v2:
enabled: false
prometheus:
enabled: false
')
1 | Enable the experimental feature of Ingress Sidecar TLS Termination. |
2 | Disable telemetry to save resources and complexity. |
Verify installation.
$ kubectl get deployments \ --namespace istio-system \ --output wide NAME READY UP-TO-DATE AVAILABLE CONTAINERS IMAGES SELECTOR istiod 1/1 1 1 discovery docker.io/istio/pilot:1.17.1 istio=pilot
Install Istio ingress gateway.
kubectl create namespace istio-ingress
helm install \
istio-ingress \
istio/gateway --version 1.17.1 \
--namespace istio-ingress \
--wait
Verify installation.
$ kubectl get deployments \ --namespace istio-ingress \ --output wide NAME READY UP-TO-DATE AVAILABLE CONTAINERS IMAGES SELECTOR istio-ingress 1/1 1 1 istio-proxy auto app=istio-ingress,istio=ingress
4. Create (user-provided) TLS keys and certificates
4.1. Client 3
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-3-root-ca-key.pem
openssl req \
-new \
-x509 \
-key client-3-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=client-3-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out client-3-root-ca-crt.pem
Click to see instructions for creating key and certificate for the client.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-3-key.pem
openssl req \
-new \
-key client-3-key.pem \
-keyform PEM \
-subj "/CN=client-3" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out client-3.csr
openssl x509 \
-req \
-in client-3.csr \
-inform PEM \
-days 365 \
-sha256 \
-CA client-3-root-ca-crt.pem \
-CAform PEM \
-CAkey client-3-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out client-3-crt.pem
4.2. Sidecar S1 Port P2
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out sc-s1-p-p2-root-ca-key.pem
openssl req \
-new \
-x509 \
-key sc-s1-p-p2-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=sc-s1-p-p2-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out sc-s1-p-p2-root-ca-crt.pem
Click to see instructions for creating key and certificate for the sidecar port.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out sc-s1-p-p2-key.pem
openssl req \
-new \
-key sc-s1-p-p2-key.pem \
-keyform PEM \
-subj "/CN=sc-s1-p-p2" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out sc-s1-p-p2.csr
cat <<EOF > sc-s1-p-p2.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = end_entity
[ req_distinguished_name ]
[ end_entity ]
subjectAltName = DNS:server-1,DNS:server-1.apps-1,DNS:server-1.apps-1.svc.cluster.local
subjectKeyIdentifier=hash
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
authorityKeyIdentifier = keyid
EOF
openssl x509 \
-req \
-in sc-s1-p-p2.csr \
-inform PEM \
-extfile sc-s1-p-p2.cnf \
-extensions end_entity \
-days 365 \
-sha256 \
-CA sc-s1-p-p2-root-ca-crt.pem \
-CAform PEM \
-CAkey sc-s1-p-p2-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out sc-s1-p-p2-crt.pem
4.3. Sidecar S1 Port P3
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out sc-s1-p-p3-root-ca-key.pem
openssl req \
-new \
-x509 \
-key sc-s1-p-p3-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=sc-s1-p-p3-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out sc-s1-p-p3-root-ca-crt.pem
Click to see instructions for creating key and certificate for the sidecar port.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out sc-s1-p-p3-key.pem
openssl req \
-new \
-key sc-s1-p-p3-key.pem \
-keyform PEM \
-subj "/CN=sc-s1-p-p3" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out sc-s1-p-p3.csr
cat <<EOF > sc-s1-p-p3.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = end_entity
[ req_distinguished_name ]
[ end_entity ]
subjectAltName = DNS:server-1,DNS:server-1.apps-1,DNS:server-1.apps-1.svc.cluster.local
subjectKeyIdentifier=hash
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
authorityKeyIdentifier = keyid
EOF
openssl x509 \
-req \
-in sc-s1-p-p3.csr \
-inform PEM \
-extfile sc-s1-p-p3.cnf \
-extensions end_entity \
-days 365 \
-sha256 \
-CA sc-s1-p-p3-root-ca-crt.pem \
-CAform PEM \
-CAkey sc-s1-p-p3-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out sc-s1-p-p3-crt.pem
4.4. Client 5
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-5-root-ca-key.pem
openssl req \
-new \
-x509 \
-key client-5-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=client-5-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out client-5-root-ca-crt.pem
Click to see instructions for creating key and certificate for the client.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-5-key.pem
openssl req \
-new \
-key client-5-key.pem \
-keyform PEM \
-subj "/CN=client-5" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out client-5.csr
openssl x509 \
-req \
-in client-5.csr \
-inform PEM \
-days 365 \
-sha256 \
-CA client-5-root-ca-crt.pem \
-CAform PEM \
-CAkey client-5-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out client-5-crt.pem
4.5. Client 7
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-7-root-ca-key.pem
openssl req \
-new \
-x509 \
-key client-7-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=client-7-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out client-7-root-ca-crt.pem
Click to see instructions for creating key and certificate for the client.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out client-7-key.pem
openssl req \
-new \
-key client-7-key.pem \
-keyform PEM \
-subj "/CN=client-7" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out client-7.csr
openssl x509 \
-req \
-in client-7.csr \
-inform PEM \
-days 365 \
-sha256 \
-CA client-7-root-ca-crt.pem \
-CAform PEM \
-CAkey client-7-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out client-7-crt.pem
4.6. Server 2
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out srv-2-p-p1-p2-root-ca-key.pem
openssl req \
-new \
-x509 \
-key srv-2-p-p1-p2-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=srv-2-p-p1-p2-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out srv-2-p-p1-p2-root-ca-crt.pem
Click to see instructions for creating key and certificate for the server.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out srv-2-p-p1-p2-key.pem
openssl req \
-new \
-key srv-2-p-p1-p2-key.pem \
-keyform PEM \
-subj "/CN=srv-2-p-p1-p2" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out srv-2-p-p1-p2.csr
cat <<EOF > srv-2-p-p1-p2.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = end_entity
[ req_distinguished_name ]
[ end_entity ]
subjectAltName = DNS:server-2,DNS:server-2.apps-1,DNS:server-2.apps-1.svc.cluster.local,DNS:server-2.example.com,DNS:server-2-passthrough-tls.example.com,DNS:server-2-passthrough-mtls.example.com
subjectKeyIdentifier=hash
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
authorityKeyIdentifier = keyid
EOF
openssl x509 \
-req \
-in srv-2-p-p1-p2.csr \
-inform PEM \
-extfile srv-2-p-p1-p2.cnf \
-extensions end_entity \
-days 365 \
-sha256 \
-CA srv-2-p-p1-p2-root-ca-crt.pem \
-CAform PEM \
-CAkey srv-2-p-p1-p2-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out srv-2-p-p1-p2-crt.pem
4.7. Ingress Gateway IG1
Click to see instructions for creating key and certificate for root CA.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out ig-1-p-p1-p2-root-ca-key.pem
openssl req \
-new \
-x509 \
-key ig-1-p-p1-p2-root-ca-key.pem \
-keyform PEM \
-sha256 \
-subj "/CN=ig-1-p-p1-p2-root-ca" \
-days 7300 \
-addext "keyUsage = keyCertSign,cRLSign" \
-utf8 \
-batch \
-outform PEM \
-out ig-1-p-p1-p2-root-ca-crt.pem
Click to see instructions for creating key and certificate for the ingress gateway.
openssl genpkey \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-outform PEM \
-out ig-1-p-p1-p2-key.pem
openssl req \
-new \
-key ig-1-p-p1-p2-key.pem \
-keyform PEM \
-subj "/CN=ig-1-p-p1-p2" \
-sha256 \
-utf8 \
-batch \
-outform PEM \
-out ig-1-p-p1-p2.csr
cat <<EOF > ig-1-p-p1-p2.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = end_entity
[ req_distinguished_name ]
[ end_entity ]
subjectAltName = DNS:server-2.example.com,DNS:server-2-tls.example.com,DNS:server-2-mtls.example.com
subjectKeyIdentifier=hash
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
authorityKeyIdentifier = keyid
EOF
openssl x509 \
-req \
-in ig-1-p-p1-p2.csr \
-inform PEM \
-extfile ig-1-p-p1-p2.cnf \
-extensions end_entity \
-days 365 \
-sha256 \
-CA ig-1-p-p1-p2-root-ca-crt.pem \
-CAform PEM \
-CAkey ig-1-p-p1-p2-root-ca-key.pem \
-CAkeyform PEM \
-CAcreateserial \
-outform PEM \
-out ig-1-p-p1-p2-crt.pem
5. Create and configure application namespace
kubectl create ns "apps-1"
Enforce mTLS traffic for all mesh workloads in the namespace.
kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: apps-1
spec:
mtls:
mode: STRICT
EOF
6. Deploy application workloads outside the mesh
6.1. Deploy Client 2
kubectl -n apps-1 \
create secret generic \
client-2-certs \
--from-file=ca.crt=./sc-s1-p-p2-root-ca-crt.pem
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: client-2
namespace: apps-1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-2
namespace: apps-1
spec:
replicas: 1
selector:
matchLabels:
app: client-2
template:
metadata:
labels:
app: client-2
spec:
terminationGracePeriodSeconds: 0
serviceAccountName: client-2
volumes:
- name: certs
secret:
secretName: client-2-certs
optional: false
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep", "infinity"]
imagePullPolicy: IfNotPresent
volumeMounts:
- name: certs
mountPath: /etc/tls
EOF
6.2. Deploy Client 3
kubectl -n apps-1 \
create secret generic \
client-3-certs \
--from-file=tls.key=./client-3-key.pem \
--from-file=tls.crt=./client-3-crt.pem \
--from-file=ca.crt=./sc-s1-p-p3-root-ca-crt.pem
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: client-3
namespace: apps-1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-3
namespace: apps-1
spec:
replicas: 1
selector:
matchLabels:
app: client-3
template:
metadata:
labels:
app: client-3
spec:
terminationGracePeriodSeconds: 0
serviceAccountName: client-3
volumes:
- name: certs
secret:
secretName: client-3-certs
optional: false
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep", "infinity"]
imagePullPolicy: IfNotPresent
volumeMounts:
- name: certs
mountPath: /etc/tls
EOF
7. Deploy and configure application workloads in the mesh
7.1. Deploy Server 1
kubectl -n apps-1 \
create secret generic \
server-1-certs \
--from-file=p2-tls.key=./sc-s1-p-p2-key.pem \
--from-file=p2-tls.crt=./sc-s1-p-p2-crt.pem \
--from-file=p3-tls.key=./sc-s1-p-p3-key.pem \
--from-file=p3-tls.crt=./sc-s1-p-p3-crt.pem \
--from-file=client-3-ca.crt=./client-3-root-ca-crt.pem
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: server-1
namespace: apps-1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: server-1-nginx-config
namespace: apps-1
data:
nginx.conf: |
pid /tmp/nginx.pid;
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 8080;
root /usr/share/nginx/html;
index index.html;
server_name server-1;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: server-1
namespace: apps-1
spec:
replicas: 1
selector:
matchLabels:
app: server-1
template:
metadata:
labels:
app: server-1
sidecar.istio.io/inject: "true"
annotations:
sidecar.istio.io/userVolume: '{"certs":{"secret":{"secretName":"server-1-certs","optional":false}}}'
sidecar.istio.io/userVolumeMount: '{"certs":{"mountPath":"/etc/istio/certs/","readOnly":true}}'
spec:
serviceAccountName: server-1
volumes:
- name: nginx-config
configMap:
name: server-1-nginx-config
containers:
- name: nginx
image: nginxinc/nginx-unprivileged
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
---
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: server-1
namespace: apps-1
spec:
workloadSelector:
labels:
app: server-1
ingress:
- port:
name: p1
number: 9080
protocol: HTTP
defaultEndpoint: 0.0.0.0:8080
- port:
name: p2
number: 9443
protocol: HTTPS
defaultEndpoint: 0.0.0.0:8080
tls:
mode: SIMPLE
privateKey: /etc/istio/certs/p2-tls.key
serverCertificate: /etc/istio/certs/p2-tls.crt
- port:
name: p3
number: 9444
protocol: HTTPS
defaultEndpoint: 0.0.0.0:8080
tls:
mode: MUTUAL
privateKey: /etc/istio/certs/p3-tls.key
serverCertificate: /etc/istio/certs/p3-tls.crt
caCertificates: /etc/istio/certs/client-3-ca.crt
---
apiVersion: v1
kind: Service
metadata:
name: server-1
namespace: apps-1
labels:
app: server-1
service: server-1
spec:
selector:
app: server-1
ports:
- name: http-internal
port: 80
targetPort: 9080
protocol: TCP
- name: https-external-tls
port: 443
targetPort: 9443
protocol: TCP
- name: https-external-mtls
port: 444
targetPort: 9444
protocol: TCP
---
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: server-1
namespace: apps-1
spec:
selector:
matchLabels:
app: server-1
mtls:
mode: STRICT
portLevelMtls:
9443:
mode: DISABLE
9444:
mode: DISABLE
EOF
7.2. Deploy Client 1
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: client-1
namespace: apps-1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-1
namespace: apps-1
spec:
replicas: 1
selector:
matchLabels:
app: client-1
template:
metadata:
labels:
app: client-1
sidecar.istio.io/inject: "true"
spec:
terminationGracePeriodSeconds: 0
serviceAccountName: client-1
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep", "infinity"]
imagePullPolicy: IfNotPresent
EOF
7.3. Deploy Server 2
kubectl -n apps-1 \
create secret generic \
server-2-certs \
--from-file=p1-tls.key=./srv-2-p-p1-p2-key.pem \
--from-file=p1-tls.crt=./srv-2-p-p1-p2-crt.pem \
--from-file=p2-tls.key=./srv-2-p-p1-p2-key.pem \
--from-file=p2-tls.crt=./srv-2-p-p1-p2-crt.pem \
--from-file=client-5-ca.crt=./client-5-root-ca-crt.pem
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: server-2
namespace: apps-1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: server-2-nginx-config
namespace: apps-1
data:
nginx.conf: |
pid /tmp/nginx.pid;
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 8443 ssl;
root /usr/share/nginx/html;
index index.html;
server_name server-2.example.com;
ssl_certificate /etc/nginx-certs/p1-tls.crt;
ssl_certificate_key /etc/nginx-certs/p1-tls.key;
}
server {
listen 8444 ssl;
root /usr/share/nginx/html;
index index.html;
server_name server-2.example.com;
ssl_certificate /etc/nginx-certs/p2-tls.crt;
ssl_certificate_key /etc/nginx-certs/p2-tls.key;
ssl_client_certificate /etc/nginx-certs/client-5-ca.crt;
ssl_verify_client on;
}
server {
listen 8080;
root /usr/share/nginx/html;
index index.html;
server_name server-2.example.com;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: server-2
namespace: apps-1
spec:
replicas: 1
selector:
matchLabels:
app: server-2
template:
metadata:
labels:
app: server-2
sidecar.istio.io/inject: "true"
spec:
serviceAccountName: server-2
volumes:
- name: nginx-config
configMap:
name: server-2-nginx-config
- name: nginx-certs
secret:
secretName: server-2-certs
containers:
- name: nginx
image: nginxinc/nginx-unprivileged
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8443
- containerPort: 8444
- containerPort: 8080
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
- name: nginx-certs
mountPath: /etc/nginx-certs
readOnly: true
---
apiVersion: v1
kind: Service
metadata:
name: server-2
namespace: apps-1
labels:
app: server-2
service: server-2
spec:
selector:
app: server-2
ports:
- name: https-tls
port: 443
targetPort: 8443
protocol: TCP
- name: https-mtls
port: 444
targetPort: 8444
protocol: TCP
- name: http
port: 80
targetPort: 8080
protocol: TCP
EOF
7.4. Configure Ingress Gateway 1
kubectl -n istio-ingress \
create secret generic \
ingress-gw-1-p-1-certs \
--from-file=key=./ig-1-p-p1-p2-key.pem \
--from-file=cert=./ig-1-p-p1-p2-crt.pem
kubectl -n istio-ingress \
create secret generic \
ingress-gw-1-p-2-certs \
--from-file=key=./ig-1-p-p1-p2-key.pem \
--from-file=cert=./ig-1-p-p1-p2-crt.pem \
--from-file=cacert=./client-7-root-ca-crt.pem
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingress-gw-1-1
namespace: apps-1
spec:
selector:
istio: ingress
servers:
- name: "vp3"
hosts:
- "server-2-passthrough-tls.example.com"
port:
name: tls-passthrough
number: 443
protocol: TLS
tls:
mode: PASSTHROUGH
- name: "vp1"
hosts:
- "server-2-tls.example.com"
port:
name: https-tls
number: 443
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: ingress-gw-1-p-1-certs
- name: "vp2"
hosts:
- "server-2-mtls.example.com"
port:
name: https-mtls
number: 443
protocol: HTTPS
tls:
mode: MUTUAL
credentialName: ingress-gw-1-p-2-certs
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: server-2-1
namespace: apps-1
spec:
hosts:
- "server-2-passthrough-tls.example.com"
- "server-2-tls.example.com"
- "server-2-mtls.example.com"
gateways:
- ingress-gw-1-1
tls:
- match:
- port: 443
sniHosts:
- "server-2-passthrough-tls.example.com"
route:
- destination:
host: server-2.apps-1.svc.cluster.local
port:
number: 443
http:
- name: tls
match:
- authority:
exact: "server-2-tls.example.com"
route:
- destination:
host: server-2.apps-1.svc.cluster.local
port:
number: 80
- name: mtls
match:
- authority:
exact: "server-2-mtls.example.com"
route:
- destination:
host: server-2.apps-1.svc.cluster.local
port:
number: 80
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingress-gw-1-2 (1)
namespace: apps-1
spec:
selector:
istio: ingress
servers:
- name: "vp4"
hosts:
- "server-2-passthrough-mtls.example.com"
port:
name: mtls-passthrough
number: 443
protocol: TLS
tls:
mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: server-2-2
namespace: apps-1
spec:
hosts:
- "server-2-passthrough-mtls.example.com"
gateways:
- ingress-gw-1-2
tls:
- match:
- port: 443
sniHosts:
- "server-2-passthrough-mtls.example.com"
route:
- destination:
host: server-2.apps-1.svc.cluster.local
port:
number: 444
EOF
1 | When both TLS passthrough and mTLS passthrough ports were in the same Gateway , only one of them worked. That was the reason to create the second Gateway . |
8. Test
8.1. Test Client 1 → Server 1
kubectl -n apps-1 exec \
"$(kubectl -n apps-1 get pod -l app=client-1 -o jsonpath={.items[0].metadata.name})" \
-c sleep -- \
curl -v \
"http://server-1.apps-1.svc.cluster.local:80/"
* Connected to server-1.apps-1.svc.cluster.local . . . port 80 (#0) > GET / HTTP/1.1 > Host: server-1.apps-1.svc.cluster.local . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html> < HTTP/1.1 200 OK < server: envoy . . . < x-envoy-upstream-service-time: 8 . . .
8.2. Test Client 2 → Server 1
kubectl -n apps-1 exec \
"$(kubectl -n apps-1 get pod -l app=client-2 -o jsonpath={.items[0].metadata.name})" \
-c sleep -- \
curl -v \
"https://server-1.apps-1.svc.cluster.local:443/" \
--cacert /etc/tls/ca.crt
* Connected to server-1.apps-1.svc.cluster.local . . . port 443 (#0) * ALPN: offers h2,http/1.1 . . . * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN: server accepted h2 * Server certificate: * subject: CN=sc-s1-p-p2 . . . * subjectAltName: host "server-1.apps-1.svc.cluster.local" matched cert's "server-1.apps-1.svc.cluster.local" * issuer: CN=sc-s1-p-p2-root-ca * SSL certificate verify ok. . . . * using HTTP/2 * h2h3 [:method: GET] * h2h3 [:path: /] * h2h3 [:scheme: https] * h2h3 [:authority: server-1.apps-1.svc.cluster.local] . . . > GET / HTTP/2 > Host: server-1.apps-1.svc.cluster.local . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html> < HTTP/2 200 < server: istio-envoy . . . < x-envoy-upstream-service-time: 0 < x-envoy-decorator-operation: server-1.apps-1:9443/* . . .
8.3. Test Client 3 → Server 1
kubectl -n apps-1 exec \
"$(kubectl -n apps-1 get pod -l app=client-3 -o jsonpath={.items[0].metadata.name})" \
-c sleep -- \
curl -v \
"https://server-1.apps-1.svc.cluster.local:444/" \
--cacert /etc/tls/ca.crt \
--key /etc/tls/tls.key \
--cert /etc/tls/tls.crt
* Connected to server-1.apps-1.svc.cluster.local (10.96.139.141) port 444 (#0) * ALPN: offers h2,http/1.1 . . . * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN: server accepted h2 * Server certificate: * subject: CN=sc-s1-p-p3 . . . * subjectAltName: host "server-1.apps-1.svc.cluster.local" matched cert's "server-1.apps-1.svc.cluster.local" * issuer: CN=sc-s1-p-p3-root-ca * SSL certificate verify ok. . . . * using HTTP/2 * h2h3 [:method: GET] * h2h3 [:path: /] * h2h3 [:scheme: https] * h2h3 [:authority: server-1.apps-1.svc.cluster.local:444] . . . > GET / HTTP/2 > Host: server-1.apps-1.svc.cluster.local:444 . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html> < HTTP/2 200 < server: istio-envoy . . . < x-envoy-upstream-service-time: 0 < x-envoy-decorator-operation: server-1.apps-1:9444/* . . .
8.4. Test Client 4 → Server 2
INGW_IP=$(\
kubectl get service/istio-ingress -n istio-ingress \
-o=jsonpath='{.status.loadBalancer.ingress[0].ip}' \
)
curl -v \
"https://server-2-passthrough-tls.example.com:443/" \
--resolve "server-2-passthrough-tls.example.com:443:${INGW_IP}" \
--noproxy "server-2-passthrough-tls.example.com" \
--cacert ./srv-2-p-p1-p2-root-ca-crt.pem
* Added server-2-passthrough-tls.example.com:443:172.18.255.201 to DNS cache * Hostname server-2-passthrough-tls.example.com was found in DNS cache * Trying 172.18.255.201:443... * TCP_NODELAY set * Connected to server-2-passthrough-tls.example.com (172.18.255.201) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 . . . * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server accepted to use http/1.1 * Server certificate: * subject: CN=srv-2-p-p1-p2 . . . * subjectAltName: host "server-2-passthrough-tls.example.com" matched cert's "server-2-passthrough-tls.example.com" * issuer: CN=srv-2-p-p1-p2-root-ca * SSL certificate verify ok. > GET / HTTP/1.1 > Host: server-2-passthrough-tls.example.com . . . < HTTP/1.1 200 OK < Server: nginx/1.23.3 . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html>
8.5. Test Client 5 → Server 2
INGW_IP=$(\
kubectl get service/istio-ingress -n istio-ingress \
-o=jsonpath='{.status.loadBalancer.ingress[0].ip}' \
)
curl -v \
"https://server-2-passthrough-mtls.example.com:443/" \
--resolve "server-2-passthrough-mtls.example.com:443:${INGW_IP}" \
--noproxy "server-2-passthrough-mtls.example.com" \
--cacert ./srv-2-p-p1-p2-root-ca-crt.pem \
--key ./client-5-key.pem \
--cert ./client-5-crt.pem
* Added server-2-passthrough-mtls.example.com:443:172.18.255.201 to DNS cache * Hostname server-2-passthrough-mtls.example.com was found in DNS cache * Trying 172.18.255.201:443... * TCP_NODELAY set * Connected to server-2-passthrough-mtls.example.com (172.18.255.201) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 . . . * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server accepted to use http/1.1 * Server certificate: * subject: CN=srv-2-p-p1-p2 . . . * subjectAltName: host "server-2-passthrough-mtls.example.com" matched cert's "server-2-passthrough-mtls.example.com" * issuer: CN=srv-2-p-p1-p2-root-ca * SSL certificate verify ok. > GET / HTTP/1.1 > Host: server-2-passthrough-mtls.example.com . . . < HTTP/1.1 200 OK < Server: nginx/1.23.3 . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html>
8.6. Test Client 6 → Server 2
INGW_IP=$(\
kubectl get service/istio-ingress -n istio-ingress \
-o=jsonpath='{.status.loadBalancer.ingress[0].ip}' \
)
curl -v \
"https://server-2-tls.example.com:443/" \
--resolve "server-2-tls.example.com:443:${INGW_IP}" \
--noproxy "server-2-tls.example.com" \
--cacert ./ig-1-p-p1-p2-root-ca-crt.pem
* Added server-2-tls.example.com:443:172.18.255.201 to DNS cache * Hostname server-2-tls.example.com was found in DNS cache * Trying 172.18.255.201:443... * TCP_NODELAY set * Connected to server-2-tls.example.com (172.18.255.201) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 . . . * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=ig-1-p-p1-p2 . . . * subjectAltName: host "server-2-tls.example.com" matched cert's "server-2-tls.example.com" * issuer: CN=ig-1-p-p1-p2-root-ca * SSL certificate verify ok. . . . > GET / HTTP/2 > Host: server-2-tls.example.com . . . < HTTP/2 200 < server: istio-envoy . . . < x-envoy-upstream-service-time: 8 . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html>
8.7. Test Client 7 → Server 2
INGW_IP=$(\
kubectl get service/istio-ingress -n istio-ingress \
-o=jsonpath='{.status.loadBalancer.ingress[0].ip}' \
)
curl -v \
"https://server-2-mtls.example.com:443/" \
--resolve "server-2-mtls.example.com:443:${INGW_IP}" \
--noproxy "server-2-mtls.example.com" \
--cacert ./ig-1-p-p1-p2-root-ca-crt.pem \
--key ./client-7-key.pem \
--cert ./client-7-crt.pem
* Added server-2-mtls.example.com:443:172.18.255.201 to DNS cache * Hostname server-2-mtls.example.com was found in DNS cache * Trying 172.18.255.201:443... * TCP_NODELAY set * Connected to server-2-mtls.example.com (172.18.255.201) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 . . . * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=ig-1-p-p1-p2 . . . * subjectAltName: host "server-2-mtls.example.com" matched cert's "server-2-mtls.example.com" * issuer: CN=ig-1-p-p1-p2-root-ca * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) . . . > GET / HTTP/2 > Host: server-2-mtls.example.com . . . < HTTP/2 200 < server: istio-envoy . . . < x-envoy-upstream-service-time: 2 . . . <html> . . . <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> . . . </body> </html>