top of page

Як розбудовувати безпечний ланцюг постачання програмного забезпечення. Досвід Grammarly

Оновлено: 23 квіт.


Розірваний ланцюг

Загрози в ланцюжку постачання програмного забезпечення стають все більш актуальними. Атаки на SolarWinds, Log4Shell та нещодавній випадок компрометації пакета balldb-go підтверджують, що навіть невеликі вразливості можуть спричинити масштабні наслідки. Безпека залежить не лише від того, що створює компанія, а й від сторонніх компонентів, які вона використовує.


Сергій Василенко, інженер-програміст у Grammarly, має понад 13 років досвіду в індустрії, працював інженером, техлідом та менеджером, а також веде блог DevDosvid. На конференції DevOps fwdays'25 Сергій поділився підходами, які допомогли Grammarly зменшити кількість критичних уразливостей на 59% та автоматизувати 90% патчів безпеки. Публікуємо головне з його виступу.



Що ми робимо в Grammarly


Безпека нашого ПЗ залежить від надійності компонентів, які ми використовуємо у процесі збірки та розгортання. Останній внутрішній аудит показав, що 96% виявлених уразливостей пов'язані з ланцюгом постачання.


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


Але захист важливий не лише для B2B-клієнтів. Ми використовуємо багатокористувацьку архітектуру, тому незалежно від того, допомагає наше ПЗ урядовцю чи студенту, рівень безпеки має бути високим.



Основні виклики


Ми підтримуємо інновації та швидкість розробки, тому в інженерів мінімум обмежень у виборі CI/CD-конфігурацій та інструментів.


Але це має свою ціну:


  • CI-конфігурації відрізняються від проєкту до проєкту.

  • В одній компанії можуть використовуватися десятки мов, фреймворків та AWS-акаунтів.

  • Немає єдиного стандарту на назви джобів чи етапів у CI/CD.


Як при такій різноманітності впровадити універсальні заходи безпеки? Для початку подивімось на цифри. 


Результати Grammarly після впровадження заходів безпеки

Тепер розберемо, як ми цього досягли. 



Чотири ключові принципи безпеки


Ми будуємо нашу систему безпеки на чотирьох принципах:


  1. Відтворювана безпека — перетворюємо захисні механізми на код, який виконується однаково при кожному запуску.

  2. Автоматизований аналіз — створюємо системи, які розуміють контекст і приймають рішення на основі актуальних даних.

  3. Автоматичне виправлення — мінімізуємо ручне втручання завдяки оновленням коду та стандартним фіксам.

  4. Орієнтація на розробників — створюємо інструменти, які зручні для самих інженерів, а не лише для аудиторів безпеки.



Захист на етапі зборки


  1. Перший важливий момент — безпека сторонніх залежностей. Для їх оновлення ми використовуємо Renovate. Це інструмент з відкритим кодом і безкоштовний для використання. Ми додали трохи власної оркестрації навколо нього. Наша автоматизація запускає Renovate на репозиторіях, і Renovate створює пул-реквести (ПР).


Ми визначаємо два типи таких ПР:


  • Security PRs (термінові виправлення вразливостей).

  • Update PRs (оновлення залежностей).


Ми намагаємося групувати пов'язані зміни семантично, щоби оптимізувати кількість ПР. Якщо це Security ПР, він буде об’єднаний одразу після успішного проходження пайплайну. 

Щоби автоматизувати процес для RenovAid, оскільки кожен ПР має бути схвалений, ми використовуємо робо-користувача для схвалення таких ПР замість реальних людей. Пайплайн Renovate налаштовано на виконання кожні дві години, і він складається з трьох окремих задач: Generator, Runner та Status.


Задача Generator запитує GitLab і збирає список усіх активних проєктів. Цей список потрапляє в чергу SQS, що запускає задачу Runner. Задача Runner створює 20 копій одночасно, так що кожен runner обробляє один проєкт з черги за раз, блокуючи його в черзі під час обробки та видаляючи з черги після завершення.


Саме тут Renovate виконує свою роботу. Кожен CI job запускає чергу Renovate SQS, щоби перевірити, чи є ще незавершені елементи в черзі. Якщо є, він відправить звіт для аналізу того, що пішло не так.


Існує окремий процес з окремим розкладом, який працює за допомогою AWS Lambda для схвалення security ПР, створених Renovate, і автоматичного об’єднання ПР, якщо всі перевірки пройшли успішно. Таким чином, усе працює без нагляду і допомагає нам підтримувати безпеку коду на понад 600 репозиторіях.


