Шёл 2024 год, ситуация в мире становилась всё более неблагоприятной: данные утекали, компании за это получали штрафы. Пользователи разделились на 2 лагеря. Первые максимально заботились о защите, а вторые надеялись, что провайдер, у которого хранятся их данные, относится к первому лагерю.

Один из наших клиентов обнаружил, что резервные копии ClickHouse, которые он хранит в S3 провайдера CROC, не очень-то и шифруются. А ФСТЭК суров. Перед нами была поставлена, казалось бы, тривиальная задача. C ClickHouse регулярно работаем, бэкапы делаем (кстати, с помощью ClickHouse-backup, но об этом позже), но с шифрованием бэкапов столкнулись впервые.

Решили поглубже изучить вопрос, и пришли к распределительному камню. Там написано:

  • Налево пойдёшь — в SSE-S3 попадёшь;

  • Направо пойдёшь — силу SSE-KMS познаешь;

  • Прямо пойдёшь — SSE-C будет твоим ответом.

Мы пошли прямо, так как у нашего провайдера на тот момент имелась только SSE-C поддержка. Но другие 2 процесса тоже изучили.

Виды бэкапов ClickHouse (и не только)

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

  • полный бэкап, при котором сохраняется вся информация объекта: таблица, база данных и т.д.;

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

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

Немного информации о том, с чем мы работаем и при чём тут ClickHouse-backup

Несмотря на то, что СlickHouse позволяет делать как полные, так и инкрементные бэкапы, нам этого было недостаточно. Нам хотелось более гибкого функционала, полноценной автоматизации и нормального хранения бэкапов, такого, чтобы место на дисках внезапно не кончалось. Нашим решением стал clickhouse-backup. У него есть немало плюсов:

  1. Приложение работает автономно, в режиме watch. Его не нужно запускать руками. Не нужно костылять писать скрипты для автоматизации процесса. Можно просто запустить команду типа: $ clickhouse-backup watch --watch-interval=1h --full-interval=24h и всё, раз в сутки (в нашем случае) будет создаваться полный бэкап, раз в час — инкрементный.

  2. Есть вариант с запуском api-сервера. Это может быть полезно для контроля за бэкапами и процессами. Чтобы его включить и настроить, нам потребуется добавить блок API в конфигурационный файл. Для этого нужно перейти по адресу, на котором работает clickhouse-backup:

api:
  listen: "0.0.0.0:7171"
  enable_metrics: true

запустить сlickhouse-backup в режиме server:

$ clickhouse-backup server

по адресу <host_address>:7171 будет доступно API для взаимодействия с функционалом clickhouse-backup. Если вам интересно узнать подробнее, то ознакомиться можете здесь.

  1. Вывод метрик clickhouse-backup (а мы любим метрики). После того как вы активируете режим API server, по адресу <host_address>:7171/metrics будут отображаться метрики.

  2. Для эффективного использования пунктов 1, 2 и 3 рекомендуем пользоваться  systemd unit файлом примерно такого содержания:

[Unit]
Description=clickhouse-backup server
[Service]
Type=simple
ExecStart=/usr/bin/clickhouse-backup server \
          --watch \
          --watch-interval=1h \
          --full-interval=24h
SyslogIdentifier=clickhouse-backup
Restart=always
RestartSec=1
StartLimitInterval=0
ProtectHome=yes
NoNewPrivileges=yes
ProtectSystem=full
[Install]
WantedBy=multi-user.target

Вследствие этого мы получаем соответствующее комбо — сервер API и метрики доступны, за работой clickhouse-backup следит система.

  1. Глобальные изменения конфигурации не требуют вмешательства в работу кластера Clickhouse. Сам Clickhouse к изменениям относится неоднозначно: что-то сам применяет сразу, а что-то — после перезапуска хоста (а перезапускать продовый кластер — это, извините, моветон);

  2. Получение списка бэкапов (далее распишу этот момент чуть подробнее);

  3. Ну и то почему вы читаете сей пост — возможность шифрования бэкапов.

