ОС Альт СП Kubernetes

Материал из ALT Linux Wiki

Используемые инструменты контейнеризации

В настоящее время для разворачивания решений контейнеризации используются два основных альтернативных инструментария:

  • dockerd, containerd (serverfull) - серверный вариант запуска контейнеров. Клиенты через CLI-команду docker обращаются к dockerd-демону, который через сервис containerd запускает контейнеры.
  • podman, cri-o (serverless) - CLI-команда podman и kubernetes-движок cri-o непосредственно запускают контейнеры бех обращения к серверу.

В рамках дистрибутива ALTLinux реализовано решение podman, cri-o по следующим причинам:

  • podman, cri-o поддерживают политики контейнеризации, позволяющие организовать группы пользователей с разными правами доступа к docker-образам при запуске контейнеров. Это обеспечивает поддержку «Требования по безопасности информации к средствам контейнеризации» приказа № 118 ФСТЭК России.
  • podman, cri-o по своей идеологии разработки (serverless) поддерживают rootless-режим запуска контейнеров.

Дерево используемых интсрументов контейнеризации

rootfull и rootless режим запуска контейнеров

podman

CLI команда podman является аналогом CLI команды docker и поддерживает аналогичный набор параметров. Команда docker для запуска контейнеров обращается к dockerd-демону, работающему на том же или другом компьютере. Контейнер запускается на том же узле, где работает демон dockerd обычно с правами root. В этом случае все контейнеры запускаются в rootfull режиме независимо от какого пользователя запущена CLI команда docker.

CLI команда podman запускает контейнеры на том же узле, где была запущена не обращаясь к отдельному демону. Контейнеры запускаются с правами того пользователя, от имени которого была запущена команда podman. Это позволяет организовать как rootfull, так и rooless режим запуска контейнера.

rootfull режим запуска контейнеров

До появления группы команд podsec, cri-o все контейнеры запускались в rootfull режиме.

Основное отличие rootfull режима от rottless - запуск образов в которых в качестве пользователя указан пользователь root.

# podman inspect <образ> | jq .[0].User
root

В rootfull режиме процесс работающий в контейнере работает от имени пользователя root с идентификатором (UID) 0. Причем и в рамках HOST-системы в которой запущен контейнер это процесс также имеет идентификатор UID=0. Если в контейнер примонтирован каталог HOST-системы, то злоумышленник через команду

podman exec -i <id_conteiner> rm -rf <каталог_монтирования> 

может удалить монтируемый каталог HOST-системы или изменить его. Podman rootfull.drawio.png

rootless режим запуска контейнеров

rootless режим запуска контейнеров позволяет запускать образы в которых в качестве пользователя указан пользователь root таким образом, что процесс работающий в контейнере работает от имени пользователя root с идентификатором (UID), а в рамках HOST системы этом процесс имеет идентификатор пользователя который запустил данный контейнер. Podman rootless1.png Данный режим доступен для пользователей входящих в группу podman. Он включается командой:

# control newgidmap podmanonly
# control newuidmap podmanonly

Эти команды для команд /usr/bin/newuidmap, /usr/bin/newguidmap устанавливают флаг cap_setuid=ep при котором они запускаются с правами root для пользователей, входящих в группу podman:

# ls -l /usr/bin/newuidmap /usr/bin/newgidmap
-rwx--x--- 1 root podman 41440 апр 26  2024 /usr/bin/newgidmap
-rwx--x--- 1 root podman 41440 апр 26  2024 /usr/bin/newuidmap
# getcap /usr/bin/newuidmap /usr/bin/newgidmap
/usr/bin/newuidmap cap_setuid=ep
/usr/bin/newgidmap cap_setgid=ep

kubernetes

Системные требования

Минимальные системные требования:

  • 2GB ОЗУ или больше;
  • 2 ядра процессора или больше;
  • Все машины должны быть доступны по сети друг для друга;
  • Все машины должны успешно разрешать имена hostname друг друга (через DNS или hosts);
  • Своп должен быть выключен.

Для разворачивания нижеописанных кластеров достаточно несколько (две и более) машин (nodes), одна из которых будет мастер (controlplane)-узлом. Остальные узлы будут рабочими (worker).

controlplane-узлы обеспечивают управление кластером и поддерживают по порту 6443 kubernetes API.

Работа с кластером (создание deployment's, replicasetreplicaset's, podreplicaset's, configmap's, ...) производится через этот API либо внешними приложениями либо через CLI команду kubeclt.

Если разворачивается более одного controlplane-узла, то необходимо либо вручную перераспределять запросы к ним, либо разворачивать в рамках кластера балансировщик для равномерного распределения API-запросов между controlplane-узлами кластера.

Все пользовательские POD'ы (контейнеры) разворачиваются на worker узлах. В связи с этим при необходимости для них можно выделить большее количество оперативной, дисковой памяти и большее число ядер процессора.

Если на controlplane-узлах не предполагается большой нагрузки, то их можно перестроить на поддержку в том числе и пользовательских POD'ов. В этом случае "kubernetes-кластер" можно развернуть и на одном узле.

Варианты разворачивания ОС Альт СП kubernetes

Режим работы пользователя root кластера:

  • rootfull режим - пользователь root кластера и контейнеров в нем имеет равные права с пользователем root HOST-системы.
  • rootless режим - пользователь root кластера и контейнеров в нем имеет права обычного пользователя в HOST-системе (fake-root).

Использование политик доступа:

  • В незащищенном режиме.
  • В защищенном режиме с использованием политик доступа.

Использование регистраторов:

  • внешний регистратор ;
  • внутренний регистратор registry.local или комбинированный вариант;

rootfull kubernetes

При разворачивании kubernetes кластера в rootfull режиме на каждом узле systemd-сервис kubelet разворачивается с привелегиями пользователя root. Остальные сервисы кластера, разворачиваемые сервисом kubelet из docker-образов kubernetes также разворачиваются с привелегиями пользователя root. Все PODы, запускаемые в рамках кластера от docker-образов с пользователем root (например стандартный образ nginx) также имеют привелегии пользователя root HOST-системы. Дерево процессов rootfull kubernetes

Работа в режиме rootfull несет определенные риски, так как злоумышленник может в POD'е с привелегиями root примонтировать каталог файловой системы HOST-узла (включая корневой) и получить доступ с правами root к файлам этого каталога вплоть до их удаления.

В kubernetes версии 1.33 и старше появилась возможность запуска POD'ов в rootless-режиме. Но это касается только POD'ов пользователя. Системные kubernetes POD'ы работают c приведениями пользователя root и при наличии в них уязвимостей злоумышленник может их использовать для атаки на kubernetes-кластер.

Для исключения этих уязвимостей есть возможность разворачивать кластер в rootless-режиме. Этот вариант разворачивания описал в главе [1]. Этот вариант разворачивания также позволяет:

  • Создавать пользователей классов:
  1. Администратор кластера - управление кластером, создание пользователей, мониторинг работы кластера.
  2. Создатель образов - создание образов, их подпись и помещение в docker-регистратор.
  3. Пользователь образов - запуск POD'ов подписанных образов только с ограниченного перечня docker-регистраторов.
  • Мониторить состояние кластере на предмет:
  1. наличия уязвимостей в образах и контейнерах;
  2. подозрительных изменений в каталогах работающего POD'аж
  3. изменения политик безопасности;
  4. подозрительных запросов к API-кластера;
  5. и другие

Перечисленные возможности также можно развернуть и в режиме rootfull. Далее рассматриваются все варианты разворачивания с их применением.

rootless Kubernetes (podsec-k8s)

RootlessTree.png При разворачивании kubernetes кластера в rootless режиме на каждом узле systemd-сервис kubelet разворачивается командой nsenter с окружении обычного пользователя u7s-admin. Остальные сервисы кластера, разворачиваемые сервисом kubelet из docker-образов kubernetes также разворачиваются командой rootlessctl в окружени пользователя u7s-admin. Все PODы, запускаемые в рамках кластера от docker-образов с пользователем root (например стандартный образ nginx) в рамках POD'а имеют владельца root с идентификтором UID=0, позволяющего работать с ядром системы с привелегиями суперпользователя. В рамках же HOST-системы POD работает с привелегиями обычного пользователя u7s-admin с идентификтором UID=486.

Дерево процессов rootless kubernetes

Это значительно снижает поверхность атаки на кластер, так как злоумышленник, войдя в контейнер может нарушить его работу, но в рамках HOST-системы имея права обычного пользователя он не сможет существенно нарушить работу системы.

Для разворачивание rootless кластера необходимо установить необходимые RPM-пакеты командой:

apt-get install podsec podsec-k8s podsec-inotify

Разворачивание rootless kubernetes возможно в следующих режимах:

  • На платформах c10f в защищенном режиме с ISO-диска в локальной сети без доступа в Интернет;
  • На платформах ALTLinux p10, p11, ... с использованием внешнего docker-регистратора kubernetes образов.

В свою очередь на платформах ALTLinux p10, p11, ... возможно разворачивание как в защищенном, так и незащищенном режиме.

Защищенный режим предполагает наличие политик доступа, обеспечивающих наличие классов пользователей с различными правами доступа:

  • Администратор - создание пользователей других классов, мониториг работы кластера.
  • Создатель образов - создание образов, их подписывание и размещение на локальном или внешнем регистраторе.
  • Пользователь образов - запуск POD'ов, Deployments, RepliceSets, ... на основе подписанных образов на локальном или внешнем регистраторе.

Пользователь образов и Администратор могут использовать в своей работе только подписанные образы и только с сконфигурированного регистратора. При ручной правке конфигураций есть возможность использовать нескольких регистраторов с образами подписанными несколькими пользователями класса Создатель образов.

При разворачивании rootless kubernetes с доступом в Интернет можно использовать различные регистраторы docker-образов:

  • ALTLinux - docker-регистратор registry.altlinux.org (по умолчанию);
  • Нативный kubernetes - docker-регистратор registry.k8s.io;
  • Другие docker-регистраторы образов kubernetes.

Балансироващик REST-запросов к кластеру

Установка балансировщика REST-запросов haproxy

Если в вышеописанных вариантах разворачивания rootfull Или rootless-кластера устанавливается более одного controlplane узла, то по умолчанию распределять API-запросы к этим узлам кластера необходимо вручную.

rootless kubernetes-кластер без балансировщика haproxy

Ручная балансировка запросов к API-интерфейсам master-узлов путем указания у клиентов адресов различных master-узлов довольно неудобна, так как не обеспечивает равномерного распределения запросов по узлам кластера и не обеспечивает автоматической отказоустойчивости при выходе из строя master-узлов.

Решает данную проблему установка балансировщика нагрузки haproxy.

rootless kubernetes-кластер без балансировщика haproxy

Перевод кластера в режим балансировки запросов через haproxy возможен, но данная процедура не гарантирует корректный перевод на всех версиях kubernetes и ее не рекомендуют применять на production кластерах.

Так что наиболее надежным способом создания кластера с балансировкой запросов является создание нового кластера.

Настройка балансировщика REST-запросов haproxy

Балансировщик REST-запросов haproxy можно устанавливать как на отдельный сервер, так на один из серверов кластера.

Variant haproxy master.drawio.png

Если балансировщик устанавливается на rootless сервер кластера, то для балансировщика необходимо выделить отдельный IP-адрес. Если на этом же сервере функционируют локальный регистратор (registry.local) и сервер подписей (sigstore.local), то IP-адрес балансировщика может совпадать c IP-адресами этих сервисов.

