☸️ Перехват трафика контейнеров в Kubernetes

Мануал

Легко захватить сетевой трафик с помощью инструмента захвата (например, tcpdump), если у нас есть доступ к сетевому интерфейсу.

Но в Kubernetes это непросто.

Есть несколько вариантов, например: контейнер sidecar, плагин захвата, контейнер docker, прямой доступ в том же пространстве имен.

Управляемые Kubernetes имеют усиленную конфигурацию безопасности, поэтому в зависимости от наших возможностей доступа и прав на кластер, приведенные ниже примеры могут работать (если есть возможность доступа к ОС ноды, если контейнер разрешен для запуска от имени root и т.д.).

Приведенные ниже примеры протестированы на кластере K8s на базе VM.

Инструменты

В большинстве случаев контейнер, в котором будет перехватываться сетевой трафик, конечно же, не содержит никаких инструментов захвата.

Часть альтернативных вариантов использует новый контейнер, который содержит инструменты захвата сети.

Классный образ Docker – https://github.com/nicolaka/netshoot, который содержит несколько сетевых инструментов, например: tcpdump, tshark, termshark, iptraf-ng.

Параметры будут выполняться как команда, например:

sudo docker run -it --rm --net container:k8s_nginx_my-nginx-b7d7bc74d-zxx28_default_ae4ee834-fb5d-4ec4-86b1-7834e538c666_0 nicolaka/netshoot tcpdump -i eth0 -s 0 -Xvv tcp port 80

Tcpdump может перенаправлять захваченные пакеты в stdout, который можно сохранить или использовать, например, на хосте:

tcpdump -i eth0 -s 0 -w - >/tmp/container.cap
tcpdump -i eth0 -s 0 -w - | wireshark -k -i -

Параметр -v /tmp:/tmp/tmp может быть полезен в тех случаях, если перехваченные пакеты записываются в файл внутри контейнера.

Есть несколько инструментов перехвата, для которых нужно больше прав (tshark, termshark), например, в docker запустить CLI : –cap-add=NET_ADMIN –cap-add=CAP_NET_RAW .

И, наконец, целый пример с наличием оболочки для запуска такого рода инструментов:

sudo docker run -it --rm -v /tmp:/tmp/tmp --cap-add=NET_ADMIN --cap-add=CAP_NET_RAW --net container:k8s_nginx_my-nginx-b7d7bc74d-zxx28_default_ae4ee834-fb5d-4ec4-86b1-7834e538c666_0 nicolaka/netshoottermshark

Прямой доступ к ноде K8s

Это самая безобидная ситуация, потому что нам не нужно трогать кластер K8s.

Во-первых, мы должны знать имя контейнера Docker и место, на котором он запущен, например:

kubectl get pod -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
my-nginx-b7d7bc74d-zxx28   1/1     Running   0          10m   10.244.2.2   node2   <none>           <none>
vagrant ssh node2
sudo docker ps | grep my-nginx-b7d7bc74d-zxx28
f22ab3b7130b nginx "/docker-entrypoint.…"

39 minutes ago Up 39 minutes 

k8s_nginx_my-nginx-b7d7bc74d-zxx28_default_ae4ee834-fb5d-4ec4-86b1-7834e538c666_0
62c7cacd66c1 k8s.gcr.io/pause:3.2 "/pause" 3

9 minutes ago Up 39 minutes k8s_POD_my-nginx-b7d7bc74d-zxx28_default_ae4ee834-fb5d-4ec4-86b1-7834e538c666_0

Перехват с помощью контейнера nicolaka/netshoot

Контейнер nicolaka/netshoot должен быть запущен в том же сетевом пространстве имен (задается командой –net container: ):

sudo docker run -it --rm --net container:k8s_nginx_my-nginx-b7d7bc74d-zxx28_default_ae4ee835d-4ec4-86b1-7834e538c666_0 nicolaka/netshoot tcpdump -i eth0 -s 0 -Xvv tcp port 80

Захват в сетевом пространстве имен контейнера

Docker использует сетевые пространства имен ядра Linux для изоляции контейнеров.

Имея root-доступ, его можно использовать для перехвата трафика:

sudo docker inspect --format '{{ .State.Pid }}' k8s_nginx_my-nginx-b7d7bc74d-zxx28_default_ae4ee834-fb5d-4ec4-86b1-7834e538c666_0
sudo nsenter -t 17900 -n /bin/bash -xec 'ip a; tcpdump -i eth0 -s 0 -Xvv tcp port 80'

Можно передавать захваченные tcpdump пакеты в Wireshark, см. пример скрипта для удаленного захвата в реальном времени на

https://github.com/pgillich/kubeadm-vagrant/blob/master/Ubuntu/capture_pod.sh.

Пример использования (имя контейнера необязательно):