Есть один спорный момент: clickhouse-backup не умеет хранить данные локально. Для нас это больше плюс, так как появляется защита от неопытного инженера, который решит, что хранить бэкапы удалённо это не круто.

В общем, вещь полезная, потому и используем. Для бэкапов у нас в арсенале ещё есть такой инструмент как nxs-backup, который сам в скором времени научится бэкапировать Clickhouse и уже помогает с MySQL, Mariadb, Percona, PostgreSQL, Redis, а ещё всякие полезные штуки умеет. Как шифровать научится — вообще цены ему не будет.

Разбираемся с шифрованием

На старте страшно и непонятно. Какие-то ключи, алгоритмы требования… Проваливаемся в документацию AWS, там всё подробно расписано, так же есть хорошая подробная статья (с картинками!).

Стоит, наверное, упомянуть, что ссылки на документацию Amazon предоставлены для ознакомления в качестве справочной информации. Подразумевается, что протокол s3 есть у тех облачных провайдеров РФ, которые разрабатывались совместимыми с API AWS ( а таких немало).

SSE-C

Алгоритм работы SSE-C можно описать следующим образом:

  • Clickhouse-backup (далее – клиент) отправляет шифрованный http-запрос с указанием в хедерах описанной выше информации (кстати, если вы попытаетесь передавать шифрованные данные по нешифрованному каналу связи, то ничего у вас не получится. Потому что что? Правильно! Безопасность должна быть безопасной!);

  • S3 (далее – сервер) запрашивает у клиента ключ шифрования, который был использован при шифровании данных;

  • Сервер использует предоставленный ключ шифрования для расшифровки данных;

  • Расшифрованные данные возвращаются клиенту.

Для работы нам понадобятся:

  1. алгоритм шифрования — aes256;

  2. 256-битный зашифрованный секрет/токен/ключ в формате base64;

  3. base64-закодированный 128-битного хеш MD5 созданного ранее ключа шифрования.

Настройка SSE-C шифрования на стороне клиента

Если кратко: с помощью маленького скрипта на Python мы сгенерили рандомный ключ, обновили конфигурацию… и готово.

Скрипт выглядит вот так:

import os
import base64
import hashlib

KEY = os.urandom(32)
b64_key = base64.b64encode(KEY).decode(encoding="utf-8")
print(b64_key)
result = hashlib.md5(KEY)
b64_md5 = base64.b64encode(result.digest()).decode(encoding="utf-8")
print(b64_md5)

Конфигурацию clickhouse-backup необходимо модифицировать следующим образом:

s3:
  …
  sse_customer_algorithm: AES256
  sse_customer_key: <тут вывод того что нам выдал print(b64_key)>
  sse_customer_key_md5: <тут вывод print(b64_md5)>

SSE-KMS

Шифрование осуществляется на стороне сервера с использованием ключей, которые были созданы ранее. Они хранятся в специальном сервисе управления ключами — в Key Management Service (далее KMS). Созданный ключ KMS указывается в настройках бакета. Этот ключ будет использоваться для шифрования всех новых объектов или при загрузке объекта через API.

Клиент (clickhouse-backup) использует только ID ключа и encryption-context (уточню: шифрование с помощью encryption context поддерживается при операциях с симметричными ключами KMS. Это не является обязательным требованием, но строго рекомендуется).

Объекты шифруются перед сохранением в хранилище и расшифровываются при скачивании из него. Шифрование по умолчанию применяется ко всем новым объектам, при этом загруженные ранее объекты остаются без изменений.

Конфигурация в таком случае будет следующая:

s3:
   …
   sse: aws:kms
   sse_kms_key_id: <тут id ключа>
   sse_kms_encryption_context: <тут  encryption context закодированный base64 набор пар ключ значение типа {“key1”:”value”, “key2”:”value”}> 