Если планируется создание отказоустойчивого решения на основе нескольких серверов haproxy, то для них кроме собственного IP-адреса необходимо будет для всех серверов haproxy выделить один общий IP-адрес, который будет иметь master-балансировщик.


Полная настройка отказоустойчивого кластера haproxy из 3-х узлов описана в документе ALT Container OS подветка K8S. Создание HA кластера.

Здесь же мы рассмотрим создание и настройка с одним сервером haproxy с балансировкой запросов на master-узлы.

Установите пакет haproxy:

# apt-get install haproxy

Отредактируйте конфигурационный файл /etc/haproxy/haproxy.cfg:

  • добавьте в него описание frontend’a main, принимающего запросы по порту 8443:
     frontend main
      bind *:8443
      mode tcp
      option tcplog
      default_backend apiserver
    
  • добавьте описание backend’а apiserver:
    backend apiserver
      option httpchk GET /healthz
      http-check expect status 200
      mode tcp
      option ssl-hello-chk
      balance     roundrobin
      server master01 <IP_или_DNS_начального_мастер_узла>:6443 check
      server master02 <IP_или_DNS_второго_мастер_узла>:6443 check
      ...
    
  • запустите haproxy:
# systemctl enable haproxy
# systemctl start haproxy

Настройка отказоустойчивого кластера серверов haproxy, keepalived

Масштабирование haproxy, установка пакетов

Если необходимо создать отказоустойчивое решение допускающее выход haproxy-севрера из строя установите haproxy на несколько серверов. Файлы конфигурации haproxy<.code> на всех сервервх должны быть идентичны.

Для контроля доступности haproxy и переназначений виртуального адреса дополнительно установите на каждом сервис keepalived:

# apt-get install haproxy keepalived

Конфигурирование keepalived

kubeenetes кластер с haproxy и keepalived

Создайте файл конфигурации 'keepalived' /etc/keepalived/keepalived.conf:

! /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    router_id LVS_K8S
}
vrrp_script check_apiserver {
  script "/etc/keepalived/check_apiserver.sh"
  interval 3
  weight -2
  fall 10
  rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface br0
    virtual_router_id  51
    priority 101
    authentication {
        auth_type PASS
        auth_pass 42
    }
    virtual_ipaddress {
        10.150.0.160 
    }
    track_script {
        check_apiserver
    }
}

На одном из узлов установите параметр state в значение MASTER и параметр priority в значение 101. На остальных параметр state в значение BACKUP и параметр priority в значение 100.

Скрипт /etc/keepalived/check_apiserver.sh проверяет доступность балансировщика haproxy:

#!/bin/sh

errorExit() {
    echo "*** $*" 1>&2
    exit 1
}

APISERVER_DEST_PORT=8443
APISERVER_VIP=10.150.0.160
curl --silent --max-time 2 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"
if ip addr | grep -q ${APISERVER_VIP}; then
    curl --silent --max-time 2 --insecure https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_DEST_PORT}/"
fi

Параметр APISERVER_DEST_PORT задает порт балансировщиков haproxy, параметр APISERVER_VIP виртуальный адрес, через который будут взаимодействовать master (control plane) узлы кластера k8s.

Скрипт проверяет работоспособность haproxy на локальной машине.

Подробности см. на [[1]] А если в настоящее время виртуальный адрес принадлежит текущему узлу, то и работоспособность haproxy через виртуальный адрес.

Добавьте флаг на выполнение скрипта:

chmod a+x /etc/keepalived/check_apiserver.sh

При работающем балансировщике и хотя бы одному доступному порту 6443 на master-узлах скрипт должен завершаться с кодом 0.

Подробности см. на Keepalived

Разворачивание rootless кластера

Выбор варианта разворачивания

При разворачивании в режиме rootless в зависимости от использования локального регистратора docker-образов и применения политик доступа к ним можно обеспечить следующие варианты разворачивания rootless-кластера:

Варианты разворачивания rootless кластера
Локальный регистратор Политики доступа Примечание
Да Да Стандартный вариант при использовании команд пакета podsec-k8s: Возможность разворачивания в локальной сети без доступа в Интернет с поддержкой классов пользователей с ограничением доступа к docker-регистраторам
Да Нет Возможность разворачивания в локальной сети без доступа в Интернет. Политики доступа расширены или отсутствуют.
Нет Да Поддержка классов пользователей с ограничением доступа к docker-регистраторам. Политики доступа стандартные или расширены.
Нет Нет Стандартный вариант разворачивания rootfull-решений, но в поддержкой работы узлов кластера в rootless-режиме

Разворачивание с локальным регистратора и с политиками доступа в rootless режиме

Основной особенностью при разворачивании rootless кластера с ISO-диска в защищённом режиме без доступа в Интернет является то, что docker-образы загружаются командой podsec-load-sign-oci в локальный регистратор registry.local из каталога /media/ALTLinux/containers/ примонтированного диска. InitRegistryFromDVD 1.drawio.png Каталог содержит два файла-архива слоев образов в формате OCI:

  • base_amd64.tar.xz - базовые образы для создания новых образов;
  • k8s_amd64.tar.xz - kubernetes-образы для разворачивания кластера.

Разворачивании rootless кластера как и rootfull кластера производится через команду kubeadm. Формат вызова команды на Master ControlPlane, остальных ControlPlane's и Worker's аналогичен. Основные отличия:

  • Для Master ControlPlane необходимо не один, а два IP-адреса.
  • На Master ControlPlane устанавливаются дополнительные сервисы.
  • Запускается не нативная команда kubeadm из каталога /usr/bin, а оболочка над ним, обеспечивающая создания rootless-окружения пользователя u7s-admin.

Дополнительные сервисы включают в себя:

  • регистратор docker-образов с доменом registry.local;
  • WEB-сервер открытых ключей и Container Network Policy с доменом sigstore.local;
  • сервер анализа уязвимостей trivy.

Установка пакетов

Установите пакеты podsec:

apt-get install podsec-k8s podsec podsec-k8s-rbac podsec-inotify trivy

Выделение IP адресов для Master ControlPlane

Из за особенностей rootless-режима для Master ControlPlane необходимо выделит два IP-адреса. Один адрес будет использоваться в rootless-окружения, второй для обращения к основным ControlPlane сервисам Master узла.

В качестве примера выделим два адреса (в общем случае метод выделения может быть другим):

  • 192.168.122.70 - IP-адрес для rootless окружения;
  • 192.168.122.80 - IP- адрес для обращения к основным ControlPlane сервисам Master узла.

Допустим мы имеем на узлу интерфейс enp1s0. В этом случае структура файлов каталога /etc/net/ifaces/enp1s0 описания интерфейса ensp1s0 будет выглядеть следующим образом:

Файл options:

BOOTPROTO=static
TYPE=eth
CONFIG_WIRELESS=no
SYSTEMD_BOOTPROTO=static
CONFIG_IPV4=yes
DISABLED=no
NM_CONTROLLED=no
SYSTEMD_CONTROLLED=no

Файл ipv4address:

192.168.122.70/24
192.168.122.80/24

Файл ipv4route:

default via 192.168.122.1

Файл resolv.conf:

nameserver 192.168.122.1

После конфигурации данных файлов необходимо перезагрузить узел.

Интерфейс для данных параметров после перезагрузки выглядит следующим образом:

# ip a show dev enp1s0
2: enp1s0:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:db:e1:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.70/24 brd 192.168.122.255 scope global enp1s0
       valid_lft forever preferred_lft forever
    inet 192.168.122.80/24 brd 192.168.122.255 scope global secondary enp1s0
       valid_lft forever preferred_lft forever
    ...

Создание файлов конфигурации Container Network Policy на Master ControlPlane в rootless режиме

Для создания Container Network Policy на Master узле вызовите команду:

# podsec-create-policy 192.168.122.70 # ip-aдрес_регистратора и WEB-сервера подписей
Добавление привязки доменов registry.local sigstore.local trivy.local к IP-адресу 192.168.122.70
Создание группы podman
Инициализация каталога /var/sigstore/ и подкаталогов хранения открытых ключей и подписей образов
Создание каталога и подкаталогов  /var/sigstore/
Создание группы podman_dev
Создание с сохранением предыдущих файла политик /etc/containers/policy.json
Создание с сохранением предыдущих файл /etc/containers/registries.d/default.yaml описания доступа к открытым ключам подписантов
Добавление insecure-доступа к регистратору registry.local в файле /etc/containers/registries.conf
Настройка использования образа registry.local/k8s-c10f1/pause:3.9 при запуска pod'ов в podman (podman pod init)

KuberDeployment Master1.drawio.png

После выполнения команды:

  • файл /etc/host должен содержать строку:
...
192.168.122.70 registry.local sigstore.local trivy.local
  • файл /etc/containers/policy.json, являющийся symlink'ом к файлу /etc/containers/policy_YYYY-MM-DD_HH:mm:SS должен иметь содержимое (запрет доступа по всем ресурсам):
{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {}
  }
}
  • файл /etc/containers/registries.d/default.yaml, являющийся symlink'ом к файлу /etc/containers/registries.d/default_YYYY-MM-DD_HH:mm:SS должен иметь содержимое (ю URLs доступа к серверу подписей):
default-docker:
  lookaside: http://sigstore.local:81/sigstore/
  sigstore: http://sigstore.local:81/sigstore/

Данные файлы конфигурируют сервис cri-o, который контролирует доступ к docker-образам согласно указанным политикам. На данный момент файл /etc/containers/policy.json запрещает доступ к образам по всем видам транспортов, кроме доступа к docker-регистраторам. На данный момент список регистраторов пуст.

Разворачивание дополнительных сервисов на Master ControlPlane в rootless режиме

KuberDeployment Master2+.drawio.png

Для разворачивание дополнительных сервисов на Master ControlPlane поднимите сервисы регистратора и WEB-сервера подписей командой:

# podsec-create-services

Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install. Executing: /lib/systemd/systemd-sysv-install enable nginx Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service. registry Created symlink /etc/systemd/system/multi-user.target.wants/docker-registry.service → /lib/systemd/system/docker-registry.service.

Проверьте функционирование сервисов:

# netstat -nlpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
...
tcp        0      0 0.0.0.0:81                  0.0.0.0:*                   LISTEN      14996/nginx -g daem
...
tcp        0      0 :::80                       :::*                        LISTEN      15044/docker-regist
...
tcp        0      0 :::4954                     :::*                        LISTEN      30864/trivy

...

Порт 81 должен слушать сервис nginx. Порт 80 ервис docker-registry. Порт 4954 сервис trivy.

Создание на Master ControlPlane пользователей различных классов

При разворачивании kubernetes в rootless режиме в системе могут существовать пользователи нескольких классов:

  • Администратор системы - пользователь root.
  • Создатель образов контейнеров.
  • Пользователь информационной системы.
  • Обычный пользователь.
Администратор системы

Роль администратора системы выполняет пользователь root:

  • имеющий права создавать других пользователей
  • имеющий права просматривать системные логи в том числе логи по безопасности;
  • не имеющий права создавать новые образы и
  • может (как и пользователь информационной системы) использовать только подписанные образы образы с регистратора registy.local.
Создание пользователя разработчика образов контейнеров

Разработчика образов контейнеров в отличие от других пользователей имеет доступ ко всем образам по всем протоколам. Это необходимо для возможности создания новых образов для локального регистратора registry.local. Кроме этого пользователь этого класса при создании формирует электронную подпись, которую может использовать при размещении образов на регистраторе registry.local.

