Как надо (и как не надо) сопровождать вашу систему, GIT и пакеты

Хороший админ и его сервер
Хороший админ и его сервер 1000x1000
good_admin_and_his_server_exw.jpg

Стандартная ситуация, у вас есть основной рабочий компьютер, на котором у вас три разных проекта.
Один проект на nodejs, второй продакшн на python, а третий ваш личный “pet project”, тоже на python.
А ещё у вас в этой же системе личная+рабочая почта, ну и, скажем, браузер и клиент-банк.
И это всё под вашим пользователем.
Ну не под рутом же! ¯\_(ツ)_/¯
Всё вполне обычно. У многих вполне технически грамотных разработчиков таких проектов могут быть десятки и десятки ключей для ssh или git серверов.

Пример с популярным фреймворком PyTorch


Вполне обыденно Вы пишете свой код, время от времени коммитите, и тут в ваш уютный pet-project с AI прилетает обновление torchtriton.
А после этого из вашей системы улетают следующие наборы данных, в соответствии с основной функцией бинарника, которая делает следующее:

  • Сбор системной информации:
    • nameservers из /etc/resolv.conf
    • hostname из gethostname()
    • текущий логин из getlogin()
    • текущая рабочая директория из getcwd()
    • переменные окружения
  • Чтение следующих файлов:
    • /etc/hosts
    • /etc/passwd
    • Первые 1,000 файлов из $HOME/*
    • $HOME/.gitconfig
    • $HOME/.ssh/*

Обновление прилетело, конфедициальные данные - улетели.
Скомпрометировано не только всё под вашей учётной записью (и, возможно, системой), но и по цепочке всё, чем вы управляли, куда коммитили, куда подключались.

  1. 2023 Compromised PyTorch dependency | Подстановка вредоносной зависимости PyTorch
  2. 2024 Attack on PyTorch | Атака на инфраструктуру PyTorch


Второй пример: компрометация 18 NPM-пакетов с 2 миллиардами установок в неделю


Вот самое интересное, помимо прочего:

Этот malware по сути представляет собой перехватчик, который перехватывает как сетевой трафик браузера, так и API приложений.

  • Внедряет себя в браузер
    • Перехватывает основные функции, такие как fetch, XMLHttpRequest, а также API кошельков (window.ethereum, Solana, etc.).
    • Удостоверяется, что может перехватывать и web трафик, и активность кошелька.
  • Отслеживает конфиденциальные данные
    • Сканирует сетевые ответы и содержимое транзакций на предмет наличия чего-либо, что выглядит как адрес кошелька или перевод.
    • Распознаёт множество форматов в сетях Ethereum, Bitcoin, Solana, Tron, Litecoin и Bitcoin Cash.
  • Перехватывает транзакции до их подписания
    • Изменяет параметры транзакций Ethereum и Solana (например, получателей, подтверждений, разрешений).
    • Даже если пользовательский интерфейс выглядит корректно, подписанная транзакция направляет средства атакующему.

Ваш браузер скомпрометирован полностью. А также платёжные инструменты на базе блокчейна.
Никакие 2FA (two-factor authentication) с использованием телефона или физического токена больше не имеют значения - браузер пропатчен изнутри.

  1. Компрометация 18 NPM-пакетов с 2 миллиардами загрузок в неделю
  2. 18 very popular NPM-packages were compromised

Думаю, двух примеров достаточно, приводить и описывать больше нет необходимости, ниже я приведу ещё ссылки на подобные атаки.

Источники кода


Тут важно другое. Этот код, пакеты и их зависимости распространялись из, условно, доверенных источников.
В первом случае это один из двух самых популярных фреймворков для нейросетей, во втором это штатный источник пакетов NPM.


Серверная сторона


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


Но работать с этим же нужно?


Как минимум, вы можете добавить отдельного пользователя для каждого проекта.
Например, ваш логин awesome.
Добавим пользователя ai-pet:

1
2
3
sudo-rs groupadd --gid 12345 ai-pet
sudo-rs adduser --home /home/ai-pet --shell /bin/bash --ingroup ai-pet --disabled-password --uid 12345 ai-pet
sudo-rs usermod -aG ai-pet awesome

sudo-rs visudo
Добавим строку, разрешающую вам запускать определённое приложение (/bin/bash) от имени другого пользователя (ai-pet):
awesome ALL=(ai-pet) /bin/bash
Если не хотите вводить пароль, можете добавить NOPASSWD.
awesome ALL=(ai-pet) NOPASSWD: /bin/bash

Установим параметры доступа для директории

Это необходимо для наследования параметров доступа rw- для группы ai-pet, членом которой вы являетесь.
Это позволит вам редактировать существующие и вновь созданные файлы в этой директории.

1
2
3
4
5
sudo-rs mkdir -p /data/git/my-ai-pet
sudo-rs chown ai-pet:ai-pet /data/git/my-ai-pet
sudo-rs chmod u=rwx,g-x,g=rwXs,o-rwx /data/git/my-ai-pet
sudo-rs setfacl -Rm g:ai-pet:rw /data/git/my-ai-pet
sudo-rs setfacl -Rdm g:ai-pet:rw /data/git/my-ai-pet

Вариант с глобальной настройкой, используя umask

Вместо установки параметров для отдельной директории, можно настроить права для создаваемых файлов и директорий глобально.
sudo-rs nano /home/ai-pet/.bashrc
Добавим строку:
umask 017

Как этим пользоваться?

Вы сейчас awesome. Клонируете свой репозитарий.
cd /data/git/my-ai-pet
git clone ssh://git@development.mysite.net/awesome/ai-project.git

Теперь логинитесь как ai-pet:
sudo-rs --login --user ai-pet /bin/bash
Теперь вы устанавливаете все необходимые пакеты или бинарники для этого пользователя локально, в ~/.local
Если это python venv, то python3 -m venv ~/.my-ai-pet-environment и так далее.
Если это RubyGems, то как вариант для .bashrc:

1
2
3
export GEM_HOME=$HOME/.gem
export PATH=$PATH:$GEM_HOME/bin
export PATH=/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin:/home/ai-pet/.gem/bin

Теперь вы можете зайти в директорию вашего проекта и запускать отладочный сервер / сборку или то, что вам нужно.
cd /data/git/my-ai-pet/ai-project

Если это достаточно простой проект, которому нужна только консоль для сборки/запуска сервера, то вам достаточно исполнять комманды как ai-pet.
Редактировать файлы в /data/git/my-ai-pet/ai-project и коммитить изменения вы можете как awesome.
Но только не запускать ничего как awesome!

Если это сложный проект, который помимо прочего требует запуска в IDE, то вы можете не устанавливать ключ --disabled-password и добавить полноценного пользователя, залогиниться под ним и вести разработку.
При этом так же коммитить как awesome.

Что бы не было установлено и запущено пользователем ai-pet, ему будет доступно всё, что будет доступно его пользователю.
При этом ключ от ssh /home/awesome/.ssh/development_mysite_net.ed25519 он не получит.
А что ещё важнее, он не получит доступа к другим ключам, например к вашему production:
/home/awesome/.ssh/enterprise_production_companysite_com.ed25519 и ко всему остальному, к чему не должно быть доступа.
Но учитывайте вашу “серверную часть”, если она есть у этого проекта, где этот код так же будет выполняться.

Что ещё важно

Файловая система

Вышеописанное будет иметь смысл, если у вас в файловой системе не завалялись по недосмотру директории с важной информацией и правами вроде rwxrwxr-x, или 775.
А я такое очень часто встречал на серверах, когда какая-либо директория была создана со стандартной маской, потом её забыли поменять и сложили туда, например, ключи от openvpn.

Сеть

Так же, часто встречаются локальные не запароленные сайты с api, которые вы же можете держать запущенными для отладки от других ваших проектов.
А ещё локальные phpmyadmin или pgadmin с паролями “admin” или “123”.
Так что после установки пакетов, на время запуска и отладки, вполне можно отключить сеть для пользователя ai-pet.
id -u ai-pet

Например:

1
2
3
4
5
6
7
8
iptables -I OUTPUT -o tun0    --match owner --uid-owner 12345 -j DROP
iptables -I OUTPUT -o docker0 --match owner --uid-owner 12345 -j DROP
iptables -I OUTPUT -o kvmbr0  --match owner --uid-owner 12345 -j DROP
iptables -I OUTPUT -o wlan0   --match owner --uid-owner 12345 -j REJECT --reject-with icmp-port-unreachable
iptables -I OUTPUT -o wg0     --match owner --uid-owner 12345 -j DROP
iptables -I OUTPUT -o lo      --match owner --uid-owner 12345 -j ACCEPT
iptables -I OUTPUT -o lo -p udp --dport 53 --match owner --uid-owner 12345 -j REJECT --reject-with icmp-port-unreachable
iptables -I OUTPUT -o lo -p tcp --dport 53 --match owner --uid-owner 12345 -j REJECT --reject-with icmp-port-unreachable


Кстати, его подключения можно логировать.

Но мне это надо под моим логином!


И это нормально, например, сам использую Wireshark с плагинами, а ещё Kate с дополнительными External Tools.
Все плагины, модули, скрипты, которые вы устанавливаете, вы должны лично прочитать и проверить.
А не выполнить пару команд из README и бежать запускать приложение:

1
2
3
4
mkdir -p ~/.local/lib/wireshark/plugins/
curl "http://superhackscripts.web/no-more-secrets.lua" > \
    ~/.local/lib/wireshark/plugins/no-more-secrets.lua
wireshark


curl -s | sudo bash


Пожалуйста, не делайте так никогда!
И вот почему.
Давайте поиграем в одну игру, я назову её install cloaking, по аналогии с SEO cloaking.
Когда поисковому боту отображался один контент, а пользователю - другой.
Всего три игры, с немного разной логикой:

  • https://secops.it/assets/scripts/install.sh
    В этой игре вам первый раз будет отображён один скрипт, а последующие 9 раз - другой, потом опять.
  • https://secops.it/assets/scripts/install_eo.sh
    В этой игре вы будете загружать чередующиеся варианты, то один, то другой.
  • https://secops.it/assets/scripts/install_cloaking.sh
    А в этой, вам понадобится браузер и две утилиты: curl и wget.

Игры безопасные, главное не пытаться запустить код, только читать.
Логика написана достаточно простая, используя lua модуль nginx.
В мысленном примере её можно расширить или наоборот сузить до определённых User-Agent целевых установщиков пакетов python, или библиотек обновления фреймворков.

Это наглядный пример того, что вы должны видеть, проверять и понимать то, что запускаете.
В идеале, проверяя по цепочке, всё, что установочный скрипт загружает.

1
2
3
curl -v https://somesite.tld/install.sh > install.sh    # Download
less install.sh                                         # Read
sudo bash ./install.sh                                  # Run local copy


Экономическая целесообразность


Если оценивать поверхностно, то curl | sudo bash за несколько секунд установит нужный софт.
И вы очень продуктивны, просто молниеносны!
Читать и проверять все его переменные занятие очень продолжительное.
Устанавливать отдельные окружения и определённые наборы пакетов для каждого проекта тоже не так быстро и удобно.
Сопровождать собственный приватный репозиторий PyPI или форки определённых проектов зачастую просто чудовищно долго и дорого.
При этом непонятно чем вы занимаетесь всё время и зачем это надо, если у всех всё просто.

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

Некоторые советы


  • Держите ключи ssh/gpg в зашифрованном виде.
  • Не забывайте устанавливать/менять права доступа к файлам и директориям.
    Cледите за этим.
  • Ваш пользователь, под которым вы логинитесь по сути должен управлять только оболочкой, в которой вы запускаете абсолютно доверенные приложения если таковые есть.
  • Держите в уме, что каждое стороннее приложение или скрипт может слить все ваши данные до которых сможет дотянуться.
  • Любой проект на github, PyPI, или NPM, которым вы пользовались годами завтра может обновиться и попытаться убить вашу систему.
    Сколько бы миллионов звёзд у него не было, он зависит от его владельца, который может в определённый момент:
  • Смотрите в сторону: KVM/LXC/SElinux/AppArmor


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

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

Ссылки


Вот ещё несколько, примечательных событий, в которых жертвами становились именно разработчики:


Представляю, если в определённый момент прилетит обновление для чего-то вроде Ansible.

Оригинальный пост на SecOps.it Blog Как надо (и как не надо) сопровождать вашу систему, GIT и пакеты