Наступний момент — це спосіб, яким ми вводимо сторонні залежності під час процесу зборки. Тут є важливим централізоване керування залежностями. Якщо ви хочете налаштувати таку систему і реалізувати її з кількох причин, ось деякі з них:


  • це дає вам єдине місце для контролю життєвого циклу сторонніх залежностей. Наприклад, ви можете автоматично запобігти використанню програмного забезпечення, яке вийшло з підтримки; 


  • ще одна причина — ви можете впроваджувати стандарти та політики. Наприклад, забезпечити, щоби Docker теги були незмінними або конкретні проєкти використовували розділені простори для зберігання своїх артефактів або залежностей;


  • коли мова йде про розслідування, набагато легше відстежувати та блокувати вразливі компоненти, і ще багато інших переваг, що дають вам контроль над важливим ланцюгом. 



Централізоване управління залежностями


Ми вибрали хмарного постачальника JFrog Artifactory, і ось які контролі ми застосували до нього. Artifactory має набір специфічних репозиторіїв для різних типів пакетів. Також є кілька загальних репозиторіїв для таких речей, як базові образи та спільні бібліотеки. Ми спрямовуємо всі сторонні залежності через Artifactory. Docker-образи з Docker Hub, пакети npm з npm.js тощо.


Інженери не можуть завантажувати нічого в Artifactory зі своїх ноутбуків. Вони можуть тільки витягувати або читати. І це не проблема для локальної розробки, оскільки потрібно завантажити щось локально, побудувати та запустити це локально. Але якщо потрібно зробити більше, є CI, якому дозволено завантажувати та зберігати артефакти.


Для CI є окремі користувачі з окремими токенами доступу, які використовуються для завантаження збірок або їх витягування під час розгортання. І щоби підняти це на ще вищий рівень, можна розглянути блокування CI для заборони вихідного трафіку в Інтернет. Таким чином, ви ефективно забезпечуєте, що ваше централізоване сховище залежностей — це єдине джерело, яке може використовуватися для залежностей.



Захист на етапі розгортання


Спершу треба визначити який контроль нам потрібен на етапі розгортання. Припустимо, що нам потрібно просканувати Docker-образ перед його розгортанням. Нагадаю про гнучкість та нестандартні CI-конфігурації — ось де це відіграє важливу роль. Виникають питання: яка назва джобу, що відповідає за розгортання? Де ми повинні виконати перевірку? Це розгортання у продакшн чи в QA-середовище? Все це виглядає складним, на перший погляд, але залишається цілком керованим.


Перш за все, попри нашу гнучкість, у нас все ж є певні стандарти. Нам допоміг  GitLab. Він має функцію Compliance Framework, що дозволяє визначати конфігурацію пайплайну для кожного проєкту. Але тут треба бути дуже обережним, адже кожен проєкт має власний CI. 


Далі є наш сканер безпеки — Viz. Це хмарна платформа безпеки, що забезпечує централізоване управління багатьма аспектами безпеки у хмарі та кодовій базі. Ми інтегрували їхній CLI-сканер у наш інструмент розгортання. Використовуючи Compliance Framework, ми можемо передавати певні змінні середовища для оркестрації сканувань та контрольних механізмів безпеки.


Механізм безпеки


Отже, наш механізм безпеки для розгортання двоетапний:


  1. Ми виконуємо сканування перед розгортанням.

  2. Ми перевіряємо Docker-образи на серверному боці.


З боку сервера є контролер доступу (admission controller), що перевіряє вхідні образи за двома критеріями:


  • образ має бути з офіційного artifactory-репозиторію або з довірених репозиторіїв для kube add-ons тощо;

  • якщо образ створений нами, він повинен бути успішно просканований Viz.


За цим стоїть механізм атестації: кожен дайджест образу зіставляється з результатами сканування, і Viz приймає фінальне рішення — дозволити розгортання чи ні. Ми інтегрували Viz у наш admission controller, щоби контролювати цей процес.


Архітектура


Подивимося на архітектуру цього процесу. Це може здатися трохи складним, але я спробую пояснити.


Зліва направо: все починається з CI/CD пайплайну, який тригериться пул-реквестом або комітом у main-гілку. Ми не знаємо точно, коли це станеться, адже назви пайплайнів і їхні конфігурації можуть відрізнятися. Але коли це відбувається, у гру вступає інструмент розгортання — Platform CLI.



