Kubernetes и кот Лихачева
4.2K subscribers
992 photos
27 videos
4 files
1.05K links
Все про Kubernetes и немного про кота Маркуса

Чат для конструктивного общения: https://t.iss.one/+Q4z_2ckAkBxhNWNi

Задать вопрос: https://t.iss.one/K8sSlurm_bot?start=question
Download Telegram
Безопасность: интегрируемся с vault

Использование k8s secrets не единственный способ доставить секреты в приложение. Более того, cluster admin может посмотреть эти секреты и есть чуть более надежные подходы.

➡️ Рассмотрим как интегрироваться с vault через vault sidecar injector.

Здесь мы пропускаем шаг создания секретов в k8s, а в vault у нас больше возможностей по аудиту доступа, и способов сказать админу, что он идет не туда, куда нужно, гораздо больше. Вплоть до законодательных запретов и серьезных последствий в некоторых особо зарегулированных сферах.

Рассмотренный подход все еще не 100% bullet proof и cluster admin все еще может зайти в контейнер и увидеть секреты, но это тоже можно решить разными способами, о которых мы поговорим как-нибудь в другой раз.

🟠 Disclaimer:

Я скромно опустил некоторые моменты, которые могут вызвать 🤦 у опытного админа.
Please open Telegram to view this post
VIEW IN TELEGRAM
Давайте разложим все по полочкам:

➡️ Minikube? Seriously? Да, для демонстрации отлично подходит. В проде вы не увидите minikube (хотя могут быть кейсы), но это тот же самый k8s.

➡️ VAULT_TOKEN=root - BIG NO-NO! Разбрасываться root токенами - это как оставлять ключи от квартиры под ковриком. В продакшене используются более сложные методы аутентификации, например, с помощью AWS IAM.

➡️ Vault в dev режиме внутри кластера? В реальном мире Vault обычно развертывается вне кластера в HA конфигурации.

➡️ Helm Chart без кастомизации. В продакшене вам потребуется тонкая настройка Vault, включая storage backend, репликацию и мониторинг.

➡️ kubectl exec внутри Vault? Зачем лезть внутрь контейнера, если есть Vault CLI? Потому что так проще показать.

➡️ Hardcoded конфиги в Deployment YAML? Используйте ConfigMaps и Secrets (да-да, irony) для хранения конфигурации и избегайте хардкодинга.
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Итак, приступим.

🐈 Запустим minikube

minikube start


🐈 Установим vault в dev режиме (в проде vault может быть вне кластера k8s и контролироваться отдельной security командой). Внимание: рестарт контейнера vault потеряет все ваши настройки в dev-режиме!

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm install vault hashicorp/vault --set "server.dev.enabled=true" -n vault --create-namespace


🐈 Получим root token

export VAULT_TOKEN=root


Да, в dev-инсталяции все настолько просто. Посмотреть можно в логах vault.

kubectl -n vault logs vault-0


Покажет вам

Development mode should NOT be used in production installations!


Мы не vault изучаем, а как его использовать для решения подобной задачи 🙂
Please open Telegram to view this post
VIEW IN TELEGRAM
4
🐈 Включим k8s auth method в Vault и настроим политики, роли и прочие сущности vault, включая секрет

kubectl -n vault exec -it vault-0 -- /bin/sh

# Далее все внутри контейнера vault-0
vault secrets enable -path=k8s kv-v2
vault kv put k8s/myapp/prod mykey="prod-secret"

# И включим интеграцию k8s с vault
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"


Продолжаем настройку все так же внутри контейнера с vault. Создаем политику.

vault policy write k8s-myapp-prod - <<EOF
path "k8s/data/myapp/prod" {
capabilities = ["read"]
}
EOF


И связываем ее с service account

vault write auth/kubernetes/role/myapp-prod \
bound_service_account_names=myapp-prod-sa \
bound_service_account_namespaces=app-prod \
policies=k8s-myapp-prod \
ttl=1h
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🐈 Создаем тестовое приложение (уже не в контейнере vault выполняем)

kubectl create ns app-prod
k -n app-prod create sa myapp-prod-sa


И задеплоим

echo '
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: app-prod
labels:
app: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp-prod"
vault.hashicorp.com/agent-inject-template-CREDENTIALS: |
{{- with secret "k8s/data/myapp/prod" -}}
MYKEY={{ .Data.data.mykey }}
{{- end -}}
spec:
serviceAccountName: myapp-prod-sa
terminationGracePeriodSeconds: 1
containers:
- name: busybox
image: busybox
command:
- /bin/sh
- -c
- |
while true; do
env $(cat /vault/secrets/credentials | xargs);
printenv;
sleep 10;
done
' > app.yaml

