Make. Build. Break. Reflect.
1.18K subscribers
132 photos
1 video
1 file
140 links
Полезные советы, всратые истории, странные шутки и заметки на полях от @kruchkov_alexandr
Download Telegram
Перешёл на Ask every time в Cursor IDE.

Устал разгребать дерьмо от агента.
Прям утомился и устал.

Реалии февраля 2026:
- игнорирует allow list в README.md
- игнорирует allow list в настройках Cursor IDE
- игнорирует файл(ы) rules Cursor IDE
- игнорирует allow для MCP серверов
- игнорирует явные запреты в AGENTS.md
- игнорирует рут сандбокс (может легко выходить из песочницы)
- при моих запретах средствами операционной системы начинает писать питон скрипты, чтобы обойти запреты доступа к файловой системе и просить аппрув на выполнение🤨

Каждый раз без зазрения совести использует опасные команды без аппрува, если ему захотелось
- terraform apply -target -auto-approve
- kubectl delete/apply
- terragrunt run-all -auto-approve
- aws .. modify ..
и многое другое. Печаль.

Модели:
- Claude-4.5-sonnet
- 4.6 Opus

Четырёх сильных ошибок мне хватило.
Благо git всё помнит и можно ревертнуть руками, а косяк был без персистент даты.

Мне нравится этот новый поезд LLM и агентов.
Однако слишком быстро стремящийся поезд.
Предпочитаю быть за рулем, или хотя бы быть в вагоне с кабиной управления, а не ехать в заднем вагоне с закрытыми глазами.

- - -
На скрине часть моей драмы-переписки.
В итоге у меня сейчас 0 MCP серверов, пустые allow list, пару костыль-ограничений средствами операционки и Ask every time.
Печаль.
Please open Telegram to view this post
VIEW IN TELEGRAM
😁205👍5🥴2😢1
Ямлоделие

О, дивный мир, на работе я узнал об ограничении GitLab на парсинг YAML файла с пайплайном.

Проскочила ошибка, уронила всё.
Unable to run pipeline
`gitlab-ci.yml`: The parsed YAML is too big

Документация привела к
https://docs.gitlab.com/administration/instance_limits/#maximum-size-and-depth-of-cicd-configuration-yaml-files

Интереса ради спуллил их репу https://gitlab.com/gitlab-org/gitlab
Аригато, кстати, что git репозиторий весит всего 2.79 GiB, просто невероятное удобство😊.

Затем спросил нейронку сколько памяти/ресурсов экономится при парсинге 2 или 4 мегабайтных файлов и какие причины такого ограничения.

Самое интересное - max_yaml_size_bytes это не физический размер файла, а память, выделяемая на объекты при парсинге.
То есть 2 MB YAML может жрать больше 2 MB RAM в зависимости от структуры.

Зачем вообще лимит?
Погуглил, почитал документацию, посмотрел код.
- DoS защита
один огромный YAML может положить весь Sidekiq (классная архитектура 😀)
- OOM превенция
память пре-аллоцируется, условно 100 пайплайнов по 100 MB = изда RIP серверу
- производительность
парсинг гигантских YAML тормозит всех (чужие проекты, пайплайны)
- code smell детектор (это я/мы)
если у вас 2+ MB YAML, что-то пошло не так в архитектуре

Ну штош, значит пора оптимизировать содержимое пайплайнов.😭
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👌31
#aws #rds #troubleshooting

Приходят как-то коллеги, говорят - нет прав включить Enhanced Logging на RDS Proxy.
Для дебага очень надо на пару часов.
Ок, смотрю политику, а там rds:* на Resource = "*".
Говорю - всё есть, всё должно работать.
Хоть дропайте всё к херам (stage account).

Возвращаются - нет, всё равно не работает.
Проверяю глазами ещё раз полиси.
Да вроде всё ок.
Говорю сделайте релогин и попробуйте ещё, все права есть.

Снова возвращаются, сделав релогин.
Говорят "включаем, а оно не включается".
Делаю невозмутимое лицо, хотя внутри уже кривлюсь как хурма: описание проблемы уровня "включаем, а не включается, ошибок нет" звучит слишком уныло, чтобы воспринимать его всерьёз.

Прошу объяснить подробнее.
Мне показывают по шагам:
- зашли в консоль
- зашли в RDS
- нашли нужный proxy
- нажали Edit/Modify (я не помню название кнопки)
- поставили галочку "Activate enhanced logging"
- нажали "Modify proxy"
- видят надпись "Modifying proxy ..."
- видят зелёный баннер "Successfully modified proxy ..."
- выходят, заходят снова - галочки нет

Нигде ни одной ошибки.
Всё выглядит как успех.
Но изменение не применяется.
В ивентах пусто.