Що відбувається всередині Platform CLI?


  • Перед фактичним розгортанням запускаються сканери безпеки, які перевіряють Docker-образи, вказані в маніфесті розгортання.

  • Якщо сканування не пройдено — розгортання не відбувається.

  • Сканер використовує VCLI для аналізу образу та відправки метаданих у хмару.

  • Результати сканування зіставляються з налаштуваннями політик безпеки та нашим кастомним списком винятків (ignore list).


Попри остаточний статус сканування, ми завжди відправляємо зворотний зв’язок у пул-реквест або в логи джобу, щоби користувачі розуміли, що відбувається. Якщо образ відповідає всім вимогам безпеки — ми переходимо до розгортання.


Далі у нас два бекенди: ECS і EKS.


  • ECS (Elastic Container Service) — це пропрієтарний оркестратор від AWS. Тут усе просто, він не має складних інтеграцій, тому розгортання відбувається без додаткових перевірок.


  • EKS (Elastic Kubernetes Service) — тут усе складніше. Образи спочатку переносяться в clean-room registry, а потім контролер доступу перевіряє їх на рівні кластера.



Що ми дізналися, впроваджуючи ці контролі?


Головна ідея — не просто блокувати розгортання, а попереджати розробників про вразливості та заохочувати їх виправляти їх заздалегідь. Ми зрозуміли, що іноді делівері важливіша за безпеку, а сканери можуть давати помилкові спрацьовування.


Саме тому ми додали:


  1. гнучкий список винятків, щоби користувачі могли ігнорувати певні CVE; 

  2. процедуру екстреного обходу блокування (break-the-glass) для критичних випадків.


Крім того, ми винесли та перевірили на практиці три важливі уроки щодо стратегічного партнерства:


  • потрібно залучати користувачів до розробки рішень — це простий, але дуже потужний інструмент;

  • треба розгортати механізми контролю поетапно, щоби спочатку ловити найкритичніші баги;

  • важливо взаємодіяти з інфраструктурними командами, аби разом досягати кращих результатів.



Моніторинг та управління вразливостями у продакшні


Тепер, коли ми розібралися, як організовується безпека на етапах збирання та розгортання, настав час перейти до фінальної фази — операцій. Тут ми зосередимося на підтримці безпеки під час роботи сервісу у продакшні.


Ваш сервіс в певному середовищі, у якого є свої конфігурації та налаштування. Щобільше, якщо вчора сканування безпеки показало нульові вразливості, сьогодні в тому ж ПЗ може з’явитися нова, адже вразливості виявляються кожного дня. Але просто знати про наявність вразливості — недостатньо. Важливо розуміти, наскільки вона небезпечна.


Тут на допомогу приходить контекстна оцінка вразливостей у реальному часі. Саме для цього існують платформи хмарної безпеки — такі як Viz. Коли у реальному часі виявляється вразливість, це допомагає зрозуміти, які фактори на неї впливають:


  • мережевий доступ,

  • вектор атаки,

  • ймовірність експлуатації,

  • та інші параметри, прив’язані до поточного стану інфраструктури.


Усе це відбувається автоматично і з можливістю використовувати як вбудовані інтеграції, так і кастомні рішення через API. І так, ми активно використовуємо API.



Що робити, коли виявлено вразливість?


Важливо не просто зафіксувати проблему, а й вирішити її. Тут на допомогу приходить система управління завданнями, керована SLA. SLA у цьому випадку — це своєрідний контракт між командою безпеки та іншими командами, що гарантує своєчасне виправлення проблеми з високим пріоритетом.


Ми використовуємо кастомні Lambda-функції для автоматизації створення задач і сповіщень.


  • Для управління задачами — Jira.


  • Для пошуку власників сервісів і відправлення інструкцій у Slack — Cartography із кастомними скриптами.


Cartography — це інструмент, який об’єднує хмарні ресурси та зв’язки між ними у вигляді графа з чіткими залежностями. Він є безплатним і має відкритий вихідний код.

Якщо якась задача виходить за межі SLA, команда безпеки отримує сповіщення та застосовує політику втручання.


Суть цієї політики: якщо проблема не вирішена вчасно, команда безпеки втручається, допомагає власникам сервісу та прискорює деплой виправлення. Для цього команда безпеки переглядає та схвалює всі пул-реквести щодо цього сервісу, щоби переконатися, що зусилля спрямовані на вирішення проблеми.


Ваше ПЗ настільки ж безпечне, наскільки захищена кожна частина ланцюга постачання —  від коду, який ви пишете, до залежностей, які використовуєте, і аж до фінального середовища у продакшені.


Кнопка для підписки на телеграм-канал High Bar Journal

© 2035 by Business Name. Made with Wix Studio™

bottom of page