IT-инфраструктура для бизнеса и творчества

Kubernetes: безопасное управление секретами с GitOps

Перед началом статьи хочу сказать, что еще больше полезной и нужной информации вы найдете в нашем Telegram-канале. Подпишитесь, мне будет очень приятно.

Kubernetes: безопасное управление секретами с GitOps

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

Больше всего ошибок при работе с GitOps связано со структурой. Структура репозиториев имеет первостепенное значение. От того, как организована GitOps в компании, часто зависит её успех или неудача.

Разобравшись со структурой, можно переходить к следующей по важности задаче — обеспечению сохранности секретных данных. Обычно выбирается компромиссный вариант решения этой задачи: получение доступа теми, кому эти данные не нужны, либо секретный ключ в общем доступе, который передаётся через Slack или 1Password.

Многим знаком такой способ хранения секретных данных на Kubernetes, как Sealed Secrets — неплохое решение, основанное на PKI, совместном использовании открытого ключа для шифрования и на установке закрытого ключа в кластер. Но всё-таки есть решение получше.

Лучший способ хранить секреты

Mozilla SOPS — этот инструмент поддерживает несколько источников материала для ключа шифрования, в том числе AWS KMS, GCE, Vault, PGP и другие. Он даёт возможность использовать материал внешнего ключа из AWS KMS для шифрования и дешифрования секретных данных. Кроме того, он поддерживает схему разделения секрета Шамира и группы ключей, фактически позволяя определять, какие ключи и в каком количестве требуются для расшифровки.

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

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

Хорошая новость: Flux имеет первоклассную поддержку SOPS.

Структура

В начале статьи уже упоминалось о том, что очень важно правильно структурировать репозитории.

В структурировании выделяются две составляющие: расположение содержимого в репозитории и количество используемых репозиториев. Лично у меня три репозитория. Один для определения каждого базового ресурса, который необходим кластеру для функционирования; один для работы со всеми релизами продукта, за который отвечает команда разработчиков; и один для секретных данных. Я использую SOPS только с репозиторием, выделенным под секретные данные.

Flux и SOPS предоставляют нам эту возможность с беспрецедентной гибкостью.

Структура файла

|-- .sops.yaml |-- secrets |-- dev (environment/cluster) |-- database.yaml |-- stg (environment/cluster) |-- database.yaml |-- prd (environment/cluster) |-- database.yaml

Эта структура файла даёт нам базу, необходимую для применения правил создания SOPS, чтобы контролировать то, как файлы шифруются. Можно использовать различные ключи, группы ключей и пороговые значения. Кроме того, такая структура позволяет указать Flux конкретный путь, которым он должен заниматься во время клонирования, а также определиться с содержимым кластера. Вот пример (нерабочий, но всё же):

creation_rules: - path_regex: secrets/dev/.* encrypted_regex: "^(data|stringData)$" kms: arn: kms-arn role: ksm-role - path_regex: secrets/stg/.* encrypted_regex: "^(data|stringData)$" kms: arn: kms-arn role: ksm-role - path_regex: secrets/prd/.* encrypted_regex: "^(data|stringData)$" kms: arn: kms-arn role: ksm-role

Масштабирование

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

Настройка SOPS

Теперь, когда мы рассмотрели теоретические принципы, можем переходить к практическому их осуществлению. Есть множество способов применения SOPS, но реализую я лишь один из них. Это AWS KMS с несколькими ключами и профилем экземпляра AWS, который Flux может использовать.

Настройка AWS

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

aws kms create-key

Не забудьте собрать названия ресурсов Amazon для последующего использования.

Теперь нам нужен файл .sops.yaml с правилами создания. В реальном сценарии порог должен быть не менее 2, а ключи — превышать это пороговое значение, по крайней мере, на N+1.

Начнём с создания базового каталога, а также файла .sops.yaml. Ниже показано его содержимое (замените названия ресурсов Amazon на валидные):

creation_rules: - path_regex: secrets/dev/.* encrypted_regex: "^(data|stringData)$" shamir_threshold: 2 key_groups: - kms: - arn: arn:aws:kms:us-west-2:000000000000:key/b5d44bf0-7ec5-49a9-b404-bc4d8b4036fb - kms: - arn: arn:aws:kms:us-west-2:000000000000:key/16d44186-2393-40d9-90e1-9a2f92fd5863 - kms: - arn: arn:aws:kms:us-west-2:000000000000:key/2120d2c1-a89e-4aeb-844f-f17ae2abd210