Повторяю сам под своим admin пермишнсетом - у меня работает, галочка стоит.
В ивентах изменения появились.

Ладно, добавляю себя временно в эту группу и начинаю воспроизводить.
Захожу под их пермишнсетом, повторяю их действия.
Та же история - "Successfully modified", а галочка не стоит.

Думаю что не так.
Выхожу из SSO, захожу снова - галочки нет.
Сбрасываю кеш браузера - галочки нет.
Пробую другой браузер - галочки нет.
Смотрю в developer tools в браузере - никаких ошибок, никаких 403, всё 200.
Смотрю в сам UI консоли - ни единого красного баннера, ни единого намёка на проблему.
Само собой инкогнито мод браузера не помог.
Ивентов нет.

Потратил на это ещё чуть времени, пока до меня наконец не дошло.
Дурачок, - думаю я, - а CloudTrail-то ты проверил?

Иду в CloudTrail, фильтрую по ModifyDBProxy.
Нахожу событие. И там:
"errorCode": "AccessDenied",
"errorMessage": "User: ... is not authorized to perform: iam:PassRole
on resource: arn:aws:iam::ACCOUNT_ID:role/rds-proxy-wanker
because no identity-based policy allows the iam:PassRole action"

Вот оно.

AWS при вызове ModifyDBProxy передаёт IAM роль прокси в Secrets Manager.
Для этого нужно право передать (PassRole) именно эту роль.
rds:* тут ни при чём - это отдельное действие на отдельный ресурс.

В политике группы iam:PassRole был разрешён только для одной конкретной роли.
Про rds-proxy-* никто не подумал.

Добавляем в политику:
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::ACCOUNT_ID:role/rds-proxy-*"
}

Применяем, перелогиниваемся.
Галочка встаёт и остаётся стоять.
Ивенты пишутся.

Итоги:
- "Successfully modified" в AWS консоли не гарантирует, что изменение применилось 🥲
- AccessDenied может прилетать не на само действие, а на зависимое - в данном случае на iam:PassRole
- Консоль эту ошибку не показывает совсем. Молча глотает. Никакого красного баннера. Никаких ошибок. Никаких ивентов. 😎
- CloudTrail - первое место, куда надо смотреть, когда "всё работает, но не работает"
- rds:* не равно "всё для RDS". Некоторые операции тащат за собой зависимости на другие сервисы
- нехер снова кривить свою морду, коллеги всё же солнышки-зайчики и правы, даже если пояснение проблемы тупорылое
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯182😭1
#ai #kubernetes

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

Понадобилось, внезапно, по работе посмотреть большое количество логов.
Да-да, есть под рукой VMui/OpenSearch/Grafana и так далее, но логов много и подов много, веб не поможет в моем дотошном осмотре. Немного специфичная задача была.
Надо было прям в моменте глянуть чо там чо там на нескольких десятках подов, да ещё и с дебаг левелом.

Думаю "блин, ну я вроде сам это относительно недавно делал через node proxy, даже заметку писал, дай повторю".
https://xn--r1a.website/makebreakreflect/205
Нашёл, зашёл, а там уныло - много команд, мне лень.
Не, ну там десяток нод, десятка 4 подов, не буду же я на каждую ноду по SSH/SSM залезать, это тупо.
В общем скормил мой же текст поста нейронке, говорю дай мне универсальный скрипт/команду, чтобы выкачать все файлы по фильтру неймспейса/пода, чтобы локально это исследовать.

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

Вариант 1: через файловую систему ноды
Этот вариант ходит напрямую в /var/log/containers/ через Node Proxy API и выкачивает raw-файлы логов.
Полезен, если нужно получить именно файлы с диска (например, для анализа ротации или когда нужен точный формат файла).
NAMESPACE="kube-system"
POD_PATTERN="kube-proxy"

for POD in $(kubectl get pods -n $NAMESPACE | grep $POD_PATTERN | awk '{print $1}'); do
NODE=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.nodeName}')
CONTAINER_ID=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.status.containerStatuses[0].containerID}' | sed 's|containerd://||')
CONTAINER_NAME=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.containers[0].name}')
LOG_FILE="${POD}_${NAMESPACE}_${CONTAINER_NAME}-${CONTAINER_ID}.log"

echo "⬇️ Downloading: $POD → $NODE"
kubectl get --raw "/api/v1/nodes/${NODE}/proxy/logs/containers/${LOG_FILE}" > "./${POD}.log"
echo "✓ Saved: ./${POD}.log"
done


Вариант 2: Через официальный API containerLogs
Этот вариант использует документированный endpoint kubelet API.
Работает с namespace/pod/container напрямую, не требует знания внутренней структуры файловой системы ноды.
NAMESPACE="kube-system"
POD_PATTERN="kube-proxy"