SSE-S3

Тут на самом деле всё просто. SSE-S3 является методом шифрования в AWS по умолчанию. Работает так: S3 сам генерирует ключ и использует его для шифрования и расшифровки.

Но куда же без подводных камней. Появилась проблема: старые бэкапы так и остаются нешифрованными. Мы не нашли какого-то нативного решения этого вопроса. Поэтому тут чистая импровизация: либо из разряда удалить старые бэкапы после создания определённого количества успешных шифрованных, либо перезалить их с шифрованием (что даже звучит больно и долго). Но если кто вдруг знает какой-то крутой метод — делитесь в комментариях.

Восстанавливаем из бэкапа через clickhouse-backup и расшифровываем

Выше я уже рассказал, как выглядит шифрование и что происходит в процессе. Как вы понимаете, восстановление из шифрованных бэкапов — процесс обратный. Выглядит он следующим образом:

  1. Смотрим, что у нас имеется по бэкапам в запасах:

$ clickhouse-backup list

В ответ получаем вот такой список:

full-20240327090532        1.87MiB      27/03/2024 09:05:32   remote                               gzip, regular
increment-20240327090632   1014.07KiB   27/03/2024 09:06:32   remote   +full-20240327090532        gzip, regular
increment-20240327090732   3.05MiB      27/03/2024 09:07:32   remote   +increment-20240327090632   gzip, regular
increment-20240327090832   3.05MiB      27/03/2024 09:08:32   remote   +increment-20240327090732   gzip, regular
increment-20240327090932   99.12MiB     27/03/2024 09:09:35   remote   +increment-20240327090832   gzip, regular
increment-20240327091032   100.62MiB    27/03/2024 09:10:33   remote   +increment-20240327090932   gzip, regular
increment-20240327091132   100.62MiB    27/03/2024 09:11:32   remote   +increment-20240327091032   gzip, regular
increment-20240327091232   100.62MiB    27/03/2024 09:12:32   remote   +increment-20240327091132   gzip, regular
increment-20240327091332   100.62MiB    27/03/2024 09:13:32   remote   +increment-20240327091232   gzip, regular
  1. Отправляем запрос на восстановление данных в базе:

$ clickhouse-backup restore  --tables=db.table increment-20240327091332

  1. clickhouse-backup отправляет запрос к S3, чтобы получить доступ к зашифрованным данным;

  2. S3 в свою очередь запрашивает у clickhouse-backup ключ шифрования, который был использован при шифровании данных;

  3. clickhouse-backup предоставляет ключ шифрования S3;

  4. S3 использует предоставленный ключ шифрования для расшифровки данных;

  5. clickhouse-backup получает расшифрованные данные;

  6. clickhouse-backup начинает процесс восстановления.

И да прибудет с вами сила, и да не придётся вам никогда восстанавливать данные из бэкапа!

Заключение

Давайте подытожим. Была поставлена задача — шифровать резервные копии данных из ClickHouse самым надёжным из доступных методов. Исходя из описанной выше ситуации был выбран метод шифрования SSE-C, который поддерживается clickhouse-backup, соответственно, никаких архитектурных перестановок не потребовалось, всё осталось на своих местах. Просто процесс взаимодействия с хранилищем был немного изменён, что для нас является прозрачным решением.

Все последующие бэкапы шифруются, данные людей в безопасности, заказчик доволен, все счастливы.

Что же получили мы. Опыт и новые знания. А ещё мы углубились в процессы резервного копирования и работу сlickhouse-backup.

Что же получили вы? Немного информации (для кого-то новой). А кто-то даже нашёл решение своей проблемы, чему мы будем очень рады. На этом всё.

Всем DevOps!

Кстати! Хочу пригласить вас подписаться на наши соцсети: Telegram, vc.ru и YouTube. У нас весело и интересно :)

Комментарии (0)