./capture_pod.sh default my-nginx-596d59c679-9t4vp nginx

Перехват с помощью ksniff

Его можно установить с помощью krew, см.: https://github.com/eldadru/ksniff#installation.
Пример для перехвата из другого пода:
kubectl sniff loki-0 -v -n monitoring -p -r /data/sniff/loki-0.cap | wireshark -k -i -
В приведенном выше примере /tmp был недоступен для записи, поэтому для файла захвата внутри контейнера захвата был указан другой путь.

Захват трафика в контейнере

Если у вас есть статически скомпилированный tcpdump (см. пример здесь: https://www.androidtcpdump.com/android-tcpdump/compile ), можно скопировать единственный бинарник в контейнер.
Смотрите пример удаленного перехвата в реальном времени с помощью tcpdump и Wireshark:
kubectl cp tcpdump my-nginx-596d59c679-9t4vp:/tmp;
kubectl exec -t my-nginx-596d59c679-9t4vp -- /tmp/tcpdump -i eth0 -s 0 -w - | wireshark -k -i -
Все описанные выше действия, включая статическую компиляцию, ограничены одним скриптом.
См. пример использования (имя контейнера необязательно):
./capture_in_pod.sh default my-nginx-596d59c679-9t4vp nginx
Эта альтернатива предполагает наименьшие права в кластере Kubernetes: для пода (контейнера) требуются только kubectl cp и exec, без перезапуска этого пода.
Доступ к сайд контейнерам, нодам не требуются.

Дополнительный контейнер в том же поде

У этой альтернативы есть недостаток:
Под будет перезапущен, поскольку добавляется контейнер sidecar.
Добавление сайдкар контейнера:
kubectl edit deployment my-nginxspec:
  (...)
  template:
    (...)
    spec:
      containers:
      - command:
        - /bin/sleep
        - infinity
        image: nicolaka/netshoot
        name: netshoot
      - image: nginx:latest
        imagePullPolicy: Always
        name: nginx
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
Вышеуказанная операция редактирования может быть выполнена с помощью патча kubectl
см. подробнее: https://medium.com/@xxradar/how-to-tcpdump-effectively-in-kubernetes-part-1-a1546b683d2f
Запуск оболочки netshoot:
kubectl exec my-nginx-7bc9465bb9-lqnwd -ti -c netshoot -- /bin/bash
Конечно, команда tcpdump … может быть написана вместо /bin/bash для немедленного запуска простого перехвата.
Дополнительные возможности могут быть добавлены, например, с помощью securityContext.capabilities.add (для tshark и подобных инструментов):
Termshark использует /root/.cache, поэтому рекомендуется смонтировать туда том emptyDir.
Можно перехватывать трафик с самого начала.
В этом случае следует создать том emptyDir и предложить опцию -U для tcpdump.
См. подробнее
  • https://speedscale.com/2020/07/16/capturing-network-traffic-during-container-startup/
  • https://kubernetes.io/docs/concepts/storage/volumes/#emptydir-configuration-example

Эфемерные отладочные контейнеры

В Kubernetes 1.16 появилась новая функция Ephemeral Containers, которая идеально подходит для нашего случая использования.
Если среда выполнения контейнера поддерживает ее, она включается (см.: feature-gates: EphemeralContainers=true) должным образом.
Она пока есть в alpa, см. описание функции: https://github.com/kubernetes/enhancements/issues/277.
Пример запуска отладочного контейнера:
kubectl debug -it my-nginx-b7d7bc74d-7t7nr -c netshoot --target=nginx --image=nicolaka/netshoot --image-pull-policy=IfNotPresent -- /bin/bash
Смотрите другие примеры здесь: https://downey.io/blog/kubernetes-ephemeral-debug-container-tcpdump/
Можно также использовать API.
Пример для получения вышеуказанной конфигурации:
kubectl get --raw /api/v1/namespaces/default/pods/my-nginx-b7d7bc74d-7t7nr/ephemeralcontainers | jq{
  "kind": "EphemeralContainers",
  "apiVersion": "v1",
  "metadata": {
    "name": "my-nginx-b7d7bc74d-7t7nr",
    "namespace": "default",
    "uid": "5363a259-411a-4658-9384-d136ce9b6519",
    "resourceVersion": "2893",
    "creationTimestamp": "2020-12-27T18:56:18Z"
  },
  "ephemeralContainers": [
    {
      "name": "netshoot",
      "image": "nicolaka/netshoot",
      "command": [
        "/bin/bash"
      ],
      "resources": {},
      "terminationMessagePath": "/dev/termination-log",
      "terminationMessagePolicy": "File",
      "imagePullPolicy": "IfNotPresent",
      "stdin": true,
      "tty": true,
      "targetContainerName": "nginx"
    }
  ]
}

Добавить комментарий