for POD in $(kubectl get pods -n $NAMESPACE | grep $POD_PATTERN | awk '{print $1}'); do
NODE=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.nodeName}')

CONTAINERS=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}')

for CONTAINER in $CONTAINERS; do
OUTPUT_FILE="./${POD}_${CONTAINER}.log"

echo "⬇️ Downloading: $POD/$CONTAINER from $NODE"
kubectl get --raw "/api/v1/nodes/${NODE}/proxy/containerLogs/${NAMESPACE}/${POD}/${CONTAINER}" > "$OUTPUT_FILE"
echo "✓ Saved: $OUTPUT_FILE"
done
done

аутпут
⬇️ Downloading: kube-proxy-brpfm → aks-1
✓ Saved: ./kube-proxy-brpfm.log
⬇️ Downloading: kube-proxy-bt6fp → aks-2
✓ Saved: ./kube-proxy-bt6fp.log


Ограничения обоих вариантов
Это не стандартный документированный способ получения логов контейнеров (хотя второй вариант использует документированный endpoint kubelet, это всё равно обходной путь через nodes/proxy).
Оба скрипта только для первого (индекс [0]) контейнера в поде, но мне ок по задаче.

Не заработает, если:
- у вашего пользователя нет прав nodes/proxy
- кластер имеет ограничения на node proxy access
- используется нестандартный путь для логов контейнеров (для варианта 1)
- для варианта 1, если у вас не containerd, а cri-o, поправьте sed на нечто типа:
sed -E 's/^(containerd:\/\/|cri-o:\/\/)//'

У меня крио нет, так что я хз сработает ли эта регулярка.

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

Забавно обучать/получать ответы от нейронки по своей же заметке. 😅
👍16👎31
#AWScommunity #aws

Какие интересные письма порой приходят.
И это отлично.

Впереди будет много интересного и, надеюсь, полезного.
Наконец-то я смогу весь свой неиссякаемый энтузиазм и энергию потратить на пользу сообщества.
10🔥50👏1021
#opensearch #kubernetes #troubleshooting #одинденьизжизни

"Алекс, у нас пропали логи.
Но только на стейдже."

Иду смотреть.
Куб живой, метрики зелёные, поды в норме, а в OpenSearch тишина.
Ни одного алёрта. Ладно.

Пошёл проверять по классике:
- не упал ли агент сбора логов
- не отвалились ли права
- не умер ли endpoint
- не сломали ли что-то свежими коммитами

Первый заход:
Никаких коммитов, которые могли это поломать нет.
Ок, идём в куб.
kubectl get ds -n kube-system

fluent-bit живой, все реплики Ready
kubectl get pods -n kube-system | grep fluent-bit

всё раннит
kubectl logs -n kube-system daemonset/fluent-bit --since=30m

И вот там уже не тишина.

В логах:
- validation_exception
- this action would add [4] total shards
- cluster currently has [1000]/[1000] maximum shards open


Красота.
Значит не "логов нет", а "логгер не может писать".

Дальше мысль идёт так:
куб живой, агенты живые, опенсёрч живой, с pvc всё ок.
но если шардов в потолок, значит retention не чистит старые индексы.🤔

И тут флешбек:
несколько месяцев назад мигрировали с filebeat на fluent-bit.
Индексный префикс поменяли.
Ну там было нечто типа eks-filebeat-* на eks-fluentbit-*, не помню уже.
А retention policy, как это обычно бывает в суровой реальности, осталась смотреть на старое имя индекса ☔️.

Итог:
- новые индексы копились
- старые правила их не трогали
- шардов стало 1000/1000
- OpenSearch начал отвечать 400 на bulk insert
- fluent-bit ретраил, потом сдавался
- в UI ощущение, что "логи пропали только на stage"

То есть виноват не новый деплой, не "вчерашний коммит", не магия.
Просто policy смотрела не туда.
И да, жаль, что эта policy была не в IaC, а когда-то сделана руками в консоли.
Именно поэтому при миграции её и проглядели.

Что проверил и что сработало:
- поправил index pattern в retention/ISM policy на актуальный префикс
- подчистил лишние старые индексы вручную (DELETE /indexname* предыдущих лет/месяцев)
- освободились шарды
- проверил повторно логи fluent-bit -> ошибки по maximum shards open ушли
- в OpenSearch новые логи снова пошли

Короткий вывод:
- миграция агента логирования = почти всегда миграция index naming
- если не обновить retention под новый pattern, инцидент это вопрос времени
- всё, что не в IaC, рано или поздно теряется в памяти любой команды
- алерт на долю открытых шардов нужен заранее (80-85%), а не в момент 1000/1000 (а алёрта и не было)

