Администрирование
Развёртывание и конфигурация готовых систем.
Alpine OS Config
Ansible installation, config & usage
Linux Kernel eBPF Layer
Grafana Loki installation, config & usage
Linux CLI tools & commands
Linux Containers
Static-Site-Generation
Развёртывание и конфигурация готовых систем.
Alpine OS Config
Ansible installation, config & usage
Linux Kernel eBPF Layer
Grafana Loki installation, config & usage
Linux CLI tools & commands
Linux Containers
Static-Site-Generation
apk add --update open-vm-toolsrc-service open-vm-tools start
rc-update add open-vm-toolsExternal Link In file: /etc/network/interfaces
DHCP: iface eth0 inet dhcp
STATIC:
In file: /etc/resolv.conf
External Link
rc-update add cgroups
Bugfix:
Do not install Ansible from local OS repo - it is usually older in version there. Use latest Python pip repo to install a fresh version.
Ansible will be the latest version supporting current Python in OS. So to get latest Ansible , Python must be updated as well!
Do not update the default Python in OS - it is used by system services, which may break!
Build Python:
Installing Python:
graph LR I[(Inventory)] --> R1(Role01); M(Modules) --> R1; I[(Inventory)] --> R2(Role02); M(Modules) --> R2; I[(Inventory)] --> RN(Role N); M(Modules) --> RN; R1 -->|include role| P(Playbook); R2 -->|include role| P(Playbook); RN -->|include role| P(Playbook); P --> C(Ansible Config); C --> Py(Python); Py-->|SSH|Client01; Py-->|SSH|Client02; Py-->|SSH|Client_N;
front.yml - role example:
apache / tasks / main.yml - tasks example:
Directories inside a role:
Create inventory file with IP addresses. Check computers in inventory by doing a ping:
Order of precedence for the config file:
First config file found wins, Ansible stops looking for other config files.
Specify: $ANSIBLE_
Override settings on the fly:
[defaults] forks - how many parallel processes does Ansible handle. Default=5, production recommended=20
host_key_checking - check host key before sending commands. Default=True (for production), for dev/test systems =False (easy control)
log_path - Ansible logging. Default=Null, produstion recommended - set path all Ansible users can write to.
Patterns to choose hosts/groups:
Example:
Complex pattern example: webservers:&production:!python3 # apply to web servers in production but not in the Python3 group
Playbook execution: ansible-playbook my_playbook.yml
Playbook example:
Use “- include” and “- include_vars” directives to include playbook files:
Use the output of one task for another task:
Add screen output and print content of variables:
Promt user during execution:
A handler can be informed to execute a task (restart service) only if state=changed.
Handlers syntax is the same as tasks syntax:
Commence a task if condition is True:
Check output of previous task as condition to run next task:
Bring variable check to BOOL check:
Tetragon File Monitoring
Early approach: periodic scanning of the file-system and comparing the expected state with the actual state. Limitations:
Later approach: in-kernel inotify:
pid or cgroup) of the process doing the operation -> no way to filter events based on which Kubernetes workload performed the file access;/private/data, the sequence of operations would be:/private into the directories to be watched/private/data directory/private/data was created/private/data to the directories to be watchedIf a file was created and/or accessed in /private/data/* between steps 2 and 4, there will be no inotify event for the access prior to it being added to the watch list.
inotify events:eBPF allows FIM implementation to correlate file access events with execution context such as process information (e.g., credentials) and its cloud native identity (e.g., k8s workload), perform inline updates to its internal state to avoid races, as well as implement inline enforcement on file operations.
Install eBPF hooks (kprobes, specifically) to track file operations and implement File Integrity Monitoring (FIM).
open system call to determine when a file is opened and for what access (read or write):

Hooking into a (kernel) function that happens after the path is copied from user-space to kernel-space avoids this problem since the hook operates on memory that the user-space application cannot change. Hence, instead of a system call we will install a hook into a security_function. Specifically, we will hook into the security_file_permission function which is called on every file access (there is also security_file_open which is executed whenever a file is opened). Information about the process and its parent such as binary, arguments, credentials, and others. In cloud-native environments, the events also contain information about the container and the pod that this process belongs to.
If no filtering is applied, we get a file-all policy: generate events for every file access:
file-all policy does not inform users about what file was actually accessed;Tetragon policy with filtering of SSH keys:
* security_file_permission - the eBPF hook is called on every file access in the system
* Use security_file_open and have the eBPF hook be executed whenever a file is opened. However, it means that if a file is already opened before the hook is installed, the hook will not be called and certain accesses may be missed
* Hooks into other functions such as security_file_truncate or security_file_ioctl for other operations;
eBPF lets you do observability and do inline enforcement by stopping an operation from happening by modifying the request.
Example of denying /usr/bin.cat ssh files access:
It is impossible to do proper enforcement without in-kernel filtering, because by the time the event has reached user-space it is already too late if the operation has already executed.
struct file arguments of functions such as security_file_open;/etc/ssh/ssh_host_rsa_key but the same underlying file is accessed via a different name, the access will go unnoticed;/etc/ssh/ssh_host_rsa_key named, for example, /mykey accesses via /mykey will not be caught by policies such as file-ssh-keys:fs.protected_hardlinks is set to 1, creating a link requires certain permissions on the target file);CAP_SYS_ADMIN;CAP_CHROOT.An inode number uniquely identifies an underlying file in a single filesystem. Example:
sequenceDiagram autonumber actor U as User participant A as Агент participant S as Сканнер participant F as Файл participant B as Программа eBPF participant I as Карта inodes U->>A: политика activate A activate U A->>S: шаблон S->>F: получить inode activate F activate S F-->>S: inode S->>I: обновить список inodes deactivate S activate I F->>B: событие deactivate F activate B loop Синк в ядре B->>I: запрос списка inodes I-->>B: список inodes end deactivate I B-->>A: событие deactivate B A-->>U: уведомление deactivate A deactivate U
Tracing policies can be added to Tetragon through YAML configuration files that extend Tetragon’s base execution tracing capabilities. These policies perform filtering in kernel to ensure only interesting events are published to userspace from the BPF programs running in kernel. This ensures overhead remains low even on busy systems.
https://tetragon.io/docs/getting-started/file-events/
https://isovalent.com/blog/post/file-monitoring-with-ebpf-and-tetragon-part-1/
Create dirs for Loki:
vim docker-compose.yml
The config files MUST be in the inner container directory!
Go to Grafana
Add New Data Source… → Loki
http://loki:3100
Explore
Install a Docker Driver https://grafana.com/docs/loki/latest/clients/docker-driver/
Check config: https://grafana.com/docs/loki/latest/clients/docker-driver/configuration/
vim /etc/docker/daemon.jsonsudo systemctl restart dockerCMD Hotkeys
disown
disk usage
grep
history
Список всех команд: stty -a
Ctrl+W для удаления предыдущего слова.Ctrl+U для удаления всей строки.Ctrl+C для прерывания процесса, Ctrl+Z для его приостановки и так далее).Если есть программа вроде nc без поддержки readline, то можно выполнить
rlwrap nc, чтобы эту поддержку в неё встроить.
Ctrl+E (или End) для перехода в конец строки (из emacs).Ctrl+A (или Home) для перехода в начало строки (из emacs).Ctrl+влево/вправо для перемещения вперёд/назад на 1 слово.Стрелка вверх для возврата к предыдущей команде.Ctrl+R для поиска по истории.Atuin - прекрасный инструмент для поиска по истории оболочки
Необходимо «спасти» уже запущенный процесс и перенести его в фон, даже если забыл сделать это заранее:
Быстро найти, какие файлы или папки съедают больше всего места:
-r or -R is recursive ; use -R to search entirely-n is line number, and-w stands for match the whole word.-l (lower-case L) can be added to just give the file name of matching files.-e is the pattern used during the search--exclude, --include, --exclude-dir flags used for efficient searching.
--exclude-dir parameter. For example, this will exclude the dirs dir1/, dir2/ and all of them matching *.dst/:Команда выводит все команды с номерами. Далее можно указать номер команды и выполнить её повторно
Команда позволяет исполнить сразу несколько команд из истории history.
Появится текстовый редактор, где будут команды из диапазона. Можно их отредактировать, а после выхода из редактора они все будут исполнены.
Изолированные единицы ПО.
Способ попасть внутрь контейнера без docker exec. Это спасает, если контейнер завис или внутри нет bash sh.
Попадаем внутрь окружения контейнера, как в обычный chroot.
В проекте Docker впервые предложили паковать контейнеры в послойные образы в 2013. Это позволило переносить контейнеры между машинами.
Проверим работу с образами на утилите skopeo и пробном контейнере:
Видим, что мы скачали индекс образа (image index) и blob. Изучим индекс:
По сути индекс есть манифест более высокого уровня, который содержит указатели на конкретные манифесты для определённых ОС (linux) и архитектур (amd).
Image manifest указывает на расположение конфига и набора слоёв для образа контейнера на конкретной ОС и архитектуре. Поле size указывает общий размер объекта. Теперь можно исследовать далее:
Распакуем базовый первый слой из архива и изучим его:
Это файловая система ОС! Можно изучить версию дистрибутива
Распакуем следующий слой и изучим его:
Тут лежим доп файл, созданный командой "/bin/sh -c touch my-file" - это можно увидеть в секции history. По сути исходный Dockerfile выглядел так:
В 2017 году Red Hat разработали инструмент для создания образов контейнеров по стандарту OCI - как аналог docker build.
Создадим Dockerfile vim Dockerfile и впишем в него:
Запустим сборку контейнера на базе этого файла - buildah bud
Buildah поддерживает много команд:
Почему вдруг buildah ps показ запущенных контейнеров, когда это инструмент для их СОЗДАНИЯ? А потому что в процессе создания как в buildah, так и в docker идёт запуск промежуточных контейнеров, их модификация в runtime. Каждый шаг модификации создаёт записи в history. Это потенциальная проблема ИБ: можно влезть в контейнер, пока он собирается (и запущен), если там что-то большое, и модифицировать его.
Например, приготовим манифест:
Начнём сборку:
Залезем в работающий контейнер:
После сборки можно удостовериться, что созданный файл на месте:
У buildah есть императивные команды к любой команде из Dockerfile, типа RUN или COPY. Это даёт огромную гибкость, т.к вместо огромных Dockerfile можно делить процесс создания контейнеров на части, между ними запускать любые вспомогательные инструменты UNIX.
Создадим базовый контейнер с Alpine Linux и посмотрим как он работает:
Можно запускать команды в контейнере, а также создать в нёс файл:
По-умолчанию, buildah не делает записи history в контейнер, это значит порядок команд и частота их вызова не влияют на итоговые слои. Можно поменять это поведение ключом --add-history или переменной ENV BUILDAH_HISTORY=true.
Сделаем коммит нового контейнера в образ для финализации процесса:
Можно выпустить образ в реестр Docker, либо на локальный диск в формате OCI:
Теперь сделаем в обратную сторону: удалим образ my-image из реестра и вытащим его с диска из формата OCI:
Контейнер alpine-working-container при этом ещё работает. Запустим CLI в контейнере:
Проведём mount файл-системы контейнера на локальную (В ДРУГОМ ТЕРМИНАЛЕ):
buildah unshare создаёт новое пространство имён, что позволяет подключить файловую систему как текущий не-root пользователь;--mount автоматически подключает, путь кладём в переменную среды MOUNT;Мы успешно модифицировали файловую систему контейнера через локальный mount. Проверим наличие файла:
У buildah нет даемона, значит, не нужно подключать docker.sock в контейнер для работы с Docker CLI. Это даёт гибкость и возможность делать вложенные схемы: установим buildah в контейнер, созданный buildah:
Теперь вложенный buildah готов к использованию. Нужно указать драйвер хранения VFS в контейнере, чтобы получить рабочий стек файловой системы:
Получили вложенный контейнер. Заложим его в хранилище на локальной машине -> для начала заложим образ в /my-image:
Выходим из вложенного контейнера (В ДРУГОМ ТЕРМИНАЛЕ) и копируем образ из рабочего контейнера путём mount его файловой системы:
Теперь мы вытаскиваем образ контейнера из директории прямо в локальный реестр buildah:
ВАЖНОЕ ЗАМЕЧАНИЕ: все действия с buildah не потребовали sudo. Buildah создаёт всё необходимое для каждого пользователя в папках:
~/.config/containers, конфигурация~/.local/share/containers, хранилища контейнеровДекомпозиция Dockerfile в несколько разных с помощью CPP макросов.
Инструмент для замены Docker. podman использует buildah как API для создания Dockerfile с помощью podman build. Это значит, что они разделяют одно хранилище под капотом. А это значит, что podman может запускать созданные buildah контейнеры:
External link:
chroot - утилита, которая предназначена для изоляции файловой среды приложения. Создана в Minix 1.7. Для процессов и ОЗУ не подходит, но вдохновила создание Namespaces в Linux позднее.
Для работы bash в новой среде chroot необходимо внести его копию в папку jail:
Далее нужно увидеть зависимости и перенести их:
Либо, гораздо проще перенести разом все библиотеки:
Далее, заход в окружение:
fork(2), the clone(2) API allows the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. You can pass different namespace flags to clone(2)to create new namespaces for the child process.unshare(2) - отсоединение частей контекста выполнения процесса.
setns(2) позволяет запрашивающему процессу присоединяться в разные namespaces.
proc - Besides the available syscalls, the proc filesystem populates additional namespace related files. Since Linux 3.8, each file in /proc/$PID/ns is a “magic“ link which can be used as a handle for performing operations (like setns(2)) to the referenced namespace.
Ввели в 2002 первым, ещё не знали, что понадобится много разных, потому обозвали флаг клонирования CLONE_NEWNS, что не соответствует флагам других namespaces. С помощью mnt в Linux можно изолировать группу точек монтирования для групп процессов.
We have a successfully mounted tmpfs, which is not available on the host system level:
The actual memory being used for the mount point is laying in an abstraction layer called Virtual File System (VFS), which is part of the kernel and where every other filesystem is based on.
Можно создавать на лету гибкие файловые системы. Mounts can have different flavors (shared, slave, private, unbindable), which is best explained within the shared subtree documentation of the Linux kernel.
Ввели в 2006 в Linux 2.6.19. Можно отсоединить домен и имя хоста от системы.
And if we look at the system level nothing has changed, hooray:
Ввели в 2006 в Linux 2.6.19. Можно изолировать связи между процессами. Например, общую память (shared memory = SHM) между процессами. Два процесса будут использовать 1 идентификатор для общей памяти, но при этом писать в 2 разных региона памяти.
Ввели в 2008 в Linux 2.6.24. Возможность для процессов иметь одинаковые PID в разных namespace. У одного процесса могут быть 2 PID: один внутри namespace, а второй вовне его - на хост системе. Можно делать вложенные namespace, и тогда PID у 1 процесса будет больше. Первый процесс в namespace получается PID=1 и привилегии init-процесса.
Флаг --mount-proc нужен чтобы переподключить proc filesystem из нового namespace. Иначе PID в namespace будут не видны.
Ввели в 2009 в Linux 2.6.29 для виртуализации сетей. Каждая сеть имеет свои свойства в разделе /proc/net. При создании нового namespace он содержит только loopback интерфейсы. Создадим:
Применение: создание SDN через пары виртуальных интерфейсов. Один конец пары подключается к bridge, а другой конец - к целевому контейнеру. Так работают CNI типа Flannel.
Создадим новый net namepsace:
Когда команда ip создаёт network namespace, она создаёт it will create a bind mount for it under /var/run/netns too. This allows the namespace to persist even when no processes are running within it.
The network seems down, let’s bring it up:
Let’s create a veth pair which should allow communication later on:
Both interfaces are automatically connected, which means that packets sent to veth0 will be received by veth1 and vice versa. Now we associate one end of the veth pair to our network namespace:
Добавляем адреса ip:
Теперь можно связываться в обе стороны:
It works, but we wouldn’t have any internet access from the network namespace. We would need a network bridge or something similar for that and a default route from the namespace.
Ввели в 2012-2013 в Linux 3.5-3.8 для изоляции пользователей, групп пользователей. Пользователь получает разные ID внутри и вовне namespace, а также разные привилегии.
После создания namespace, файлы /proc/$PID/{u,g}id_map раскрывают соответствия user+groupID и PID. Эти файлы пишутся лишь единожды для определения соответствий.
Ввели в 2008 в Linux 2.6.24 для квотирования и далее переделали капитально в 2016 в Linux 4.6 - ввели cgroups namespace.
Cgroup version check:
For cgroup v2, the output is cgroup2fs.
For cgroup v1, the output is tmpfs.
Cgroups memory.limit_in_bytes was deprecated because it is prone to race condition: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#deprecated-v1-core-features
Использовать memory.max (in bytes)! https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#namespace
Система выдаёт список ограничений. Поменяем ограничения памяти для этой cgroup. Также отключим swap, чтобы реализация сработала:
После того как установлено ограничение в 100Mb памяти ОЗУ, напишем приложение, которое забирает память больше чем положенные 100Mb (в случае отсутствия ограничений приложение закрывается при занятии 200Mb):
Если его запустить, то увидим, что PID будет убит из-за ограничений памяти:
Можно составлять пространства имён вместе, чтобы они делили 1 сетевой интерфейс. Так работают k8s Pods. Создадим новое пространство имён с изолированным PID:
Вызов ядра setns с приложением-обёрткой nsenter теперь можно использовать для присоединения к пространству имён. Для этого нужно понять, в какое пространство мы хотим присоединиться:
Теперь присоединяемся с помощью nsenter:
https://brianshih1.github.io/mini-container/preface.html
Система сборки и запуска контейнеров:
Можно исследовать, что runc создал mnt, uts, ipc, pid и net:
External Link:
В 2007 году Google сделали проект Let Me Contain That For You (LMCTFY), в 2008 году появился Linux Containers (LXC). Для управления LXC в 2013 году появился инструмент Docker. Далее в 2015, команда Docker разработали проект libcontainer на языке Go. Также, в 2015 вышел Kubernetes 1.0. В 2015 собрали Open Container Initiative (OCI), которые стали разрабатывать стандарты на метаданные (манифесты-спецификации), образы контейнеров, методы управления ими. В том числе, в рамках OCI создали инструмент запуска и работы с контейнерами runc.
В спецификации от runc можно увидеть всё необходимое для создания и запуска контейнера: environment variables, user + group IDs, mount points, Linux namespaces. Не хватает только файловой системы (rootfs), базового образа контейнера:
В распакованном образе можно найти готовую Runtime Specification:
В ней можно увидеть обычные поля из runc, а доп заполненные annotations:
Чтобы создать контейнер с runc, нужно его зацепить на терминал ввода команд TTY:
На существующий TTY зацепить контейнер нельзя (потому что окно удалённого xTerm не поддерживает такое), нужно создать новый виртуальный TTY и указать его сокет. Для этого надо установить Golang, скачать приложение rectty, создать с его помощью виртуальный терминал, после чего В ДРУГОМ ОКНЕ терминала создать контейнер и зацепить его на создвнный TTY:
В ДРУГОМ ОКНЕ терминала создать контейнер и зацепить его на создвнный TTY:
runc init создаёт новую среду со всеми namespaces. /bin/bash ещё не запущен в контейнере, но уже можно запускать в нём свои процессы, полезно чтоб настроить сеть:
Для запуска контейнера выполним:
Исходный runc init пропал, теперь только /bin/bash существует в контейнере. На ПЕРВОМ ОКНЕ терминала появилась консоль контейнера:
Можно проверить управление: заморозим контейнер. Во ВТОРОМ ОКНЕ терминала выполним:
Для остановки контейнера достаточно выйти из rectty-сессии, после чего удалить контейнер. Остановленный контейнер нельзя перезапустить, можно лишь пересоздать в новом состоянии:
Можно модифицировать спецификацию в контейнере (bundle/config.json):
Можно удалить разделение PID namespace процессов в контейнере с хостом:
Инструмент CRI-O разработан в 2016 при участии OCI в рамках проекта Kubernetes. Философия UNIX, максимально лёгкий аналог Docker/containerd. Он НЕ предназначен как инструмент для приёма команд от разработчиков. Задача - принимать команды от K8s. Внутри себя CRI-O использует runc как backend, и принимает команды по gRPC API как frontend.
Попробуем CRI-O с помощью спец-контейнера с crictl:
Внутри лежит файл sandbox.yml:
Из него можно создать Pod:
Need to install:
Choose a site directory and create site + repo:
Some themes (Relearn) have to be downloaded as zip. So we have a git repo inside a git repo now. Copy contents exampleSite of theme:
Launch local web-server:
Visit local site and check it.
Create a Gitlab account, make SSH Key exchange. After that, create project by CLI from hugo site folder:
Where hugotest.git is the project name.
Use Hugo template (should be in Gitlab):
Change visibility: Settings -> General -> Pages visibility = Everyone.