izi DevOps
3.22K subscribers
7 photos
20 videos
1 file
35 links
Download Telegram
Ролика у меня не будет про ТСПУ, так как тама достаточно массивная для разбора и быстро меняется. Вот есть ребята которые подробно и простым языком объяснили, как это работает. https://www.youtube.com/watch?v=4nFH7zvW0Mk
👍3❤1🔥1
Удалил файл, а место не вернулось. Что за кадром.

Что на самом деле делает rm

Когда ты вводишь rm /var/log/huge.log, ядро не трогает данные на диске. Оно удаляет запись из директории, то есть связь между именем файла и его inode. У inode есть счётчик ссылок (link count). rm уменьшает его на 1.

Данные удаляются когда совпадут два условия:
- link count = 0 (ни одно имя не указывает на файл)
- ни один процесс не держит открытый fd

Если хотя бы одно не выполнено, данные живы. Ядро так устроено не случайно: это защита от потери данных при сбоях.

Почему df врёт, а du нет

Классика: df -h показывает 99%, а du -sh / показывает 60%. Разница - это и есть "призраки". df считает по суперблоку файловой системы (сколько блоков занято), а du ходит по дереву каталогов. Если имени нет, du файл не видит, но блоки заняты.

Быстрая проверка:

df -h / | awk 'NR==2{print $3}' # что видит ядро
du -sh / 2>/dev/null # что видит дерево каталогов


Если числа сильно расходятся, значит кого-то хлопнули.

nginx -T: хак для конфига

В ролике я показал восстановление конфига через /proc/PID/fd/. Но конкретно для nginx есть способ проще, его отметили в комментариях:


nginx -T


Эта команда выводит весь текущий конфиг из памяти процесса, включая все include'ы, собранные в один вывод. Работает даже если файл на диске удалён. Но это nginx-специфичная штука. Для postgres, redis или твоего кастомного сервиса - только /proc/PID/fd/.

Когда /proc не спасёт

Всё что я показал в ролике работает ровно до одного момента: пока процесс жив. Как только он завершится, дескриптор закроется, link count упадёт до нуля, ядро пометит блоки как свободные. С этой секунды данные могут быть перезаписаны любой новой записью на диск.

Дальше начинается совсем другая история и другие инструменты, и уже без гарантий.

extundelete - работает только на ext3/ext4. Ищет метаданные удалённых файлов в журнале файловой системы. Журнал хранит последние операции, и если повезёт, там ещё есть указатели на блоки твоего файла. Важно: файловую систему нужно сразу размонтировать или перевести в read-only, иначе журнал перезапишется.


# Размонтируем или read-only
mount -o remount,ro /dev/sda1

# Ищем удалённые файлы
extundelete /dev/sda1 --restore-all


Шансы зависят от того, сколько записей было после удаления. На нагруженном сервере с активными логами, окно в минуты.

photorec - тяжёлая артиллерия. Работает на уровне блочного устройства, сканирует сектора и ищет файлы по сигнатурам (magic bytes в начале файла, например, %PDF для PDF, PK для ZIP). Не зависит от файловой системы, работает хоть на ext4, хоть на xfs, хоть на повреждённом диске.


photorec /dev/sda1


Минусы: медленно (сканирует весь диск), имена файлов не восстанавливает (только содержимое по типу), и если файл был фрагментирован, соберёт только первый кусок. Для текстовых конфигов шансы неплохие. Для больших логов - лотерея.

testdisk - часто идёт в паре с photorec. Но он больше про восстановление разделов и таблиц разделов, а не отдельных файлов. Полезен если кто-то случайно затёр partition table.


Так что не торопитесь что-то удалять и делай бэкапы.
Пиши в коментарии анекдоты про бэкапы (НЕ НАДО ДЯДЯ)

@izi.devops
🔥2❤1
В ролике дали короткий ответ на вопрос с собеседования: L4 видит IP и порт, L7 видит заголовки и URL.

▎Почему OSI не победил

OSI начали проектировать в ISO в 1978 году, финальная спека вышла в 1984. Пока комитеты согласовывали 7 уровней, в ARPANET в 1983 включили TCP/IP по умолчанию. К началу 90-х на нём работал весь интернет.

OSI остался в учебниках. Реальные стеки живут по TCP/IP. L4 и L7 в названиях балансеров это терминология OSI, наложенная сверху на TCP/IP.

▎L4

L4 балансер работает на транспортном уровне (TCP, UDP). Видит IP и порт клиента, внутрь пакета не лезет. Содержимое для него это просто байты для пересылки на бэкенд. За счёт этого он быстрый и держит много одновременных коннектов.

Где это нужно: базы (Postgres, MySQL), SMTP, любой TCP-протокол, который балансеру парсить не надо. Настраивается через HAProxy в режиме mode tcp или NGINX через блок stream {}.

Sticky на L4: чтобы клиент попадал на один и тот же сервер, балансер считает хеш от его IP. Пока клиент не сменил IP, он будет ходить на тот же бэкенд.

▎L7

L7 балансер работает на прикладном уровне (чаще всего HTTP). Он читает запрос целиком: URL, заголовки, куки. За счёт этого умеет умные вещи, недоступные на L4.

Что можно делать: роутить /api на одну группу серверов, /static на другую. A/B-тест по заголовку. Rate limit по URL. WAF, который смотрит на содержимое. Настраивается через NGINX (он по умолчанию L7) или HAProxy в mode http.

Sticky на L7: балансер ставит клиенту cookie с номером сервера и читает её на каждом запросе. Клиент идёт на свой бэкенд, даже если сменил IP.


▎Алгоритмы балансировки

Round-robin. Запросы по кругу: первый серверу A, второй B, третий C, снова A. Просто. Минус: если сервер начал тормозить, балансер всё равно шлёт ему новые запросы, очередь растёт.

Least connections. Шлём туда, где сейчас меньше активных коннектов. Логично, но коннект не показывает реальную нагрузку. По одному HTTP/2-коннекту может идти сотня запросов параллельно. На WebSocket ещё хуже: сервер с 10к idle-коннектов и 5% CPU балансер посчитает занятым, а соседнего с 100 активными и 80% CPU догрузит дальше.