Открытые ключи всех разработчиков образов для проверки подписи при их загрузке с регистратора registry.local хранятся в каталоге /var/sigstore/keys.

Для создания пользователя разработчик образов контейнеров выполните команду указав имя пользователя (обычно imagemaker):

# podsec-create-imagemakeruser <имя_пользователя_создателя образов>

Во время выполнения команды необходимо:

  • дважды ввести пароль для пользователя;
  • выбрать тип и длину GPG-ключа;
  • указать срок действия ключа;
  • ввести E-mail для ключа;
  • ввести пароль для подписывания образа.

Файл /etc/containers/policy.json, должен изменить symlink на другой файл /etc/containers/policy_YYYY-MM-DD_HH:mm:SS с содержимым (разрешение доступа к регистратору registry.local с открытым ключом пользователя imagemaker):

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker.pgp"
        }
      ]
    }
  }
}

Должен появится каталог /var/sigstore/ со следующей структурой:

├── index.html
├── keys
│   ├── imagemaker.pgp
│   └── policy.json
└── sigstore

Проверьте доступ к этому каталогу через http:

# curl -s  http://sigstore.local:81/keys/ | jq
[
  {
    "name": "imagemaker.pgp",
    "type": "file",
    "mtime": "Tue, 23 May 2023 05:43:59 GMT",
    "size": 2436
  },
  {
    "name": "policy.json",
    "type": "file",
    "mtime": "Tue, 23 May 2023 05:43:25 GMT",
    "size": 276
  }
]

Команду podsec-create-imagemakeruser можно запускать без параметров или задать несколько пользователей этого класса.

При запуска без параметров создается пользователь с именем imagemaker.

При необходимости задания нескольких пользователей к имени каждого пользователя должен быть добавлен суффикс @registry.local для указания, что все подписанные ими образы помещаются в регистратор registry.local. Вызов в этом случае выглядит так:

# podsec-create-imagemakeruser <имя_пользователя1>@registry.local  <имя_пользователя2>@registry.local ...
Создание пользователя информационной системы

Пользователь информационной системы имеет право запускать только подписанные образы с локального регистраторв registry.local.

Для создания пользователя информационной системы вызовите команду:

# podsec-create-podmanusers <имя_пользователя_информационной_системы1> [<имя_пользователя_информационной_системы2> ...]

Во время выполнения команды необходимо дважды ввести пароль для пользователя

Обычный пользователь

Обычный пользователь также имеет право запускать только подписанные образы с локального регистраторв registry.local, но для него корректный запуск контейнеров командами podman, crictl, ... не гарантируется. Создается он обычными командами создания пользователей системы.

Доступ к OCI-архиву базовых и kubernetes образов

Методы получения OCI-архивов

OCI-архивы базовых и kubernetes образов располагаются на ISO-диске в каталоге containers под именами base_amd64.tar.xz и k8s_amd64.tar.xz соответственно. Их следует скопировать в домашний каталог пользователя разработчика образов контейнеров. Вы можете самостоятельно сформировать эти архивы для разворачивания последней доступной версии kubernetes.

Формирование OCI-архива kubernetes-образов с регистратора registry.altlinux.org

Для подъема регистратора docker-образов необходимо проверить наличие и при необходимости установить установить пакет podsec-dev и пакеты podsec-k8s podsec:

apt-get install podsec-dev podsec-k8s podsec

OCI-архив kubernetes-образов представляет собой сжатый командой xz tar-архив каталога kubernetes образов в формате OCI. Хранение в формате OCI позволяет минимизировать размер архива с нескольких гигабайт до 2-х сотен мегабайт. Это достигается за счет того, что OCI-каталоге образов каждый слой образов встречается однократно. А так как kubernetes-образы имеют много общих слоев, размер OIC-каталога в несколько раз меньше, чем суммарный объем архивов образов, сохраненных командой podman save. Кроме того образы, записываются в OCI-архив с несжатыми слоями. Это позволяет эффективно их сжимать компрессором xz.

Для формирования OCI-архива kubernetes-образов из под пользователя root:

  • перейдите в каталог /root
  • если в каталоге уже существует подкаталог k8s_amd64 переименуйте его или удалите;
  • вызовите команду:
podsec-k8s-save-oci k8s_amd64 amd64 
  • Первым параметром указывается имя каталога в который помещается архив amd64.tar.xz или arm64.tar.xz в зависимости от архитектуры.
  • Вторым - имя архитектуры (amd64 или arm64).

Команда:

  • устанавливает последние версии пакетов kubeadm, crio;
  • определяет список kubernetes-образов последней версии, включая образы etcd и flannel;
  • скачивает их с регистратора registry.altlinux.org и помещает в каталог /root/.local/share/usernetes/images/c10f/amd64/ в формате containers-storage;
  • копирует образы из каталога /root/.local/share/usernetes/images/c10f/amd64/ в каталог k8s_amd64/amd64 в формате oci;
  • по окончании копирования всех образов архивирует каталог k8s_amd64/amd64 командой tar, сжимает архив и помещает в файл amd64.tar.xz указанного каталога k8s_amd64.

Для архивации других образов (например базовых c10f/distroless-base, c10f docker-регистратора registry.altlinux.org) необходимо в переменной U7S_ALTREGISTRY указать имя docker-регистратора и вызвать команду podsec-save-oci.