Это правило создания гласит, что все файлы в каталоге secrets/dev будут зашифрованы с помощью трёх различных ключей с порогом дешифрования 2.

Теперь создадим каталог secrets/dev, а также файл example.yaml со следующим содержимым:

apiVersion: v1 kind: Secret metadata: name: example type: Opaque data: username: bXktdXNlcm5hbWU= password: bXktcGFzc3dvcmQ=

Чтобы зашифровать файл, выполним такую команду:

sops -e -i secrets/dev/example.yaml

-e обозначает шифрование, -i — изменение файла на месте. Если хотите оставить файл без изменений и увидеть зашифрованный вывод, просто опустите аргумент -i.

Теперь можно сделать коммит этой настройки в репозитории.

Профиль экземпляра AWS

Потрясающей эту систему делает то, что Flux может просто воспользоваться профилем экземпляра AWS, присвоив права доступа через STS, и получить временные ключи для расшифровывания секретных данных во время работы в самом кластере.

Для этого нужно настроить права доступа с Encrypt/Decrypt (шифровать/дешифровать) или Generate permissions (генерировать разрешения) в зависимости от типа используемого ключа, а также настроить экземпляр AWS для использования профиля.

После настройки просто устанавливаем Flux в кластер с правильными опциями командной строки и ждём, когда он обновит кластер.

Настройка Flux

Чтобы убедиться в том, что Flux работает правильно, нужно, во-первых, активировать SOPS. В зависимости от метода развёртывания это может сделать параметр конфигурации или флаг CLI --sops. Во-вторых, указать экземпляру Flux конкретный путь в ветви по умолчанию, которым он должен заниматься. --git-path должен быть установлен в один из каталогов с секретными данными, например --git-path=secrets/dev.

Заключение

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