Weighted round-robin. Тот же round-robin, но серверам ставят веса. Сервер с весом 3 получит в три раза больше запросов, чем с весом 1. Используют, когда железо в пуле разное.

Power of Two Choices. Балансер берёт два случайных сервера, смотрит на их нагрузку и шлёт запрос в менее загруженный. NGINX (`random two`).

Consistent hashing. Балансер считает хеш от ключа (например, user_id) и всегда отправляет запросы одного клиента на один и тот же сервер. Если добавили или убрали сервер, переезжает только малая часть ключей. Так работают кеши и шардированные базы.

EWMA. Балансер запоминает, как быстро сервер отвечал в последние секунды. Если сервер начал тормозить, его вес падает и трафик уезжает к здоровым. Linkerd использует по умолчанию.

▎Также важно знать про Health checks

Алгоритм балансировки сам по себе не понимает, жив сервер или мёртв. Чтобы не слать трафик в мёртвый бэкенд, балансер проверяет здоровье серверов. Делает это двумя способами.

Active. Балансер каждые N секунд стучит на сервер по специальному адресу (обычно /health`). 200 OK значит жив, ошибка или таймаут: выводим из пула. Минус: проверяется только то, что заложено в эндпоинт. Часто там просто `return 200, и реальные проблемы (БД отвалилась, кеш сломался) балансер не увидит.

Passive. Балансер смотрит на настоящие пользовательские запросы. Если сервер начал отдавать много 5xx или таймаутится, его выкидывают из пула на какое-то время. В Envoy это называется outlier detection.

Slow start. Сервер только перезапустился: код холодный, кеши пустые, прогретых коннектов к БД нет (на JVM/V8 ещё и JIT не прогрет). Если сразу свалить полную нагрузку, он будет тормозить или упадёт. NGINX Plus (платная подписка) и Envoy умеют 30-60 секунд плавно поднимать вес с 0 до 100%.

Если в конфиге round_robin или least_conn по умолчанию, посмотри на random two или EWMA.

Это всё надо знать, чтобы не было проблем с Istio, Envoy, Gateway API, Ingress, Cilium, GKE.

@izi.devops
🔥10👍3
Вот два сайта, которыми я лично пользуюсь, на них можно увидеть, что вообще есть на рынке и что сейчас стоит рассмотреть из технологий.

➖➖➖
1. CNCF Landscape
🔗 landscape.cncf.io
Это карта всех инструментов современной IT-инфраструктуры. Больше тысячи штук, разложены по категориям: контейнеры, базы данных, мониторинг, безопасность, деплой.
У каждого стоит метка зрелости:
🟢 graduated — проверенный, можно ставить на боевые сервера
🔵 incubating — рабочий, но есть риски
🟠 sandbox — ранняя стадия, для экспериментов
🔴 archived — мёртвый, лучше не трогать

➖➖➖
2. Technology Radar от ThoughtWorks
🔗 thoughtworks.com/radar
ThoughtWorks это крупный IT-консалтинг. Занимаются большими компаниями: банки, ритейл, телеком. Их инженеры руками щупают инструменты в реальных проектах.
Радар шире, чем инфраструктура. Там языки программирования, фреймворки, библиотеки, базы данных, подходы к разработке, практики команд. Условно: от React и Postgres, стоит ли вашей команде уходить от микросервисов обратно к монолиту.
Раз в полгода они выпускают отчёт. Всё разложено по четырём кругам:
🟢 Adopt — рекомендуем брать сейчас
🔵 Trial — можно пробовать
🟠 Assess — присмотритесь, пока не тащите
🔴 Hold — не берите, или уходите, если уже на этом сидите

➖➖➖
Как использовать
▫️ Landscape отвечает на вопрос "что вообще существует в этой нише". Условно, ищешь, чем заменить Jenkins. Открываешь раздел CI/CD, там два десятка кандидатов.
▫️ Radar отвечает на вопрос "что про это думают практики". Заходишь, ищешь название, смотришь в каком круге.

⚠️ Важно: не копируй слепо. ThoughtWorks (radar) работает с большими компаниями. Их Adopt не всегда подходит маленькой команде или стартапу. Но как точка старта это лучшее, что есть. Также можно посмотреть несколько последних выпусков и если не нашли интересующую технологию глянуть еще более старые. Там вы узнаете, что про нее думают уважаемые челибосики.

➖➖➖
💬 Если есть какие-то сайты которыми вы пользуетесь в работе, то кидайте в комменты ссылки
🔥8👍6
🐳 Контейнеры до Docker и после

Часто говорят, что Docker придумал контейнеры. На деле изоляция процессов в Unix существует с 1979 года, а сегодня Docker уже не единственный инструмент в этой нише. Короткая хроника.

➖➖➖

▎1. chroot · 1979

Первая «изоляция» в Unix V7. Команда chroot меняет процессу корень файловой системы из своей вьюшки он не видит ничего выше, только то, что внутри новой директории.

Больше изоляции никакой: тот же список процессов, та же сеть, те же пользователи. Сегодня из chroot убегают в две строчки через /proc или mount. Но как идея точка отсчёта для всего, что было дальше.

➖➖➖

▎2. jails, Zones, LXC · 2000-2008

Изоляция доросла до полноценной.

FreeBSD jails (2000) добавили к chroot отдельную сеть и список процессов. Solaris Zones (2005) пошли дальше практически отдельная ОС внутри ОС. В Linux к 2008 году в ядро собрался полный набор namespaces и cgroups, и поверх них собрали LXC.

Технически уже всё работало: PID, сеть, монтирования, лимиты на CPU и память. Но настраивался каждый контейнер вручную конфиги cgroups, монтирования, capabilities. На фоне современного docker run это выглядело, как полноценная инженерная задача.

➖➖➖

▎3. Docker · 2013

Docker не изобрёл контейнеры. Он сделал поверх LXC (а потом libcontainer) три вещи, которых не было: формат описания (`Dockerfile`), формат поставки (image registry) и одну команду, чтобы запустить.

docker run nginx
Эта строчка скачивает образ из реестра, распаковывает в overlay и запускает процесс с namespaces+cgroups. То что в LXC занимало день теперь занимает пару секунд. Просто хороший UX поверх примитивов, которые уже были в ядре.

Что именно Docker положил поверх:

- HTTP-реестр (`/v2/...`) - pull/push образа по имени из любого облака
- контент-адресуемый формат образа со слоями и дедупликацией
- REST API через docker.sock - отсюда выросли все интеграции с IDE, CI и оркестраторами
- сеть из коробки: bridge, port mapping, embedded DNS
- volumes как отдельная абстракция для данных

Без этого «образ» был бы просто tar.gz, а не объектом, который тянется и запускается по имени одной командой.

➖➖➖

▎4. OCI · 2015

К 2015 году Docker оказался слишком монолитным: демон, билдер, runtime всё в одном бинаре. Это мешало другим: CoreOS пилил rkt, Google готовил Kubernetes.

В июне 2015 Docker и CoreOS вместе с Linux Foundation учредили OCI открытый стандарт на формат образа и runtime. Тогда же Docker выделил из libcontainer низкоуровневый запуск контейнера в отдельный проект runc. К концу года появился containerd слой управления жизненным циклом контейнеров, который потом, в 2017, ушёл в CNCF.

С этого момента «контейнер» перестал быть синонимом «Docker».

➖➖➖

▎5. Что в проде сегодня

Kubernetes больше не запускает Docker. С версии 1.24 (май 2022) dockershim удалён - kubelet общается с containerd напрямую через CRI. Если у тебя кластер свежее 1.24, на нодах нет dockerd.

Podman - те же команды, что у docker, но без демона. Каждая команда это отдельный процесс пользователя. Поддерживает rootless из коробки. Ставится по умолчанию в RHEL и Fedora.

Kaniko - сборка образов в CI. Раньше билд внутри пайплайна делали через Docker-in-Docker: поднимали dockerd внутри контейнера билда. Это требует --privileged, что фактически даёт root на хост-ядро. Альтернатива «прокинуть /var/run/docker.sock`» ещё хуже: любой процесс в контейнере делает `docker run -v /:/host и читает весь хост.

Kaniko (Google, 2018) собирает образ в user-space. Без демона, без privileged. Поэтому в Kubernetes-CI его и берут.

➖➖➖

▎Итог

Контейнер это обычный процесс ядра с namespaces и cgroups. Эти примитивы есть в Linux с 2008, а в других Unix-системах с конца 90-х.

Docker наверное самый удобный фронтенд к этим примитивам и стандарт де-факто на формат образа. Но используется обычно локально или деве или в маленьких компаниях.

Пишите в комментариях, что я забыл упомянуть.
@izi.devops
🔥13👍3
🐳 Docker безопасность: что не влезло в 2 минуты

В видео были --privileged, Leaky Vessels и 4 флага. Это база. В комментариях справедливо отметили, что есть инструменты, которые решают тот же класс проблем по-другому.

➖➖➖

▎0. Безопасность это всегда про урезание удобства

Чем строже настройка, тем больше всё ломается. Distroless ломает kubectl exec. SELinux ломает кривые маунты. Это и есть смысл.

Безопасность должна стоить дешевле, чем то, что ты потенциально можешь потерять.

Пет-проект на VPS за 200 рублей не нуждается в sandbox. А какой-нибудь SaaS Ai B2B крипто-арбитраж с чужим кодом на твоих нодах нуждается.

➖➖➖

▎1. Rootless Docker

Без root на хосте с Docker 20.10 (декабрь 2020, GA). Демон можно запустить от обычного юзера, root внутри это твой uid снаружи.


dockerd-rootless-setuptool.sh install


Сбежавший процесс получает права юзера, не root. Минусы: порты ниже 1024 без setcap, --net=host ломается, overlay через FUSE медленнее на 5–15%.

Rootless не отменяет --cap-drop=ALL и no-new-privileges. Это базовый слой, не замена остальным.

➖➖➖

▎2. Podman + crun

Ставится в RHEL и Fedora по умолчанию. CLI как у Docker, можно добавить alias docker=podman.

- нет демона, каждая команда отдельный процесс
- rootless из коробки
- интеграция с systemd через podman generate systemd

crun написан на C, старт контейнера на 30–50 мс короче runc, так что уже не сможете попить кофе пока стартует контейнер.

➖➖➖

▎3. SELinux и AppArmor

Что они реально ловят. docker-default (Ubuntu/Debian) запрещает писать в /proc/sys, mount без CAP_SYS_ADMIN, ptrace вне своего PID-неймспейса. SELinux в Fedora/RHEL даёт каждому контейнеру MCS-лейбл, файлы хоста ему не видны просто потому что лейблы не совпадают.

Большинство публичных PoC по эскалации отрубаются дефолтным AppArmor или SELinux до того, как payload отработает.

➖➖➖

▎4. Seccomp

Docker по умолчанию запрещает ~44 сисколла из 300+: kexec_load, init_module, mount, reboot.

Свой профиль собирается автоматически. Запускаешь контейнер с трассировкой, podman записывает реально вызванные сисколлы:


podman run --annotation io.containers.trace-syscall=of:./trace.json alpine


В trace.json остаются только нужные. Остальное запрещено.

➖➖➖

▎5. Самая имба Distroless и scratch

Половина уязвимостей живёт в инструментах, которых в проде быть не должно. bash, apt, curl нужны при сборке, не при запуске.


FROM gcr.io/distroless/static-debian12
COPY ./myapp /myapp
ENTRYPOINT ["/myapp"]


2–20 МБ против 200+ у обычного node. Если кто-то залез внутрь, у него нет даже ls. scratch радикальнее, там вообще пусто, для статических Go-бинарей.


➖➖➖

▎6. Где смотреть CVE и фанфакт про сканеры

Для ручной проверки https://www.cvedetails.com, листаешь по vendor → product → version. Альтернатива NVD с человеческой навигацией.

Фанфакт. В марте 2026 группировка TeamPCP скомпрометировала сам `trivy`. Force-push 110+ тегов в `aquasecurity/trivy-action`, коммиты подменили на credential-stealer: SSH-ключи, AWS-креды, kubectl-токены, npm-токены.

И также тема с названием `kamikaze.sh` проверяла timezone и локаль. Если `Asia/Tehran` или Farsi, разворачивала привилегированный DaemonSet `host-provisioner-iran` в `kube-system` и стирала данные на каждой ноде. Если не Иран, ставила persistent backdoor.


Сканер безопасности это тоже образ из реестра. Так что качай нужную версию по хешу, не по тегу.

➖➖➖

▎Итог

Контейнер это процесс с namespaces и cgroups. Безопасность это сумма независимых слоёв: rootless, MAC (SELinux/AppArmor), seccomp, образ (distroless).

Каждый слой ловит свой класс ошибок. Не упарывайся, добавляй по мере того, как растёт цена ошибки.

Пишите в комментариях, стоит ли у вас Trivy.

@izi.devops
🔥6❤2👍1
Возможно кто-то юзает Nexus на работе и не обновляется из-за ограничения по заливам пакетов в новых версиях вот вам патч на Postgres, чтобы обойти лимиты. (Небольшой кусок playbook из Ansible для деплоя Nexus)

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

# Данная функция необходима, чтобы убрать дневные и тотальные лимиты на запросы в nexus, функция делит количество обращений на 1000#
- name: "Create metrics normalization function and triggers"
community.postgresql.postgresql_query:
db: "{{ nexus_postgres_db }}"
login_user: "{{ nexus_postgres_user }}"
login_password: "{{ nexus_postgres_password }}"
login_host: "127.0.0.1"
login_port: "{{ nexus_postgres_port }}"
autocommit: true
query: |
CREATE OR REPLACE FUNCTION nexus.change_metrics_log()
RETURNS TRIGGER AS $$
BEGIN
NEW.metric_value = NEW.metric_value / 1000.0;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_divide_metrics_log ON nexus.metrics_log;
CREATE TRIGGER trg_divide_metrics_log
BEFORE INSERT ON nexus.metrics_log
FOR EACH ROW
EXECUTE FUNCTION nexus.change_metrics_log();

DROP TRIGGER IF EXISTS trg_divide_aggmetrics_log ON nexus.aggregated_metrics;
CREATE TRIGGER trg_divide_aggmetrics_log
BEFORE INSERT ON nexus.aggregated_metrics
FOR EACH ROW
EXECUTE FUNCTION nexus.change_metrics_log();
🔥7❤1
🐳 Полезные тулы по теме ролика

В последнем ролике из контейнера на 30 ГБ сделал 90 МБ. Multi-stage, alpine, кеш слоёв.

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

➖➖➖

▎1. dive - что-то типа рентгена для контейнера

docker images показывает только финальный размер.

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


brew install dive
dive your-image:tag


Сразу видишь:
• какой RUN добавил 500 МБ apt-кеша
• что COPY . . затащил node_modules или .git
• какой слой вообще ничего полезного не делает

Также там есть wasted space. Это байты, которые ты добавил в одном слое, а удалил в другом. Удалил, но они всё равно остаются в образе, потому что слои аддитивные.

В CI: dive --ci your-image ломает сборку, если waste выше порога.

➖➖➖

▎2. hadolint - проверяет Dockerfile до билда

Половину косяков из ролика hadolint ловит ещё до того, как ты запустил docker build.

Проверяет правила типа:
• FROM image:latest — нельзя, версию пин надо
• apt-get install без --no-install-recommends ставишь лишнее
• ADD для обычного файла (не архива) - используй COPY


brew install hadolint
hadolint Dockerfile


Втыкаешь в pre-commit hook и в CI одной строкой. Если правило не нравится и часто стреляет, то кидаешь игнор в конфиге.

Короче, базовая защита от популярных ошибок.

➖➖➖

▎3. slim - режет образ автоматически

Самая крутая магия из всей подборки. Берёт твой готовый образ, запускает приложение внутри, смотрит что приложение реально трогает (через `ptrace`/`eBPF`), и выкидывает всё остальное.


slim build your-image:tag


Без правки Dockerfile, без переписывания base-image.

Подвох: slim вырежет всё, чем приложение не пользовалось во время теста. Если у тебя есть код, который запускается раз в сутки по крону, а тестил ты минуту, тогда slim его выкинет и приложение или не взлетит или стрельнет.


То есть это не "запустил и забыл". Нужно прогонять реалистичную нагрузку (функциональные тесты, smoke-тесты, e2e). Хорошо работает на зрелых сервисах с понятным поведением.

➖➖➖

▎4. distroless нужен, когда alpine уже жирный

Я уже говорил про него в прошлых постах, но прям советую всем использовать, если хочется образ ещё меньше и безопаснее, то переходи с alpine на образы от Google.

Что внутри distroless:
• твой бинарник
• минимальные системные библиотеки чтобы он стартанул

Чего там НЕТ:
• shell (`sh`, `bash`)
• apt, apk, yum
• curl, wget, ls, cat ( там вообще нихера нет)


FROM gcr.io/distroless/nodejs20
COPY --from=builder /app/dist ./
CMD ["dist/server.js"]


Плюсы: образ ещё меньше (твой бинарник + ~20 МБ). Малвари внутри нечего использовать она не сможет запустить shell, скачать инструменты или хотя бы посмотреть /etc.

Минус: дебажить нельзя обычным советским docker exec sh, ведь там нет sh. (Но это и не надо в проде) Все логи и метрики делаешь через приложение. Для разработки оставляешь alpine.

chainguard, это коммерческая альтернатива distroless с тем же подходом, но с регулярными обновлениями безопасности и более широким каталогом образов.

@izi.devops
❤6🔥3👍1
Что делать когда compose up поднялся, но сеть так и не работает

Ниже 4 инструмента и приёма чтобы понять, где именно проблема.

➖➖➖

▎1. docker network inspect

Посмотри что Docker реально создал:


docker network ls
docker network inspect myproject_default


Что важно увидеть в выводе:

▸ Containers. Кто реально подключён к этой сети. Если сервиса там нет, он в другой сети, и DNS его не увидит.
▸ Subnet и Gateway. Пригодится когда есть конфликт с корпоративной сетью (10.x / 172.x). У меня такое бывало пару раз.
▸ Driver. bridge, overlay или macvlan. Compose по умолчанию поднимает bridge user-defined.
▸ Internal: true. Сеть без выхода в интернет.

Чтобы увидеть к каким сетям подключён конкретный контейнер, кастуй команду ниже:


docker inspect web --format '{{json .NetworkSettings.Networks}}' | jq


Если у web сеть frontend, а у db сеть backend то они не увидят друга.

➖➖➖

▎2. Embedded DNS на 127.0.0.11

Когда контейнер делает nslookup db, запрос летит не в гугл и не в системный resolver. Он идёт на 127.0.0.11, встроенный DNS Docker. Этот резолвер живёт в каждом контейнере на user-defined bridge.

Внутри контейнера:


$ cat /etc/resolv.conf
nameserver 127.0.0.11


Что Docker делает с запросом:

1. Сначала смотрит, есть ли контейнер с таким именем в той же сети.
2. Если нет, форвардит на хостовый DNS.

Поэтому db резолвится в IP контейнера, а google.com уходит наружу.

На дефолтном bridge (без user-defined) этого DNS нет. Поэтому docker run без compose тебя по имени не свяжет, только по IP. Это одна из причин почему дефолтный bridge мертв.


Ещё про DNS: алиасы. У сервиса в compose можно задать дополнительные имена для сети:


services:
db:
image: postgres:16
networks:
app-net:
aliases:
- postgres
- primary-db


И теперь db, postgres, primary-db резолвятся в один и тот же контейнер. Удобно при переименованиях, чтобы не трогать код приложения, если вдруг он у вас так отвратительно написан).