Постмортем одной строкой:
Логи не пропали. Мы сами забили OpenSearch старыми индексами, потому что retention policy жила в прошлом и смотрела на имя эпохи filebeat.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍202
Впервые запустил apple music на новом железе.
Да-да, я теперь снова владелец MacBook pro, но уже на M5, и скоро буду писать свои позитивные и негативные, никому не нужные, отзывы.

Штош, заманчивая цена после триала, но, пожалуй, воздержусь.
Пожалуй я буду и дальше слушать музыку в браузере или у сторонних приложений за 3,5 гроша в год.
🔥11😁9😱3🎉2
#devops #troubleshooting

Из всего невообразимого и бесконечного потока, любимого и одновременно ненавистного всеми, AIAIAIA со всех углов, мне больше всего понравились skills.
Вообще ничего больше не впечатлило за последние года два - как скиллы.
Ну да, ну эйая, ну вайбкодинг. Это даже все бабушки знают и слышали.

Но, господи, как же скиллы охрененно работают в траблшутинге.

Вот пишу я это и понимаю, что скиллы сейчас - топ артефакт в работе девопса и SRE.
Во всяком случае, как я это использую - прям рекомендация 1000000 из 1.
Я готов прыгать от счастья, осознавая сколько времени они мне экономят.

Если вы не знаете как их использовать - попробуйте.
Начните с любого простого примера.

Как использую у себя в двух словах:
- создал несколько SKILLS.md файлов (на самом деле у меня их иерархия, уже 29 скиллов)
- замапил скиллы для Cursor IDE. Можно и в Claude Code, скиллы будут работать и там и там, просто путь разный
- в скиллах и основном ридми файле описал структуру проекта(ов)
- - где ArgoCD (UI, API, git repos)
- - какой нейминг у кластеров кубернетис (AKS, EKS, GCP), нейминг неймспейсов
- - где вообще все гит репозитории и иерархию
- - адрес гитлаба и зеркало, локальные склонированные репозитории
- - что контекст переключается локально через kubectl
- - есть утилиты kubectx, jq и многое другое
- указываю все ключевые адреса репозиториев и ендпойнтов (виктория метрикс, виктория логс, алертменеджер, гитлаб и многое многое другое)
- если где есть сверхтяжелая авторизация, то говорю используй port-forward на внутренние service, там авторизации нет

В общем указал все, что надо для траблшутинга.

Как происходит процесс с моей стороны:
- прилетает алерт
- я копирую текст алерта в агент-ассистент (сейчас у меня курсор)
- жду
- агент смотрит что за кластер, неймспейс, под, контейнер
- лезет в гит, смотрит таймлайн когда кто что релизил, когда коммитили и где менялись какие версии или конфиги
- лезет в нужный кластер кубернетиса, смотрит в нужном неймспейсе, под, логи, дескрайб, ивенты кубера
- при необходимости лезет в логи виктория логс, метрики виктория метрикс (прям через curl)
- если надо, то лезет в арго
- если надо, то лезет в гит репозитории бекенда и фронтенда
- строит гипотезы, проверяет, уточняет
- при сомнениях что-то сам проверяет (ну типа если ему кажется, что проблема нехватки нод - нехватка ip адресов, то иди и проверь метрики сети в AWS и EKS CNI)
- если надо, лезет с рид правами в azure, gcp, aws cli
- выводит мне удобный формат по инциденту и вариант(ы) решений

С вероятностью 90 процентов это точный результат.

Чрезвычайно убого замазанные примеры на скриншотах.

Дальше я уже либо сам чиню, либо передаю другим командам, если дело в них.

Это только кажется, что это сложно, непонятно, магически. Нет.
Только представьте себя на месте агента: знаешь на 99 процентов структуру всей инфры, проектов, процессов.
Ты же как-то сам логически идешь по траблшутингу? Вот так и агент.

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

Алерт - копипаст - ждешь пару минут (автоаппрув или каждую команду аппрув) - ответ и решение(гипотезы).


- - -

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

На полном серьезе не хочу шарить на команду 🚬
Даже если кто из команды это читает.
Господи, кому я вру, если скажут - отдам уже завтра.☔️

Я ведь ровно так же понимаю, что написав свои 100500 скиллов, которые уже сейчас, на сотнях алертов и десятке инцидентов на 100 процентов точно дали ответ и удобный аутпут для команд, можно пошарить самостоятельному агенту или условному мидлу с зарплатой три копейки.
И я вылечу на помойку.
Вот такая грустная правда.

Скиллы - это прекрасно.
Скиллы - это главный мой артефакт работы в 2026.
Скиллы - это грустно, они на 10000 процентов подсократят работы нам, инженерам.☔️
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥236🤡3🫡1
Понедельник начинается с кофе прохождения AI adoption survey 🤡
Please open Telegram to view this post
VIEW IN TELEGRAM
😁23
#argocd #keda #kubernetes #troubleshooting #longread #одинденьизжизни

Прилетает однажды алерт.
Простой и в целом понятный.
"Сейчас за пять минут я решу это дело" подумал я.

Так появился этот лонгрид.

https://teletype.in/@kruchkov_alexandr/mwVCBuS1y6T
🦄9👻4👍2🥱1
#devops #certificate

Простая напоминалка (в том числе и себе) на 2026-2027 годы про TLS‑сертификаты.

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

Кратко по таймлайну (публичные TLS, CA/B Forum):
398 > 200 дней (с марта 2026) > 100 дней (с 2027) > 47 дней (с 2029).

Параллельно сокращают сроки повторного DCV, к 2029 остаётся до ~10 дней на переиспользование проверки домена.

CA (официальные анонсы и FAQ):
- DigiCert - переход на 199‑дневные сертификаты:
https://knowledge.digicert.com/alerts/public-tls-certificates-199-day-validity
- DigiCert - FAQ по срокам валидности:
https://www.digicert.com/faq/tls-ssl-certificate-validity
- Sectigo - про риски и 200‑дневную валидность:
https://www.sectigo.com/blog/200-day-ssl-certificate-expiration-risk
- GlobalSign - 199‑дневные сертификаты с марта 2026:
https://www.nicsrs.com/blog/globalsign-199-day-ssl-certificate-validity-2026

ACME / Let’s Encrypt и короткоживущие серты (думаю такое у многих):
- Let’s Encrypt: с 90 до 45 дней, общий план:
https://letsencrypt.org/2025/12/02/from-90-to-45.html
- Разбор перехода на 45‑дневные серты:
https://www.certkit.io/blog/45-day-certificates
- Let’s Encrypt: 6‑дневные и IP‑сертификаты (интересно в контексте мира с 47 днями):
https://www.helpnetsecurity.com/2026/01/20/lets-encrypt-6-day-tls-certificates
https://letsencrypt.org/2026/03/11/shorter-certs-certbot

Инструменты и автоматизация:
- cert-manager (релизы, где можно отслеживать поддержку коротких сроков, ACME и т.д.):
https://cert-manager.io/docs/releases/

Хорошие обзоры по общему таймлайну 398 > 47 дней и подготовке:
- Гайд по адаптации к 47‑дневным сертификатам:
https://www.centralnicreseller.com/47-day-ssl-certificates-validity-adaptation-guide-for-businesses
- Обзор сокращения сроков и влияния на процессы:
https://www.eginnovations.com/blog/ssl-tls-certificate-lifetimes-to-reduce-to-47-days

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

Владельцам приватных внутренних УЦ в изолированных контурах можно пока расслабиться 😬 - эти правила касаются именно публичных TLS.
Please open Telegram to view this post
VIEW IN TELEGRAM
😢8👍4🙏41
#пятница #всратость

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

В попытке синхронизировать календари разных систем и отметить свой отпуск (и UPTO), я вместо добавления события в рабочий календарь случайно разослал ВСЕМ в компании приглашение на ивент "Алекс в отпуске".

Классный ивент.
Примите, пожалуйста, и распишитесь.

Я просто сгораю со стыда.😦
Please open Telegram to view this post
VIEW IN TELEGRAM
😁43👏6
#aws #байки

Вторниковая байка.

У AWS SM, если по‑простому, два варианта хранения:
строковый секрет и бинарный.
Строковый в консоли можно редактировать либо как key/value (оно само собирается в JSON), либо как "сырой текст", где ты сам пишешь строку или JSON.

Однажды прилетает ошибка на проде - поды не стартуют.
После деплоя.
Начинаю разбираться - нет кубер‑секрета.
Секрета нет, потому что external secrets не смог подтянуть значение из AWS SM, ругался на какую‑то несовместимость.
Пробую ручной синк - те же ошибки.

Лезу в AWS SM - а там, блять, нет UI‑формы key/value.
Вместо привычной таблички - какой‑то поломанный сырой секрет.
Пришлось ковырять историю, спрашивать разработчиков. Оказалось вот что.

Разработчик добавил новую переменную, значением которой был JSON с множеством вложенностей.
Разумеется, без всякого экранирования, ведь это "никому не надо", а внутри - креды, пароли, спецсимволы😬.
В результате секрет перестал выглядеть как аккуратный JSON‑объект key/value, и консоль перестала показывать его в виде формы - только raw‑строка. Авс конвертнул его из строки в бинарный тип.

External secrets ожидал нормальный JSON, а получил вот эту бинарную кашу,
не смог её распарсить и отказался синхронизировать.
Новые поды без актуального секрета не стартуют, всё висит на старом релизе, который чудом ещё жив.