(function () { let cdnUrl = `https://specialsf378ef5-a.akamaihd.net/SelectelBranding/images/` let previousArticleNumber = null let currentArticleNumber = 0 let platform = 'Desktop' let articles = [ { name: 'camera', url: `${cdnUrl}CameraCat`, text: 'умную камеру для\u00A0наблюдения за\u00A0котиками', link: 'https://vc.ru/selectel/306690', num: 3 }, { name: 'chill', url: `${cdnUrl}ChillCat`, text: 'трекер, который подскажет, когда пора отдохнуть', link: 'https://vc.ru/promo/288561-eye-tracker', num: 1 }, { name: 'cloud', url: `${cdnUrl}CloudCat`, text: 'котика: даёшь ему «пять», а\u00A0он делает бэкап в облако', link: 'https://vc.ru/dev/294799-maneki-neko', num: 2 } ] let buttonCycle = document.querySelector('.button--cycle') let buttonChoose = document.querySelector('.button--choose') let buttonMobile = document.querySelector('.button--mobile') let textField = document.querySelector('.selectel-footer-subtitle') let imageAgent = document.querySelector('.image--agent') let banner = document.querySelector('.selectel-footer') buttonCycle.addEventListener('click', cycleClick) buttonChoose.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) buttonMobile.addEventListener('click', () => sendEvent(`Promo ${articles[currentArticleNumber].num} Left`, 'Click')) let media = window.matchMedia("(max-width: 570px)") media.addEventListener('change', matchMedia) function matchMedia() { if (media.matches) { platform = 'Mobile' } else { platform = 'Desktop' } update() } matchMedia() function cycleClick(event) { sendEvent(`Promo ${articles[currentArticleNumber].num} Right`, 'Click') if (event) { event.preventDefault() event.stopPropagation() } window.open('https://vc.ru/tag/selectelDIY', '_blank') //cycle(event) } function cycle(event) { // incrementArticleNumber() textField.innerHTML = generatedText() imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?3' imageAgent.setAttribute("class", "") imageAgent.classList.add('image--agent', articles[currentArticleNumber].name) banner.href = articles[currentArticleNumber].link } function update() { banner.href = articles[currentArticleNumber].link imageAgent.src = articles[currentArticleNumber].url + platform + '.svg' textField.innerHTML = generatedText() } function incrementArticleNumber() { previousArticleNumber = currentArticleNumber if (currentArticleNumber >= articles.length - 1) { currentArticleNumber = 0 } else { currentArticleNumber++ } } const sendEvent = (label, action = 'Click') => { const value = `SelectelDIY — loc: Footer — ${label} — ${action}`; if (window.dataLayer !== undefined) { window.dataLayer.push({ event: 'data_event', data_description: value, }); } }; function generatedText() { let defaultText if (platform === 'Desktop') { defaultText = `Мы тут собрали %text%. Хотите научим?` } else { defaultText = `Мы тут собрали %text%.` } return defaultText.replace('%text%', articles[currentArticleNumber].text) } function getRandom(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min } (function create() { currentArticleNumber = getRandom(0, articles.length - 1) cycle() let page = document.querySelector('.page--entry') if (page) { function insertAfter() { let parents = page.querySelectorAll('[data-id="7"]') let referenceNode = parents[0] referenceNode.parentNode.insertBefore(banner, referenceNode.nextSibling); loaded() } setTimeout(() => insertAfter(), 0) } }()) function loaded() { banner.classList.add('loaded') } loadImages([ `${cdnUrl}CameraCatDesktop.svg`, `${cdnUrl}ChillCatDesktop.svg`, `${cdnUrl}CloudCatDesktop.svg`, `${cdnUrl}CameraCatMobile.svg`, `${cdnUrl}ChillCatMobile.svg`, `${cdnUrl}CloudCatMobile.svg?3`, ]) function loadImages(urls) { return Promise.all(urls.map(function (url) { return new Promise(function (resolve) { var img = document.createElement('img'); img.onload = resolve; img.onerror = resolve; img.src = url; }); })); } }())
0
0 комментариев
Популярные
По порядку
Читать все 0 комментариев
Завод по производству идей. Как работают акселераторы, зачем они нужны стартапам и куда идти с идеей прямо сейчас

По данным Startup Genome, 9 из 10 стартапов терпят неудачу. Возможных причин «смерти» много: недостаточно протестированная гипотеза, неподтвержденная юнит-экономика, неверная стратегия или просто неудача в подходе к продажам.

Невидимый убийца: как воздух в вашем офисе делает вас глупее

Важное исследование, которое должно заставить все компании пересмотреть свои офисы.

Минэнерго включило в правила для субсидий на зарядные станции медленную зарядку и четыре выхода для электромобилей Статьи редакции

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

Бизнес — как ребенок: как мамы совмещают свое дело с заботой о детях

Как совмещать бизнес и семью? Ко Дню матери своими историями поделились бизнесвумен, которые работают c ЮKassa и занимаются детьми. Читайте, как им удается сохранять жизненный баланс и добиваться успеха.

Из науки в IT: как создать свой стартап и стать преподавателем

Как перейти в IT из другой сферы? Как разработать курс, которому нет аналогов? Как студенту получить максимум пользы от занятий? Рассказывает преподаватель OTUS Сергей Окатов, руководитель курсов «Kotlin Backend Developer» и «Kotlin Developer. Basic».

Хочу кухню как у подруги: зачем в Циан сделали поиск квартир по фото

Рассказывает Юлия Зыкова, руководитель команды «Аудитория» в Циан.

Новые гарантии для одиноких родителей

Государственная Дума РФ 9 ноября 2021 года приняла в третьем чтении закон, предоставляющий дополнительные гарантии отдельным категориям работников, имеющих несовершеннолетних дней, и вносящий поправки в Трудовой Кодекс РФ.

Опыт возврата денег за обучение дизайну у Yakovlevv.com. Тварь я дрожащая или право имею?
10 способов увеличить доходимость до вебинара в 3 раза с помощью чат-ботов

Бесплатный вебинар — один из самых эффективных способов прогреть аудиторию к покупке основного продукта. Удобный формат, полезная информация — все это помогает собрать действительно заинтересованных людей и предложить им оффер. Используйте при сборе подписчиков на бесплатный мастер-класс или вебинар платформу мессенджер-маркетинга, чтобы получить…

Что там с видеорегистраторами: где за них штрафуют, а где — дают скидку на страховку

Регуляторы вновь в деле. На этот раз они могут затронуть видеорегистраторы. Расскажу о ситуации с точки зрения страхового эксперта.

MIKI Yoshihito / Flickr / CC BY 2.0
null