➖➖➖

▎3. nicolaka/netshoot

Не нужно ставить tcpdump в свой образ. Запускаешь сайдкар, который делит сетевой namespace с проблемным контейнером:


docker run -it --rm \
--network container:web \
nicolaka/netshoot


Внутри netshoot есть всё: dig, tcpdump, nmap, iperf, mtr, nslookup, tshark, iproute2.


# 1. Резолвится ли имя?
dig db

# 2. Слушает ли db нужный порт?
nc -zv db 5432

# 3. Доходит ли пакет?
tcpdump -i any -n host db

# 4. Есть ли вообще маршрут наружу?
ip route


➖➖➖

▎4. external: true для сетей между compose-проектами

Частый кейс: фронт в одном docker-compose.yml, бэк в другом, observability в третьем, и хочется чтобы они видели друг друга по имени сервиса.

Создаёшь сеть один раз руками:


docker network create shared-net


И в каждом compose-файле ссылаешься на неё как на внешнюю:


services:
web:
image: app:1.0
networks:
- shared-net

networks:
shared-net:
external: true


external: true означает "используй уже существующую сеть". Если её нет, compose упадёт с ошибкой network not found.

Когда это к месту:

▸ несколько compose-проектов и нужно чтобы они общались
▸ база живёт вечно, а сервисы пересобираются

