Секрети в репозиторіях. Як SOPS і FluxCD рятують DevOps від помилок
На самому початку співпраці з клієнтом наша компанія проводить процес discovery. Отримавши доступ до оточення потенційного клієнта, протягом декількох днів наш CTO та ще один інженер (іноді це я) досліджують усе, що можливо, та шукають місця, які можна покращити. Де можна налаштувати автоматизацію, прибрати зайве і загалом зробити життя розробників клієнта краще.
Під час вищезгаданого процесу discovery можна зустріти один тривіальний момент: секрети та ключі, закомічені у репозиторій. І, судячи з новин про великі зливи кодових баз, це явище доволі розповсюджене навіть серед великих компаній, як-то Marcedez-Benz чи Github. А відповідно до Gitguardian, у 2023 році загалом у публічні репозиторії було викладено 12М секретів.
Чому ми бачимо секрети та ключі у репозиторіях? На мою думку, головна причина в тому, що це зручно. Усі ми розуміємо, що це небезпечно і може призвести до дуже серйозних наслідків як для компанії, так і для розробника. Що усі секрети мають зберігатися у сховищі секретів, наприклад, Hashicorp Vault, і підтягуватись у Kubernetes-поди через оператор.
Але будемо чесними, якщо компанії не потрібно пройти сертифікацію SOC2 для якогось контракту, ця таска буде лежати в беклозі та ніхто її не чіпатиме. Тому що ми завжди бажаємо простоти та зручності.
Тому сьогодні я хочу розповісти, як почати шифрувати секрети у репозиторій за допомогою SOPS, не жертвуючи зручністю. А щоб це була стаття про DevOps, налаштуємо розшифрування та деплоймент наших секретів у Kubernetes-кластер за допомогою FluxCD.
Дисклеймер #1. Підхід, який я описую далі, в жодному разі не претендує на звання найбезпечнішого. Зберігання секретів поза репозиторієм завжди буде найкращим варіантом. Цей підхід не працюватиме для всіх компаній та команд.
Дисклеймер #2. Принципи роботи FluxCD та його базові налаштування не описуються в цій статті.
SOPS
SOPS — це утиліта для шифрування/розшифрування файлів. SOPS здатен працювати з широким набором інструментів та сервісів:
- PGP;
- age;
- GCP KMS;
- Azure Key Vault;
- AWS KMS;
- Hashicorp Vault.
Базова робота з SOPS виглядає таким чином:
1. Створюємо ключ (у цьому прикладі я використовуватиму age-ключі):
18:31:20 ❯ age-keygen > age.key age-keygen: warning: writing secret key to a world-readable file Public key: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5
2. Шифруємо файл:
18:32:15 ❯ cat dou.txt hello dou.ua 18:32:37 ❯ sops -e --age age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 -i dou.txt 18:32:40 ❯ cat dou.txt { "data": "ENC[AES256_GCM,data:pTh//dMJ8jEVDFWcKg==,iv:Z5k41VNpgAS5+dh21fAxLaflC148nbLfr0yOLYRWFes=,tag:/tt5ReaCYTLhzxnlpFGRjA==,type:str]", "sops": { "kms": null, "gcp_kms": null, "azure_kv": null, "hc_vault": null, "age": [ { "recipient": "age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5", "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByRnhTZkRqci9GUzFPUU4r\nbG9vdDdkQ0JOZmhKUFd2alBiOVhaenhWMmdnCi9LZWNzNEVaVVV1MnU1dGErcXBI\nK0IwYURaY01MbUE3VVhLeHI1YWFtVlUKLS0tIFN0SzFGU0RXdTBwcy8vTFJmaWc1\nbWxRRE9yUWM3cnlDOGhJd3MxbWsyQ3cKTDidXcN7myCfaPfQALp79UHJqxcnnfMm\nOZp7FTYwUSoteoVOAcsivDTGwmzC9fAfYow8MFnEqMZHprpupGE+Zg==\n-----END AGE ENCRYPTED FILE-----\n" } ], "lastmodified": "2024-08-05T16:08:50Z", "mac": "ENC[AES256_GCM,data:h2Pjlo25AAdhq38QvAIxcNVI4u7aGk2WQc6I9I/I2R6lr1bL2ZGZWonmJwfcI4dhRPSW3nV7EXsw+C9X/gYWoajc1Oz4ImgG8ogiCMoy1mRFRG+sccXiqd39HLPxb57DM8JhMja0ERu0/Q8zns7vQPCLPqU/oCUDKkdTYgEkr3I=,iv:pg7EKRfucyfnIPEGra+o2nbsL7YkAfzroh6Td2CvpLA=,tag:9x86fvQ11RVk9g2c1zySpA==,type:str]", "pgp": null, "unencrypted_suffix": "_unencrypted", "version": "3.7.3" } }
3. Розшифровуємо файл:
18:36:29 ❯ sops -d -i dou.txt Failed to get the data key required to decrypt the SOPS file. Group 0: FAILED age163ggudjavhda8n3h65y3k95ag0jrwr6uyvtz2d4uju8w4zemf54s8m9qr8: FAILED - | no age identity found in "/Users/rtim/Library/Application | Support/sops/age/keys.txt" that could decrypt the data Recovery failed because no master key was able to decrypt the file. In order for SOPS to recover the file, at least one key has to be successful, but none were.
SOPS не має приватного ключа, що відповідає публічному ключу, яким був зашифрований файл. Як можна побачити, нам потрібно додати ключ у /Users/user/Library/ApplicationSupport/sops/age/keys.txt:
cat age.key >> '/Users/user/Library/Application Support/sops/age/keys.txt'
На Linux SOPS використовує наступний шлях для зберігання age-ключів: $XDG_CONFIG_HOME/sops/age/keys.txt або $HOME/.config/sops/age/keys.txt.
4. Розшифровуємо файл, додавши приватний ключ до сховища:
19:11:32 ❯ sops -d -i dou.txt 19:11:34 ❯ cat dou.txt hello dou.ua
Налаштовуємо правила у .sops.yaml
Базовий підхід чудово працює для одного чи двох файлів. Якщо є необхідність опрацьовувати велику кількість файлів, або послуговуватись кількома різними ключами, можемо використати creationRules у .sops.yaml, який можна покласти у корінь репозиторія. Команда sops рекурсивно шукає у репозиторії файл .sops.yaml. Якщо такий знайдено — ім’я файлу, який зараз обробляється, порівнюється з regex-виразами у цьому файлі. Ключ буде обрано з першого виразу, під який підпадає ім’я файлу.
Отже, `.sops.yaml` виглядатиме таким чином (приватні ключі я вже додав до сховища):
creation_rules: - path_regex: deploy/development/secrets/.* age: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 - path_regex: deploy/production/secrets/.* age: age14cjcdma8udezj6krmntyhdy9x0t388qjg6wuh0klslcd3mmdfgms0mymsl
Також маємо 2 секрети в окремих папках:
19:33:58 ❯ cat deploy/development/secrets/dou.txt This is a DEVELOPMENT secret from dou 19:34:01 ❯ cat deploy/production/secrets/dou.txt and this is a PRODUCTION secret from dou
Тепер, шифруючи та розшифровуючи ці два файли, SOPS буде використовувати різні ключі, відповідно до creation_rules (зверніть увагу на recipient):
19:36:43 ❯ sops -e -i ./deploy/development/secrets/* 19:36:49 ❯ cat ./deploy/development/secrets/dou.txt { "data": "ENC[AES256_GCM,data:jnSI3LqSKcoeRZX9FZ/4ZiHYBqpQSPodYRgvB/6UAIqxWfQSOnmT,iv:adWYc2ylJ1n1dIkJz4k9Jrj3Wo2ns6XxM9lTFXwC4M0=,tag:sSDIigWVTjjt5psgKW+nzQ==,type:str]", "sops": { "kms": null, "gcp_kms": null, "azure_kv": null, "hc_vault": null, "age": [ { "recipient": "age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5", "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaL1ljUi9YSjRqUHp0aWRU\nWDVCZ3JQc3kyUTN4OG1vVTJYTGcyanlqTFZzCkpITTJPdWlnWE1wYzVrbDVPK0Rm\na0VZU2orRzdtbVd5YjRtWjhaTml0QWsKLS0tIHUxYzYwMlRsb0d5bHA0MzVaQzBh\nU3BMd1V5STI2cXpHdzVFMGFQVGdpZFUKMUkpQkh+QIAuU6F9G5WnZJd8aPikkykw\nGg77inL63oNDz6V9Xws0oRlpj7YbXRqy9oUk+iToUXxx1+iEqQCLGw==\n-----END AGE ENCRYPTED FILE-----\n" } ], "lastmodified": "2024-08-05T16:36:43Z", "mac": "ENC[AES256_GCM,data:A+cxx0sYKf2h9rJJqZhGYp0fn/5AQss3Obo++mjtvdzRB0sHfpQ0yaLjvBQTjpfe8yG7eZE5GzrQUwPAPRbywsV+wVTKPsURAqpTozZH8epLh6KOMTrCq/gOiz9/woUzKoTPCkBkAN/H8v0Al6EebHdZ+1+rsCXQTVNvBJRfDuI=,iv:QRENhojGh0ihVowsX5B4VQXK3CR0JDYX9vwYwKEEpc8=,tag:lywtECYJwP1Hzr817Hmu6Q==,type:str]", "pgp": null, "unencrypted_suffix": "_unencrypted", "version": "3.7.3" } }
Production-файл виглядає так:
19:37:30 ❯ sops -e -i ./deploy/production/secrets/* 19:37:31 ❯ cat ./deploy/production/secrets/dou.txt | grep recipient "recipient": "age14cjcdma8udezj6krmntyhdy9x0t388qjg6wuh0klslcd3mmdfgms0mymsl",
Як бачимо, ці 2 файли були зашифровані різними публічними ключами, відповідно, для розшифрування будуть використовуватись різні приватні ключі.
Шифрування тільки певної частини файлу
В усіх попередніх прикладах SOPS шифрував увесь файл. При роботі з Kubernetes такий підхід може виявитись незручним. Наприклад, хотілося б бачити назву секрету без необхідності розшифровувати файл. Або ж зашифрувати тільки певну частину Helm values. Для цього потрібно у sops.yaml-файлі до відповідного правила додати атрибут encrypted_regex:
creation_rules: - path_regex: deploy/development/secrets/.*.yaml age: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 encrypted_regex: ^(data|stringData)$ - path_regex: deploy/production/secrets/.*.yaml age: age14cjcdma8udezj6krmntyhdy9x0t388qjg6wuh0klslcd3mmdfgms0mymsl
Тепер, коли я шифруватиму ConfigMap чи Secret-маніфести у відповідних директоріях, тільки частина з секретною інформацією буде зашифрована:
19:47:59 ❯ sops -e -i ./deploy/development/secrets/configmap.yaml 19:48:23 ❯ cat ./deploy/development/secrets/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: dou namespace: default data: dou: ENC[AES256_GCM,data:j+Y=,iv:irtkrZks8R4gJBIyQtIq/kUAr7bAB+r9IyDBhfBH1d4=,tag:U6Z7r8khWaHklDvvrFliSw==,type:str] glory: ENC[AES256_GCM,data:BI2Ar6AuPg==,iv:IS0UMCYwoVLyg0k0cMdSG6phagTQptx0zwzR4Ht5LK0=,tag:0GyzgTBp+joeVlR/X4dj0g==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCWDRGcDNtS3dYWVMrenNp ckpCUGJTenV3SFZmN0xTdUNhSmU5Y25mWEhJCjFwZUxnRUhmbFRibkNVd3pGZ0xR R2hlNEtHdlZydlJNMEtSN0d6OGExd28KLS0tIGZmYm5zcXFldnNta083YnVudFUr VC9zWXlhMmUwWEgxenVuZUVGSXZJNGcK+5KshVHhA/4N8DlasHbUIQpPR4VNzNvs erU9oO7AVJkRXl0keG6cNnKTPz7ekXJwOE6HsTnAzdwUg6wzOkhhbg== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-08-05T16:47:59Z" mac: ENC[AES256_GCM,data:9wWFJnF0SvcxNzkkQM+AyElRd7G6irPc54a34+jc4l0RW4h65PNImZ2KYm62MydM3zX8lPCzjfF9/2wpLciDkXRZ7CcVb/wqVSxYEMvQzu1+rElysAYStFLIJ5vk0fTZsYWn3YGMWZKzo7mAyqj4t1sQzPjcUw/AUIOZGVZO2xQ=,iv:CTLtGR/09gALoJsTMXARVOoabKMt6K3uHoSVAcqHpZI=,tag:68YdEdY7Jur9DWURunsXIQ==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.7.3 --- sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCWDRGcDNtS3dYWVMrenNp ckpCUGJTenV3SFZmN0xTdUNhSmU5Y25mWEhJCjFwZUxnRUhmbFRibkNVd3pGZ0xR R2hlNEtHdlZydlJNMEtSN0d6OGExd28KLS0tIGZmYm5zcXFldnNta083YnVudFUr VC9zWXlhMmUwWEgxenVuZUVGSXZJNGcK+5KshVHhA/4N8DlasHbUIQpPR4VNzNvs erU9oO7AVJkRXl0keG6cNnKTPz7ekXJwOE6HsTnAzdwUg6wzOkhhbg== -----END AGE ENCRYPTED FILE----- lastmodified: "2024-08-05T16:47:59Z" mac: ENC[AES256_GCM,data:9wWFJnF0SvcxNzkkQM+AyElRd7G6irPc54a34+jc4l0RW4h65PNImZ2KYm62MydM3zX8lPCzjfF9/2wpLciDkXRZ7CcVb/wqVSxYEMvQzu1+rElysAYStFLIJ5vk0fTZsYWn3YGMWZKzo7mAyqj4t1sQzPjcUw/AUIOZGVZO2xQ=,iv:CTLtGR/09gALoJsTMXARVOoabKMt6K3uHoSVAcqHpZI=,tag:68YdEdY7Jur9DWURunsXIQ==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.7.3
Завдяки цьому я все ще здатен визначити ім’я ConfigMap, її неймспейс та ключі, які в ній зберігаються. Це особливо зручно для пошуку тексту у репозиторії.
SOPS + AWS KMS
age може бути зручним коли тільки одна людина працює над проектом, тому що тільки один ключ може бути використаний для шифрування. І якщо ми почнемо передавати цей ключ усім розробникам, рано чи пізно побачимо наш же ключ у повідомленні від Github про закомічений секрет у репозиторій.
Також з часом може з’явитися необхідність розділити доступ до секретів, щоб, наприклад, тільки певні люди могли розшифрувати production-секрети.
З використанням AWS KMS це можливо налаштувати за допомогою IAM-політик. Щоб почати використовувати SOPS з попередньо створеним AWS KMS ключем, достатньо лише змінити відповідний creation_rule у .sops.yaml:
creation_rules: - path_regex: deploy/development/secrets/.*.yaml age: age1qfe60mzumfm65a34jj8fe340fe7crh2ukuxpx64t35kjl947qsuslsdrf5 encrypted_regex: ^(data|stringData)$ - path_regex: deploy/production/secrets/.*.yaml kms: arn:aws:kms:eu-central-1:123456789:key/b9a704a8-41b7-4efe-8804-756a8d9289f0 encrypted_regex: ^(data|stringData)$
Тепер для шифрування секретів для production-оточення SOPS використовуватиме вказаний KMS-ключ:
20:18:23 ❯ sops -e -i ./deploy/production/secrets/configmap.yaml 20:22:16 ❯ cat ./deploy/production/secrets/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: dou-production namespace: prod data: secret_key: ENC[AES256_GCM,data:AAj0,iv:z3Hkx2SnYmGF24jovs9Vvu8aFQ8s7dVxscQ07b0x/ZE=,tag:cu0U/5/E9nFdV6JsqBBnHA==,type:str] access_key_id: ENC[AES256_GCM,data:tR0=,iv:M9J4dWuZ1aWPR9tt5ePvbC5Jl/MGDI+mfGBY9FFQkIo=,tag:kSW5Fn6Z0yix4f1vKsujWw==,type:str] sops: kms: - arn: arn:aws:kms:eu-central-1:123456789:key/b9a704a8-41b7-4efe-8804-756a8d9289f0 created_at: "2024-08-05T17:18:23Z" enc: AQICAHgZMQAwa/aMuzPnXjzZIt4Zjx0ZP5me29gDFY9YerBw8AEM9qMO7t3ROi61YkOeTb45AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMeW7+qCL55odIz6GnAgEQgDuQhymat8UgTnlbz2j9fUCR22randELIsE5Aj+OOJma7AA9F+nMVzitMvQMBrkD+ACe+H7XxrRkf1Xt9g== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] lastmodified: "2024-08-05T17:18:24Z" mac: ENC[AES256_GCM,data:QYiyO5qEkFsqTVAIToWb1olfRksgFeEFeT2veHJ7jErBQMD9QX788xCv866mu9g4sqaNHcT2e5oYYgCTU76+Y5qBjX6GMADxderSgOaBDtH4JXI6eYLWMXt6TI6RLdMI62MQSsxyLltKpRNKMiQjZgqqHV5Y3cLEPE0QD2aI37k=,iv:nl1ikxniAJKSRaUiOMA0nlhNNpcN3yTujCS7u+5Bfz4=,tag:AOsN0DIJjDe6F5QixkuVmg==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.7.3
Зверніть увагу на aws_profile поле. Якщо під час шифрування передати аргумент —aws_profile, то вказаний профіль буде записаний у файл. Це означає, що
Розшифровуємо у Kubernetes з FluxCD
З FluxCD задача розшифрувати зашифровані SOPS-секрети стає доволі тривіальною. Бо їх Kustomization CRD має у собі нативну підтримку SOPS:
А оскільки ми тепер використовуємо AWS, нам навіть не треба вказувати secretRef. Достатньо лише надати Kustomization-контролеру права на використання KMS ключа.
В EKS-кластері це робилося б увімкненням OIDC-провайдеру та навішуванням анотації на ServiceAccount, як це описано в документації Flux. Але у мене кластер знаходиться не в AWS, тож я додам ключі до контролера через Secret. Юзер, який буде використовуватись контролером, повинен мати наступну політику:
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Decrypt", "kms:DescribeKey" ], "Effect": "Allow", "Resource": "arn:aws:kms:eu-central-1:123456789:key/b9a704a8-41b7-4efe-8804-756a8d9289f0" } ] }
Щоб передати ключі контролеру, я створив секрет ключами для доступу до AWS API:
21:56:46 ❯ kgsec -o yaml flux-aws-credentials apiVersion: v1 data: AWS_ACCESS_KEY_ID: SSB3b25kZXIgaWYgYW55Ym9keSB3aWxsIHJlYWQgaXQK AWS_DEFAULT_REGION: VWtyYWluZQo= AWS_SECRET_ACCESS_KEY: T2YgY291cnNlIHRoZXkgd2lsbCwgdGhleSBrbm93IHNvbWUgc3R1ZmYK kind: Secret metadata: creationTimestamp: "2024-08-06T18:52:08Z" name: flux-aws-credentials namespace: flux-system resourceVersion: "127984097" uid: 53efa465-dd06-4020-96a8-f4413f280a2e type: Opaque
Та додав до маніфесту kustomize-controller Deployment підтягування змін оточення з цього секрету:
Маніфест до kustmize-controller знаходиться у репозиторії, який ви використовували для бутстрапу FluxCD. Шлях у моєму випадку наступний: cluster/flux-system/gotk-components.yaml.
Нарешті настає момент для деплою у кластер. Мій головний Kustomization виглядає таким чином (непотрібні поля я приховав):
22:23:49 ❯ k get kustomizations.kustomize.toolkit.fluxcd.io dou-sops-article -o yaml apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: dou-sops-article namespace: flux-system spec: decryption: provider: sops force: false interval: 5m0s path: ./deploy/production/secrets prune: true sourceRef: kind: GitRepository name: dou-sops-article
GitRepository, який тут читається — репозиторій, де ми зберігаємо маніфести. Час створити та зашифрувати секрет:
22:26:41 ❯ k create secret generic the-most-precious-secret --from-literal ALPACKAS="are better than lamas" -o yaml --dry-run=client > deploy/production/secrets/dou-secret.yaml 22:26:49 ❯ sops -e -i ./deploy/production/secrets/dou-secret.yaml 22:26:57 ❯ cat deploy/production/secrets/dou-secret.yaml apiVersion: v1 data: ALPACKAS: ENC[AES256_GCM,data:HtWuO3JI51g09Sb+HK0ue0cbFzKVoowkTAFk9g==,iv:GJRy4mo7xOGJ4ySeVk0pPfKkSDADvCwxg9Kbp3i+y9Q=,tag:YrSUxDyR5sPCe065AKRxWw==,type:str] kind: Secret metadata: creationTimestamp: null name: the-most-precious-secret namespace: default sops: kms: - arn: arn:aws:kms:eu-central-1:341505313297:key/b9a704a8-41b7-4efe-8804-756a8d9289f0 created_at: "2024-08-06T19:29:33Z" enc: AQICAHgZMQAwa/aMuzPnXjzZIt4Zjx0ZP5me29gDFY9YerBw8AFqFlnFToFqbSP10mKet96lAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMh+zcZRbIEynz8EWlAgEQgDvglPhLvgM9h6UZnSusIZSZasqkUFFMMF2Eyk9TRxkOtU81pjvwRitVaLUpmfqyF4MhlDpvb0DCsZ8txA== aws_profile: "" gcp_kms: [] azure_kv: [] hc_vault: [] age: [] lastmodified: "2024-08-06T19:29:33Z" mac: ENC[AES256_GCM,data:kaAOz8ACNSWu9EI7PMQThKl24QzsVjuy4Ml74o6YPA0H++MzDI9M2WLtOiCK2w3ydX6R40SDQzrGaa8kVuS+tdBmJRv9g4Utfq1Py5rSn3bmc6B9u2/jTSBaxhm21Usefw9L0EuOQo8MQ8Lvw2rSpRiigF0VFU9xrtRQs5wwgGc=,iv:rPPPdrnVee42tiljg3q1BaDjBamGm/d1uKGcgzeHwIk=,tag:7/sgQxR/QmGcyd0x6u7GMg==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.7.3
Комітимо, пушимо зміни у репозиторій і біжимо оновлювати GitRepository
, щоб Flux почав створювати ресурси одразу ж. У результаті бачимо, що секрет був успішно розшифрований і створений:
22:33:16 ❯ k get secrets -n default the-most-precious-secret -o yaml apiVersion: v1 data: ALPACKAS: YXJlIGJldHRlciB0aGFuIGxhbWFz kind: Secret metadata: creationTimestamp: "2024-08-06T19:31:38Z" labels: kustomize.toolkit.fluxcd.io/name: dou-sops-article kustomize.toolkit.fluxcd.io/namespace: flux-system name: the-most-precious-secret namespace: default resourceVersion: "127996944" uid: 88f2efb4-ec8e-4236-83a1-be77ec7cb7c3 type: Opaque
Пару слів про HelmRelase
Щоб почати шифрувати values-значення для HelmRelease, варто звернутись до того самого алгоритму. Необхідно лише замість values використовувати valuesFrom і, відповідно, зберігати їх у секреті, який вже своєю чергою буде шифруватися.
Ідеї на майбутнє
Маючи процес шифрування та розшифрування секретів, можемо тепер подумати над тим, як нам впевнитись, що хтось випадково не запушив секрет, забувши його зашифрувати.
Отже, слід продумати декілька етапів:
- Локальна перевірка за допомогою git precommit хуків — існує для зручності розробників, щоб ті не чекали на виконання пайплайну. Для цього існує багато різноманітних інструментів: trufflehog, gitleaks, yelp/detect-secrets тощо.
- Якщо ваша платформа дозволяє, наприклад, як Gitlab — увімкнути захист від пушу секретів, варто це зробити. Тому що git-хуки за бажанням можна деактивувати.
- Наступною стадією має бути пайплайн, або ж вбудований у вашу платформу механізм сканування, який перевіряє репозиторій на наявність секретів. Знову ж таки, тому що хуки можна вимкнути.
- Останній етап — рев’ю. Наприклад для виявлення зміненого пайплайну.
Також варто зазначити, що усе це тримається на правильному керуванні доступами. Інакше налаштування репозиторію можуть бути змінені, а майстер/мейн може бути форс-пушнутий.
Висновок
Сподіваюсь, якщо серед читачів буде людина, у якої секрети зберігаються у відкритому вигляді в репозиторії, ця стаття покаже: їх шифрування — доволі тривіальна задача, що вирішується швидко. Але одного дня це може врятувати вас від доволі неприємного досвіду.
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів