Типичная сцена из жизни команды разработки: тимлиду или DevOps-инженеру прилетает задача от руководства — «нужно прикрутить безопасность к пайплайну». Вы не безопасник, но задача стоит, и ваш минимум: чтобы не пускало уязвимости в прод. Обычный и логичный путь — взять популярный сканер (Trivy, SonarQube, Semgrep), воткнуть его новым шагом в пайплайн GitLab CI или GitHub Actions с дефолтным конфигом и запустить на мастере.
Но спустя десять минут пайплайн с треском падает. Сканеры с гордостью выдают 480 предупреждений: от устаревшей минорной библиотеки пятилетней давности в глубине node_modules до гипотетической уязвимости в тестовом скрипте, который вообще не попадёт в прод.
Реакция команды предсказуема: через неделю постоянных ложных блокировок разработчики либо прописывают allow_failure: true, либо аккуратно забивают на алерты. Безопаснее не стало, релизы замедлились, а при упоминании «DevSecOps» все только раздражаются.
Проблема не в инструментах, а в подходе. Встроить безопасность в CI/CD — это настроить гигиенический минимум, который отлавливает реальные угрозы и при этом не тормозит поставку фич.
Чтобы практики были из реальной работы, а не из теории, материал проверил практикующий безопасник:
Нияз Кашапов
Тимлид команды AppSec в Циане, дипломный руководитель на курсе Нетологии «Специалист по информационной безопасности»
TL;DR
-
Проблема DevSecOps не в инструментах, а в подходе: сканер «из коробки» заваливает пайплайн сотнями ложных срабатываний, и команда перестаёт их читать — безопаснее не становится.
-
Гигиенический минимум — три слоя в CI/CD, каждый на своём этапе: поиск секретов (Gitleaks), проверка зависимостей (SCA, Trivy) и базовый SAST (Semgrep).
-
Главный враг — шум, а не отсутствие проверок. Его снимают Baseline по старому коду, тюнинг правил и процесс триажа; сборку блокируем только на проверенных находках.
-
Проверки делим на жёсткие (валят релиз лишь на критичном с патчем и на открытых секретах) и мягкие (просто сигналят в PR, не блокируя).
-
Внедряем поэтапно: режим аудита → фиксация Baseline → блокировки. Базовый контур собирается за несколько дней на опенсорс-инструментах.
Почему сканеры «из коробки» ломают процесс
Если запустить условный Trivy, Semgrep или SonarQube на действующем проекте без предварительной фильтрации, вы почти наверняка получите не реальную картину защищённости, а лавину из сотен ложных предупреждений.
Сканеры безопасности вендоры проектируют по принципу Maximum Coverage — «лучше перебдеть, чем недобдеть». Без настройки многие инструменты оптимизированы скорее на полноту обнаружения, чем на точность.
Разберём четыре главные причины, почему дефолтный вывод сканеров вызывает у инженеров желание выключить всё, что можно:
1. Отсутствие понимания досягаемости (Reachability)
Инструмент анализа состава ПО (SCA) анализирует файл манифеста (например, package-lock.json, go.sum или requirements.txt) и сверяет версии зависимостей с базами известных уязвимостей (CVE).
-
Как это работает: сканер находит критичную уязвимость в транзитивной зависимости и с пометкой
CRITICALблокирует билд. -
Реальность: ваша программа использует из этой библиотеки всего одну утилитарную функцию (например, форматирование даты), а уязвимый код даже не импортируется и никогда не исполняется. Для вашего сервиса эта сработка нерелевантна: уязвимый код недостижим. Но из-за отметки сканера пайплайн падает.
2. Контекстная слепота
Автоматический инструмент не знает, где и как исполняется код. Для него код публичного платёжного шлюза и внутренний CLI-скрипт для генерации тестовых данных одинаково критичны.
Пример: сканер находит зашитый API-ключ в файле tests/fixtures/mock_data.go и помечает его как утечку секрета высокого риска. Инженер вынужден тратить время, чтобы объяснить инструменту: это фейковый токен для юнит-тестов, он не ведёт в боевую инфраструктуру.
3. Тупик транзитивных зависимостей
По данным отчёта OSSRA (Black Duck, 2025), более 70% типового приложения — открытый код: своего кода в сборке лишь малая часть, остальное тянут сторонние библиотеки и их «дочки» — транзитивные зависимости.
Ловушка: сканер сообщает, что библиотека A использует библиотеку B, в которой есть CVE. При этом мейнтейнеры библиотеки A ещё не выпустили патч с обновлением. Вы попали в тупик: обновить B вручную нельзя (сломается совместимость), патча для A нет, пайплайн горит красным.
4. Усталость от алертов — «синдром мальчика и волков»
Когда разработчик регулярно видит десятки HIGH– и CRITICAL-алертов, он постепенно перестаёт воспринимать их как одинаково срочные. Если критично всё — значит, не критично ничего.
Инженеры перестают вчитываться в алерты и начинают искать пути обхода: глобально подавляют варнинги или просто привыкают игнорировать красный крестик в CI. В этот момент действительно опасная уязвимость (например, случайно забытый токен от прода) беспрепятственно уходит в репозиторий.
Главный вывод: задача инженерной команды при внедрении DevSecOps — заставить сканер искать уязвимости более фокусно, не создавая множества ложных срабатываний. Автоматика должна останавливать сборку только тогда, когда обнаружена понятная, воспроизводимая и опасная угроза.
Минимальный гигиенический стек: три обязательных слоя
Чтобы пайплайн оставался быстрым и не раздражал команду, не нужно внедрять десяток сложных сканеров одновременно. По опыту эксперта, в большинстве случаев для защиты продукта достаточно трёх базовых слоёв.
Нияз Кашапов
Тимлид команды AppSec в Циане
Цифра в 80% — из опыта работы в различных компаниях. Рассматриваемые инструменты интегрировать проще всего, и они дают результат достаточно дёшево и быстро. Такие практики, как DAST и fuzzing, требуют глубокого анализа окружения (стек, библиотеки, стенды) и настройки под конкретный кейс. Кроме того, финансово эти практики очень дорогие и не такие эффективные.
Каждый из них решает свою конкретную задачу, запускается на определённом этапе CI/CD и использует лёгкие, проверенные опенсорс-инструменты:
[ Git Push / PR ]
|
├── 1. Secret Scanning (Gitleaks) ──> Блокирует сразу (< 2 сек)
├── 2. SCA / Dependencies (Trivy) ──> Сканирует lock-файл (10-30 сек)
└── 3. Lightweight SAST (Semgrep) ──> Проверяет diff (5-20 сек)
Слой 1. Поиск секретов. Secret Scanning
Утечка API-ключей, токенов облачных провайдеров (например, AWS или Yandex Cloud) или паролей от баз данных — одна из самых массовых и недооценённых проблем. Бот-сканеры злоумышленников находят забытый токен в публичном Git легко, а живут такие секреты годами. В исследовании Truffle Security нашли 543 699 действующих секретов в публичных репозиториях: медианный возраст — 784 дня, и к моменту проверки они всё ещё работали. С корпоративным Git всё сложнее, но не забываем про внутренних нарушителей и ошибки, когда приватные репо неожиданно сливаются в публичные сервисы.
-
Как работает: ищет токены, приватные ключи, пароли и сертификаты с помощью регулярок и анализа энтропии строк.
-
Чем сканировать: Gitleaks или TruffleHog. При необходимости набор правил можно расширить дополнительными базами паттернов, например Secrets Patterns DB.
-
Когда запускать: на самых ранних этапах — в идеальном мире как
pre-commitхук на машине разработчика, либо как самый первый шаг при открытии PR. -
Почему это работает: у правильно настроенного поиска секретов обычно относительно мало ложных срабатываний. При обнаружении потенциально реального ключа проверку лучше блокировать до триажа.
Золотое правило — если секрет попал в Git-историю, он считается скомпрометированным. Его нельзя просто перекрыть новым коммитом — токен нужно немедленно отозвать и сгенерировать заново.
Слой 2. Проверка зависимостей. SCA — Software Composition Analysis
Современная разработка — это сборка приложения из сторонних пакетов. Вы пишете 500 строк своего кода, но под капотом подтягиваете 200 МБ сторонних npm, pip или go модулей.
-
Как работает: парсит lock-файлы (
package-lock.json,poetry.lock,go.sum,Cargo.lock) и сверяет версии используемых библиотек со свежими базами уязвимостей (CVE). -
Чем сканировать: Trivy, OSV-Scanner, Dependency-Check. Для постоянного мониторинга зависимостей — Dependency-Track.
-
Когда запускать: на этапе сборки или проверки PR.
-
Как снизить шум:
1. Сканируйте только манифесты и lock-файлы, а не всю файловую систему контейнера на первом этапе.
2. Включите флаг --ignore-unfixed — если для библиотеки ещё нет исправления, падающий пайплайн вам не поможет, вы только заблокируете релиз.
Нияз Кашапов
Тимлид команды AppSec в Циане
Использование
--ignore-unfixedпомогает сфокусироваться на актуальных уязвимостях, несущих риски, которые можно закрыть обновлением. Однако не стоит игнорировать „unfixed“-уязвимости. Некоторые из них можно закрыть конфигурацией приложения или другими митигирующими действиями. В процессе стоит сделать акцент на рассмотрении и анализе критичных сработок (с оценкой 8+ по CVSS).
3. Фильтруйте по порогу: падать только на CRITICAL и HIGH.
4. Настройте правила исключений. Если уязвимость в вашей системе недостижима, внесите её в исключения через конфигурационные файлы инструмента.
Слой 3. Базовый статический анализ. SAST
Статический анализ собственного кода часто путают со стандартными линтерами (ESLint, golangci-lint). Обычный линтер следит за форматированием и неиспользуемыми переменными, а SAST ищет шаблоны опасного поведения в логике приложения.
-
Как работает: находит SQL-инъекции, небезопасную десериализацию, жёстко зашитые IP-адреса, отсутствие проверки прав и использование слабых криптографических функций.
-
Чем сканировать: Semgrep (CE — для быстрых проверок; Pro Engine — для межфайлового анализа), Bandit (для Python), gosec (для Go), SpotBugs с плагином FindSecBugs — для Java. Для Node.js можно использовать NodeSecure CLI — он анализирует зависимости и ищет подозрительные паттерны в пакетах (supply-chain-слой), дополняя классический SAST.
-
Когда запускать: при создании или обновлении PR.
-
Как снизить количество ложных срабатываний:
→ Выбрать наиболее эффективные правила для своего стека. Можно использовать готовые проверенные наборы правил, например p/security-audit или p/owasp-top-ten в Semgrep.
→ При первичном анализе просканировать проект целиком. Это поможет проверить, как работают выбранные правила, и отключить те, которые дают слишком много ложных срабатываний.
→ Для постоянных проверок сканировать изменения в PR. После первоначальной настройки нет необходимости каждый раз проверять весь проект целиком — достаточно анализировать изменённый код.
Сводная матрица слоёв
|
Слой |
Инструмент |
Ориентировочное время работы (на среднем сервисе) |
Что считается блокером релиза? |
|---|---|---|---|
|
Secrets |
Gitleaks |
< 2 секунд |
Любой найденный валидный секрет или токен |
|
SCA |
Trivy |
10–30 секунд |
|
|
SAST |
Semgrep или SAST-инструмент под конкретный стек технологий (bandit, gosec, brakeman) |
5–20 секунд (на diff) 2–10 минут (на полный скан) |
Подтверждённые критические паттерны (например, SQLi) |
Мы собрали гигиенический минимум — секреты, SCA и базовый SAST. Полный DevSecOps шире: DAST, IAST, моделирование угроз, защита контейнеров.

DevSecOps за 5 месяцев с дипломом НИУ ВШЭ
Разберёте SAST, SCA, DAST, IAST, OWASP Top 10 и моделирование угроз, выстроите защиту на всех этапах разработки. Нужна база в коммерческой разработке.
Quality Gates: разделение на «жёсткие» и «мягкие» проверки
Главная ошибка при внедрении DevSecOps — настроить сканер так, чтобы он валил сборку при любом найденном недочёте. Если ваш CI/CD превратится в блокиратор по каждому варнингу, разработчики быстро найдут способ обойти проверки.
Решение — разделение проверок на два типа: блокирующие (Hard Gates) и неблокирующие (Soft Gates).
|
Параметр |
Блокирующие |
Неблокирующие |
|---|---|---|
|
Поведение CI |
Завершает проверку с ошибкой ( |
Пропускает сборку ( |
|
Что проверяет |
Критические уязвимости, где риски очевидны, а исправление однозначно. |
Потенциальные проблемы, требующие ручного анализа (триажа), или технический долг. |
|
Примеры триггеров |
• Найден открытый секрет или токен в коде. • • Прямая SQL-инъекция в новом коде (diff). |
• • Устаревший SSL или TLS протокол в конфиге. • Уязвимости в старом (legacy) коде, не затронутом в PR. |
|
Куда идёт сигнал |
Напрямую в интерфейс PR (красный крестик). |
В комментарий к PR, дашборд безопасности или таск-трекер. |
Пошаговый план внедрения Quality Gates
Чтобы команда не саботировала внедрение, переходить к блокировкам нужно в три этапа:
Этап 1. Режим аудита 2–3 недели. Shadow mode
Все сканеры запускаются с параметром allow_failure: true (или continue-on-error: true).
-
Пайплайн никогда не падает.
-
Результаты публикуются в виде отчётов или комментариев к PR.
-
Цель: собрать статистику, оценить реальный уровень шума и отлавливать ложные срабатывания до того, как они начнут мешать разработке.
Нияз Кашапов
Тимлид команды AppSec в Циане
Держите режим аудита (audit mode) минимум один полноценный релизный цикл или пока не получите достаточную выборку срабатываний (findings) для настройки правил.
Этап 2. Фиксация Baseline или базовой линии
У каждого проекта есть накопившийся технический долг. Если сканер находит 200 старых уязвимостей в коде трёхлетней давности, разработчик не должен чинить их заодно с вёрсткой новой кнопки.
-
Для этого используют Baseline (есть в Semgrep, SonarQube).
-
Фиксируется текущий «слепок» всех найденных проблем проекта. Сканер не делает акцент на старых уязвимостях и начинает показывать уязвимости только в новом коде (New Issues / Diff Scanning).
Нияз Кашапов
Тимлид команды AppSec в Циане
Сканирование diff полезно, когда CI должен быть быстрым и важно посмотреть написанный код. Однако хорошей практикой является постоянное обновление находок полными сканированиями по ночам или в выходные. Это позволяет смотреть код комплексно, особенно если инструменты поддерживают AST или taint-анализ.
-
Правило для команды: «Старый долг разбираем планово, но новый код пишем чистым».
Этап 3. Включение Hard Gates на критические триггеры
Только после фиксации Baseline и отладки правил включаются блокировки. Начните с жёсткого минимума:
-
Gitleaks: настроить блокирующую проверку новых секретов — перед коммитом или при проверке PR.
-
Trivy: настроить проверку так, чтобы PR блокировался при обнаружении новых
CRITICAL-уязвимостей, для которых есть исправление. Например, использовать--severity CRITICAL --ignore-unfixed --exit-code 1. Впоследствии gate настроить на блокировку иHIGHуязвимостей. -
Semgrep: заблокировать PR при критических паттернах в изменённых файлах.
Важный момент: блокировка в CI — это стоп-сигнал, но не окончательный запрет на релиз. Команда разработки разбирает критичные находки, требующие срочного внимания, и проводит триаж. По его результатам даже находка уровня CRITICAL может оказаться ложным срабатыванием.
Процесс триажа: как обрабатывать алерты без хаоса
Даже самый отточенный сканер время от времени будет вызывать ложные срабатывания. Если в команде не выстроена понятная процедура анализа и подавления таких алертов, пайплайн быстро обрастёт «костылями» в виде глобальных игноров, а сканирование потеряет смысл.
Процесс первичной оценки и сортировки находок называется триажем (Triage).
Когда сканер подсвечивает строку кода или зависимость, у инженера есть три основных сценария действий:
-
True Positive: сканер прав. Ошибка исправляется прямо в ветке разработки до мёрджа. Если уязвимость найдена в legacy-коде в рамках фонового сканирования — заводится задача в таск-трекере с приоритетом в зависимости от критичности.
-
False Positive: инструмент ошибся из-за недостатка контекста (например, принял тестовый хэш за API-ключ). Алерт нужно подавить так, чтобы он больше никогда не беспокоил команду при повторных сборках.
-
Acceptable Risk: уязвимость реальна, но её эксплуатация затруднена или имеет нивелируемый импакт (например, уязвимый модуль используется только во внутреннем CLI-инструменте без доступа к сети). Команда принимает риск, фиксирует его в реестре рисков и задаёт таймаут (например, пересмотреть через 3 месяца).
Нияз Кашапов
Тимлид команды AppSec в Циане
Конечно же, сценариев больше. Некоторые сработки могут потребовать больше времени на исследование. Какие-то сложно оценить по критичности. Часть рисков может быть снижена или размазана по другим сервисам. А если мы начнём рассматривать полный флоу, то обязательно необходимо посмотреть, не была ли уязвимость поэксплуатирована и стоит ли заводить инцидент ИБ.
Подавление алертов. Suppression Hygiene
Очень опасный сценарий — когда разработчики начинают массово вставлять комментарии вроде # nosec или // ignore без разбора, просто чтобы проскочить CI.
Чтобы сохранить прозрачность и контроль, используют жёсткие правила:
1. Локальные игноры (Inline / Code-level)
Подходят для SAST (Semgrep) и поиска секретов (Gitleaks).
Плохо:
apiKey = "12345-test-key" # nosec
commitHash = md5(commit) # nosec
Хорошо (с обязательным контекстом):
apiKey = "12345-test-key" # gitleaks:allow -- Mock token for local unit tests only, see SEC-402
2. Конфигурационные файлы (Project-level)
Подходят для SCA и других проверок. Для разных инструментов используются свои файлы исключений — например, .trivyignore.yaml для Trivy или .semgrepignore для Semgrep. Каждое исключение должно сопровождаться комментарием с указанием причины и ссылки на задачу, а сам файл подключают к Trivy флагом --ignorefile ./.trivyignore.yaml:
# .trivyignore.yaml
# CVE-2023-XXXXX: Уязвимость в генераторе PDF. Сервис изолирован (DMZ),
# прямого пользовательского ввода нет. Временный риск до релиза v2.4 (01.09.2026)
# Ticket: APP-1092 | Date: 2026-08-11
vulnerabilities:
- id: CVE-2023-XXXXX
paths:
- "usr/lib/libssl.so"
statement: "Уязвимость в генераторе PDF. Сервис изолирован в DMZ"
expired_at: 2026-09-01
Кто должен проводить триаж
Распространённая ошибка — заставлять отдел безопасности (AppSec) разгребать каждый алерт из CI/CD. Это создаёт узкое горлышко и задерживает релизы.
-
Первая линия — автор кода или тимлид. Разработчик лучше всех знает контекст своего PR. Он должен уметь самостоятельно обосновать и прописать инлайн-игнор для очевидного ложного срабатывания.
-
Вторая линия — Security Champion или DevOps. Инженер внутри команды разработки, прошедший минимальный инструктаж по безопасности. Он помогает оценить спорные находки и правильно оформить принятие риска.
-
Третья линия — эксперты по безопасности. Подключаются только к сложным архитектурным вопросам, массовым уязвимостям нулевого дня (0-day) или спорным ситуациям при блокировке критических релизов.
С чего начать: минимальный план внедрения
Внедрение безопасности в CI/CD не требует месяца настройки или покупки дорогих платформ. Соберите рабочий контур за три шага — по одному слою в день:
-
День 1. Внедрите поиск секретов (Gitleaks). Поставьте сканирование секретов первым шагом в PR. Настройте блокировку сборки при нахождении валидных ключей. Ложных срабатываний будет минимум, а эффект от предотвращения утечек — уже очень заметный.
-
День 2. Настройте проверку зависимостей (Trivy). Проверьте сканером только lock-файлы (
package-lock.json,poetry.lockи так далее). Включите флаги--ignore-unfixedи фильтр поCRITICAL. -
День 3. Добавьте лёгкий SAST (исходя из вашего стека технологий). Подключите базовый набор правил (OWASP Top 10) и ограничьте сканирование только изменёнными файлами (diff) в PR.
Когда три слоя собраны, не включайте блокировки сразу. Переводите пайплайн в блокирующий режим по фазам из раздела «Пошаговый план внедрения Quality Gates»: сначала режим аудита, затем Baseline, и только потом Hard Gates на критичное. По подтверждённым находкам заводите задачи и дотачивайте правила.
Чек-лист: готовность вашего MVP DevSecOps
Пройдитесь по этому списку, чтобы убедиться, что собранный пайплайн помогает команде, а не превращается в очередной источник раздражения:
▢ Защита от утечек. Сканер секретов (Gitleaks / TruffleHog) проверяет каждый PR и блокирует сборку при нахождении реальных секретов.
▢ Фокус на lock-файлах. Проверка зависимостей (SCA) анализирует только манифесты/lock-файлы, а не проверяет весь контейнер целиком на этапе разработки.
▢ Игнорирование патчей-фантомов. Для проверки зависимостей включён флаг --ignore-unfixed (не блокировать сборку из-за CVE без существующего исправления).
▢ Сканирование только кода, затрагивающего функциональность. SAST-сканер проверяет не весь монорепозиторий, а только код, связанный с изменяемой функциональностью.
▢ Зафиксирован Baseline. Старый технический долг вынесен в отдельный список или слепок и не блокирует текущую работу над новыми фичами.
▢ Чёткие Quality Gates. Жёсткие блокировки (Hard Gates) настроены только на CRITICAL проблемы с готовыми исправлениями и поиск секретов.
▢ Регламент обработки срабатываний. В команде есть стандарт для ложных срабатываний — с обязательным указанием причины и ссылки на тикет (# gitleaks:allow -- Mock token, see SEC-101).
▢ Скорость пайплайна. Для монолитов и крупных сервисов нормой считается от 1 до 10 минут; чем больше кодовая база, тем дольше.
▢ Назначен Security Champion. В команде разработки есть человек, умеющий проводить базовый триаж находок и помогать коллегам с разбором алертов.
Главное
DevSecOps — это процесс, а не набор инструментов. Нельзя просто «купить сканер» и стать защищёнными. Это полноценная работа, которая требует навыков и времени. Главная цель — сделать безопасность естественной частью процесса поставки кода. Предложенные решения не закрывают вопрос безопасности полностью, но помогают выиграть время с минимальными рисками и потерями, пока процесс не перейдёт к профильному специалисту.
Шум убивает безопасность. Если CI генерирует сотни ложных срабатываний, разработчики со временем начинают их игнорировать. Поэтому сканер должен сигнализировать только о действительно важных угрозах.
Исправлять уязвимости как можно раньше. Чем быстрее проблема обнаружена — например, в IDE или в PR, — тем проще и дешевле её исправить. Поэтому проверки стоит размещать как можно ближе к этапу написания кода. А практики безопасной разработки помогают предотвращать часть уязвимостей ещё до проверок и сокращают объём требующихся исправлений.
> Где потренироваться и чему научиться
Безопасность в пайплайне перестала быть заботой одного отдела — её всё чаще прикручивают сами разработчики и девопсы. Разобраться стоит заранее, а не когда сканер уже завалил релиз, и начать можно с малого и бесплатного:
-
занятия «Секретный рецепт DevSecOps» — чтобы понять, что на самом деле означает Sec в ежедневной работе DevOps-команды и с чего вообще начинать;
-
открытого вебинара «Информационная безопасность: как построить карьеру в цифровом будущем» — чтобы увидеть, как на практике защищают данные и противостоят атакам, и понять, стоит ли расти в ИБ дальше;
-
мастер-класса «Искусственный интеллект в разработке» — чтобы на реальных примерах посмотреть, как ИИ ускоряет работу разработчика и как это сказывается на трудоустройстве.
А если хочется не разовое знакомство, а закрыть то, что в статье осталось за скобками, — DAST, моделирование угроз, защиту контейнеров и требования ФСТЭК, — и вырасти в грейде, стоит смотреть на системное обучение с наставниками и дипломом:
-
на курсе «Специалист по информационной безопасности» — чтобы постепенно войти в ИБ, выбрать специализацию в DevSecOps или Pentest и освоить модули под требования ФСТЭК для работы на крупных проектах;
-
на курсе «DevOps-инженер PRO» — если хочется укрепить сам пайплайн и инфраструктуру: CI/CD, контейнеры и автоматизацию на уровне опытного инженера;
-
на курсе «Нейросети для разработчиков» — чтобы встроить ИИ в повседневную работу, от генерации кода до настройки CI/CD и документации.
А чтобы держать знания свежими без привязки к одной программе — инструменты и угрозы в безопасности меняются быстро, — пригодится База знаний Нетологии: 16 000+ видеоуроков и вебинаров по ИТ от практиков из Авито, VK и Яндекса, до 10 роликов в день — бесплатно.
Автор: dungle