export U7S_ALTREGISTRY=registry.altlinux.org  
podsec-save-oci base_amd64 amd64 '' c10f/distroless-base c10f/alt
  • Первым параметром указывается имя каталога в который помещается архив amd64.tar.xz.
  • Вторым - имя архитектуры (amd64 или arm64).
  • Третьим - тип транспорта (по умолчанию docker://).
  • В последующих параметрах указываются список архивируемых образов.

Команда:

  • устанавливает последние версии пакетов kubeadm, crio;
  • принимает список архивируемых образов;
  • скачивает их с указанного переменной среды U7S_ALTREGISTRY регистратора и помещает в каталог /root/.local/share/usernetes/images/c10f/amd64/ в формате containers-storage;
  • копирует образы из каталога /root/.local/share/usernetes/images/c10f/amd64/ в каталог k8s_amd64/amd64 в формате oci;
  • по окончании копирования всех образов архивирует каталог k8s_amd64/amd64 командой tar, сжимает архив и помещает в файл amd64.tar.xz указанного каталога base_amd64.

Если Вам необходимо архивировать образы архитектуры arm64 укажите вторым параметром команд arm64.

Полученные OCI-архивы следует скопировать в домашний каталог пользователя разработчика образов контейнеров.

Подписывание образов и размещения на локальном регистраторе

KuberDeployment Master3.drawio.png

Загрузите kubernetes-образы от пользователя imagemaker.

$ podsec-load-sign-oci <имя_файла_OCI-архива> amd64 <E-mail_подписанта>

Во время выполнения скрипта будет запрошен пароль для подписи. Ввод пароля подписи

Внимание: Данную команду нельзя запускать путем получения прав пользователя через команду su - imagemaker, так как устанавливаются не все переменные среды. Сделайте полный заход под пользователем, например по протоколу ssh:

# ssh imagemaker@localhost

или через machinectl:

# machinectl shell imagemaker@

После выполнения скрипта проверьте наличие образов в регистраторе:

# curl -s registry.local/v2/_catalog | jq
{
  "repositories": [
    "c10f/coredns",
    "c10f/etcd",
    "c10f/flannel",
    "c10f/flannel-cni-plugin",
    "c10f/kube-apiserver",
    "c10f/kube-controller-manager",
    "c10f/kube-proxy",
    "c10f/kube-scheduler",
    "c10f/pause",
    "k8s-c10f2/coredns",
    "k8s-c10f2/etcd",
    "k8s-c10f2/flannel",
    "k8s-c10f2/flannel-cni-plugin",
    "k8s-c10f2/kube-apiserver",
    "k8s-c10f2/kube-controller-manager",
    "k8s-c10f2/kube-proxy",
    "k8s-c10f2/kube-scheduler",
    "k8s-c10f2/pause"
  ]
}

Внимание: Кроме образов с префиксом c10f/ в результате запроса могкт быть отображены образы с префиксами k8s-c10f1, k8s-c10f2. Это связано с обеспечением совместимости предыдущего способа именования префикса образов k8s-c10f1<N> и текущего c10f.

Проверьте наличие подписей в каталоге /var/sigstore/sigstore/k8s-c10f2:

# tree /var/sigstore/sigstore/c10f/
/var/sigstore/sigstore/c10f/
├── coredns@sha256=...
│   └── signature-1
├── etcd@sha256=...
│   └── signature-1
├── flannel-cni-plugin@sha256=...
│   └── signature-1
├── flannel@sha256=...
│   └── signature-1
├── kube-apiserver@sha256=...
│   └── signature-1
├── kube-controller-manager@sha256=...
│   └── signature-1
├── kube-proxy@sha256=...
│   └── signature-1
├── kube-scheduler@sha256=...
│   └── signature-1
└── pause@sha256=...
    └── signature-1

Число подкаталогов должно совпадать с числом образов в регистраторе и каждый подкаталог должен иметь файл signature-1.

Создание дополнительных образов и размещение их в локальном регистраторе

Копирование образов с других регистраторов

Для копирования образа (например nginx) с внешнего регистратора загрузите его в пользователе разработчика образов контейнеров:

[imagemaker@master1 ~]$ podman pull registry.altlinux.org/c10f/nginx:1.26.3
Trying to pull registry.altlinux.org/c10f/nginx:1.26.3...
Getting image source signatures
Copying blob 1bc1a80e4a16 done   | 
Copying blob 82e02127e2b4 skipped: already exists  
Copying blob 98ed66316f15 done   | 
Copying blob a2bb93664add skipped: already exists  
Copying blob b43be2116c2b done   | 
Copying blob 4df9de94b01c done   | 
Copying blob 5c2c8c67033f done   | 
Copying config d8a1de134d done   | 
Writing manifest to image destination
d8a1de134d2ea60956f987b6c47acdac0e8dcc863c6a98e16210f9a82e860932

Навешайте на него тег с именем локального регистратора registry.local:

[imagemaker@master1 ~]$ podman tag registry.altlinux.org/c10f/nginx:1.26.3 registry.local/c10f/nginx:1.26.3

Поместите образ в локальный регистратор, указав в флаге --sign-by E-mail пользователя введенные при создании ключей пользователя:

[imagemaker@master1 ~]$ podman push --tls-verify=false --sign-by="..." registry.local/c10f/nginx:1.26.3

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

Getting image source signatures
Copying blob f0e31397ae37 done   | 
Copying blob 60204c5adf60 done   | 
Copying blob 9a0fa9f6d6ea done   | 
Copying blob 5957b6b3c861 done   | 
Copying blob f06afe809992 done   | 
Copying blob e268fece4a97 done   | 
Copying blob 6cb900dcc924 done   | 
Copying config d8a1de134d done   | 
Writing manifest to image destination
Creating signature: Signing image using simple signing
Storing signatures
Создание собственных образов

Создайте каталог (например net-tools) для хранения артефактов образа и перейдите в него:

[imagemaker@master1]$ mkdir  net-tools
[imagemaker@master1]$ cd  net-tools

Сформируйте Dockerfile:

FROM registry.altlinux.org/c10f/alt

RUN apt-get update && \
    apt-get install -y tcpdump nmap bind-utils

Создайте от образа registry.altlinux.org/c10f/alt дочерний образ registry.local/net/net-tools:

[imagemaker@master1 net-tools]$ podman build -t registry.local/net/net-tools .
STEP 1/2: FROM registry.altlinux.org/c10f/alt
STEP 2/2: RUN apt-get update &&     apt-get install -y tcpdump nmap bind-utils
Get:1 http://update.altsp.su c10f2/branch/x86_64 release [3576B]
...
Fetched 46.1MB in 11s (3845kB/s)
Reading Package Lists...
Building Dependency Tree...
Reading Package Lists...
Building Dependency Tree...
The following extra packages will be installed:
...
Done.
COMMIT registry.local/net/net-tools
--> 06c7112c2901
Successfully tagged registry.local/net/net-tools:latest

Поместите образ в локальный регистратор, указав в флаге --sign-by E-mail пользователя введенные при создании ключей пользователя:

podman push --tls-verify=false --sign-by="kaf@basealt.ru" registry.local/net/net-tools:latest

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

Getting image source signatures
Copying blob 9a0fa9f6d6ea done   | 
Copying blob 08347d1dc3ae done   | 
Copying blob 5957b6b3c861 done   | 
Copying config 06c7112c29 done   | 
Writing manifest to image destination
Creating signature: Signing image using simple signing
Storing signatures

Разворачивание Master ControlPlane узла

Инициализация

KuberDeployment Master4.drawio.png

Измените переменную PATH:

export PATH=/usr/libexec/podsec/u7s/bin/:$PATH

Запустите команду:

# kubeadm -v 9 init  --apiserver-advertise-address 192.168.122.80

При запуске в параметре --apiserver-advertise-address укажите IP-адрес API-интерфейса kube-apiserver. Этот адрес должен отличаться от IP-адреса регистратора и WEB-сервера подписей.

Внимание: По умолчанию уровень отладки устанавливается в 0. Если необходимо увеличить уровень отладки укажите перед подкомандой init флаг -v n. Где n принимает значения от 0 до 9-ти.

Внимание: При запуске с балансировщиком запросов haproxy необходимо дополнительно указать параметр --control-plane-endpoint:

# kubeadm init --apiserver-advertise-address 192.168.122.80 --control-plane-endpoint <IP_адрес_haproxy>:<PORT_haproxy>

После:

  • генерации сертификатов в каталоге /etc/kuarnetes/pki,
  • загрузки образов, -генерации conf-файлов в каталоге /etc/kubernetes/manifests/, /etc/kubernetes/manifests/etcd/
  • запуска сервиса kubelet и Pod'ов системных kubernetes-образов

инициализируется kubernet-кластер из одного узла.

По окончании скрипт выводит строки подключения master(Control Plane) и worker-узлов:

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:.. --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join xxx.xxx.xxx.xxx:6443 --token ... --discovery-token-ca-cert-hash sha256:...

Запомните команды kubeadm join ... подключения controplane и worker узлов.


Для проверки состояния kubernetes из одного master-узла выполните команды:

# kubectl get daemonsets.apps -A
NAMESPACE      NAME              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-flannel   kube-flannel-ds   1         1         1       1            1           <none>                   102s
kube-system    kube-proxy        1         1         1       1            1           kubernetes.io/os=linux   8h

Число READY каждого daemonset должно быть равно числу DESIRED и должно быть равно числу узлов кластера.

# kubectl get nodes
NAME       STATUS   ROLES           AGE     VERSION
host-...    Ready    control-plane   ...   v1.xx.x

Проверьте работу всех ресурсов:

# kubectl get all -A
NAMESPACE     NAME                                   READY   STATUS    RESTARTS   AGE
kube-system   pod/coredns-c7df5cd6c-5pkkm            1/1     Running   0          19m
kube-system   pod/coredns-c7df5cd6c-cm6vf            1/1     Running   0          19m
kube-system   pod/etcd-host-212                      1/1     Running   0          19m
kube-system   pod/kube-apiserver-host-212            1/1     Running   0          19m
kube-system   pod/kube-controller-manager-host-212   1/1     Running   0          19m
kube-system   pod/kube-proxy-lqf9c                   1/1     Running   0          19m
kube-system   pod/kube-scheduler-host-212            1/1     Running   0          19m

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  19m
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   19m

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   19m

NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   2/2     2            2           19m

NAMESPACE     NAME                                DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-c7df5cd6c   2         2         2       19m

Состояние всех Pod’ов должны быть в 1/1.

Проверьте состояние дерева rootless-процессов:

# pstree
...
├─systemd─┬─(sd-pam)
│         ├─dbus-daemon
│         ├─nsenter.sh───nsenter───_kubelet.sh───kubelet───11*[{kubelet}]
│         └─rootlesskit.sh───rootlesskit─┬─exe─┬─conmon───kube-controller───7*[{kube-controller}]
│                                        │     ├─conmon───kube-apiserver───8*[{kube-apiserver}]
│                                        │     ├─conmon───kube-scheduler───7*[{kube-scheduler}]
│                                        │     ├─conmon───etcd───8*[{etcd}]
│                                        │     ├─conmon───kube-proxy───4*[{kube-proxy}]
│                                        │     ├─2*[conmon───coredns───8*[{coredns}]]
│                                        │     ├─rootlesskit.sh───crio───10*[{crio}]
│                                        │     └─7*[{exe}]
│                                        ├─slirp4netns
│                                        └─8*[{rootlesskit}]
...

Процесс kubelet запускается как сервис в user namespace процесса rootlesskit.

Все остальные процессы kube-controller, kube-apiserver, kube-scheduler, kube-proxy, etcd, coredns запускаются как контейнеры от соответствующих образов в user namespace процесса rootlesskit. Обеспечение запуска обычных POD’ов на мастер-узле

Установка режима запуска на Controlplane пользовательских POD'ов

По умолчанию на master-узле пользовательские Podы не запускаются. Если необходимо снять это ограничение наберите команду:

# kubectl taint nodes <host> node-role.kubernetes.io/control-plane:NoSchedule-
node/<host> untainted

Где <host> - имя master-узла, отображаемое в выводе команды:

# kubectl get nodes

Подключение ControlPlane и Worker узлов

Установка пакетов

Установите пакеты podsec необходимые для функционировния узла.

# apt-get install podsec-k8s podsec podsec-inotify
Создание файлов конфигурации Container Network Policy на ControlPlane или Worker в rootless режиме

KuberDeployment5.drawio.png Для создание файлов конфигурации подключаемого узла вызовите команду:

# podsec-create-policy <IP_адрес_MASTER_Controlplane_узла>
Добавление привязки доменов registry.local sigstore.local trivy.local к IP адресу 192.168.122.70
Создание группы podman
Инициализация /var/sigstore/ каталога /var/sigstore/ и подкаталогов хранения открытых ключей и подписей образов
Создание каталога и подкаталогов /var/sigstore/
Создание с сохранением предыдущего файла политик /etc/containers/policy.json
Создание с сохранением предыдущего файла /etc/containers/registries.d/default.yaml описания доступа к открытым ключам подписантов
Добавление insecure-доступа к регистратору registry.local в /etc/containers/registries.conf file

Команда произведен действия аналогичные действиям команды на Master ControlPlane узле, но файл конфигурации /etc/containers/policy.json скопирует с Master узла.

Подключение узла к кластеру

Измените переменную PATH:

export PATH=/usr/libexec/podsec/u7s/bin/:$PATH

KuberDeployment6.drawio.png Для подключения Controlplane или Worker узла к кластеру скопируйте строку подключения kubeadm join ... из логов команды инициализации Master Controlplane узла.

# kubeadm join ...

После завершения выполнения команды проверьте по отображаемым на мониторе логам ее успешность и запустите на Master ControlPlane узле команду:

# kubectl get nodes
NAME       STATUS   ROLES           AGE     VERSION
host-xxx    Ready    control-plane  ---     v1....
host-yyy    Ready    <none>         ---     v1....

Подключенный узел должен отображаться в списке с состоянием Ready.

Другие способы разворачивания rootless кластера

Разворачивание с локальным регистратора с расширенными политиками доступа или без них

Политики доступа описываются в файле /etc/containers/policy.json. ФОрмат этого файла описан в документе containers-policy.json - syntax for the signature verification policy file. Ниже описаны методы корректировки данного файла с помощью команды jq, обеспечивающей корректность структуры json-файла. При необходимости Вы можете править этот файл текстовым редактором соблюдая корректность JSON-структуры. Если Вы корректируете данный файл на Master Conrolplane узле ДО разворачивания остальный узлов скопируей его в каталог /var/sigstore/keys/. В этом случае это файл автоматически скопируется в каталог /etc/containers/ разворачиваемого узла. Если узлы развернуты скопируйте этот файл в каталог /etc/containers/ развернтых узлов.

По умолчанию файл /etc/containers/policy.json политик доступа к docker-образам настраивается на максимальные ограничения:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker.pgp"
        }
      ]
    }
  }
}

Пользователям разрешается доступ только к образам на локальном регистраторе registry.local, подписанными ключом пользователя imagemaker.

При необходимости Вы можете расширить политику:

  • указав дополнительные допустимые ключи подписи;
  • расширив список доступных docker-регистраторов;
  • расширив список доступных транспортов.
Добавление нового пользователя разработчика образов контейнеров

В описанной выше схеме создается только один пользователь разработчика образов контейнеров. Вы можете, используя скрипт podsec-create-imagemakeruser создавать неограниченое число пользователей данного класса. К сожалению в версиях podsec <= 1.1.20 при добавлении нового пользователя список предыдущих пользователей в файле /etc/containers/policy.json обнуляется. Ниже приведена последовательность действия для поддержания корректного списка пользователей.

Файл /etc/containers/policy.json является символической ссылкой на файл политик с именем по дате создания политики. Например:

# ls -l /etc/containers/policy*
-rw-r--r-- 1 root root 256 авг 28  2024 /etc/containers/policy_2025-10-29_12:53:52
-rw-r--r-- 1 root root  98 окт 30 12:53 /etc/containers/policy_2025-10-30_12:53:52
-rw-r--r-- 1 root root 276 окт 30 13:23 /etc/containers/policy_2025-10-30_13:23:00
lrwxrwxrwx 1 root root  26 ноя 10 14:20 /etc/containers/policy.json -> policy_2025-10-30_13:23:00

Данный подход позволяет хранить историю политик для данного узла и при необходимости переключать текущие политики перелинковкой файла policy.json:

 
# cd /etc/containers; ln -sf policy_<время_создания_политики> policy.json

Запомните в переменной oldPolicyFile имя файла текущей политики.

# policyFile='policy.json'
# oldPolicyFile=$(readlink $policyFile)

В данном примере oldPolicyFile будет хранить имя файла policy_2025-10-30_13:23:00 в каталоге /etc/containers/. Файл содержит JSON-объект описания политики:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker.pgp"
        }
      ]
    }
  }
}

Создайте нового пользователя:

# podsec-create-imagemakeruser imagemaker2

В процессе создания пользователя создастся новый файл политик с именем /etc/containers/policy_2025-11-10_14:19:00 и файл политик /etc/containers/policy.json залинкуется на него.

# ls -l /etc/containers/policy*
-rw-r--r-- 1 root root 256 авг 28  2024 /etc/containers/policy_2025-10-29_12:53:52
-rw-r--r-- 1 root root  98 окт 30 12:53 /etc/containers/policy_2025-10-30_12:53:52
-rw-r--r-- 1 root root  98 окт 30 13:22 /etc/containers/policy_2025-10-30_13:22:40
-rw-r--r-- 1 root root 276 окт 30 13:23 /etc/containers/policy_2025-10-30_13:23:00
-rw-r--r-- 1 root root 277 ноя 10 14:19 /etc/containers/policy_2025-11-10_14:19:00
lrwxrwxrwx 1 root root  26 ноя 10 14:20 /etc/containers/policy.json -> policy_2025-11-10_14:19:00