kubectl -n app-prod apply -f app.yaml
Please open Telegram to view this post
VIEW IN TELEGRAM
🐈 Посмотрим, что vault sidecar успешно был добавлен в наше приложение (имя пода будет другим)

kubectl -n app-prod describe pod app-78fd56f64b-vtrks


А также увидим секрет

kubectl -n app-prod exec -it app-78fd56f64b-vtrks -- cat /vault/secrets/credentials
# И в логах
kubectl -n app-prod logs app-7ccc6557fb-bzj2b | grep MYKE


Итого: секрет доставили в приложение всего лишь за пару шагов. Правда k8s это просто? :sarcasm:

Поделитесь вашими кейсами выстраивания относительно защищенных кластеров (нет, ничто не безопасно на 100%, пока оно включено 🙂)
Please open Telegram to view this post
VIEW IN TELEGRAM
Как я перестал волноваться и полюбил ротацию секретов. Часть 1

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

И могут заканчиваться сложными, когда приходит регулятор и требует внедрения конкретных мер защиты, а то придет снова и сделает [censored].

Посмотрим что в нашем любимом k8s для этого есть ⬇️
Please open Telegram to view this post
VIEW IN TELEGRAM
Методы ротации

🔷 Ручной способ: стучим по клавиатуре

kubectl patch secret mysecret -p '{"stringData":{"mykey":"newvalue"}}'


Человеческий фактор никто не отменял. Одна ошибка и ты ошибся. Ауф.

Если наше прекрасное приложение может на лету прочитать изменения, то оно увидит изменение секрета при условии, что секрет примонтирован как volume.

Но не сразу, а с лагом в минуту, потому что такие настройки по умолчанию у kubelet (см. тут, параметр syncFrequency)

🔷 2. Внешние операторы: когда хочется как у взрослых

➡️ HashiCorp Vault: Это мы посмотрели в предыдущем посте.
➡️ External Secrets Operator: Еще один способ синхронизировать секреты в кластере.
➡️ AWS Secrets Manager: Если ваша инфраструктура в AWS, то почему бы и нет?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21
Механизмы доставки

Как секреты попадают в под

🔷 Volume Mount с автообновлением
Kubelet делает это почти незаметно. Знай себе, читай файлики с файловой системы внутри пода.

🔷 Sidecar
Неважно, vault это или что-то еще. Главное, что в большинстве случаев изменения все так же оказываются видны в файловой системе пода.

🚨 ВНИМАНИЕ! ENV переменные не позволяют выполнить ротацию

Никакой динамики, только полный редеплой. Конечно, есть окольные пути, но применять такое на самом деле кажется за гранью.
Please open Telegram to view this post
VIEW IN TELEGRAM
Сценарии ротации: От простого к сложному

➡️ API ключи: Легкий уровень

🟠 Создаем новый ключ
🟠 Обновляем сервисы
🟠 Удаляем старый ключ

➡️ JWT подписи: Средний уровень сложности

🟠 Добавляем поддержку нового секрета для подписи в приложение
🟠 Деплоим
🟠 Смотрим по метрикам, когда перестали приходить токены со старым секретом (вы же добавили метрики?)
🟠 Удаляем старый секрет и опционально меняет код обратно

func (jm *JWTManager) IsTokenValid(token string) bool {
// Проверяем токен двумя секретами
return validateWithCurrentKey(token) ||
validateWithPreviousKey(token)
}



Продолжение в посте завтра, где посмотрим на более сложный кейс, как менять credentials вашей любимой БД на лету.
Please open Telegram to view this post
VIEW IN TELEGRAM
Как я перестал волноваться и полюбил ротацию секретов. Часть 2

БД коннекты: режим nightmare

Какие проблемы могут возникнуть при пересоздании коннектов на лету:

➡️ Незакрытые транзакции (а значит отмененные, но возможно не сразу);
➡️ Риски обрыва соединений и резкого всплеска нагрузки на базу при массовом переподключении;
➡️ Риски потери коннектов и зависания их на стороне БД вплоть до connection timeout вашей любимой БД.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Базовый код для выполнения реконнектов (не использовать в проде без понимания всех последствий!), который лишь показывает идею, реальных нюансов сильно больше:

package main

import (
"context"
"database/sql"
"fmt"
"sync"
"time"
"io/ioutil"

_ "github.com/jackc/pgx/v5/stdlib"
)

type DBManager struct {
db *sql.DB
lock sync.Mutex
password string
}

func NewDBManager() *DBManager {
password := getPasswordFromSecretStore()
db, err := connectDB(password)
if err != nil {
panic(fmt.Sprintf("Failed to connect to DB: %v", err))
}

manager := &DBManager{
db: db,
password: password,
}

go manager.watchPasswordChanges()

return manager
}

