github.com/imran-kn/cilium-fork@v1.6.9/Documentation/gettingstarted/cassandra.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_cassandra: 8 9 ********************************** 10 How to Secure a Cassandra Database 11 ********************************** 12 13 This document serves as an introduction to using Cilium to enforce Cassandra-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 **NOTE:** Cassandra-aware policy support is still in beta phase. It is not yet ready for 19 production use. Additionally, the Cassandra-specific policy language is highly likely to 20 change in a future Cilium version. 21 22 .. include:: gsg_requirements.rst 23 24 Deploy the Demo Application 25 =========================== 26 27 Now that we have Cilium deployed and ``kube-dns`` operating correctly we can 28 deploy our demo Cassandra application. Since our first 29 `HTTP-aware Cilium Star Wars demo <https://www.cilium.io/blog/2017/5/4/demo-may-the-force-be-with-you>`_ 30 showed how the Galactic Empire used HTTP-aware security policies to protect the Death Star from the 31 Rebel Alliance, this Cassandra demo is Star Wars-themed as well. 32 33 `Apache Cassanadra <http://cassandra.apache.org>`_ is a popular NOSQL database focused on 34 delivering high-performance transactions (especially on writes) without sacrificing on availability or scale. 35 Cassandra operates as a cluster of servers, and Cassandra clients query these services via a 36 the `native Cassandra protocol <https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec>`_ . 37 Cilium understands the Cassandra protocol, and thus is able to provide deep visibility and control over 38 which clients are able to access particular tables inside a Cassandra cluster, and which actions 39 (e.g., "select", "insert", "update", "delete") can be performed on tables. 40 41 With Cassandra, each table belongs to a "keyspace", allowing multiple groups to use a single cluster without conflicting. 42 Cassandra queries specify the full table name qualified by the keyspace using the syntax "<keyspace>.<table>". 43 44 In our simple example, the Empire uses a Cassandra cluster to store two different types of information: 45 46 - **Employee Attendance Records** : Use to store daily attendance data (attendance.daily_records). 47 - **Deathstar Scrum Reports** : Daily scrum reports from the teams working on the Deathstar (deathstar.scrum_reports). 48 49 To keep the setup small, we will just launch a small number of pods to represent this setup: 50 51 - **cass-server** : A single pod running the Cassandra service, representing a Cassandra cluster 52 (label app=cass-server). 53 - **empire-hq** : A pod representing the Empire's Headquarters, which is the only pod that should 54 be able to read all attendance data, or read/write the Deathstar scrum notes (label app=empire-hq). 55 - **empire-outpost** : A random outpost in the empire. It should be able to insert employee attendance 56 records, but not read records for other empire facilities. It also should not have any access to the 57 deathstar keyspace (label app=empire-outpost). 58 59 All pods other than *cass-server* are Cassandra clients, which need access to the *cass-server* 60 container on TCP port 9042 in order to send Cassandra protocol messages. 61 62 .. image:: images/cilium_cass_gsg_topology.png 63 64 The file ``cass-sw-app.yaml`` contains a Kubernetes Deployment for each of the pods described 65 above, as well as a Kubernetes Service *cassandra-svc* for the Cassandra cluster. 66 67 .. parsed-literal:: 68 69 $ kubectl create -f \ |SCM_WEB|\/examples/kubernetes-cassandra/cass-sw-app.yaml 70 deployment.extensions/cass-server created 71 service/cassandra-svc created 72 deployment.extensions/empire-hq created 73 deployment.extensions/empire-outpost created 74 75 Kubernetes will deploy the pods and service in the background. 76 Running ``kubectl get svc,pods`` will inform you about the progress of the operation. 77 Each pod will go through several states until it reaches ``Running`` at which 78 point the setup is ready. 79 80 :: 81 82 $ kubectl get svc,pods 83 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 84 service/cassandra-svc ClusterIP None <none> 9042/TCP 1m 85 service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15h 86 87 NAME READY STATUS RESTARTS AGE 88 pod/cass-server-5674d5b946-x8v4j 1/1 Running 0 1m 89 pod/empire-hq-c494c664d-xmvdl 1/1 Running 0 1m 90 pod/empire-outpost-68bf76858d-flczn 1/1 Running 0 1m 91 92 93 Step 3: Test Basic Cassandra Access 94 =================================== 95 96 First, we'll create the keyspaces and tables mentioned above, and populate them with some initial data: 97 98 .. parsed-literal:: 99 100 $ curl -s \ |SCM_WEB|\/examples/kubernetes-cassandra/cass-populate-tables.sh | bash 101 102 Next, create two environment variables that refer to the *empire-hq* and *empire-outpost* pods: 103 104 :: 105 106 $ HQ_POD=$(kubectl get pods -l app=empire-hq -o jsonpath='{.items[0].metadata.name}') 107 $ OUTPOST_POD=$(kubectl get pods -l app=empire-outpost -o jsonpath='{.items[0].metadata.name}') 108 109 110 Now we will run the 'cqlsh' Cassandra client in the *empire-outpost* pod, telling it to access 111 the Cassandra cluster identified by the 'cassandra-svc' DNS name: 112 113 :: 114 115 $ kubectl exec -it $OUTPOST_POD cqlsh -- cassandra-svc 116 Connected to Test Cluster at cassandra-svc:9042. 117 [cqlsh 5.0.1 | Cassandra 3.11.3 | CQL spec 3.4.4 | Native protocol v4] 118 Use HELP for help. 119 cqlsh> 120 121 Next, using the cqlsh prompt, we'll show that the outpost can add records to the "daily_records" table 122 in the "attendance" keyspace: 123 124 :: 125 126 cqlsh> INSERT INTO attendance.daily_records (creation, loc_id, present, empire_member_id) values (now(), 074AD3B9-A47D-4EBC-83D3-CAD75B1911CE, true, 6AD3139F-EBFC-4E0C-9F79-8F997BA01D90); 127 128 We have confirmed that outposts are able to report daily attendance records as intended. We're off to a good start! 129 130 The Danger of a Compromised Cassandra Client 131 ============================================ 132 133 But what if a rebel spy gains access to any of the remote outposts that act as a Cassandra client? 134 Since every client has access to the Cassandra API on port 9042, it can do some bad stuff. 135 For starters, the outpost container can not only add entries to the attendance.daily_reports table, 136 but it could read all entries as well. 137 138 To see this, we can run the following command: 139 140 :: 141 142 $ cqlsh> SELECT * FROM attendance.daily_records; 143 144 loc_id | creation | empire_member_id | present 145 --------------------------------------+--------------------------------------+--------------------------------------+--------- 146 a855e745-69d8-4159-b8b6-e2bafed8387a | c692ce90-bf57-11e8-98e6-f1a9f45fc4d8 | cee6d956-dbeb-4b09-ad21-1dd93290fa6c | True 147 5b9a7990-657e-442d-a3f7-94484f06696e | c8493120-bf57-11e8-98e6-f1a9f45fc4d8 | e74a0300-94f3-4b3d-aee4-fea85eca5af7 | True 148 53ed94d0-ddac-4b14-8c2f-ba6f83a8218c | c641a150-bf57-11e8-98e6-f1a9f45fc4d8 | 104ddbb6-f2f7-4cd0-8683-cc18cccc1326 | True 149 074ad3b9-a47d-4ebc-83d3-cad75b1911ce | 9674ed40-bf59-11e8-98e6-f1a9f45fc4d8 | 6ad3139f-ebfc-4e0c-9f79-8f997ba01d90 | True 150 fe72cc39-dffb-45dc-8e5f-86c674a58951 | c5e79a70-bf57-11e8-98e6-f1a9f45fc4d8 | 6782689c-0488-4ecb-b582-a2ccd282405e | True 151 461f4176-eb4c-4bcc-a08a-46787ca01af3 | c6fefde0-bf57-11e8-98e6-f1a9f45fc4d8 | 01009199-3d6b-4041-9c43-b1ca9aef021c | True 152 64dbf608-6947-4a23-98e9-63339c413136 | c8096900-bf57-11e8-98e6-f1a9f45fc4d8 | 6ffe024e-beff-4370-a1b5-dcf6330ec82b | True 153 13cefcac-5652-4c69-a3c2-1484671f2467 | c53f4c80-bf57-11e8-98e6-f1a9f45fc4d8 | 55218adc-2f3d-4f84-a693-87a2c238bb26 | True 154 eabf5185-376b-4d4a-a5b5-99f912d98279 | c593fc30-bf57-11e8-98e6-f1a9f45fc4d8 | 5e22159b-f3a9-4f8a-9944-97375df570e9 | True 155 3c0ae2d1-c836-4aa4-8fe2-5db6cc1f92fc | c7af1400-bf57-11e8-98e6-f1a9f45fc4d8 | 0ccb3df7-78d0-4434-8a7f-4bfa8d714275 | True 156 31a292e0-2e28-4a7d-8c84-8d4cf0c57483 | c4e0d8d0-bf57-11e8-98e6-f1a9f45fc4d8 | 8fe7625c-f482-4eb6-b33e-271440777403 | True 157 158 (11 rows) 159 160 161 Uh oh! The rebels now has strategic information about empire troop strengths at each location in the galaxy. 162 163 But even more nasty from a security perspective is that the outpost container can also access information in any keyspace, 164 including the deathstar keyspace. For example, run: 165 166 :: 167 168 $ cqlsh> SELECT * FROM deathstar.scrum_notes; 169 170 empire_member_id | content | creation 171 --------------------------------------+----------------------------------------------------------------------------------------------------------------+-------------------------------------- 172 34e564c2-781b-477e-acd0-b357d67f94f2 | Designed protective shield for deathstar. Could be based on nearby moon. Feature punted to v2. Not blocked. | c3c8b210-bf57-11e8-98e6-f1a9f45fc4d8 173 dfa974ea-88cd-4e9b-85e3-542b9d00e2df | I think the exhaust port could be vulnerable to a direct hit. Hope no one finds out about it. Not blocked. | c37f4d00-bf57-11e8-98e6-f1a9f45fc4d8 174 ee12306a-7b44-46a4-ad68-42e86f0f111e | Trying to figure out if we should paint it medium grey, light grey, or medium-light grey. Not blocked. | c32daa90-bf57-11e8-98e6-f1a9f45fc4d8 175 176 (3 rows) 177 178 We see that any outpost can actually access the deathstar scrum notes, which mentions a pretty serious issue with the exhaust port. 179 180 Securing Access to Cassandra with Cilium 181 ======================================== 182 183 Obviously, it would be much more secure to limit each pod's access to the Cassandra server to be 184 least privilege (i.e., only what is needed for the app to operate correctly and nothing more). 185 186 We can do that with the following Cilium security policy. As with Cilium HTTP policies, we can write 187 policies that identify pods by labels, and then limit the traffic in/out of this pod. In 188 this case, we'll create a policy that identifies the tables that each client should be able to access, 189 the actions that are allowed on those tables, and deny the rest. 190 191 As an example, a policy could limit containers with label *app=empire-outpost* to only be able to 192 insert entries into the table "attendance.daily_reports", but would block any attempt by a compromised outpost 193 to read all attendance information or access other keyspaces. 194 195 .. image:: images/cilium_cass_gsg_attack.png 196 197 Here is the *CiliumNetworkPolicy* rule that limits access of pods with label *app=empire-outpost* to 198 only insert records into "attendance.daily_reports": 199 200 .. literalinclude:: ../../examples/kubernetes-cassandra/cass-sw-security-policy.yaml 201 202 A *CiliumNetworkPolicy* contains a list of rules that define allowed requests, meaning that requests 203 that do not match any rules are denied as invalid. 204 205 The rule explicitly matches Cassandra connections destined to TCP 9042 on cass-server pods, and allows 206 query actions like select/insert/update/delete only on a specified set of tables. 207 The above rule applies to inbound (i.e., "ingress") connections to cass-server pods (as indicated by "app:cass-server" 208 in the "endpointSelector" section). The rule applies different rules based on whether the 209 client pod has labels "app: empire-outpost" or "app: empire-hq" as indicated by the "fromEndpoints" section. 210 211 The policy limits the *empire-outpost* pod to performing "select" queries on the "system" and "system_schema" 212 keyspaces (required by cqlsh on startup) and "insert" queries to the "attendance.daily_records" table. 213 214 The full policy adds another rule that allows all queries from the *empire-hq* pod. 215 216 Apply this Cassandra-aware network security policy using ``kubectl`` in a new window: 217 218 .. parsed-literal:: 219 220 $ kubectl create -f \ |SCM_WEB|\/examples/kubernetes-cassandra/cass-sw-security-policy.yaml 221 222 If we then again try to perform the attacks from the *empire-outpost* pod, we'll see that they are denied: 223 224 :: 225 226 $ cqlsh> SELECT * FROM attendance.daily_records; 227 Unauthorized: Error from server: code=2100 [Unauthorized] message="Request Unauthorized" 228 229 This is because the policy only permits pods with labels app: empire-outpost to insert into attendance.daily_records, it does 230 not permit select on that table, or any action on other tables (with the exception of the system.* and system_schema.* 231 keyspaces). Its worth noting that we don't simply drop the message (which 232 could easily be confused with a network error), but rather we respond with the Cassandra Unauthorized error message. 233 (similar to how HTTP would return an error code of 403 unauthorized). 234 235 Likewise, if the outpost pod ever tries to access a table in another keyspace, like deathstar, this request will also be 236 denied: 237 238 :: 239 240 $ cqlsh> SELECT * FROM deathstar.scrum_notes; 241 Unauthorized: Error from server: code=2100 [Unauthorized] message="Request Unauthorized" 242 243 This is blocked as well, thanks to the Cilium network policy. 244 245 Use another window to confirm that the *empire-hq* pod still has full access to the cassandra cluster: 246 247 :: 248 249 $ kubectl exec -it $HQ_POD cqlsh -- cassandra-svc 250 Connected to Test Cluster at cassandra-svc:9042. 251 [cqlsh 5.0.1 | Cassandra 3.11.3 | CQL spec 3.4.4 | Native protocol v4] 252 Use HELP for help. 253 cqlsh> 254 255 The power of Cilium's identity-based security allows *empire-hq* to still have full access 256 to both tables: 257 258 :: 259 260 261 $ cqlsh> SELECT * FROM attendance.daily_records; 262 loc_id | creation | empire_member_id | present 263 --------------------------------------+--------------------------------------+--------------------------------------+--------- 264 a855e745-69d8-4159-b8b6-e2bafed8387a | c692ce90-bf57-11e8-98e6-f1a9f45fc4d8 | cee6d956-dbeb-4b09-ad21-1dd93290fa6c | True 265 266 <snip> 267 268 (12 rows) 269 270 271 Similarly, the deathstar can still access the scrum notes: 272 273 :: 274 275 $ cqlsh> SELECT * FROM deathstar.scrum_notes; 276 277 <snip> 278 279 (3 rows) 280 281 Cassandra-Aware Visibility (Bonus) 282 ================================== 283 284 As a bonus, you can re-run the above queries with policy enforced and view how Cilium provides Cassandra-aware visibility, including 285 whether requests are forwarded or denied. First, use "kubectl exec" to access the cilium pod. 286 287 :: 288 289 $ CILIUM_POD=$(kubectl get pods -n kube-system -l k8s-app=cilium -o jsonpath='{.items[0].metadata.name}') 290 $ kubectl exec -it -n kube-system $CILIUM_POD /bin/bash 291 root@minikube:~# 292 293 Next, start Cilium monitor, and limit the output to only "l7" type messages using the "-t" flag: 294 295 :: 296 297 root@minikube:~# cilium monitor -t l7 298 Listening for events on 2 CPUs with 64x4096 of shared memory 299 Press Ctrl-C to quit 300 301 In the other windows, re-run the above queries, and you will see that Cilium provides full visibility at the level of 302 each Cassandra request, indicating: 303 304 - The Kubernetes label-based identity of both the sending and receiving pod. 305 - The details of the Cassandra request, including the 'query_action' (e.g., 'select', 'insert') 306 and 'query_table' (e.g., 'system.local', 'attendance.daily_records') 307 - The 'verdict' indicating whether the request was allowed by policy ('Forwarded' or 'Denied'). 308 309 Example output is below. All requests are from *empire-outpost* to *cass-server*. The first two requests are 310 allowed, a 'select' into 'system.local' and an 'insert' into 'attendance.daily_records'. 311 The second two requests are denied, a 'select' into 'attendance.daily_records' and a select into 'deathstar.scrum_notes' : 312 313 :: 314 315 <- Request cassandra from 0 ([k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:app=empire-outpost]) to 64503 ([k8s:app=cass-server k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default]), identity 12443->16168, verdict Forwarded query_table:system.local query_action:selec 316 <- Request cassandra from 0 ([k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:app=empire-outpost]) to 64503 ([k8s:app=cass-server k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default]), identity 12443->16168, verdict Forwarded query_action:insert query_table:attendance.daily_records 317 <- Request cassandra from 0 ([k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:app=empire-outpost]) to 64503 ([k8s:app=cass-server k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default]), identity 12443->16168, verdict Denied query_action:select query_table:attendance.daily_records 318 <- Request cassandra from 0 ([k8s:io.cilium.k8s.policy.serviceaccount=default k8s:io.kubernetes.pod.namespace=default k8s:app=empire-outpost]) to 64503 ([k8s:app=cass-server k8s:io.kubernetes.pod.namespace=default k8s:io.cilium.k8s.policy.serviceaccount=default]), identity 12443->16168, verdict Denied query_table:deathstar.scrum_notes query_action:select 319 320 Clean Up 321 ======== 322 323 You have now installed Cilium, deployed a demo app, and tested 324 L7 Cassandra-aware network security policies. To clean up, run: 325 326 :: 327 328 $ minikube delete 329 330 After this, you can re-run the tutorial from Step 1.