Новый файл /etc/containers/policy_2025-11-10_14:19:00 в элементе .transports.docker."registry.local" содержит массив с описанием тропы до открытого ключа нового пользователя :

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker2.pgp"
        }
      ]
    }
  }
}

Приведенные ниже команды объединять массивы:

# cd /etc/containers; 
# registryArray=$(jq -sc '.[0].transports.docker."registry.local" + .[1].transports.docker."registry.local"' $policyFile $oldPolicyFile)
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# jq '.transports.docker."registry.local"|='$registryArray $policyFile > $newPolicyFile
# ln -sf $newPolicyFile policy.json

Файл /etc/containers/policy.json будет содержать массив со всеми открытым ключами пользователей:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker2.pgp"
        },
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/var/sigstore/keys/imagemaker.pgp"
        }
      ]
    }
  }
}

Вы можете не использовать данные команды, а поправить файл /etc/containers/policy.json в текстовом редакторе.

Добавление нового регистратора

Вы можете добавить в файле /etc/containers/policy.json новый регистратор в объект .transports.docker. Например registry.altlinux.org:

№ cd /etc/containers; 
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# jq '.transports.docker."registry.altlinux.org"|=[{type:"insecureAcceptAnything"}]' policy.json > $newPolicyFile
# ln -sf $newPolicyFile policy.json

В данном примере политика доступа insecureAcceptAnything для регистратора registry.altlinux.org разрешает досступ к нему без ограничений. Вы можете поменять политику доступа на другую. Файл /etc/containers/policy.json будет иметь вид:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.local": [
        ...
      ],
      "registry.altlinux.org": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}

В этом случае загрузка образов с регистратора registry.altlinux.org будет производится без проверки подписи:

# podman pull registry.altlinux.org/alt/alt
Trying to pull registry.altlinux.org/alt/alt:latest...
Getting image source signatures
Copying blob c549b474d68c done   | 
Copying blob 8d382247a69f done   | 
Copying config 97c70e35e5 done   | 
Writing manifest to image destination
97c70e35e5754c99e2739a341746ab94e4f118e9c63ceaba806e369af80075e9
Расширение списка доступных транспортов

Политики доступа к образам поддерживают несколько типов транспорта:

  • atomic - Образ располагается в регистраторе типа atomic.
  • containers-storage - образы хранятся в каталоге файловой системы в формате драйвера (например для podman/cri-o: [overlay@/var/lib/containers/storage]).
  • dir - Образ располагается в локальном каталоге;
  • docker - Образ располагается в docker-регистраторе поддерживающий протокол "Docker Registry HTTP API V2".
  • docker-archive - Образ в виде tar-архива созданный командами docker save, podman save, ...
  • oci -Образ располагается в локальном каталоге в формате "Open Container Image Layout Specification". В таком формате в виде сжатого tar-архива хранятся kubernetes-образы на ISO диске дистрибутива;
  • tarball - Заархивированная (tar) файловая система образа.
  • ...

Рассмотрим пример добавления транспорта docker-archive. Текущая политика запрещает получать образы их архивного файла, полученными командами docker save, podman save.

# podman load -i /tmp/nginx.tar
 * docker-archive: Source image rejected: Running image docker-archive:/tmp/nginx.tar:registry.altlinux.org/c10f/nginx:latest is rejected by policy.

Для обеспечения возможности получать образы из архивов этого формата добавим транспорт docker-archive с типом insecureAcceptAnything:

№ cd /etc/containers; 
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# jq '.transports."docker-archive"."".[0].type="insecureAcceptAnything"' policy.json > $newPolicyFile
# ln -sf $newPolicyFile policy.json

Файл /etc/containers/policy.json получит вид:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
        ...
    },
    "docker-archive": {
      "": {
        "policy": [
          {
            "type": "insecureAcceptAnything"
          }
        ]
      }
    }
  }
}

После этого из архива /tmp/nginx.tar экспортируется образ registry.altlinux.org/c10f/nginx:latest:

# podman load -i /tmp/nginx.tar 
Getting image source signatures
Copying blob f0e31397ae37 skipped: already exists  
Copying blob 9a0fa9f6d6ea skipped: already exists  
Copying blob f06afe809992 skipped: already exists  
Copying blob 5957b6b3c861 skipped: already exists  
Copying blob e268fece4a97 skipped: already exists  
Copying blob 60204c5adf60 skipped: already exists  
Copying blob 6cb900dcc924 skipped: already exists  
Copying config d8a1de134d done   | 
Writing manifest to image destination
Loaded image: registry.altlinux.org/c10f/nginx:latest
Отключение политик доступа

Сформируйте новый файл политик:

# cd /etc/containers
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# echo '{"default":[{"type":"insecureAcceptAnything"}],"transports":{"docker-daemon":{"":[{"type":"insecureAcceptAnything"}]}}}' | jq > $newPolicyFile
# ln -sf $newPolicyFile policy.json

Текущий файл политик будет иметь следующую структуру:

{
  "default": [
    {
      "type": "insecureAcceptAnything"
    }
  ],
  "transports": {
    "docker-daemon": {
      "": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}

При этой конфигурации поддерживаются все виде транспортов и загрузка образов со всех регистраторов.

Расширение политик доступа к docker-образам для конкретных пользователей
Описание пользовательских файлов политик доступа к docker-образам

Кроме общесистемного файла описания политик доступа /etc/containers/policy.json к docker-образам в домашнем каталоге пользователя в подкаталоге .config/containers/ может располагаться пользовательский файл policy.json. Дерево файлов описания политик доступа Политики доступа этого файла перекрывают политики доступа общесистемного файла /etc/containers/policy.json.

Для пользователей класса "Создатель образов" файл .config/containers/policy.json обеспечивает выключение политик для данного пользователя:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
        ...
    },
    "docker-archive": {
      "": {
        "policy": [
          {
            "type": "insecureAcceptAnything"
          }
        ]
      }
    }
  }
}

Пользователи класса "Пользователь образов" содержит каталог .config/containers с запретом записи в него файлов.

# lsattr .config
----i---------e------- .config/containers

Таким образом пользователи данного класса используют общесистемный файл описания политик доступа /etc/containers/policy.json и не могут создать свой альтернативный файл .config/containers/policy.json.

Образы и контейнеры запускаемые из суперпользователя root располагаются в каталоге /var/lib/containers. Образы и контейнеры запускаемые из обычных пользователей располагаются в каталоге ~/.local/share/containers.

Kubernetes-образы и kubernetes-контейнеры запускаемые из под пользователя u7s-admin располагаются в каталоге ~u7s-admin/.local/share/usernetes/containers/. Причем каталог ~u7s-admin/.local/share/containers/ придинкован к этому каталогу. Это обеcпечивает единый каталог хранения образов и контейнеров запускаемых командой podsec и запускаемых движком cri-o rootless kubernetes.

Конфигурирование пользовательских файлов политик доступа к docker-образам

Конфигурирование пользовательских файлов ~/.config/containers/poliсy.json политик доступа к docker-образам ничем не отличается от конфигурирования общесистемного файла /etc/containers/policy.json, описанного в разделе Разворачивание с локальным регистратора с расширенными политиками доступа или без них.

Разворачивание без локального регистратора с политиками доступа

При разворачивании без локального регистратора kubernetes-образы берутся с регистратора registry.altlinux.org. В связи с этим необходимо в файле политик обеспечить доступ к этому регистратору командами:

# cd /etc/containers
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# jq '.transports.docker|={"registry.altlinux.org":[{"type":"insecureAcceptAnything"}]}' policy.json 
# ln -sf $newPolicyFile policy.json

Файл политик /etc/containers/poliсy.json будет иметь вид:

{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "registry.altlinux.org": [
        {
          "type": "insecureAcceptAnything"
        }
      ]
    }
  }
}

Для разворачивания kubernetes-кластера с регистратора registry.altlinux.org перед запуском команды инициализации Master Controlplane узла kubeadm init ... или команды присоединения узла kubeadm join ... необходимо установить переменные определяющие регистратор с которого загружаются образы и версию kubernetes. Регистратор с которого загружаются образы задается переменной U7S_REGISTRY.

# export U7S_REGISTRY=registry.altlinux.org

Список доступных версий kubernetes можно получить командой:

# curl -ks https://$U7S_REGISTRY/v2/c10f/kube-apiserver/tags/list | 
  jq -r '.tags|map(select(.|startswith("v")))[]'
v1.26.3
v1.26.6
v1.26.10
v1.26.11
v1.26.14
v1.26.15
v1.27.12
v1.27.14
v1.27.16
v1.28.8
v1.28.10
v1.28.12
v1.28.13
v1.28.14
v1.28.15
v1.29.8
v1.29.9
v1.29.14
v1.29.15
v1.30.4
v1.30.5
v1.30.10
v1.30.12
v1.30.14
v1.31.1
v1.31.6
v1.31.8
v1.31.10
v1.31.12
v1.32.4
v1.32.6
v1.32.8
v1.33.2
v1.33.4

Версия разворачивания kubernetes определяется командой U7S_KUBEVERSION:

# export U7S_KUBEVERSION=v1.33.4

После установки переменных U7S_REGISTRY, U7S_KUBEVERSION на Master Controlplane узле вызывается команда инициализации узла кластера:

# export PATH=/usr/libexec/podsec/u7s/bin/:$PATH
# kubeadm init  ...

На остальных подключаемых узлов кластера:

# export PATH=/usr/libexec/podsec/u7s/bin/:$PATH
# kubeadm join  ...

Разворачивание без локального регистратора и без политик доступа

Если при функционировании кластера нет необходимости в поддержке политик доступа её можно выключить как описано выше командой:

# cd /etc/containers
# newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
# echo '{"default":[{"type":"insecureAcceptAnything"}],"transports":{"docker-daemon":{"":[{"type":"insecureAcceptAnything"}]}}}' | jq > $newPolicyFile
# ln -sf $newPolicyFile policy.json

Далее, как описано в предыдущем разделе определяются переменные U7S_REGISTRY, U7S_KUBEVERSION и разворачиваются Controlplane и Worker узлы кластера.

Разворачивание rootfull кластера

Выбор варианта разворачивания

При разворачивании в режиме rootfull в зависимости от использования локального регистратора docker-образов и применения политик доступа к ним можно обеспечить следующие варианты разворачивания rootfull-кластера:

Варианты разворачивания rootfull кластера
Локальный регистратор Политики доступа Примечание
Нет Нет Стандартный вариант
Да Да Возможность разворачивания в локальной сети без доступа в Интернет с поддержкой классов пользователей с ограничением доступа к docker-регистраторам
Да Нет Возможность разворачивания в локальной сети без доступа в Интернет
Нет Да Поддержка классов пользователей с ограничением доступа к docker-регистраторам

Разворачивание без локального регистратора и политик доступа

Данный вариант является наиболее общеприменимым, но он не позволяет:

  • разворачивать kubernetes-кластер в локальной сети без доступа в Интернет;
  • ограничивать доступ к внешним docker-регистраторам с потенциальными уязвимостями в поддерживаемых образах пользователям различных классов.

Развертывание Master ControlPlane узла

Установка RPM-пакетов

Необходимо установить следующие пакеты:

# apt-get update
# apt-get -y install \
    kubernetes<minorKubeVerison>-kubeadm \
    kubernetes<minorKubeVerison>-kubelet \
    kubernetes<minorKubeVerison>-crio \
    cri-tools<kminorKubeVerison> \
    jq yq

Где <minorKubeVerison> - любая доступная в репозитории минорная версия >=1.26 без начального символа v. Список доступных минорных версий можно посмотреть запросом:

# curl -ks https://registry.altlinux.org/v2/c10f/kube-apiserver/tags/list |   
jq -r '.tags|map(select(.|startswith("v")))|map(.[1:5])|map(select(.>"1.25"))[]' |
sort -uV

Где <платформа> - ALTLinux-платформа на которой разворачивается кластер (sisyphus, p10, c10f, p11, ...). См. файл /etc/os-release.

На текущий момент (26.10.2025) список доступных минорных версий для платформы c10f:

# curl -ks https://registry.altlinux.org/v2/c10f/kube-apiserver/tags/list |    
jq -r '.tags|map(select(.|startswith("v")))|map(.[1:5])|map(select(.>"1.25"))[]' | 
sort -uV
1.26
1.27
1.28
1.29
1.30
1.31
1.32
1.33

Желательно устанавливать минорную версию с максимальным номером.

Команда установки пакетов kubernetes версии 1.33 будет выглядеть следующим образом:

# apt-get update
# apt-get -y install \
    kubernetes1.33-kubeadm \
    kubernetes1.33-kubelet \
    kubernetes1.33-crio \
    cri-tools1.33 \
    jq yq
Запуск сервисов

После установки пакетов выбранной версии нужно активировать сервисы crio, kubelet и запустить сервис crio:

# systemctl enable --now crio
# systemctl enable kubelet
Инициализация Master ControlPlane узла

Для инициализация Master ControlPlane узла после установки пакетов и запуска сервисов выполните на узле под пользователем root команду:

# kubeadm init \
   -v <DebugLevel> \
   --control-plane-endpoint <IP> \
   --upload-certs \
   --pod-network-cidr=<POD_NET> \
   --image-repository=registry.altlinux.org/<платформа> \
   --kubernetes-version=<kubeversion> 

Где:

  • <DebugLevel> - Уровень отладки в интервале 0-9;
  • <IP> - IP адрес узра приема API-запросов. Если в кластере один Controlplane узел, то это адрес этого узла. Если в кластере несколько Controlplane узлов - адрес haproxy балансировщика запросов.
  • <POD_NET> - Диапазон IP-адресов, используемый для организации внутренеей kuberbetes сети для взаимодействия POD'ов. Этот диапазон зависит от типа CNI (см. ниже):
   flannel - 10.244.0.0/16;
   calico - 192.168.0.0/16.  
  • <платформа> - ALTLinux-платформа на которой разворачивается кластер (sisyphus, p10, c10f, p11, ...). См. файл /etc/os-release;
  • <kubeVerison> - любая доступная в репозитории в рамках выбранной минорной версии полная (patch) версия без начального символа v. Список доступных полных (patch) версий можно посмотреть запросом:
curl -ks https://registry.altlinux.org/v2/<платформа>/kube-apiserver/tags/list | 
jq -r '.tags | map(select(.[1:5]=="<minorKubeVerison>")) | map(.[1:])[]' |
sort -uV

Например на текущий момент (26.10.2025) список доступных полных (patch) версий для минорной версии 1.33 платформы p11:

# curl -ks https://registry.altlinux.org/v2/p11/kube-apiserver/tags/list | 
jq -r '.tags | map(select(.[1:5]=="1.33")) | map(.[1:])[]' |
sort -uV
1.33.1
1.33.2
1.33.3
1.33.4

Желательно устанавливать версию с максимальным номером.

Например команда инициализации Master Controlplane узла на платформе p11 версии kubernetes 1.33.4 c CNI flannel будет выглядеть следующим образом:

# kubeadm init \
   -v 9 \
   --control-plane-endpoint <EndpointIP> \
   --upload-certs \
   --pod-network-cidr=10.244.0.0/16 \
   --image-repository=registry.altlinux.org/p11 \
   --kubernetes-version=1.33.4 2>&1 | 
tee /tmp/kubeadm.log

В данном примере вывод команды kubeadm в stdout, stderr объединяются и записываются в файл /tmp/kubeadm.log для возможного последующего анализа.

В случае успешного разворачивания вывод команды закачивается текстом:

Your Kubernetes control-plane has initialized successfully!
...
You can now join any number of control-plane nodes running the following command on each as root:

kubeadm join <EndpointIP>:6443 --token <token> \
        --discovery-token-ca-cert-hash sha256:<hash> \
        --control-plane --certificate-key <key>
...
Then you can join any number of worker nodes by running the following on each as root:

kubeadm join <EndpointIP>:6443 --token <token> \
        --discovery-token-ca-cert-hash sha256:<hash> 

Первая команда используется для подключения controlplane-узлов. Вторая команда - для подключения worker-узлов. Обратите внимание, что первая команда отличается от второй только наличием флагов --control-plane --certificate-key <key>.

Для тех пользователей, которые будут работать с kubernetes-кластером (включая пользователя root) необходимо в домашнем каталоге пользователя создать подкаталог .kube, скопировать в него файл /etc/kubernetes/admin.conf под именем config и установить владельца каталога и файла.

# user=<имя_пользователя_с_доступом_в_кластер>
# userhome=$(getent passwd "$user" | cut -d: -f6)
# mkdir -p $userhome/.kube
# cp -i /etc/kubernetes/admin.conf $userhome/.kube/config
# chown -R $user:$user $userhome/.kube

Для пользователя root можно не копируя файл конфигурации указать его в переменной KUBECONFIG:

# export KUBECONFIG=/etc/kubernetes/admin.conf

Разворачивание kubernetees на master controlplane узле производится в течении нескольких минут. По окончании разворачивании вывод команды

# kubectl get all -A

должен выглядить следующим образом:

NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE
kube-system   pod/coredns-84c9f8566b-mg2wf          0/1     Pending   0          ...
kube-system   pod/coredns-84c9f8566b-zhrqr          0/1     Pending   0          ...
kube-system   pod/etcd-host-98                      1/1     Running   0          ...
kube-system   pod/kube-apiserver-host-98            1/1     Running   0          ...
kube-system   pod/kube-controller-manager-host-98   1/1     Running   0          ...
kube-system   pod/kube-proxy-d55w6                  1/1     Running   0          ...
kube-system   pod/kube-scheduler-host-98            1/1     Running   0          ...

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  67m
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   67m

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   67m

NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   0/2     2            0           67m

NAMESPACE     NAME                                 DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-84c9f8566b   2         2         0       67m

Вывод для всех POD'ов имеет в поле READY значение 1/1 за исключением двух POD'ов pod/coredns-..., имеющих значение 0/1. Значение 1/1 для этих двух POD'ов установится после настройка внутренней сети кластера (CNI).

Команда

# kubectl get nodes -o wide

выводит список из одного узла:

NAME      STATUS     ROLES           AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                        KERNEL-VERSION      CONTAINER-RUNTIME
host-98   NotReady   control-plane   8h    v1.33.4   192.168.122.98   <none>        ALT Server 11.0 (Mendelevium)   6.12.27-6.12-alt1   cri-o://1.33.4

Состояние NotReady сменится на Ready после настройка внутренней сети кластера (CNI).

Подключение Worker узлов

Для добавления Worker узла установите пакеты

# apt-get update
# apt-get -y install \
    kubernetes<kubeVerison>-kubeadm \
    kubernetes<kubeVerison>-kubelet \
    kubernetes<kubeVerison>-crio \
    cri-tools<kubeVerison>

и активируйте сервисы

# systemctl enable --now crio
# systemctl enable kubelet

Скопируйте строку подключения Worker узла и выполните ее на подключаемом узле:

kubeadm join <EndpointIP>:6443 --token <token> \
        --discovery-token-ca-cert-hash sha256:<hash> 2>&1 |
tee /tmp/kubeadm.log

В случае возникновении ощибок разворачивания логи можно будет посмотреть в файле /tmp/kubeadm.log.

Подключение ControlPlane узлов

Добавлять ControlPlane узел имеет смысл только в том случае когда у Вас установлен балансировщик API-запросов к кластеру.

Для добавления ControlPlane узла установите пакеты

# apt-get update
# apt-get -y install \
    kubernetes<kubeVerison>-kubeadm \
    kubernetes<kubeVerison>-kubelet \
    kubernetes<kubeVerison>-crio \
    cri-tools<kubeVerison>

и активируйте сервисы

# systemctl enable --now crio
# systemctl enable kubelet

Скопируйте строку подключения ControlPlane узла и выполните ее на подключаемом узле:

kubeadm join <EndpointIP>:6443 --token <token> \
        --discovery-token-ca-cert-hash sha256:<hash> \
        --control-plane --certificate-key <key>


Настройка внутренней сети кластера в различных CNI