Альтернатива: затащить всё в один compose-файл. Если проектов уже три и они растут, external чище.

@izi.devops
1👍8❤2
Пока я делаю ластовый ролик по файловой системе докера, скажите какую тему дальше взять?
Anonymous Poll
18%
Copy fail уязвимость
52%
Начинать ролики про k8s
64%
Сети, маршруты и тд
2%
Я предложу тебе тему в комментариях
👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Copy Fail (CVE-2026-31431): и все твои гачиремиксы у майора в папке

➖➖➖

▎Что произошло

скрипт открывает специальный системный сокет (`AF_ALG`, обычно используется для крипты). из-за бага в ядре через этот сокет можно записать четыре байта в любой файл, который ты можешь читать.

меняешь четыре байта в /usr/bin/sudo, и проверка пароля начинает всегда возвращать «ок, ты root». теперь sudo bash запускает шелл без пароля.

➖➖➖

▎Почему контейнеры опаснее физического хоста

этот сокет проходит сквозь Docker и Kubernetes namespace. контейнер видит его так же, как хост.

сценарий в k8s. взломан один pod. он правит четыре байта в общем системном бинаре (`busybox`, ld-linux, что угодно из shared base image). этот бинарь лежит в page cache ядра ноды. соседние pod'ы запускают его и подхватывают.