Ну, короче, разработчик с того времени узнал, что такое экранирование
и почему "просто запихнуть JSON как есть" - это не самое лучшее архитектурное решение.

А я на память, как медальку, сохранил скрин с этой ебалой.

- - -
Воспоминание навеяно обсуждением в кубер‑чате про .NET проблемы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7🥰3
#kubernetes #tools

Я не фанат UI интерфейсов для работы с кубами, мне привычнее сношения с kubectl и алиасами баша.
Быстро, четко и по делу, но понимаю, что многим ближе визуальные инструменты.
Когда-то сделал подборку UI для коллег - решил поделиться и здесь.
Вдруг кому пригодится.

Инструменты для работы с Kubernetes (UI и терминальные клиенты):


Терминал-UI:
- k9s - https://github.com/derailed/k9s ❤️ (мой фаворит)
- kdash-rs - https://github.com/kdash-rs/kdash
- lazyk8s - https://github.com/berge472/lazyk8s (не понял в чем его прикол)
- kubetui - https://github.com/sarub0b0/kubetui
- kubeterm - https://github.com/kbterm/kubeterm

Desktop GUI:
- Seabird - https://github.com/getseabird/seabird ❤️(отличное UX и активное развитие)
- Freelens - https://github.com/freelensapp/freelens
- Jet-Pilot - https://github.com/unxsist/jet-pilot
- Aptakube - https://github.com/aptakube/aptakube (многие разработчики пересели в тот раз именно на него)
- Lens - https://github.com/lensapp/lens
- Headlamp - https://github.com/kubernetes-sigs/headlamp

Веб-UI и панели управления:
- kite - https://github.com/zxh326/kite
- Devtron - https://github.com/devtron-labs/devtron
- KubeClipper - https://github.com/kubeclipper/kubeclipper (не проверял лично)
- Karpor - https://github.com/KusionStack/karpor (не проверял лично)
- Cyclops - https://github.com/cyclops-ui/cyclops (не проверял лично)

Не гарантирую, что что-то из этого не стало платным или наоборот бесплатным - лицензии и платформенная поддержка MacOS/Win постоянно меняются, так что лучше проверять актуально на момент задачи.
👍9🔥3
#troubleshooting #aws #azure #kubernetes #одинденьизжизни

Пришёл алерт ранним утром. Ровно после начала рабочего дня в 10.01.
Чёткий. Конкретный. С именем сервиса, неймспейсом и метрикой.
Не что-то не то с подами, а прям: HighMemoryUsage, payment-service, namespace production, порог 85%, текущее значение 91%.

Открыл дашборд - всё выровнено, таймлайн читается, spike начался ровно в момент последнего деплоя.
Зашёл в ArgoCD, посмотрел diff последнего релиза.
Разработчик увеличил replicas с 3 до 5, но не тронул resources.limits.memory.
Всё сошлось.

Написал разработчику. Он ответил сразу.
Сказал да, моя ошибка и прислал PR через 10 минут.
В PR было описание: что изменил, почему, как проверял локально, какой эффект ожидается.
Коммиты по делу: fix(payment-service): set memory limits and requests after OOM incident.
Не fix, не wip, не asdfgh.
Поправил лимиты, добавил requests заодно.

Пока ждал PR, попросил Cursor посмотреть helm values на предмет других неоптимальных лимитов.
Нашёл два места. Предложил изменения. Ничего лишнего не тронул.

Я заапрувил, ArgoCD задеплоил. Память упала. Алерт закрылся сам.
CI прошёл с первого раза. Без flaky tests. Без "ой, retry нажмите, оно иногда так".

Разработчик после апрува написал в PR: спасибо за ревью, понял, учту.
Следующий его PR пришёл уже с нормальными лимитами. Без напоминания.
В Slack за всё это время никто ничего не написал.

Кстати, на прошлой неделе коллективно договорились не писать @here и @channel в каналах с людьми, если это не реальный прод-инцидент. Все согласились и все соблюдают.

Параллельно пришло письмо от AWS: они самостоятельно обнаружили, что у нас один из инстансов работает в неоптимальном режиме, и предложили конкретный тип на замену - дешевле на 15%.
Написал в саппорт уточнить детали, ответили за полчаса, классные ребята из лондонского офиса, поболтали за жизнь на английском.

Ещё с утра был запланирован автоапдейт AKS - security patch.
Прошёл сам, пока я занимался инцидентом.
Все ноды обновились по очереди, ни одна не упала в NotReady.
API не дёргался. Поды не пересоздавались хаотично.
kubectl get nodes после апдейта вернул всё Ready. С первого раза.
Странно, что я удивляюсь этому, ведь так было всегда, это стабильный и любимый всеми Ажур.