Сетевые плагины CNI (Container Network Interface) обеспечивают создание оверлейной сети kubernetes-кластера в рамках которой производится обмен данными между PO'ами и через POS'ы coredns поддерживается свой DNS-сервис. Существует множество сетевых плагинов CNI, основные из которых:

  • flannel - Простой. легко разворачиваемый плагин обеспечивающий создание оверлейной сети в диапазоне IP-алресов 10.244.0.0/16.
  • calico - Сетевой плагин обеспечивающий создание оверлейной сети в диапазоне IP-алресов 192.168.0.0/16 и использующий eBPF ((Extended Berkeley Packet Filter), поддерживающий обработку IP-пакетов кластера на уровне ядра операционный системы.
  • cilium - Сетевой плагин обеспечивающий создание оверлейной сети в диапазоне IP-алресов 10.244.0.0/16, использующий eBPF и множество дополнительных сервисов по обработке IP-пакетов и защите сетевого трафика.
CNI flannel

Для Flannel с использованием образов на базе ALT: Манифесты находятся по адресу https://altlinux.space/cloud/manifests/src/branch/master/flannel/<платформа>. Где <платформа> на текущий момент (25.10.2024): sisyphus, p10, c10f. Каждый каталог платформы содержит каталоги

 
├── <major>
│   ├── <minor>
│   │   └── <patch>
│   │       └── kube-flannel.yml
...
└── latest
    └── kube-flannel.yml

Выберите для Вашей платформы (например c10f) установленной версии kubernetes необходимую версию flannel (например 0.27.3) и наберите команду разворачивания:

$ kubectl apply -f https://altlinux.space/cloud/manifests/raw/branch/master/flannel/c10f/0/27/3/kube-flannel.yml

Для последних версий kubernetes можно использовать последнюю версию flannel (тег latest):

$ kubectl apply -f https://altlinux.space/cloud/manifests/raw/branch/master/flannel/c10f/latest/kube-flannel.yml
CNI calico
Перед запуском calico-манифестов перейдите в каталог /etc/cni/net.d/:
# cd /etc/cni/net.d/
и создайте файл 100-crio-bridge.conflist
# cp 100-crio-bridge.conflist.sample 100-crio-bridge.conflist
Запустите POD'ы из calico-манифестов:
$ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/master/manifests/tigera-operator.yaml
$ kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/master/manifests/custom-resources.yaml
CNI cilium

В настоящее время (18.11.2025) идет создание docker-образов для регистратора registry.altlinux.org.

Разворачивание нативного CNI cilium описано на странице Cilium Quick Installation: Cilium 1.18.4 documentation

Другие способы разворачивания rootfull кластера

Остальные варианты разворачивания rootfull-кластера с поддержкой локального регистратора и политик доступа требует установки группы пакетов podsec:

apt-get install podsec-k8s podsec podsec-inotify

После установки пакетов необходимо, как и в случае rootless кластера на Master ControlPlane:

  • создать файлы конфигурации Container Network Policy;
  • развернуть дополнительные сервисы.

В дальнейшем в зависимости от варианта разворачивания возможно:

  • создание на Master ControlPlane пользователей различных классов;
  • установка и настройка мониторинга безопасности системы.
Создание файлов конфигурации Container Network Policy на Master ControlPlane в rootfull режиме

Для создания Container Network Policy на Master узле вызовите команду:

# podsec-create-policy <IP> # ip-aдрес_регистратора и WEB-сервера подписей

Где:

  • <IP> - IP-адрес основного интерфеса узла

Проверка файлов конфигурации Container Network Policy подробно описана в разделе разворачивания rootless-kubernetes Создание файлов конфигурации Container Network Policy на Master ControlPlane в rootless режиме

Разворачивание дополнительных сервисов на Master ControlPlane в rootfull режиме

Для разворачивание дополнительных сервисов на Master ControlPlane поднимите сервисы регистратора и WEB-сервера подписей командой:

# podsec-create-services

Вывод команды и проверка функционирование сервисов подробно описана в разделе разворачивания rootless-kubernetes Разворачивание дополнительных сервисов на Master ControlPlane в rootless режиме

Разворачивание без локального регистратора с политиками доступа

Данный вариант разворачивания позволит ограничить список docker-регистриторов к которым имеют доступ пользователи.


Разворачивание с локальным регистратора без политик доступа

Этот вариант позволяет ускорить разворачивание кластера, так как все образы будут располанаться в локальной сети, что ускорит их загрузку на узлы кластера.

Первоначально необходимо сформировать oci-архивы kubernetes-образов. Если у Вас есть ISO-oбраз дистрибутива C10F, то архив можно взять с этого диска. Он располагается в каталоге /containers/ под именем k8s_amd64.tar.xz.

Если у Вас ISO-образа нет или Вы планируете установить последнюю версию образов, то Вы можете сформировать архив с регистратора registry.altlinux.org.

Описание формирования архива OIC-образов Вы можете найти в разделе Формирование OCI-архива kubernetes-образов с регистратора registry.altlinux.org .

Необходимые команды команды описаны в разделах разворачивания rootless-kubernetes: Подписывание образов и размещения на локальном регистраторе

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

cd /etc/containers
newPolicyFile=policy_$(date '+%Y-%m-%d_%H:%M:%S')
jq '.default[0].type|="insecureAcceptAnything"' /etc/containers/policy.json > $newPolicyFile
ln -sf $newPolicyFile policy.json

или в текстовом редакторе подправить значения поля default[0].type c reject на insecureAcceptAnything.

Это отключит контроль политик использования docker-образов.

Подписывание архивированных образов и помещение их в регистратор

В качестве oci-архива kubetnetes образов можно взять либо файл /containers/k8s_amd64.tar.xz расположенный на ISO-образе C10F, либо сформировать собственный архив. Процесс создания сжатого OCI-архива образов описан в разделе Формирование OCI-архива kubernetes-образов с регистратора registry.altlinux.org

Необходимые команды команды описаны в разделах разворачивания rootless-kubernetes: Подписывание образов и размещения на локальном регистраторе

Разворачивание с локальным регистратора и с политиками доступа

Поддержка управление доступом на основе ролей (RBAC)

Поддержку управления доступом на основе ролей (RBAC) обеспечивает пакет podsec-k8s-rbac.

В пакет podsec-k8s-rbac входит набор скриптов для работы с RBAC - Role Based Access Control:

  • podsec-k8s-rbac-create-user - создание RBAC-пользователя;
  • podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя;
  • podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места;
  • podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли;
  • podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя;
  • podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли.

podsec-k8s-rbac-create-user - создание RBAC-пользователя

Формат:

podsec-k8s-rbac-create-user имя_пользователя

Описание:

Скрипт:

  • создает RBAC пользователя
  • создает в домашнем директории каталог .kube
  • устанавливаются соответствующие права доступа к каталогам.

podsec-k8s-rbac-create-kubeconfig - создание ключей, сертификатов и файла конфигурации RBAC-пользователя

Формат:

podsec-k8s-rbac-create-kubeconfig имя_пользователя[@<имя_удаленного_пользователя>] [группа ...]

Описание: Скрипт должен вызываться администратором безопасности средства контейнеризации.

Для rootless решения имя удаленного пользователя принимается u7s-admin.

Для rootfull решения необходимо после символа @ указать имя удаленного пользователя.

Скрипт в каталоге ~имя_пользователя/.kube производит:

  • Создании личного (private) ключа пользователя (файл имя_пользователя.key).
  • Создание запроса на подпись сертификата (CSR) (файл имя_пользователя.key).
  • Запись запроса на подпись сертификата CSR в кластер.
  • Подтверждение запроса на подпись сертификата (CSR).
  • Создание сертификата (файл имя_пользователя.crt).
  • Проверку корректности сертификата
  • Формирование файла конфигурации пользователя (файл config)
  • Добавление контекста созданного пользователя

podsec-k8s-rbac-create-remoteplace - создание удаленного рабочего места

Формат:

podsec-k8s-rbac-create-remoteplace ip-адрес

Описание:

Скрипт производит настройку удаленного рабочего места пользователя путем копирования его конфигурационного файла.

podsec-k8s-rbac-bindrole - привязывание пользователя к кластерной или обычной роли

Формат:

podsec-k8s-rbac-bindrole имя_пользователя role|role=clusterrole|clusterrole роль имя_связки_роли [namespace]

Описание:

Скрипт производит привязку пользователя к обычной или кластерной роли используя имя_связки_роли.

Параметры:

  • имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;
  • тип роли может принимать следующие значения:
   * role - пользователь привязывется к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);
   * role=clusterrole - пользователь привязывется к обычной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае обязателен);
   * clusterrole - пользователь привязывется к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).
  • роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;
  • имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.
  • namespace - имя namespace для обычной роли.

podsec-k8s-rbac-get-userroles - получить список кластерные и обычных ролей пользователя

Формат:

podsec-k8s-rbac-get-userroles имя_пользователя [showRules]

Описание:

Скрипт формирует список кластерные и обычных ролей которые связаны с пользователем. При указании флага showRules, для каждой роли указывается список правил ("rules:[...]"), которые принадлежат каждой роли пользователя.

Результат возвращается в виде json-строки формата:

{
  "": {
    "clusterRoles": [...],
    "roles": {
      "allNamespaces": [...],
      "namespaces": [
        {
          "": [...],
          ...
        }
    }
  }
}

Где [...] - массив объектов типа:

{
  "bindRoleName": "",
  "bindedRoleType": "ClusterRole|Role",
  "bindedRoleName": "",
  "unbindCmd": "podsec-k8s-rbac-unbindrole ..."
}

podsec-k8s-rbac-unbindrole - отвязывание пользователя от кластерной или обычной роли

Формат:

podsec-k8s-rbac-unbindrole имя_пользователя role|clusterrole роль имя_связки_роли [namespace]

Описание:

Скрипт производит отвязку роли от кластерной или обычной роли, созданной командой podsec-k8s-rbac-bindrole. Полный текст команды можно получить в выводе команды podsec-k8s-rbac-get-userroles в поле unbindCmd. Если в указанном имя_связки_роли объекте класса rolebindings или clusterrolebindings еще остаются пользователи - объект модифицируется. Если список становится пуст - объект удаляется.

Параметры:

  • имя_пользователя должно быть создано командой podsec-k8s-rbac-create-user и сконфигурировано на доступ к кластеру командой podsec-k8s-rbac-create-kubeconfig;
  • тип роли может принимать следующие значения:
   * role - пользователь привязывается к обычной роли с именем <роль> (параметр namespace в этом случае обязателен);
   * clusterrole - пользователь привязывается к кластерной роли используя кластерную роль с именем <роль> (параметр namespace в этом случае должен отсутствовать).
  • роль - имя обычной или кластерной роли в зависимости от предыдущего параметра;
  • имя_связки_роли - имя объекта класса rolebindings или clusterrolebindings в зависимости от параметра тип роли. В рамках этого объекта к кластерной или обычной роли могут быть привязаны несколько пользователей.
  • namespace - имя namespace для обычной роли.

Мониторинг безопасности системы

Мониторинг безопасности системы обеспечивает пакет podsec-inotify.

В пакет podsec-inotify входит набор скриптов для мониторинга безопасности системы:

  • podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле;
  • podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах;
  • podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле;
  • podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла;
  • podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy.

Настройка сервиса trivy

Часть скриптов мониторинга для обнаружения уязвимостей использует сканер trivy.

Сканер безопасности trivy работает как клиент сервера trivy принимающего соединения по порту 4954 на узле с доменом trivy.local. Если Ваш узел работает в составе кластера, то необходимо:

  • на одном из узлов кластера поднять сервер trivy командой:
systemctl enable --now trivy
  • на всех узлах кластера прописать в файле /etc/hosts строку
<IP-адрес_узла_сервера_trivy> trivy.local

Если Ваш узел вне кластера необходимо:

  • на узле поднять сервер trivy командой:
systemctl enable --now trivy
  • прописать в файле /etc/hosts строку
127.0.0.1 trivy.local

На платформе c10f сервер trivy запускается автоматически скриптом podsec-create-services на мастер-сервере кластера, привязка домена trivy.local к IP-адресу сервера производится автоматически скриптом podsec-create-policy.

Мониторинг сообщений об уязвимостей через nagwad

Все сообщения об обнаруженных уязвимостях скрипты записывают в системный лог в следующем формате:

<месяц> <день> <время> <host> <имя_скрипта>[<id>]: <уровень_уязвимости>: <текст_сообщения>

Посмотреть эти сообщения можно командой:

journalctl  -t <имя_скрипта>

Например:

journalctl  -t podsec-inotify-check-vuln
июл 16 06:22:36 host-136 podsec-inotify-check-vuln[383501]: Critical: В образе registry.altlinux.org/k8s-sisyphus/kube-apiserver:v1.30.1 пользователя u7s-admin обнаружены критические и высокие уязвимости.
...

Для передачи сообщений серверу мониторинга общей инфраструктуры icigna необходимо поднять сервис nagwad:

# apt-get install nagwad-service
# systemctl enable --now nagwad

В файловой системе создастся каталог /var/log/nagwad/<boot_uid>/podsec/. Все сообщения об уязвимостях сервис nagwad будет записывать из системного лога в данный каталог в файлы под именем podsec.<message_id>.<level>.

Например /var/log/nagwad/3c22e4b3-d4d7-4975-a49c-f630a15c041d/podsec/:

CRITICAL: podsec-inotify-check-vuln(Critical) В образе registry.altlinux.org/k8s-sisyphus/kube-apiserver:v1.30.1 пользователя u7s-admin обнаружены критические и высокие уязвимости.

Эти файлы в дальнейшем передаются серверу мониторинга общей инфраструктуры icigna.

podsec-inotify-check-policy - проверка настроек политики контейнеризации на узле

Формат:

podsec-inotify-check-policy [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m  интервал] х-w интервалъ [-l интервал] [-d интервал]

Описание: Плугин проверяет настройки политики контейнеризации на узле.

Проверка идет по следующим параметрам:

  • файл policy.json установки транспортов и политик доступа к регистраторам:
Параметр контроля пользователей Вес метрики
имеющих defaultPolicy != reject, но не входящих в группу podman_dev 102
не имеющих не имеющих registry.local в списке регистраторов для которых проверяется наличие электронной подписи образов 103
имеющих в политике регистраторы для которых не проверяется наличие электронной подписи образов 104
имеющих в списке поддерживаемых транспорты отличные от docker (транспорт получения образов с регистратора) 105


  • файлы привязки регистраторов к серверам хранящим электронные подписи (файл привязки о умолчанию default.yaml и файлы привязки регистраторов *.yaml каталога registries.d). Наличие (число) пользователей:
Параметр контроля пользователей Вес метрики
не использующих хранилище подписей http://sigstore.local:81/sigstore/ как хранилище подписей по умолчанию 106
  • контроль групп пользователей
  1. наличие пользователей имеющих образы, но не входящих в группу podman:
Параметр контроля пользователей Вес метрики
наличие пользователей имеющих образы, но не входящих в группу podman 101
  1. наличие пользователей группы podman (за исключением входящих в группу podman_dev):
Параметр контроля пользователей Вес метрики
входящих в группу wheel 101
имеющих каталог .config/containers/ открытым на запись и изменения 90 * доля_нарушителей
не имеющих файла конфигурации .config/containers/storage.conf 90 * доля_нарушителей

доля_нарушителей считается как: число_нарушителей / число_пользователей_группы_podman

Все веса метрик суммируются и формируется итоговая метрика.

podsec-inotify-check-containers - проверка наличия изменений файлов в директориях rootless контейнерах

Формат:

podsec-inotify-check-containers

Описание:

Скрипт:

  • создаёт список директорий rootless контейнеров, существующих в системе,
  • запускает проверку на добавление,удаление, и изменение файлов в директориях контейнеров,
  • отсылает уведомление об изменении в системный лог.

podsec-inotify-check-images - проверка образов на предмет их соответствия настройки политикам контейнеризации на узле

Формат:

podsec-inotify-check-images [-v[vv]] [-a интервал] [-f интервал] -c интервал -h интервал [-m  интервал] х-w интервалъ [-l интервал] [-d интервал]

Описание:

Плугин проверяет образы на предмет их соответствия настройки политикам контейнеризации на узле. Проверка идет по следующим параметрам:

Параметр контроля пользователей Вес метрики
наличие в политике пользователя регистраторов не поддерживающие электронную подпись 101
наличие в кэше образов неподписанных образов 101
наличие в кэше образов вне поддерживаемых политик 101

Все веса метрик суммируются и формируется итоговая метрика.

podsec-inotify-check-kubeapi - мониторинг аудита API-интерфейса kube-apiserver control-plane узла

Формат:

podsec-inotify-check-kubeapi [-d]

Описание: Скрипт производит мониторинг файла /etc/kubernetes/audit/audit.log аудита API-интерфейса kube-apiserver.

Политика аудита располагается в файле /etc/kubernetes/audit/policy.yaml:

apiVersion: audit.k8s.io/v1
kind: Policy
omitManagedFields: true
rules:
# do not log requests to the following 
- level: None
  nonResourceURLs:
  - "/healthz*"
  - "/logs"
  - "/metrics"
  - "/swagger*"
  - "/version"
  - "/readyz"
  - "/livez"

- level: None
  users:
    - system:kube-scheduler
    - system:kube-proxy
    - system:apiserver
    - system:kube-controller-manager
    - system:serviceaccount:gatekeeper-system:gatekeeper-admin

- level: None
  userGroups:
    - system:nodes
    - system:serviceaccounts
    - system:masters

# limit level to Metadata so token is not included in the spec/status
- level: Metadata
  omitStages:
  - RequestReceived
  resources:
  - group: authentication.k8s.io
    resources:
    - tokenreviews

# extended audit of auth delegation
- level: RequestResponse
  omitStages:
  - RequestReceived
  resources:
  - group: authorization.k8s.io
    resources:
    - subjectaccessreviews

# log changes to pods at RequestResponse level
- level: RequestResponse
  omitStages:
  - RequestReceived
  resources:
  - group: "" # core API group; add third-party API services and your API services if needed
    resources: ["pods"]
    verbs: ["create", "patch", "update", "delete"]

# log everything else at Metadata level
- level: Metadata
  omitStages:
  - RequestReceived

Текущие настройки производят логирование всех обращений "несистемных" пользователей (в том числе анонимных) к ресурсам kubernetes.

Скрипт производит выборку всех обращений, в ответ на которые был сформирован код более 400 - запрет доступа. Все эти факты записываются в системный журнал и накапливаются в файле логов /var/lib/podsec/u7s/log/kubeapi/forbidden.log, который периодически передается через посту системному адмиристратору.

Параметры:

  • -d - скирпт запускается в режиме демона, производящего онлайн мониторинг файла /etc/kubernetes/audit/audit.log и записывающего факты запросов с запретом доступа в системный журнал и файл логов /var/lib/podsec/u7s/log/kubeapi/forbidden.log.
  • при запуске без параметров скрипт посылает файл логов /var/lib/podsec/u7s/log/kubeapi/forbidden.log почтой системному администратору (пользователь root) и обнуляет файл логов.

В состав пакета кроме этого скрипта входят:

  • файл описания сервиса /lib/systemd/system/podsec-inotify-check-kubeapi.service. Для его запуска екобходимо выполнить команды:
 
  # systemctl enable  podsec-inotify-check-kubeapi.service
  # systemctl start  podsec-inotify-check-kubeapi.service
  
  • файл для cron /etc/podsec/crontabs/podsec-inotify-check-kubeapi. Файл содержит единственную строку с описанием режима запуска скрипта podsec-inotify-check-kubeapi для передачи почты системному администратору.
 Скрипт запускается один раз в 10 минут.
 Во время установки пакета строка файла (в случае ее отсутствия) дописыватся в crontab-файл /var/spool/cron/root пользователя root.   
 Если необходимо изменить режим запуска скрипта или выключить его это можно сделать командой редактирования crontab-файла:
  #  crontab -e
  

podsec-inotify-check-vuln - мониторинг docker-образов узла сканером безопасности trivy

Формат:

podsec-inotify-check-vuln

Описание:

Скрипт производит мониторинг docker-образов узла сканером безопасности trivy:

  • Если скрипт запускается от имени пользователя root скрипт:
  1. проверяет сканером trivy rootfull образы;
  2. для всех пользователей каталога /home/ проверяется наличие rootless-образов. При их наличии проверяет сканером trivy эти образы.
  • Если скрипт запускается от имени обычного пользователя проверяется наличие rootless-образов. При их наличии проверяет сканером trivy эти образы.

Результат анализа посылается в системный лог. Если при анализе образа число обнаруженных угроз уровня HIGH больше 0, результат посылается почтой системному администратору (root).

Параметры:

Отсутствуют.

Периодический запуск скрипта

В состав пакета кроме этого входит systemd/timers файл /usr/lib/systemd/system/podsec-inotify-check-vuln.timer.

При его активации командой:

systemctl enable podsec-inotify-check-vuln.timer

каждый час запускается скрипт мониторинга.

Период запуска можно указать в описателе OnCalendar вышеуказанного systemd/timers файла.

Установка и настройка ingress-контролера

Ingress-контроллер обеспечивает переадресацию http(s) запросов по указанным шаблонам на внутренние сервисы kubernetes-кластера. Для bare-metal решений и решений на основе виртуальных машин наиболее приемлимым является ingress-nginx контроллер.

При применении Ingress-контроллера нет необходимости создавать Nodeport-порты и пробрасывать их из namespace пользователя u7s-admin. Ingress-контроллер переадресует http{s) запрос через сервис непосредственно на порты Pod'ов входящих в реплики deployment.

Установка и настройка ingress-nginx-контролера в кластере

Использование ingress-контроллера

Для установки Ingress-контроллера скопируйте его YAML-манифест:

curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/baremetal/deploy.yaml -o ingress-nginx-deploy.yaml

Выберите свободный порт в диапазона 30000 - 32767 (например 31000) и добавьте его в элемент spec.ports.appProtocol==http Yaml-описании kind==Service:

...
---
kind: Service
spec:
  ports:
  - appProtocol: http
    ...
    nodePort: 31000
...

Если в Вашем решении используется ТОЛЬКО локальный регистратор registry.local

  • создайте алиасы образам nginx:
podman tag registry.k8s.io/ingress-nginx/controller:v1.8.0@sha256:744ae2afd433a395eeb13dc03d3313facba92e96ad71d9feaafc85925493fee3 registry.local/ingress-nginx/controller:v1.8.0
podman tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407@sha256:543c40fd093964bc9ab509d3e791f9989963021f1e9e4c9c7b6700b02bfb227b registry.local/ingress-nginx/kube-webhook-certgen:v20230407

и поместите их в локальный регистратор:

podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/controller
podman push --tls-verify=false --sign-by='<EMAIL>' registry.local/ingress-nginx/kube-webhook-certgen
  • исправьте имена образов в скачанном нанифесте на имена образов в локальном регистраторе.

Запустите Ingress-nginx-контролер:

kubectl apply -f ingress-nginx-deploy.yaml

На одном или нескольких kubernet-узлах (эти узла в дальнейшем нужно прописать в файле конфигурации балансировщика haproxy) пробросьте порт nginx-контроллера (31000) из namespace пользователя u7s-admin в сеть kubernetes:

nsenter_u7s rootlessctl add-ports 0.0.0.0:31000:31000/tcp

Настройка Ingress-правил

Kubernetes поддерживает манифесты типа Ingress (kind: Ingress) описывающие правила переадресации запросов URL http-запррса на внутренние порты сервисов (kind: Service) kubernetes. Сервисы в свою очередь перенаправляют запросы на реплики Pod'ов, входящих в данный сервис.

Общий вид Ingress-манифеста:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: <ingress_имя>
spec:
  ingressClassName: nginx          
  rules:
  - host: <домен_1>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: <имя_сервиса_1>
            port:
              number: 80
      - path: /<тропа_1>
        pathType: Prefix
        backend:
          service:
            name: <имя_сервиса_2>
            port:
              number: 80
  - host: <домен_2>
    ...

Где:

  • host: <домен_1>, <домен_2>, ... - домены WEB-серверов на которых приходит запрос;
  • path:/>, path:/<тропа_1> - тропы (префиксы запросов после домена)
  • pathType: Prefix - тип троп: Prefix или Exact;
  • service: - имя сервиса на который перенаправляется запрос, если полученный запрос соответсвует правилу;
  • port - номер порта на который перенаправляется запрос.

Если запросу соответствует несколько правил, выбирается правило с наиболее длинным префиксом.

Подробности смотри в Kubernetes: Ingress

Настройка haproxy и DNS

Добавьте в файлы конфигурации haproxy /etc/haproxy/haproxy.conf переадресацию запросов на порт 80 (http) по IP-адресу балансировщика haproxy на IP-адреса kubernet-узлов на которых выбранный порт nginx-контроллера (31000) проброшен из namespace пользователя u7s-admin в сеть kubernetes:

frontend http
    bind *:80
    mode tcp
    option tcplog
    default_backend http

backend http
    mode tcp
    balance     roundrobin
        server <server1> <ip1>:31000 check
        server <server2> <ip2>:31000 check

Заведите DNS-запись связывающую DNS-имя http-сервиса с IP-адресам haproxy-сервера.

  1. ...