func connectDB(password string) (*sql.DB, error) {
dsn := fmt.Sprintf("postgres://myuser:%s@localhost:5432/postgres?sslmode=disable", password)
return sql.Open("pgx", dsn)
}

func (m *DBManager) watchPasswordChanges() {
for {
time.Sleep(1 * time.Second)
newPassword := getPasswordFromSecretStore()
if newPassword != m.password {
fmt.Println("Password changed, reconnecting...")
m.reconnect(newPassword)
}
}
}

func (m *DBManager) reconnect(newPassword string) {
m.lock.Lock()
defer m.lock.Unlock()

db, err := connectDB(newPassword)
if err != nil {
fmt.Println("Failed to reconnect to DB:", err)
return
}

oldDB := m.db
m.db = db
m.password = newPassword

if oldDB != nil {
oldDB.Close()
}
}

func getPasswordFromSecretStore() string {
content, err := ioutil.ReadFile("mysecret") // /var/run/secrets/credentials - пример пути в поде
if err != nil {
panic(err) // Не делайте так в проде)
}

text := string(content)
return text
}

func main() {
manager := NewDBManager()

ctx := context.Background()
for {
var now time.Time
if err := manager.db.QueryRowContext(ctx, "SELECT NOW()").Scan(&now); err != nil {
fmt.Println("Query failed:", err)
} else {
fmt.Println("DB time:", now)
}
time.Sleep(time.Second)
}
}
Когда автоматизация ротации секретов это реальная необходимость

➡️Критерии для полной автоматизации:

🟠Регуляторные требования
- Финансы
- Медицина
- Госсектор
🟠Уровень риска
- Критичность инфраструктуры
- Стоимость компрометации

Ротация секретов — это не только про безопасность, но и про надежность. Чем меньше ручного труда, тем меньше вероятность фатальных ошибок.

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

Поделитесь своими историями внедрения «правильного» управления секретами в инфраструктуре ⬇️
Please open Telegram to view this post
VIEW IN TELEGRAM
Сегодня попробуем вернуться в 2007 (но это неточно).

Если фразы Crawling in My Skin и Your Tears Don't Fall где-то в глубине пробуждают память о беспечном (или не очень беспечном) детстве, то этот пост точно для вас.

Что вы слушаете во время работы?

➡️ Если вы engineering manager, вероятно, у вас не так много времени остается между встречами на сфокусированную работу.

➡️ Если вы инженер, иногда удается выкроить 2–3 часа полного фокуса, когда не хочется отвлекаться: войти в состояние потока, поразбираться с причинами инцидентов, потюнить алерты, оптимизировать код инфры, дописать уже наконец скрипт автоматизации, который давно собирались доделать, или поразгребать технический долг.

Расскажу, что помогает мне сосредоточиться на работе ⬇️
Please open Telegram to view this post
VIEW IN TELEGRAM
Никаких lofi girl, хотя lofi gopher тоже иногда хорошо заходит.

Пойдем по нарастающей — от того, что у всех на слуху (а у меня и сейчас, в 2025-м), и относительно легкого для восприятия, к тому, что кому-то покажется крайне суровым.

Мотивацией к написанию этого поста стало посещение совместного концерта Trivium и Bullet for My Valentine. Назвали они это Poisoned Ascendancy Tour. Почему? The Poison — наитоповейший альбом BFMV, а Ascendancy — не менее шикарный альбом Trivium.
4🔥31👍1
Из той эпохи могу еще назвать то, что до сих пор играет в наушниках во время работы или на пробежке.

Все группы вам наверняка известны и в представлении не нуждаются. Описываю их, что называется, in no particular order:

🟠 Linkin Park. Честер Беннингтон — rest in peace. А что думаете про их новый альбом и новую вокалистку?
🟠 System of a Down. Сержа Танкяна, конечно, еще никто, на мой субъективный взгляд, не смог перепеть или повторить успех группы. Армяне везде. Барев дзес всем причастным!
🟠 Avenged Sevenfold. Отборнейший металкор. Википедия причисляет их к множеству жанров, но синдром утенка никто не отменял, так что для меня они остаются именно металкором.

Думаю, вы уловили суть. Многие через это проходили.
Please open Telegram to view this post
VIEW IN TELEGRAM
5
Как прекрасно включить себе в очередной раз что-то из старичков и разбираться с очередным дашбордом в Grafana, писать правила алертинга или разбирать причины деградации в одном из тысяч микросервисов. А может, ковырять манифесты в Kubernetes, разбираясь, почему новый Helm-чарт сломал продакшен.

В следующем посте поговорим про темы, которые тоже давно не отпускают. Знаете шутки в стиле «я слушаю всё подряд»? Возможно кто-то поменяет свое мнение или скажет, что автор не в себе, раз ему такое нравится.
Для затравки — о чем поговорим дальше