Коллеги наконец перестали спорить что лучше - Windows или Mac.
Все пересели на MacBook. Тихо. Без срачей. Без "но у меня Visual Studio".

Перед обедом написал PM.
Он ответил, что предлагает отменить ежедневный стендап на этой неделе - нет открытых топиков, нет смысла тратить время.

Ещё PM сказал, что руководство приняло решение: гонка за AI - не наша история.
Главное - люди, процессы и спокойная работа.
Никаких срочных внедрений, никакого "а вы уже используете агентов?", никаких OKR про AI-трансформацию до конца квартала.
Ведь главное, чтобы у всех были рабочие места и зарплата вовремя, а не это ваше ИИ.

Весь день занял 40 минут рабочего времени.
Остальное время занимался самообразованием и гладил кота.

Написал постмортем.
Никто не переспрашивал.
Кто-то из команды сам обновил страницу в Notion по итогам инцидента.
Без задачи. Без напоминания. Просто взял и обновил.

NewRelic прислал автоматический отчёт: data ingestion за месяц снизился на 12%. Сам.
Пообедал в 13:00. Целый час, как положено.
Статус в Slack стоял 🍕 Lunch.
Никто не написал. Никто не позвонил. Никто не "быстрый вопрос".

Хороший день.
Может, когда-нибудь так и будет.😀
Please open Telegram to view this post
VIEW IN TELEGRAM
😁359😢5🔥3
#guardduty #aws #eks #security #troubleshooting

Guardduty, фишинг. Часть 1 из 2.

Никого не трогаю. Прилетает алёрт от AWS GuardDuty.
Backdoor:EC2/C&CActivity.B!DNS

Продакшн нода в EKS делает DNS-запрос к x****.cn - по базам GuardDuty это известный фишинговый/DGA домен.
Серьёзность - High. В слаке шум, все смотрят на меня.
Блин, почему в мою смену секьюрити инциденты 😬

Окей. Поехали.
Cмотрим что за нода
aws ec2 describe-instances \
--instance-ids i-0xxxxxxxxxxxxxxxxx \
--region us-east-1 \
--query 'Reservations[0].Instances[0].{IP:PrivateIpAddress,State:State.Name,LaunchTime:LaunchTime}'

Нода уже terminated. Karpenter убил её раньше, чем я добрался до расследования.
Живого форензика (digital forensics) нет.
Прекрасное начало.
kubectl get nodes -o wide
# ноды нет

Ладно, живыми данными не поработать, идём в логи.

Cмотрим что вообще крутилось на ноде
Хорошо, что у нас включено всё логирование control plane EKS. Идём в CloudWatch, смотрим логи шедулера - кто и когда был назначен на эту ноду.
aws logs start-query \
--log-group-name /aws/eks/my-cluster/cluster \
--start-time $(date -v-83H +%s) \
--end-time $(date +%s) \
--query-string 'fields @timestamp, @message | filter @logStream like "scheduler" | filter @message like "ip-10-0-25-132" | sort @timestamp asc | limit 50' \
--region us-east-1

Первая попытка падает с MalformedQueryException. Оказывается, ретеншн у лог группы EKS - 84 часа. Нода жила 4 дня. Часть ранних данных уже недоступна. Запоминаем это на потом.

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

Cмотрим образы, которые пушились незадолго до инцидента
aws ecr describe-images \
--repository-name my-ecr-repo \
--region us-east-1 \
--query 'sort_by(imageDetails, &imagePushedAt)[-10:].[imagePushedAt,imageTags[0],imageDigest]' \
--output table

Вижу два образа, которые запушились за ~30-60 минут до алёрта GuardDuty. Первая гипотеза: кто-то накатил заражённый образ.

Лезу в git, смотрю коммиты.
Один коммит - это Dependabot, обновил grpc. Проверяю go.sum - стандартные пакеты, нормальные хеши. Второй коммит - двухфайловый вайтспейс-фикс в SQL query builder, strings.TrimSpace в одном файле.

Всё это вообще не то. Red herring, мать ее.

Исключаем DaemonSet
kubectl get daemonsets -A

DaemonSet крутится на всех нодах кластера - их там под 44 штуки. Если бы DaemonSet был заражён, GuardDuty бы полыхал по всем нодам разом. Сработало только на одну. Значит, не DaemonSet.

Базовый образ Dockerfile смотрю - официальный golang bookworm, мультистейдж, всё чисто.

Смотрю дополнительные файндинги GuardDuty
aws guardduty list-findings \
--detector-id $(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) \
--finding-criteria '{"Criterion":{"type":{"Eq":["Backdoor:EC2/C&CActivity.B!DNS","Trojan:EC2/DNSDataExfiltration","DefenseEvasion:EC2/UnusualDoHActivity"]}}}' \
--region us-east-1

