Если мы говорим о безопасности в Kubernetes, первым делом нужно защитить ключевые компоненты кластера (pod’ы) от внешнего воздействия и ограничить риски внутри самих pod’ов. Чем меньше процессов в pod’е, тем меньше уязвимостей в кластере.
В этой статье мы обсудим, почему рискованно сохранять в pod’е параметры количества процессов по умолчанию, и как решить проблему.
Конфигурация по умолчанию
По умолчанию в Kubernetes pod’ы наследуют параметры количества процессов у хоста, а на хосте установлено очень большое значение. Узнать его можно командой:
$ cat /proc/sys/kernel/pid_max
Риски конфигурации по умолчанию
Если в pod’е число процессов не ограничено или лимит очень большой, злоумышленник может запустить ветвящуюся бомбу (fork bomb), что приведёт к отказу в обслуживании и сбою системы из-за нехватки ресурсов.
Профилактика
В Kuberbetes 1.20 есть стабильная функция, которая позволяет ограничивать количество процессов в pod’е на уровне ноды в конфигурации kubelet.
Полную конфигурацию kubelet см. здесь.
Давайте посмотрим пример. Что нам понадобится:
Работающий кластер Kubernetes (можно использовать minikube или kind).
Знакомство с командами kubectl.
Общее представление об администрировании Linux.
В этом примере мы используем minikube.
Что делать
Часть 1. Сначала мы проверим конфигурацию по умолчанию — сколько процессов в pod’е разрешено.
Запускаем кластер Kubernetes через minikube:
Примечание. Нужна версия Kubernetes не ниже 1.20. На момент написания статьи minikube использовал версию 1.23.3.
$minikube start
Проверяем, что нода кластера запущена:
$ kubectl get nodes
Выполняем команду, чтобы запустить развёртывание nginx:
echo "apiVersion: apps/v1
kind: Deployment
metadata:
name: pid-limit
labels:
app: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
volumes:
containers:
- name: pid-limit
image: busybox:1.28
command: [ "sh", "-c", "sleep 1h" ]
ports:
- containerPort: 80
" | kubectl apply -f -
Проверяем, что pod nginx запущен:
$ kubectl get pods
Давайте проверим лимит процессов в этом pod’е. Сначала подключимся к pod’у по SSH:
Примечание. Замените <POD_NAME> именем вашего pod’а.
$ kubectl exec -it <POD_NAME> -- sh
Смотрим значение в /proc/sys/kernel/pid_max:
$ cat /proc/sys/kernel/pid_max
4194304
Как видите, количество процессов практически не ограничено.
Примечание. Это значение унаследовано из того же пути в ноде Kubernetes.
Мы можем в этом убедиться, подключившись к ноде кластера Kubernetes командой minikube ssh и выполнив ту же команду cat /proc/sys/kernel/pid_max.
Часть 2. В этой части мы проверим, действительно ли большое количество процессов создаёт проблему.
Подключаемся к pod’у:
$ kubectl exec -it <POD_NAME> -- sh
Создаём простой shell-скрипт с циклом для запуска 100 процессов. Я обычно работаю в vi, но вы можете выбрать любой редактор. Я создаю 100 процессов, чтобы показать, что число процессов в pod’е не ограничено. Вы можете указать другое число или выбрать другой способ ветвления процессов.
# vi test.sh
Этот скрипт запустит 100 процессов в фоновом режиме:
#!/bin/sh
for i in `seq 1 100`
do
sleep 1h &
done
Сохраняем файл, добавляем разрешение на выполнение и выполняем его:
# chmod +x test.sh
# ./test.sh &
Проверяем выполняющиеся процессы:
# ps -ef
Мы видим, что все процессы запустились и им ничего не помешало. В реальной жизни процессов будет не 100. Они будут бесконтрольно ветвиться, поглощая ресурсы системы, пока она не остановится.
Часть3. В этой части мы изменим конфигурацию kubelet, чтобы задать лимит процессов в pod’е.
Выполняем следующую команду и входим на ноду minikube как пользователь root, чтобы можно было редактировать файл конфигурации kubelet:
$ minikube ssh
$ su -
# vi /var/lib/kubelet/config.yaml
Добавляем следующую строку в конец файла, чтобы велеть kubelet запускать pod максимум с 10 процессами, и сохраняем файл:
podPidsLimit: 10
Перезапускаем сервис kubelet, чтобы применить новую конфигурацию:
# systemctl restart kubelet
Сейчас у нас два типа pod’ов:
Выполняющиеся pod’ы, которые нужно перезапустить, чтобы к ним применилась конфигурация kubelet.
Новые pod’ы, которые будут запущены уже с новой конфигурацией.
Перезапустим развёртывание, используя стратегию rollout:
$ kubectl rollout restart deployment/pid-limit
Подключаемся к pod’у по SSH и выполняем тот же shell-скрипт, что и раньше. Мы видим сообщение о том, что процессы больше ветвить нельзя, потому что мы достигли лимита:
По желанию можно вывести запущенные процессы, чтобы убедиться, что скрипт создавал процессы, пока не был достигнут лимит, указанный в конфигурации kubelet:
Заключение
В этой статье мы настроили в kubelet ограничение на количество процессов, которые можно запустить в pod’e Kubernetes. Без этого ограничения существовал риск того, что процессы будут ветвиться, пока не израсходуют все ресурсы и не уронят кластер.
Курс «Безопасность в Kubernetes» с практикой на стендах.
Комментарии (2)
Pycz
13.01.2023 18:07+1Вот бы было классно, если можно бы было это на уровне подов/деплойментов организовать. Конкретно интересует в разрезе реализации песочницы для кода, в которой запускается произвольный код - для проверки решений задач по спортивному программированию, например.
TyVik
А для ограничения inode есть что-то подобное?
Вообще была бы интересна статья о побеге из этой песочницы или хотя бы возможности выглянуть в ноду. Помню, что в моей системе смог узнать модель жёсткого диска, это как пример.