github.com/imran-kn/cilium-fork@v1.6.9/Documentation/gettingstarted/kafka.rst (about) 1 .. only:: not (epub or latex or html) 2 3 WARNING: You are looking at unreleased Cilium documentation. 4 Please use the official rendered version released here: 5 http://docs.cilium.io 6 7 .. _gs_kafka: 8 9 ************************ 10 Securing a Kafka cluster 11 ************************ 12 13 This document serves as an introduction to using Cilium to enforce Kafka-aware 14 security policies. It is a detailed walk-through of getting a single-node 15 Cilium environment running on your machine. It is designed to take 15-30 16 minutes. 17 18 .. include:: gsg_requirements.rst 19 20 Deploy the Demo Application 21 =========================== 22 23 Now that we have Cilium deployed and ``kube-dns`` operating correctly we can 24 deploy our demo Kafka application. Since our first demo of Cilium + HTTP-aware security 25 policies was Star Wars-themed we decided to do the same for Kafka. While the 26 `HTTP-aware Cilium Star Wars demo <https://www.cilium.io/blog/2017/5/4/demo-may-the-force-be-with-you>`_ 27 showed how the Galactic Empire used HTTP-aware security policies to protect the Death Star from the 28 Rebel Alliance, this Kafka demo shows how the lack of Kafka-aware security policies allowed the 29 Rebels to steal the Death Star plans in the first place. 30 31 Kafka is a powerful platform for passing datastreams between different components of an application. 32 A cluster of "Kafka brokers" connect nodes that "produce" data into a data stream, or "consume" data 33 from a datastream. Kafka refers to each datastream as a "topic". 34 Because scalable and highly-available Kafka clusters are non-trivial to run, the same cluster of 35 Kafka brokers often handles many different topics at once (read this `Introduction to Kafka 36 <https://kafka.apache.org/intro>`_ for more background). 37 38 In our simple example, the Empire uses a Kafka cluster to handle two different topics: 39 40 - *empire-announce* : Used to broadcast announcements to sites spread across the galaxy 41 - *deathstar-plans* : Used by a small group of sites coordinating on building the ultimate battlestation. 42 43 To keep the setup small, we will just launch a small number of pods to represent this setup: 44 45 - *kafka-broker* : A single pod running Kafka and Zookeeper representing the Kafka cluster 46 (label app=kafka). 47 - *empire-hq* : A pod representing the Empire's Headquarters, which is the only pod that should 48 produce messages to *empire-announce* or *deathstar-plans* (label app=empire-hq). 49 - *empire-backup* : A secure backup facility located in `Scarif <http://starwars.wikia.com/wiki/Scarif_vault>`_ , 50 which is allowed to "consume" from the secret *deathstar-plans* topic (label app=empire-backup). 51 - *empire-outpost-8888* : A random outpost in the empire. It needs to "consume" messages from 52 the *empire-announce* topic (label app=empire-outpost). 53 - *empire-outpost-9999* : Another random outpost in the empire that "consumes" messages from 54 the *empire-announce* topic (label app=empire-outpost). 55 56 All pods other than *kafka-broker* are Kafka clients, which need access to the *kafka-broker* 57 container on TCP port 9092 in order to send Kafka protocol messages. 58 59 .. image:: images/cilium_kafka_gsg_topology.png 60 61 The file ``kafka-sw-app.yaml`` contains a Kubernetes Deployment for each of the pods described 62 above, as well as a Kubernetes Service for both Kafka and Zookeeper. 63 64 .. parsed-literal:: 65 66 $ kubectl create -f \ |SCM_WEB|\/examples/kubernetes-kafka/kafka-sw-app.yaml 67 deployment "kafka-broker" created 68 deployment "zookeeper" created 69 service "zook" created 70 service "kafka-service" created 71 deployment "empire-hq" created 72 deployment "empire-outpost-8888" created 73 deployment "empire-outpost-9999" created 74 deployment "empire-backup" created 75 76 Kubernetes will deploy the pods and service in the background. 77 Running ``kubectl get svc,pods`` will inform you about the progress of the operation. 78 Each pod will go through several states until it reaches ``Running`` at which 79 point the setup is ready. 80 81 :: 82 83 $ kubectl get svc,pods 84 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 85 kafka-service ClusterIP None <none> 9092/TCP 2m 86 kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10m 87 zook ClusterIP 10.97.250.131 <none> 2181/TCP 2m 88 89 NAME READY STATUS RESTARTS AGE 90 empire-backup-6f4567d5fd-gcrvg 1/1 Running 0 2m 91 empire-hq-59475b4b64-mrdww 1/1 Running 0 2m 92 empire-outpost-8888-78dffd49fb-tnnhf 1/1 Running 0 2m 93 empire-outpost-9999-7dd9fc5f5b-xp6jw 1/1 Running 0 2m 94 kafka-broker-b874c78fd-jdwqf 1/1 Running 0 2m 95 zookeeper-85f64b8cd4-nprck 1/1 Running 0 2m 96 97 Setup Client Terminals 98 ====================== 99 100 First we will open a set of windows to represent the different Kafka clients discussed above. 101 For consistency, we recommend opening them in the pattern shown in the image below, but this is optional. 102 103 .. image:: images/cilium_kafka_gsg_terminal_layout.png 104 105 In each window, use copy-paste to have each terminal provide a shell inside each pod. 106 107 empire-hq terminal: 108 :: 109 110 $ HQ_POD=$(kubectl get pods -l app=empire-hq -o jsonpath='{.items[0].metadata.name}') && kubectl exec -it $HQ_POD -- sh -c "PS1=\"empire-hq $\" /bin/bash" 111 112 empire-backup terminal: 113 :: 114 115 $ BACKUP_POD=$(kubectl get pods -l app=empire-backup -o jsonpath='{.items[0].metadata.name}') && kubectl exec -it $BACKUP_POD -- sh -c "PS1=\"empire-backup $\" /bin/bash" 116 117 outpost-8888 terminal: 118 :: 119 120 $ OUTPOST_8888_POD=$(kubectl get pods -l outpostid=8888 -o jsonpath='{.items[0].metadata.name}') && kubectl exec -it $OUTPOST_8888_POD -- sh -c "PS1=\"outpost-8888 $\" /bin/bash" 121 122 outpost-9999 terminal: 123 :: 124 125 $ OUTPOST_9999_POD=$(kubectl get pods -l outpostid=9999 -o jsonpath='{.items[0].metadata.name}') && kubectl exec -it $OUTPOST_9999_POD -- sh -c "PS1=\"outpost-9999 $\" /bin/bash" 126 127 128 Test Basic Kafka Produce & Consume 129 ================================== 130 131 First, let's start the consumer clients listening to their respective Kafka topics. All of the consumer 132 commands below will hang intentionally, waiting to print data they consume from the Kafka topic: 133 134 In the *empire-backup* window, start listening on the top-secret *deathstar-plans* topic: 135 136 :: 137 138 $ ./kafka-consume.sh --topic deathstar-plans 139 140 In the *outpost-8888* window, start listening to *empire-announcement*: 141 142 :: 143 144 $ ./kafka-consume.sh --topic empire-announce 145 146 Do the same in the *outpost-9999* window: 147 148 :: 149 150 $ ./kafka-consume.sh --topic empire-announce 151 152 Now from the *empire-hq*, first produce a message to the *empire-announce* topic: 153 154 :: 155 156 $ echo "Happy 40th Birthday to General Tagge" | ./kafka-produce.sh --topic empire-announce 157 158 This message will be posted to the *empire-announce* topic, and shows up in both the *outpost-8888* and 159 *outpost-9999* windows who consume that topic. It will not show up in *empire-backup*. 160 161 *empire-hq* can also post a version of the top-secret deathstar plans to the *deathstar-plans* topic: 162 163 :: 164 165 $ echo "deathstar reactor design v3" | ./kafka-produce.sh --topic deathstar-plans 166 167 This message shows up in the *empire-backup* window, but not for the outposts. 168 169 Congratulations, Kafka is working as expected :) 170 171 The Danger of a Compromised Kafka Client 172 ======================================== 173 174 But what if a rebel spy gains access to any of the remote outposts that act as Kafka clients? 175 Since every client has access to the Kafka broker on port 9092, it can do some bad stuff. 176 For starters, the outpost container can actually switch roles from a consumer to a producer, 177 sending "malicious" data to all other consumers on the topic. 178 179 To prove this, kill the existing ``kafka-consume.sh`` command in the outpost-9999 window 180 by typing control-C and instead run: 181 182 :: 183 184 $ echo "Vader Booed at Empire Karaoke Party" | ./kafka-produce.sh --topic empire-announce 185 186 Uh oh! Outpost-8888 and all of the other outposts in the empire have now received this fake announcement. 187 188 But even more nasty from a security perspective is that the outpost container can access any topic 189 on the kafka-broker. 190 191 In the outpost-9999 container, run: 192 193 :: 194 195 $ ./kafka-consume.sh --topic deathstar-plans 196 "deathstar reactor design v3" 197 198 We see that any outpost can actually access the secret deathstar plans. Now we know how the rebels got 199 access to them! 200 201 Securing Access to Kafka with Cilium 202 ==================================== 203 204 Obviously, it would be much more secure to limit each pod's access to the Kafka broker to be 205 least privilege (i.e., only what is needed for the app to operate correctly and nothing more). 206 207 We can do that with the following Cilium security policy. As with Cilium HTTP policies, we can write 208 policies that identify pods by labels, and then limit the traffic in/out of this pod. In 209 this case, we'll create a policy that identifies the exact traffic that should be allowed to reach the 210 Kafka broker, and deny the rest. 211 212 As an example, a policy could limit containers with label *app=empire-outpost* to only be able to consume 213 topic *empire-announce*, but would block any attempt by a compromised container (e.g., empire-outpost-9999) 214 from producing to *empire-announce* or consuming from *deathstar-plans*. 215 216 .. image:: images/cilium_kafka_gsg_attack.png 217 218 Here is the *CiliumNetworkPolicy* rule that limits access of pods with label *app=empire-outpost* to 219 only consume on topic *empire-announce*: 220 221 .. literalinclude:: ../../examples/policies/getting-started/kafka.yaml 222 223 A *CiliumNetworkPolicy* contains a list of rules that define allowed requests, meaning that requests 224 that do not match any rules are denied as invalid. 225 226 The above rule applies to inbound (i.e., "ingress") connections to kafka-broker pods (as 227 indicated by "app: kafka" 228 in the "endpointSelector" section). The rule will apply to connections from pods with label 229 "app: empire-outpost" as indicated by the "fromEndpoints" section. The rule explicitly matches 230 Kafka connections destined to TCP 9092, and allows consume/produce actions on various topics of interest. 231 For example we are allowing *consume* from topic *empire-announce* in this case. 232 233 The full policy adds two additional rules that permit the legitimate "produce" 234 (topic *empire-announce* and topic *deathstar-plans*) from *empire-hq* and the 235 legitimate consume (topic = "deathstar-plans") from *empire-backup*. The full policy 236 can be reviewed by opening the URL in the command below in a browser. 237 238 Apply this Kafka-aware network security policy using ``kubectl`` in the main window: 239 240 .. parsed-literal:: 241 242 $ kubectl create -f \ |SCM_WEB|\/examples/kubernetes-kafka/kafka-sw-security-policy.yaml 243 244 If we then again try to produce a message from outpost-9999 to *empire-annnounce*, it is denied. 245 Type control-c and then run: 246 247 :: 248 249 $ echo "Vader Trips on His Own Cape" | ./kafka-produce.sh --topic empire-announce 250 >>[2018-04-10 23:50:34,638] ERROR Error when sending message to topic empire-announce with key: null, value: 27 bytes with error: (org.apache.kafka.clients.producer.internals.ErrorLoggingCallback) 251 org.apache.kafka.common.errors.TopicAuthorizationException: Not authorized to access topics: [empire-announce] 252 253 This is because the policy does not allow messages with role = "produce" for topic "empire-announce" from 254 containers with label app = empire-outpost. Its worth noting that we don't simply drop the message (which 255 could easily be confused with a network error), but rather we respond with the Kafka access denied error 256 (similar to how HTTP would return an error code of 403 unauthorized). 257 258 Likewise, if the outpost container ever tries to consume from topic *deathstar-plans*, it is denied, as 259 role = consume is only allowed for topic *empire-announce*. 260 261 To test, from the outpost-9999 terminal, run: 262 263 :: 264 265 $./kafka-consume.sh --topic deathstar-plans 266 [2018-04-10 23:51:12,956] WARN Error while fetching metadata with correlation id 2 : {deathstar-plans=TOPIC_AUTHORIZATION_FAILED} (org.apache.kafka.clients.NetworkClient) 267 268 This is blocked as well, thanks to the Cilium network policy. Imagine how different things would have been if the empire had been using 269 Cilium from the beginning! 270 271 Clean Up 272 ======== 273 274 You have now installed Cilium, deployed a demo app, and tested both 275 L7 Kafka-aware network security policies. To clean up, run: 276 277 :: 278 279 $ minikube delete 280 281 After this, you can re-run the tutorial from Step 1.