TCP Traffic
This task shows you how to set up Istio authorization policy for TCP traffic in an Istio mesh.
Before you begin
Before you begin this task, do the following:
Read the Istio authorization concepts.
Install Istio using the Istio installation guide.
Deploy two workloads named
sleep
andtcp-echo
together in a namespace, for examplefoo
. Both workloads run with an Envoy proxy in front of each. Thetcp-echo
workload listens on port 9000, 9001 and 9002 and echoes back any traffic it received with a prefixhello
. For example, if you send “world” totcp-echo
, it will reply withhello world
. Thetcp-echo
Kubernetes service object only declares the ports 9000 and 9001, and omits the port 9002. A pass-through filter chain will handle port 9002 traffic. Deploy the example namespace and workloads using the following command:$ kubectl create ns foo $ kubectl apply -f <(istioctl kube-inject -f @samples/tcp-echo/tcp-echo.yaml@) -n foo $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
Verify that
sleep
successfully communicates withtcp-echo
on ports 9000 and 9001 using the following command:$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9000 connection succeeded
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded
Verify that
sleep
successfully communicates withtcp-echo
on port 9002. You need to send the traffic directly to the pod IP oftcp-echo
because the port 9002 is not defined in the Kubernetes service object oftcp-echo
. Get the pod IP address and send the request with the following command:$ TCP_ECHO_IP=$(kubectl get pod "$(kubectl get pod -l app=tcp-echo -n foo -o jsonpath={.items..metadata.name})" -n foo -o jsonpath="{.status.podIP}") $ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c "echo \"port 9002\" | nc $TCP_ECHO_IP 9002" | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9002 connection succeeded
Configure ALLOW authorization policy for a TCP workload
Create the
tcp-policy
authorization policy for thetcp-echo
workload in thefoo
namespace. Run the following command to apply the policy to allow requests to port 9000 and 9001:$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: ALLOW rules: - to: - operation: ports: ["9000", "9001"] EOF
Verify that requests to port 9000 are allowed using the following command:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9000 connection succeeded
Verify that requests to port 9001 are allowed using the following command:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded
Verify that requests to port 9002 are denied. This is enforced by the authorization policy which also applies to the pass through filter chain, even if the port is not declared explicitly in the
tcp-echo
Kubernetes service object. Run the following command and verify the output:$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c "echo \"port 9002\" | nc $TCP_ECHO_IP 9002" | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Update the policy to add an HTTP-only field named
methods
for port 9000 using the following command:$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: ALLOW rules: - to: - operation: methods: ["GET"] ports: ["9000"] EOF
Verify that requests to port 9000 are denied. This occurs because the rule becomes invalid when it uses an HTTP-only field (
methods
) for TCP traffic. Istio ignores the invalid ALLOW rule. The final result is that the request is rejected, because it does not match any ALLOW rules. Run the following command and verify the output:$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Verify that requests to port 9001 are denied. This occurs because the requests do not match any ALLOW rules. Run the following command and verify the output:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Configure DENY authorization policy for a TCP workload
Add a DENY policy with HTTP-only fields using the following command:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: DENY rules: - to: - operation: methods: ["GET"] EOF
Verify that requests to port 9000 are denied. This occurs because Istio doesn’t understand the HTTP-only fields while creating a DENY rule for tcp port and due to it’s restrictive nature it denies all the traffic to the tcp ports:
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Verify that the requests to port 9001 are denied. Same reason as above.
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Add a DENY policy with both TCP and HTTP fields using the following command:
$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tcp-policy namespace: foo spec: selector: matchLabels: app: tcp-echo action: DENY rules: - to: - operation: methods: ["GET"] ports: ["9000"] EOF
Verify that requests to port 9000 is denied. This occurs because the request matches the
ports
in the above-mentioned deny policy.$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9000" | nc tcp-echo 9000' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' connection rejected
Verify that requests to port 9001 are allowed. This occurs because the requests do not match the
ports
in the DENY policy:$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- sh -c 'echo "port 9001" | nc tcp-echo 9001' | grep "hello" && echo 'connection succeeded' || echo 'connection rejected' hello port 9001 connection succeeded
Clean up
Remove the namespace foo:
$ kubectl delete namespace foo