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.