Нашлось ещё пара файндингов - не игнорируем, но пока продолжаем копать основное.

Наконец-то заглядываю в логи приложения (когда же я, бл, научусь это смотреть первым 🤡)
Открываю OpenSearch, ищу по домену x********.cn , в индексе EKS-логов.
8 записей. Все - из одного контейнера. Смотрю на таймстемпы:
12:51:19 - PaymentWebhookProcessor: starting to parse note
12:51:19 - PaymentWebhookProcessor: no images found in note
...
13:21:38 - PaymentWebhookProcessor: starting to parse note <- ВОООТ ОНО
13:21:38 - PaymentWebhookProc...

13:21:38 UTC - точь-в-точь время алёрта GuardDuty.

Попался!
Please open Telegram to view this post
VIEW IN TELEGRAM
😁1
#guardduty #aws #eks #security #troubleshooting

Guardduty, фишинг. Часть 2 из 2.


Что произошло на самом деле
Схема простая до слёз:
Пользователь отправил сообщение с URL -> x*****.cn
|
Сервис обработки контента внутри кубера получил вебхук(?)
|
Функция(?) PaymentWebhookProcessor внутри POD-а попыталась резолвить домен
(логика: ищет превью/изображения по URL-ам в тексте)
|
DNS-запрос к x*****.cn с EKS ноды, на которой запущен под
|
GuardDuty: ALARM


Никакой малвари. Нода не скомпрометирована. Credentials не угнаны.
Просто сервис резолвит любой домен из пользовательского контента - а юзер прислал фишинговую ссылку.
Фишинг не удался (дополнительно логи смотрел).

Несколько часов расследования, CloudTrail, CloudWatch, ECR history, git blame - ответ лежал в логах приложения и нашёлся бы за 15 минут, если бы я туда полез первым делом.

Что реально плохо и надо починить
Само расследование закончилось словами "малвари нет", но по ходу вскрылись настоящие проблемы 🚬.

- SSRF
PaymentWebhookProcessor резолвит произвольные URL из пользовательского контента без какой-либо валидации.
Это означает, что любой клиент может прислать:
http://169.254.169.254/latest/meta-data/
И сервис спокойно попробует его резолвнуть - прямо с production ноды, у которой есть IAM-роль со повышенными правами (это вообще, бл, отдельный разговор какого рожна нода имела доступ к некоторым AWS ресурсам).

Фикс очевидный - блокировать приватные диапазоны перед любым исходящим запросом:
169.254.0.0/16  (AWS metadata endpoint)
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8

- GuardDuty будет шуметь вечно
Пока сервис зачем-то резолвит пользовательские URL без фильтрации - каждый раз, когда кто-то пришлёт фишинговую ссылку, GuardDuty будет стрелять алёртом. Это шум, который убивает доверие к алёртингу.

Решение: AWS Route 53 Resolver DNS Firewall с managed threat lists.
- AWSManagedDomainsMalwareDomainList
- AWSManagedDomainsAggregateThreatList
Известные вредоносные домены блокируются до DNS-резолва автоматически, без ручных списков.
https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resolver-dns-firewall-managed-domain-lists.html

- URL-резолвинг в основном процессе
Длиннее задача, но правильная: вынести резолвинг URL в изолированный асинхронный воркер с ограниченным egress.
Без доступа во внутреннюю сеть. Без IAM-роли с правами на всё.

Выводы и итоги:
- в логи приложения надо идти первым. Не после нескольких часов инфра-форензики.☔️
- ретеншн CloudWatch-логов для EKS control plane 84 часа - маловато. Нода жила 4 дня, ранние события уже недоступны. Надо поднять. Не критикал, но надо бы.
- сервисы, которые резолвят URL из пользовательского контента, будут встречать фишинг. Это не вопрос если, это вопрос когда. Надо документировать и обрабатывать проактивно, а не разбираться в панике с High severity алёртом.
- SSRF - это не гипотетическая угроза, это прямая дыра в production при текущей архитектуре.
- кто дает права EKS нодам на доступ к AWS ресурсам, то негодяй, нельзя так
- с одной стороны я переживал тогда, что потратил много времени на расследование, копая изначально не туда, с другой стороны рад, ведь без полной картины происходящего я не смог бы понять были ли скомпроментированы креды или нет, был ли хак или нет, а так вроде и пополнил знания
- используйте сервисы AWS, многие из них хорошо помогают в траблшутинге, пусть и стоят денег

False positive закрыт, но работы добавилось.

- - -
В последствии все косяки были закрыты, все Security/AWS бест практисы внедрены.
Все потенциальные известные "дыры" закрыты, PII защищены, даже инвесторы спали спокойно после детальнейшнего постмортема.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10