ISTIO MTLS SUMMARY
THIS IS BASED ON ISTIO OFFICIAL PACKAGE ISTIO-1.3.5(FOLLOWING ALL OF ISTIO DOCS MUST OVER THE WALL,YOU CAN REFERENCE THE LATEST VERDION DOCS), BECAUSE OUR ENV VERSION IS BASED ON THIS VERSION.
Test Flow
TODO
Update MeshPolicy from PERMISSIVE to STRICT
NOTE: More infor about this in our ENV, please look at following Question section Q2
Reference link MutlalTls-Mode
kubectl get meshpolicy
kubectl edit meshpolicy
///// then manually update mtls.mode to STRICT
Generate CA ,key and cert
You can reference this link secure-ingress-mount
git clone https://github.com/nicholasjackson/mtls-go-example
cd mtls-go-example
/////// Note: change <password> to any value you like
./generate.sh your_fqdn <password>
mkdir ../your_fqdn && mv 1_root 2_intermediate 3_application 4_client ../your_fqdn && cd ..
Then all of CA key and cert moved into your_fqdn DIR.
NOTE: Following all of create sceret command must under the same DIR with your_fqdn.
Intergration MTLS
Incoming Traffic with MTLS (Ingressgateway)
-
Deploy a httpbin service under default namespace and enabled sidecar injection
Reference command: kubectl apply -f <(istioctl kube-inject -f httpbin.yaml)
apiVersion: v1 kind: Service metadata: name: httpbin labels: app: httpbin spec: ports: - name: http port: 8000 targetPort: 80 selector: app: httpbin --- apiVersion: apps/v1 kind: Deployment metadata: name: httpbin spec: replicas: 1 selector: matchLabels: app: httpbin version: v1 template: metadata: labels: app: httpbin version: v1 spec: containers: - image: docker.io/kennethreitz/httpbin imagePullPolicy: IfNotPresent name: httpbin ports: - containerPort: 80
-
Create two secrets istio-ingressgateway-certs and istio-ingressgateway-ca-certs under istio-system namespace
kubectl create -n istio-system secret tls istio-ingressgateway-certs --key your_fqdn/3_application/private/your_fqdn.key.pem --cert your_fqdn/3_applicationcerts/your_fqdn.cert.pem kubectl create -n istio-system secret generic istio-ingressgateway-ca-certs --from-file=your_fqdn/2_intermediate/certs/ca-chain.cert.pem
-
Define a Gateway and VirtualService and DestinationRule
kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: httpbin-gateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: MUTUAL serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key caCertificates: /etc/istio/ingressgateway-ca-certs/ca-chain.cert.pem hosts: - "*" EOF
kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "your_fqdn" gateways: - httpbin-gateway http: - match: - uri: prefix: /status - uri: prefix: /delay route: - destination: port: number: 8000 host: httpbin EOF
kubectl apply -f - <<EOF apiVersion: "networking.istio.io/v1alpha3" kind: DestinationRule metadata: name: httpbin spec: host: "httpbin" trafficPolicy: tls: mode: ISTIO_MUTUAL EOF
-
Check Cert and CA
kubectl exec -it $(kubectl get pods -n istio-system| grep ingress | awk -F " " '{print $1}') -n istio-system -- ls /etc/istio/ingressgateway-certs/ kubectl exec -it $(kubectl get pods -n istio-system| grep ingress | awk -F " " '{print $1}') -n istio-system -- ls /etc/istio/ingressgateway-ca-certs/
-
Using CURL command to test it.
NOTE: 31390 is NodePort, because in minikube we haven’t EXTERNAL-IP, so we must use Node port. detailed message please reference link ingress-control
curl -HHost:your_fqdn --resolve your_fqdn:31390:10.120.20.63 --cacert your_fqdn/2_intermediate/certs/ca-chain.cert.pem --cert your_fqdn/4_client/certs/your_fqdn.cert.pem --key your_fqdn/4_client/private/your_fqdn.key.pem https://your_fqdn:31390/status/418 -v
The successful mark is you will see a teapot
Outgoing Traffic with MTLS (ServiceEntry)
-
Create an external namespace
kubectl create namespace external
-
Create client secret under external namespace
kubectl create -n external secret tls testsleep --key your_fqdn/4_client/private/your_fqdn.key.pem --cert your_fqdn/4_client/certs/your_fqdn.cert.pem kubectl create -n external secret generic ca --from-file=your_fqdn/2_intermediate/certs/ca-chain.cert.pem
-
Deploy sleep service under external namespace.
NOTE: Following files ,you must install it under external namespace.
-
Deploy sleep under external namespace and enable injection.
Reference command: kubectl apply -f <(istioctl kube-inject -f sleep.yaml) -n external
###contents of the sleep.yaml apiVersion: v1 kind: ServiceAccount metadata: name: sleep --- apiVersion: v1 kind: Service metadata: name: sleep labels: app: sleep spec: ports: - port: 80 name: http selector: app: sleep --- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep annotations: sidecar.istio.io/inject: "true" sidecar.istio.io/userVolume: '{"client-secret": {"secret": {"secretName":"testsleep"}},"ca-secret": {"secret": {"secretName":"ca"}}}' sidecar.istio.io/userVolumeMount: '{"client-secret": {"mountPath":"/etc/client-cert/","readOnly":true},"ca-secret":{"mountPath":"/etc/ca-cert/"}}' spec: serviceAccountName: sleep containers: - name: sleep image: governmentpaas/curl-ssl command: ["/bin/sleep", "3650d"] imagePullPolicy: IfNotPresent ---
-
Deploy VS,DR, SE under external namespace
Reference command: kubectl apply -f xxxx.yaml -n external
#### contents of the XXXX.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: test-external namespace: external spec: hosts: - your_fqdn http: - match: - port: 31380 route: - destination: host: your_fqdn port: number: 31390 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: test-external spec: host: your_fqdn trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 31390 tls: mode: MUTUAL clientCertificate: /etc/client-cert/tls.crt privateKey: /etc/client-cert/tls.key caCertificates: /etc/ca-cert/ca-chain.cert.pem --- apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: test-external spec: hosts: - your_fqdn ports: - number: 31380 name: http protocol: HTTP - number: 31390 name: https protocol: HTTPS resolution: DNS location: MESH_EXTERNAL
-
-
Check CA and Cert
kubectl exec -it $(kubectl get pods -n external| grep sleep | awk -F " " '{print $1}') -c istio-proxy -n external -- ls /etc/client-cert/ kubectl exec -it $(kubectl get pods -n external| grep sleep | awk -F " " '{print $1}') -c istio-proxy -n external -- ls /etc/ca-cert/
-
Test Client
-
kubectl exec into sleep container
kubectl exec -it $(kubectl get pods -n external| grep sleep | awk -F " " '{print $1}') -c sleep -n external -- /bin/sh
-
Testing
curl -HHost:your_fqdn --resolve your_fqdn:31380:10.120.20.63 -svL http://your_fqdn:31380/status/418 curl -HHost:your_fqdn --resolve your_fqdn:31390:10.120.20.63 -svL http://your_fqdn:31390/status/418
The successful mark is you will see a teapot
-
Test Analysis(IP and FQDN)
-
update VS, DR supports wildcard. please look at following example.
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: test-external namespace: external spec: hosts: - "*.com" http: - match: - port: 31380 route: - destination: host: "*.com" port: number: 31390 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: test-external spec: host: "*.com" trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 31390 tls: mode: MUTUAL clientCertificate: /etc/client-cert/tls.crt privateKey: /etc/client-cert/tls.key caCertificates: /etc/ca-cert/ca-chain.cert.pem
-
update SE to support FQDN and IP
-
test wildcard
NOTE: this scenario,“resolution” field must fills in “NONE”, otherwise appear an error when configuration.
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: test-external spec: hosts: - '*.com' ports: - number: 31380 name: http protocol: HTTP - number: 31390 name: https protocol: HTTPS resolution: NONE location: MESH_EXTERNAL
# curl -HHost:your_fqdn --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418 # curl -HHost:your_fqdn --http2-prior-knowledge -svL http://your_fqdn:31380/status/418
Test result:(maybe need other configuration) :503.
-
test IP only
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: test-external spec: hosts: - '*.com' endpoints: - address: 10.120.20.63 ports: - number: 31380 name: http protocol: HTTP - number: 31390 name: https protocol: HTTPS resolution: STATIC location: MESH_EXTERNAL
# curl -HHost:your_fqdn --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418
Test result: OK. haven’t DNS resolution.
# curl --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418
Test result: 502
-
test FQDN only
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: test-external spec: hosts: - '*.com' endpoints: - address: your_fqdn ports: - number: 31380 name: http protocol: HTTP - number: 31390 name: https protocol: HTTPS resolution: DNS location: MESH_EXTERNAL
# curl -HHost:your_fqdn --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418
Test result: OK. have DNS resolution.
-
test both FQDN and IP
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: test-external spec: hosts: - '*.com' endpoints: - address: your_fqdn - address: 10.120.20.63 ports: - number: 31380 name: http protocol: HTTP - number: 31390 name: https protocol: HTTPS resolution: DNS location: MESH_EXTERNAL
# curl -HHost:your_fqdn -svL http://your_fqdn:31380/status/418
Test result: OK , have DNS resolution
# curl -HHost:your_fqdn --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418
Test result: OK, haven’t DNS resolution
# curl --http2-prior-knowledge -svL http://your_fqdn:31380/status/418
Test result: OK.
# curl --http2-prior-knowledge -svL http://10.120.20.63:31380/status/418
Test result: 502
-
-
Test Analysis
-
IP Only.
Even only we have IP, we still must override Host header to match VS host. so here has a problem.
-
FQDN Only.
This is OK. if FQDN can match VS host.
-
Both IP and FQDN.
Here still have a problem the same as only IP scenario.
-
Conclusion
C1. If want to transfor port from 31380 to 31390 as above client Test, you must add 31380 port message into related ServiceEntry,otherwise curl connection will be reset by peer(this is because if you didn’t add 31380 port message in SE, the sleep service doesn’t listen 0.0.0.0 31380.). so if we have many different port need transfor, then we must add port into related SE.
Questions
Q1. Testing ingressgateway HTTP Protocol is HTTP2.0, but Through sleep container, HTTP Protocol is HTTP1.1. Is this normal phenomenon?
A1. using "–http2-prior-knowledge" in the curl command ,then HTTP protocol is HTTP2. so this isn’t an issue.
Q2. If i set meshpolicy tls mode is PERMISSIVE . then i can’t install httpbin DR, the test also can success. but if i set it to STRICT, then test will failed. so maybe in our ENV, we don’t need set mode to STRICT(default is PERMISSIVE), because our ENV internal service communications don’t use TLS as our previous ARCH.
Q3. If we use directly IPs to send out to external service, we must override Host header to match VS host, otherwise we have 502 error.