без CAP_SYS_ADMIN, без --privileged, без сетевой связности. побег из контейнера не нужен. ты компрометируешь соседей со своего же пода.

➖➖➖

▎Как защититься

Обновляй ядро или seccomp в docker или kyverno в кубе
👍5❤3🔥2
Что не влезло в ролик про MAC, ARP и DHCP

➖➖➖

▎1. Gratuitous ARP. Как переезжает виртуальный IP

Обычный ARP это вопрос «у кого 192.168.1.1». Gratuitous ARP это ARP, который никто не просил. Машина просто кричит в сеть: «теперь этот IP у меня, мой MAC такой».

Зачем. Есть VIP, который держит keepalived или VRRP. Основной упал, резерв поднял IP у себя. У всех в кэше старый MAC, трафик летит в мёртвый сервер. Резерв шлёт gratuitous ARP - все обновляют кэш, трафик идёт в живой.

Где встречается: keepalived, Pacemaker, kube-vip, MetalLB в L2.


tcpdump -i eth0 -n 'arp and arp[14:4] = arp[24:4]'


Фильтр ловит gratuitous независимо от того, request это или reply.

➖➖➖

▎2. Два одинаковых MAC в одной сети

MAC обычно уникальный (первые 3 байта - это вендор), но его можно поменять руками. Иногда два устройства реально оказываются с одним MAC: склонировали виртуалку, дешёвый вендор повторил адрес, кто-то задал руками.

Что происходит на свиче. CAM это MAC → порт. Свич видит aa:bb на порту 5 запоминает. Потом видит тот же aa:bb на порту 12 перезаписывает. Через секунду опять видит на 5-м порту. Называется это поведение MAC flapping.

Симптомы: пинг через раз, в логах свича MAC flap between port 5 and port 12, ARP-кэш нестабильный.

Найти:


# linux bridge
bridge fdb show | sort | uniq -c -w 17 | sort -rn | head


Фикс - сменить MAC на одном из устройств:


ip link set dev eth0 address 02:00:00:11:22:33


