Задача прилетела одной строкой: полсотни с лишним AWS Lambda-функций на Python 3.8, и до end of life рантайма оставались месяцы. На бумаге обновление рантайма Python 3.8, у которого заканчивается поддержка, — это просто смена версии. На деле, как только AWS перестаёт патчить устаревший рантайм, каждая незакрытая уязвимость становится твоей личной. К этим функциям никто не прикасался больше года. Вся коллективная мудрость умещалась в три слова: «Работает — не трогай».
Знаменитые последние слова.
Вскрываем капсулы времени
Я начал там, где начинается любой честный аудит, — с CloudWatch. Частота вызовов, error rate, даты последних изменений. Всё, что не срабатывало 90 дней, отправлялось в список кандидатов на удаление: самая быстрая миграция — это функция, которую ты удалил. Потом я открыл тех, кто выжил.
Это было не обновление Python. Это была археология. Каждая функция — маленькая капсула времени с надписью «написал в полночь и больше не возвращался». Вот что было внутри:
- Захардкоженные креды — даже не переменные окружения, а сырые строки прямо в коде. В одной лежали доступы к базе аж с 2019 года.
- Устаревшие библиотеки с известными CVE, не запиненные ни к чему.
- Ноль тестов на все пятьдесят с лишним функций. Не жиденький сьют. Ноль.
- IAM-роли с
Action: "*",Resource: "*"— нараспашку, все до единой. - Функции, ходящие в RDS через открытый интернет, без всякой VPC-изоляции.
- Документация, равная имени функции. И всё.
Почему не «большой взрыв»
Соблазн — переписать всё разом, щёлкнуть рубильником и молиться. Этот фильм я уже видел. Поэтому пошёл по одной функции за раз.
Каждая получала один и тот же набор процедур: рантайм с 3.8 на 3.11, каждая зависимость запинена и проверена на известные CVE, а общие библиотеки вынесены в Lambda layers — чтобы следующее обновление делалось в одном месте, а не в пятидесяти. Потом я писал тесты — для большинства это были первые тесты в их жизни — и документацию, которая объясняла, зачем функция существует, а не только что она делает. Именно «зачем» и нужно следующему инженеру в три часа ночи.
Раскатка — canary-деплой со сдвигом трафика: 5 процентов, потом 25, потом полный переход, с автоматическим откатом, если подскакивал error rate. Никакого большого взрыва. Никаких молитв. Если функция косячила на 5 процентах трафика, до остальных 95 она не доходила.
Security hardening тем же заходом
Раз уж я и так был внутри каждой функции, чинить рантайм и оставлять остальное гнить было бы должностным преступлением. Поэтому харденинг шёл параллельно:
- IAM переписан с нуля по принципу least privilege. Никаких wildcard. Каждая роль получила ровно те права, что функция использует, и ни одним больше.
- Захардкоженные креды переехали в Secrets Manager, с ротацией и подтягиванием в рантайме.
- Функции с доступом к базе заведены в VPC с приватными сабнетами — и RDS перестал торчать в открытый интернет.
Результат
Пятьдесят с лишним функций спустя:
- Каждая функция на поддерживаемом рантайме.
- Ноль даунтайма за всю миграцию.
- Ноль wildcard-политик в IAM.
- Ноль захардкоженных секретов.
- Реальные тесты. Документация, за которую следующий инженер меня не проклянёт.
И небольшой бонус, которого я не ждал: cold start'ы уменьшились, потому что Python 3.11 просто быстрее 3.8. Скучная рутинная задача тихо сделала платформу шустрее.
«Не трогай» — это страх, а не стратегия
Вот что забывают любители serverless. «Serverless» не значит «без обслуживания» — это значит, что обслуживание невидимо, пока рантайм не доходит до end of life и всё не отваливается. Сервера, на который можно ткнуть пальцем, нет, поэтому долг прячется ещё лучше.
«Не трогай» — это технический долг на языке страха. Каждый день, что ты игнорируешь рантайм на end of life, набегают проценты — пока не приходят к оплате в виде инцидента с безопасностью или пейджа в три ночи. Каждая система «не трогай» в твоём стеке — это будущий алерт, который тебя разбудит.
Так что выбери одну на этой неделе. Самую старую, самую страшную, ту, которую лучше не трогать. Открой. Прочитай. Прогони тесты. Либо окажется, что всё в порядке, и будешь спать спокойнее, либо найдёшь креды из 2019-го раньше, чем их найдёт атакующий.
Какая система в твоём стеке самая старая и нетронутая прямо сейчас?