➖➖➖

▎3. Два DHCP-сервера в одной сети

DHCP Discover это broadcast. Несколько серверов в одной сети значит, что каждый ответит из них и клиент возьмёт первый пришедший Offer.

▸ Подняли dnsmasq или ещё один тестовый DHCP, забыли выключить.

Защита на свиче - DHCP snooping. Свич знает, на каких портах разрешён DHCP-сервер (uplink), на остальных Offer/ACK режутся.


tcpdump -i eth0 -n 'udp port 67'


Если в логе несколько IP-источников Offer в сети больше одного DHCP.

Легитимный второй DHCP это failover: ISC DHCP failover или HA решения. Два сервера синхронят время аренды, клиент не замечает падения одного.

➖➖➖

▎4. DHCP FORCERENEW. Когда сервер выдёргивает клиента

Обычно цикл такой: клиент получил lease, через половину аренды шлёт Renew. Всё инициирует клиент.

А что если поменяли шлюз или DNS и хотим, чтобы клиенты подхватили это сейчас, не через 12 часов? Для этого есть FORCERENEW (RFC 3203).

Сервер шлёт клиенту unicast FORCERENEW. Клиент должен немедленно уйти в Renew и получить свежий ACK с новыми опциями.

Условия:

▸ Сервер и клиент знают общий ключ (HMAC-MD5). Без ключа клиент игнорит, иначе любой левый сервер мог бы дёргать чужих.
▸ Клиент поддерживает: ISC dhclient, systemd-networkd.

Альтернатива если FORCERENEW не настроен, поставить короткий lease (5 минут) на время изменений, дождаться пока все обновятся, вернуть нормальный.

➖➖➖

Не пропусти следующий ролик про VPN
👍9❤6🔥1
Я купил микрофон нормальный, теперь будет собственная озвучка 🌚
3🔥26❤7👍1
Думали я делал только видосики всё это время? А вот нет, еще я сделал сайт с теорией и маленько практики добавил в браузере.
https://izidevops.com/
если находите какие-то артефакты или ошибки или прочую шляпу пишите. Скоро буду делать нормальные практические уроки по куберу и разным другим технологиям по подписке. Всех поднял, обнял, пользуйтесь ❤️
🔥21❤3👾1
VPN темы, которые не влезли в 2 минуты

Дефолтный wg-quick up wg0 гонит весь трафик в туннель, DNS течёт мимо, при обрыве ноут долбит гугл с реальным IP, а DPI со временем научится резать (WG он уже режет спокойно). Примеры на WireGuard, но у других протоколов те же грабли.

➖➖➖

▎1. Split tunnel через AllowedIPs

Отдельной галочки «split tunnel» в WireGuard нет, всё решает одно поле в [Peer]:


[Peer]
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0


0.0.0.0/0 это full tunnel, весь IPv4 в туннель (вариант из видео). Хочешь гнать в VPN только корп-подсеть, а ютуб напрямую:


AllowedIPs = 10.10.0.0/16, 172.16.0.0/12


wg-quick пропишет в ip route только эти префиксы, дефолт останется на роутере. Реверса (всё минус что-то) в WG нет.

➖➖➖

▎2. Kill switch против утечки при обрыве

Туннель упал, ноут продолжает слать пакеты, и без kill switch они уйдут через роутер с реальным IP. Сам wg-quick его не делает, но в man-странице есть готовый рецепт через PostUp`/`PreDown (маршруты при этом дефолтные, `Table = auto`):


[Interface]
Address = 10.0.0.2/32
PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT


Правило режет любой исходящий не через wg0, кроме самих шифро-пакетов WG (по fwmark`) и localhost. `-j REJECT, а не DROP, чтобы приложения сразу видели ошибку, а не висели в таймауте. wg-quick down снимает правило, после ребута нет ни правила, ни туннеля.

В клиентах отдельной кнопки kill switch нет. На Android его включают в системных настройках VPN («Постоянная VPN» + «Блокировать соединения без VPN»), на iOS и macOS похожий эффект даёт «On-Demand», который сам переподнимает упавший туннель.

➖➖➖

▎3. DNS leak. Куда деваются `.com`-запросы

Туннель поднят, AllowedIPs = 0.0.0.0/0, а /etc/resolv.conf всё ещё nameserver 192.168.1.1. Браузер резолвит google.com через домашний роутер, тот идёт к провайдеру. Сам HTTPS потом шифрованный и в туннеле, но какие домены ты открывал провайдер уже записал.

Лечится строкой в конфиге, wg-quick подменит resolv.conf:


[Interface]
DNS = 1.1.1.1, 1.0.0.1


Теперь DNS летит к Cloudflare через туннель, провайдер видит только поток на твой сервер.

Проверка: dnsleaktest.com → Extended Test. Вылез провайдер, значит течёт.

На macOS DNS живёт не в resolv.conf, а в scutil, и mDNSResponder может кэшировать своё. Лечится dscacheutil -flushcache; sudo killall -HUP mDNSResponder.

➖➖➖

▎4. MTU. Почему «после VPN всё тормозит»

VPN кладёт твой пакет в новый: IP-заголовок снаружи, заголовок WireGuard, UDP. ~60 байт оверхеда. Пихаешь 1500 в туннель, на выходе 1560, это больше MTU линка, пакет фрагментируется или дропается.

wg-quick сам считает MTU как у маршрута до сервера минус 80, на обычном Ethernet это 1420, обычно норм. Но PPPoE режет до 1492, мобила до 1400, IPv6-туннели до 1280, и 1420 уже не лезет:


[Interface]
MTU = 1380


Симптом: handshake проходит, а большая страница виснет на середине, картинки в телеге не грузятся, rsync зависает. Тест:


ping -M do -s 1372 1.1.1.1 # Linux: 1372 + 28 = 1400
ping -D -s 1372 1.1.1.1 # macOS


Проходит на 1372 и нет на 1400, значит MTU около 1400. Ставь 1380 с запасом.

➖➖➖

▎5. Когда обычный WireGuard режется DPI

DPI у больших провайдеров узнаёт WireGuard по первому пакету: хэндшейк всегда начинается с 0x01 0x00 0x00 0x00 и имеет фиксированный размер (148 байт). Видит этот префикс и рубит соединение.

➖➖➖

Писал огромную пасту, какие впн сейчас работают, но подумал что могут притянуть за распространение. Так что залетайте в инсту под последний ролик про ВПН, там в комментах обсуждение (хотя если вы тут, то впн у вас уже есть). По белым спискам инфа тоже быстро устаревает, но на гитхабе есть рабочие варианты (поднять прокси у российского провайдера, который сам в белом списке, и всё заработает).
👍9🔥3❤1
Привет! Запускаю в закрытую бету платформу для подготовки к CKAD и CKA. Там нет видосов, но есть живые лабы: у тебя прямо в браузере настоящий кластер, задания и автопроверка.
Беру 5 человек в первую группу. Взамен прошу пройти несколько уроков и честно сказать, что непонятно, где баг или где некрасиво.
Доступ бесплатный. Нужны и те, кто уже знает куб, и те, кто только слышал про него, но хочет разобраться. Если интересно, напиши в комментариях свой уровень: новичок, готовлюсь к CKAD, или уже работаю с k8s.
🔥15❤1
Как ТСПУ ищет тунель, ни разу не заглянув внутрь

Зашифрованный трафик нельзя прочитать. Но это и не нужно. Цензору хватает того, как поток выглядит снаружи: энтропии первого пакета, ритма рукопожатия, формы сессии и того, что ответит сервер на пустой запрос. Самая изученная штука в этом плане великий китайский факрвол GFW: его правила вытащили наружу академики, и по ним видно, что именно меряет машина. ТСПУ сначала шел тем же путём, полностью переняв опыт, но сейчас вырвался вперед и блочит даже то, что не умеет китайский.

Дисклеймер: разбор механики по открытым источникам, без инструкций по обходу и без рекламы сервисов.

➖➖➖

▎1. Энтропия первого пакета

В ноябре 2021 GFW включил детектор «полностью зашифрованного» трафика. Логика обратная: система не ищет VPN, она вычёркивает всё, что на VPN не похоже, и блокирует остаток. Пять правил-исключений (USENIX Security 2023):

считается доля единичных бит на байт в первом TCP-пейлоаде. Если ≤3.4 или ≥4.6 бит, то поток помилован. Зашифрованные данные держатся около середины (примерно половина бит это единицы), и именно это выдаёт их;
Также помилование, если первые 6+ байт лежат в печатном ASCII (0x20–0x7e), либо печатных байт больше половины, либо подряд идёт больше 20 печатных символов;
Ну и помилование по сигнатуре TLS и HTTP-методов (GET, POST и т.п.).

Тонкость: блокируется только ~26% соединений к подозрительному серверу, и работает это лишь по TCP. UDP детектор не трогает вовсе.

➖➖➖

▎2. Отпечаток рукопожатия

Даже обёрнутый протокол выдаёт себя порядком пакетов. Разбор OpenVPN (USENIX Security 2022) показал: в заголовке есть поле opcode, у него больше 10 значений, и их последовательность в начале сессии складывается в узнаваемый узор. Второй признак P_ACK: эти пакеты без TLS-нагрузки, одинакового размера, и в ванильном OpenVPN и в XOR-обфускации первый ACK обычно приходит третьим пакетом сессии.

obfs4 этот класс атак держит: на каждый мост нужен отдельный секрет, который клиент обязан доказать в первом сообщении, поэтому отпечатка наружу не торчит.

➖➖➖

▎3. Форма потока вместо содержимого

Размеры пакетов и паузы между ними рисуют картинку, которую читает потом загоняют в ИИ для анализа потока.

➖➖➖

▎4. Активное прозванивание

Если пассивно непонятно система сама стучится на сервер. И может определять действительно ли вы стучитесь на сервер просто так и там не установлен впн, повторяет ваш запрос и смотрит что ему отвечает сервер.
➖➖➖

▎5. Один пул и география

С 2025 в ТСПУ заявлены поведенческие ML-признаки: длительность сессий, частота подключений к одному адресу, объёмы. Общий публичный сервер палится очень быстро, из-за того, что тысячи незнакомых клиентов на одном IP такого узора в норме не дают. И раскатка идёт не разом: с декабря 2025 отлов VLESS сначала включили в Татарстане и Удмуртии, потом в Свердловской области, потом в Москве.

➖➖➖

Итого, что мы имеем.
Детектор не читает байты, он меряет их статистику: энтропию первого пакета, порядок opcode, форму потока, ответ на прозвон и плотность клиентов на адресе. Чем ровнее и многолюднее канал, тем быстрее он попадает в остаток, который блокируют. Всё по открытым источникам, цифры устаревают за недели. Также нет прямой информации по ТСПУ, это все или сливы от людей кто с ним работает или косвенный анализ поведения или тестовые лабы.

Поэтому платные решения меньше живут, чем собственные
1❤10🔥4
Серты, что не влезло в ролик

Самое частое и обидное это дата. Внутри серта две метки, начало и конец срока, браузер просто сверяет их с часами системы. Опоздал на минуту, и держи NET::ERR_CERT_DATE_INVALID. Мелочь, скажешь ты, но на этом падали Microsoft Teams в 2020 и Spotify позже. Огромные сервисы легли, потому что забыли продлить серт. Сейчас серты и так живут недолго: публичные CA выдают максимум на 398 дней, а в 2025 решили резать до 47 дней к 2029 году.


openssl x509 -in cert.pem -noout -dates


➖➖➖

Дальше браузер смотрит, на чьё имя серт выписан. Берёт хост из адресной строки и ищет его в поле SAN. Раньше для этого был Common Name, но он устарел, теперь имя живёт только в SAN. Серт выдан на bank.ru, а ты зашёл на www.bank.ru, которого в списке нет, и получаешь ERR_CERT_COMMON_NAME_INVALID. Подпись правильная, цепочка целая, серт настоящий, просто не от этого адреса.


openssl x509 -in cert.pem -noout -ext subjectAltName


Заодно проверяется, чем серт подписан. SHA-1 выкинули ещё в 2017, RSA короче 2048 бит не пропустят. Тут обычно всё ровно.

➖➖➖

Допустим, у банка украли приватный ключ. По дате серт ещё годен, но доверять ему уже нельзя, его надо отозвать досрочно. Для этого два механизма: CRL, длинный список отозванных, и OCSP, точечный вопрос «этот серт ещё живой?». Куда стучаться, зашито прямо в серте.


openssl x509 -in cert.pem -noout -ocsp_uri


Только в жизни всё держится на честном слове. Сервер OCSP у CA прилёг, и браузер не ругается, а молча пускает (soft-fail), иначе при каждом сбое CA встал бы весь интернет. То есть атакующему хватит заблокировать OCSP, и отозванный серт снова «валиден». Плюс каждым запросом ты докладываешь центру, какие сайты открываешь. В итоге браузеры на живой OCSP махнули рукой: Chrome таскает свои CRLSets, Firefox городит CRLite. Проверка вроде есть, а по факту её отключили сами вендоры.

➖➖➖

Корневой серт вшит в систему, серт банка прилетает при подключении, а промежуточный между ними сервер обязан отдать сам. Не положил его в конфиг, и цепочка до корня не достроится. Самое противное, что на десктопе Chrome промолчит: нужный промежуточный он уже видел на других сайтах и достанет из кэша. А свежий браузер или curl без кэша упрутся в ошибку.


openssl s_client -connect bank.ru:443 -showcerts



ОБЯЗАТЕЛЬНО К ПРОЧТЕНИЮ

И отзыв только что перестал быть теорией. В июне 2026 GlobalSign начал массово отзывать серты российских сайтов: CA/Browser Forum обязал центры сверять клиентов с санкционными списками. Под раздачу попали тысячи доменов, а среди западных коммерческих сертов в рунете у GlobalSign было около 90%.

Тут и вылезает та самая дырявость. Эффект размазанный: где-то браузер увидит отзыв и покраснеет, где-то soft-fail и урезанные CRLSets пропустят. Оттого и оценки гуляют, от десятков тысяч сайтов до «не больше 5%» по версии Минцифры. Ведомство зовёт на серты своего Национального удостоверяющего центра (НУЦ), их бесплатно выдают через Госуслуги. Только корень НУЦ по умолчанию доверяют лишь Яндекс.Браузер и Атом, в Chrome его ставят руками.


И вот тут стоит дважды подумать. Корень это абсолютное доверие: кто им владеет, тот может выписать валидный серт на любой домен, хоть на gmail, хоть на твой банк, и браузер покажет зелёный замок без вопросов. Тот самый человек посередине из ролика, только впускаешь ты его сам.

Для НУЦ это значит, что владелец корня (Минцифры, а значит и СПЕЦСЛУЖБЫ) при желании может вклиниться в твой HTTPS на своей инфраструктуре, у провайдера, и читать трафик, который ты считаешь зашифрованным. Поэтому если корень НУЦ всё же нужен, держи его в отдельном браузере под госуслуги и госбанки, а НЕ СУЙ в системное хранилище для всего подряд.
👍7❤1
Всем привет! Запустил тренажёр по Kubernetes. Живые лабы с настоящим kubectl прямо в браузере:

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

izidevops.com
11🔥22😍2❤1🏆1🤝1
VPN включён. А тебя всё равно видно.

Под прошлым видео разгорелся спор: мол, все эти деаноны работают только на древних L2TP и SSTP, а на Xray с Reality такое не прокатит.

Reality прячет одну вещь: факт, что ты под VPN, от провайдера. Сайт, который тебя палит, живёт на другом уровне.

➖➖➖

▎1. Транспорт и браузер это разные слои

Reality, uTLS, VLESS, выбор протокола, всё это про транспорт. Как трафик доезжает до сервера и как выглядит со стороны провайдера. Деанон на сайте происходит выше, в самом браузере.

➖➖➖

▎2. DNS течёт на уровне резолвера

Браузер спрашивает «где сайт?» у DNS. Если резолвинг не завёрнут в туннель, запрос уходит мимо, и провайдер видит список доменов. Xray тоже течёт, если не настроить sniffing и роутинг DNS руками. Протокол сам по себе тут не помогает.

➖➖➖

▎3. WebRTC сливает реальный IP

WebRTC шлёт STUN-запросы по UDP напрямую через сетевой стек ОС, мимо настроек прокси браузера. Если трафик идёт мимо туннеля (браузерный VPN, proxy-режим, split-tunnel), пара строк JS вытаскивает твой реальный адрес, пока VPN спокойно крутится. Системный full-tunnel это закрывает, но дефолтная связка «расширение + браузер» нет. Надёжнее всего рубить WebRTC в самом браузере, а не надеяться на VPN.

➖➖➖

▎4. Фингерпринт от протокола не зависит

uTLS правда меняет отпечаток TLS-хендшейка, это JA3/JA4. Но это сетевой уровень, до того как выполнится хоть одна строчка JS. А canvas, WebGL, шрифты, разрешение, язык сайт снимает из самого браузера. Хоть SSTP, хоть Reality, отпечаток один и тот же.

➖➖➖

▎5. Залогинен значит опознан

SameSite-куки это защита от CSRF, к деанону отношения ноль. Сидишь залогиненным в гугле, вк, я.браузере, тебя палит сессия. Хоть через цепочку из 50 прокси. Дальше идёт поведение: движения мыши, ритм печати, паттерны кликов. Всё сшивается в один профиль, и узнают тебя даже с нового адреса.

➖➖➖

▎6. «А Hysteria2 и новые ядра уже всё чинят»

Частично. TUN / full-tunnel режим перехватывает весь UDP ОС и может завернуть STUN в туннель. Но это руками включить, а не дефолт.

Люди и сейчас ловят DNS-утечку на VLESS/Reality, и каждый раз лечится одним: руками настроить DNS-роутинг через туннель. Reality сам по себе резолвинг не закрывает.
[Xray #4299](https://github.com/XTLS/Xray-core/discussions/4299) · [Xray #5618](https://github.com/XTLS/Xray-core/discussions/5618)

И главное: фингерпринт и поведение ни одно ядро не трогает в принципе. Это прикладной слой.
3❤11👍4🤔1