В скриптах часто есть временные файлы, lock-файлы, промежуточные каталоги и другие следы работы. Проблема начинается, когда скрипт падает посередине. Например: временный файл остался в
/tmp; lock-файл не удалился; mount point не размонтировался; сервис был остановлен, но обратно не запущен; промежуточные данные остались в неконсистентном состоянии. Для таких случаев в bash есть trap. Он позволяет выполнить команду или функцию при наступлении события: выход из скрипта, ошибка, Ctrl+C, kill и так далее.
#!/usr/bin/env bash
set -euo pipefail
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
echo "working in $TMP_DIR"
# основная логика скрипта
mktemp -d создает временный каталогcleanup() описывает, что нужно убратьtrap cleanup EXIT гарантирует запуск cleanup при выходе из скрипта. Даже если скрипт завершится с ошибкой, временный каталог будет удален.Можно ловить не только обычный выход, но и прерывание с клавиатуры:
trap cleanup EXIT INT TERM
EXIT - любой выход из скриптаINT - прерывание через Ctrl+CTERM - сигнал завершения процесса
#!/usr/bin/env bash
set -euo pipefail
LOCK_FILE="/tmp/myjob.lock"
cleanup() {
rm -f "$LOCK_FILE"
}
trap cleanup EXIT
if [[ -e "$LOCK_FILE" ]]; then
echo "script already running"
exit 1
fi
touch "$LOCK_FILE"
# основная логика
sleep 30
Но тут важный момент: для защиты от повторного запуска лучше использовать flock, а не просто проверку файла. Зато trap отлично подходит, чтобы гарантированно убрать временные следы после работы.
error_handler() {
echo "error on line $LINENO"
}
trap error_handler ERR
Так можно быстрее понять, где именно скрипт упал.
cleanup() {
rm -rf "$TMP_DIR"
}
on_error() {
echo "script failed on line $LINENO"
}
trap cleanup EXIT
trap on_error ERR
trap не делает скрипт надежным сам по себе. Он только помогает корректно завершиться и прибрать за собой.
#bash #scripting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥3
find - одна из самых полезных и часто используемых утилит в linux. Но именно с ней часто происходят самые неприятные аварии. Хотели удалить старые логи - удалили не тот каталог. Хотели поправить права - сломали половину /var/www. Хотели найти временные файлы - случайно зацепили mount point. И проблема не в find, а в том, что он очень послушный. Что написали - то и сделал.
Поэтому главное правило: сначала показать, потом делать.
find /var/log -name "*.gz" -delete
find /var/log -name "*.gz" -print
Посмотрели список, убедились, что там нет ничего лишнего и только потом добавили действие:
find /var/log -name "*.gz" -delete
Для удаления по возрасту тоже сначала проверяем:
find /backup -type f -mtime +30 -print
И только после проверки:
find /backup -type f -mtime +30 -delete
-type. Если удаляем файлы, явно пишем:
find /backup -type f -name "*.tar.gz" -mtime +14 -delete
Если меняем права каталогов:
find /var/www -type d -exec chmod 755 {} \;
Если меняем права файлов:
find /var/www -type f -exec chmod 644 {} \;
Это защищает от классической ошибки, когда одинаковые права случайно применяют и к файлам, и к директориям.
chmod часто удобнее использовать + вместо \;:
find /var/www -type f -exec chmod 644 {} +
Так команда запускается не для каждого файла отдельно, а пачками. На больших каталогах это заметно быстрее.
Если в путях могут быть пробелы, переводы строк или странные символы, используйте безопасную связку:
find /data -type f -name "*.log" -print0 | xargs -0 rm -f
Но если можно обойтись встроенным
-delete, часто лучше использовать его:
find /data -type f -name "*.log" -mtime +7 -delete
find /var/log -maxdepth 1 -type f -name "*.gz" -print
Так find не уйдет рекурсивно во все вложенные каталоги. И наоборот, если нужно пропустить верхний уровень:
find /data -mindepth 1 -type d -empty -delete
Это помогает не удалить сам корневой каталог поиска, если он вдруг окажется пустым.
#bash #find
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
Когда сервис падает, первое желание - открыть
/var/log и начать искать глазами. Но на современных linux-системах часто быстрее идти сразу в journalctl. journalctl показывает логи из systemd-journald: сервисы, kernel-сообщения, boot-логи, ошибки юнитов и многое другое.
journalctl -u nginx
Так смотрим логи конкретного сервиса.
Если нужен только последний запуск:
journalctl -u nginx -b
-b ограничивает вывод текущей загрузкой системы. Это удобно, чтобы не утонуть в старых событиях.Посмотреть последние строки:
journalctl -u nginx -n 100
Следить за логами в реальном времени:
journalctl -u nginx -f
Это аналог tail -f, только для systemd-журнала.
systemctl status nginx
journalctl -u nginx -xe
status дает краткую картину, а journalctl уже показывает детали: ошибки конфига, проблемы с правами, отсутствующие файлы, failed dependency и так далее.
Очень удобно фильтровать по времени:
journalctl -u nginx --since "10 minutes ago"
или так:
journalctl -u nginx --since "2026-06-24 10:00" --until "2026-06-24 11:00"
Для разбора аварий это особенно полезно: сужаете окно до момента инцидента и смотрите только нужный кусок.
journalctl -p err -b
Где
-p err показывает сообщения уровня error и выше за текущую загрузку.
journalctl -k -b
Тут можно увидеть проблемы с дисками, драйверами, OOM, сетевыми интерфейсами и железом. Например, если подозрение на OOM killer:
journalctl -k -b | grep -i "killed process"
Еще полезно смотреть логи предыдущей загрузки, если сервер уже перезагрузился после аварии:
journalctl -b -1
А список доступных загрузок:
journalctl --list-boots
Так можно понять, что происходило перед reboot, panic или зависанием.
journalctl -u nginx --no-pager
Для вывода в JSON, если нужно парсить:
journalctl -u nginx -o json
#linux #journalctl
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍1
Pagefile в Windows часто вызывает споры. Особенно на серверах, где много RAM. Логика обычно такая: "У нас 64/128/256 ГБ памяти, зачем нам файл подкачки? Отключим и освободим место на диске." Звучит разумно, но в проде это может закончиться странными ошибками, падениями приложений и отсутствием нормального crash dump после аварии.
Pagefile - это не просто "медленная RAM на диске". В Windows он нужен для управления виртуальной памятью и commit limit. Проще говоря, система и приложения могут резервировать память, даже если прямо сейчас физически вся RAM не занята. А commit limit считается примерно как: RAM + pagefile.
Если pagefile отключить, общий лимит commit уменьшается. И приложение может получить ошибку выделения памяти даже тогда, когда в Task Manager "свободная память вроде есть".
• SQL Server и другие тяжелые сервисы;
• терминальные серверы;
• серверы с большим количеством процессов;
• системы, где нужны crash dump после BSOD;
• приложения, которые активно резервируют память.
Отключать pagefile полностью обычно плохая идея. Да, иногда это работает годами. А потом в момент нагрузки сервер внезапно начинает вести себя странно: сервисы падают, процессы не стартуют, в логах появляются ошибки memory allocation, а нормального дампа для расследования нет.
Самый безопасный вариант для большинства серверов: оставить System managed size. Windows сама подберет размер pagefile под нагрузку и конфигурацию системы. Если диск маленький или есть строгие требования по месту, можно задать фиксированный минимальный размер, но полностью отключать не стоит.
Например:
• оставить небольшой pagefile на системном диске для crash dump;
• вынести основной pagefile на отдельный быстрый диск, если это реально нужно;
• контролировать не только RAM, но и commit usage.
Memory\Committed Bytes
Memory\Commit Limit
Paging File\% Usage
Если Committed Bytes регулярно приближается к Commit Limit, проблема не в размере pagefile как таковом. Система реально упирается в доступный commit, и нужно разбираться с нагрузкой или памятью.
Отдельный нюанс - crash dump. Для полноценного дампа памяти Windows может требовать pagefile на системном диске достаточного размера. Если pagefile отключен или слишком мал, после падения системы вы можете не получить нужный дамп.
#windowsserver #pagefile
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Настройка времени на серверах обычно вспоминается только тогда, когда мониторинг начинает ругаться. В debian сейчас часто по умолчанию работает
systemd-timesyncd. Обычно там уже прописаны стандартные NTP-серверы debian вроде:
0.debian.pool.ntp.org
или серверы времени от хостера, если система развернута из его шаблона.
В большинстве случаев все работает само, и трогать ничего не нужно. Но если синхронизация начинает мигать в мониторинге, приходится вспоминать, где это настраивается. Типичная ошибка в логах может выглядеть так:
systemd-timesyncd[308]: Timed out waiting for reply from 195.90.182.235:123 (0.debian.pool.ntp.org)
То есть timesyncd отправил запрос на NTP-сервер, но не дождался ответа. Первое, что можно попробовать - это заменить набор серверов времени. Основной конфиг находится здесь:
/etc/systemd/timesyncd.conf
Но лучше не править его напрямую, а сделать drop-in конфигурацию:
mkdir -p /etc/systemd/timesyncd.conf.d
nano /etc/systemd/timesyncd.conf.d/custom.conf
Добавляем свои NTP-серверы:
[Time]
NTP=0.ru.pool.ntp.org 1.ru.pool.ntp.org 2.ru.pool.ntp.org 3.ru.pool.ntp.org
FallbackNTP=ntp0.vniiftri.ru
После этого перечитываем конфигурацию и перезапускаем службу:
systemctl daemon-reload
systemctl restart systemd-timesyncd
Проверить общие настройки времени:
timedatectl
А конкретный статус синхронизации:
timedatectl timesync-status
В выводе будет видно, какой сервер сейчас используется, например:
Server: 51.250.35.68 (0.ru.pool.ntp.org)
Логи службы удобно смотреть так:
journalctl -u systemd-timesyncd
Если время вообще не синхронизируется с внешними серверами, не спешите часами копаться в конфиге. Очень часто NTP-трафик режется на стороне провайдера. Причина простая: UDP/123, как и DNS, может использоваться в DDoS amplification-атаках. Поэтому некоторые провайдеры блокируют внешний NTP и предлагают использовать свои внутренние серверы времени.
Так что если запросы наружу стабильно не проходят, иногда самый быстрый путь - сразу спросить у провайдера: какой NTP-сервер нужно использовать в вашей сети?
Есть и обходные варианты. Например, можно использовать htpdate, который получает время через HTTP:
apt install htpdate
Но это уже скорее запасной вариант, а не замена нормальной NTP-синхронизации.
#linux #timesyncd
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Когда инфраструктурой управляет несколько администраторов или команд, иногда всплывает неприятная ситуация: с сервера пропал агент, приложение или важный компонент. Например, был установлен Zabbix Agent, антивирус, backup-клиент или monitoring exporter, а потом внезапно его нет.
В Windows такие вещи часто можно расследовать через Event Viewer. Если приложение было установлено через MSI-пакет, Windows пишет события от провайдера MsiInstaller в журнал Application.
11707 - приложение успешно установлено
11724 - приложение успешно удалено
То есть можно посмотреть, кто и когда устанавливал или удалял нужный MSI-пакет.
$appname='*Zabbix*'
Get-WinEvent -FilterHashtable @{
LogName="Application"
ID=11707,11724
ProviderName='MsiInstaller'
} |
Where-Object {
$_.Message -like $appname
} |
Select-Object TimeCreated,
@{
Name='Username'
Expression={
(New-Object System.Security.Principal.SecurityIdentifier($_.UserId)).
Translate([System.Security.Principal.NTAccount]).Value
}
},
Message
В результате получим:
• время события
• пользователя
• текст события MSI Installer
• информацию об установке или удалении приложения
Так можно быстро понять, когда именно агент был удален и под какой учетной записью это произошло. Это полезно не только для Zabbix. Можно искать любое MSI-приложение:
$appname='*Chrome*'
$appname='*Backup*'
$appname='*Endpoint*'
Get-WinEvent -FilterHashtable @{
LogName="Application"
ID=11707,11724
ProviderName='MsiInstaller'
} |
Select-Object TimeCreated, Id, ProviderName, UserId, Message
Этот способ работает именно для приложений, которые ставились или удалялись через MSI. Если программу удалили вручную, через portable-директорию, скриптом с удалением файлов или сторонним инсталятором без нормальной записи в MSI-события, этих Event ID может не быть.
Еще один нюанс - глубина хранения логов. Если журнал Application уже перезаписан, старые события вы не найдете. Поэтому для нормального расследования лучше заранее настроить: увеличенный размер журналов, Windows Event Forwarding, централизованный сбор логов и аудит административных действий
#windowsserver #eventviewer
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2
nc знают почти все. Но если нужно не просто открыть порт или проверить соединение, а быстро связать между собой TCP, UDP, Unix socket, файл, stdin/stdout или сделать простой туннель тут очень выручает socat. Название расшифровывается примерно как SOcket CAT. По сути, это утилита, которая умеет соединять два конца передачи данных. Этими концами могут быть:TCP-порт;
UDP-порт;
Unix socket;
файл;
pipe;
stdin/stdout;
псевдотерминал;
TLS-соединение.
apt install socat
socat TCP-LISTEN:8080,fork STDOUT
Теперь все, что прилетает на порт 8080, будет выводиться в терминал.
Можно сделать простой echo-сервер:
socat TCP-LISTEN:8080,fork EXEC:/bin/cat
Подключаемся:
nc 127.0.0.1 8080
И все, что отправляем, возвращается обратно.
socat TCP-LISTEN:8080,fork TCP:10.10.20.15:80
Теперь локальный порт 8080 будет проксировать трафик на 10.10.20.15:80. Это удобно, когда нужно быстро проверить сервис, временно открыть доступ или обойти неудобную сетевую схему без настройки полноценного reverse proxy.
socat TCP-LISTEN:9000,fork UNIX-CONNECT:/run/app/app.sock
И наоборот - TCP в Unix socket:
socat UNIX-LISTEN:/tmp/test.sock,fork TCP:127.0.0.1:8080
socat - UDP-LISTEN:5353,fork
И отправка UDP-пакета:
echo "test" | socat - UDP:127.0.0.1:5353
socat - это не замена нормальному reverse proxy, firewall, VPN или service mesh. Но для быстрой диагностики и временного связывания разных типов соединений он невероятно удобен. Когда нужно срочно понять, что происходит с портом, сокетом или сетевым потоком, socat часто оказывается тем самым инструментом, который решает задачу одной командой.
Главное - не забыть потом убрать временный listener.
#linux #socat
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
Timezone кажется мелочью ровно до первого инцидента. Сервис упал в 03:10. Мониторинг сработал в 00:10. В логах приложения ошибка в 05:10. А cron, который точно должен был запуститься ночью, вообще отработал не тогда. И начинается расследование не аварии, а вопроса: "чье это вообще время?" Проблема в том, что в инфраструктуре легко получить несколько разных временных реальностей:
• сервер живет в UTC;
• приложение пишет логи в локальном времени;
• контейнер использует другой timezone;
• база хранит timestamp без offset;
• мониторинг показывает время браузера;
• cron работает по системному времени хоста;
• разработчик смотрит все из своей локальной зоны.
•
В итоге события вроде бы относятся к одному инциденту, но на таймлайне разъезжаются на 2–3 часа.
timedatectl
Посмотреть текущую дату с зоной:
date
Проверить UTC:
date -u
Для systemd-журнала удобно явно указывать временное окно:
journalctl --since "2026-05-29 03:00" --until "2026-05-29 03:30"
Но важно понимать: это окно интерпретируется в локальном времени системы, с которой вы работаете.
С cron тоже есть нюанс. Если на сервере стоит Europe/Moscow, то запись:
0 3 * * * /opt/scripts/backup.sh
запустится в 03:00 по времени этого сервера.
Если такой же cron стоит на другом сервере в UTC, задача фактически поедет на несколько часов.
• хранить серверное время в UTC;
• писать логи с timezone или offset;
• использовать ISO 8601 формат;
• не хранить timestamp без понимания зоны;
• явно документировать, в каком времени работает cron;
• синхронизировать время через NTP;
• при расследовании сразу строить единый таймлайн.
2026-05-29T03:10:42Z
# или так:
2026-05-29T06:10:42+03:00
2026-05-29 03:10:42
Потому что без timezone непонятно, это UTC, Москва, Хельсинки, время контейнера или фантазия приложения.
#linux #timezone
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
Иногда нужно быстро объединить несколько разных дисков в один общий каталог, но без RAID, LVM и сложной перестройки хранилища. Например, есть два диска, смонтированные так:
/mnt/sda
/mnt/sdb
А хочется получить одну общую точку:
/mnt/storage. Чтобы приложения видели это как единое хранилище, а файлы физически раскладывались по исходным дискам. Для такой задачи хорошо подходит mergerfs. Это не RAID и не замена бэкапам. Это именно логическое объединение каталогов в одну файловую систему поверх уже существующих маунт поинтов.• видеосервер с архивом камер. Можно объединить несколько разнородных дисков и указать видеосерверу один общий путь.
• сервер со статикой или кэшем. Когда отказоустойчивость не критична, но нужно удобно расширять объем.
• backup-хранилище. Для некоторых сценариев бэкапов логическое объединение дисков вполне допустимо.
• сервер с дисками разного размера. Например, есть 2 ТБ + 3 ТБ, и хочется получить один общий каталог без плясок с RAID-массивами.
/dev/sda и /dev/sdb. Создаем разделы, файловые системы и монтируем их:
cfdisk /dev/sda
cfdisk /dev/sdb
mkfs.ext4 /dev/sda1
mkfs.ext4 /dev/sdb1
mkdir -p /mnt/sda1 /mnt/sdb1
mount /dev/sda1 /mnt/sda1
mount /dev/sdb1 /mnt/sdb1
Проверяем:
df -h
Теперь ставим mergerfs:
apt install mergerfs
Создаем общую точку монтирования:
mkdir -p /mnt/storage
И объединяем два диска:
mergerfs -o defaults,allow_other,category.create=mfs,moveonenospc=true,minfreespace=1G \
/mnt/sda1:/mnt/sdb1 /mnt/storage
После этого
/mnt/storage будет показывать суммарный объем двух дисков. Проверяем:
df -h | grep storage
allow_other - Позволяет видеть файловую систему не только root, но и другим пользователям.category.create=mfs - Новые файлы создаются там, где больше свободного места. mfs - most free space.moveonenospc=true - Если при записи на выбранном диске закончилось место, mergerfs попробует перенести файл на другой диск.minfreespace=1G - Если на диске осталось меньше 1 ГБ, новые файлы туда больше не пишутся.
dd if=/dev/zero of=/mnt/storage/tempfile1 bs=1M count=1000
dd if=/dev/zero of=/mnt/storage/tempfile2 bs=1M count=1000
ls /mnt/storage
ls /mnt/sda1
ls /mnt/sdb1
Файлы будут видны в общей точке
/mnt/storage, но физически лежать на одном из исходных дисков. Это важное отличие от RAID: каждый файл хранится целиком на конкретном диске, а не размазывается блоками по всем устройствам.• можно объединять диски разного размера
• не нужно пересобирать массив
• легко добавить новый диск
• файлы остаются читаемыми напрямую с исходных mount point’ов
• удобно для статичных данных, кэша, архивов и бэкапов
То есть mergerfs дает удобство единого пространства, но не дает отказоустойчивость. Для постоянного подключения mergerfs можно добавить в
/etc/fstab или оформить через systemd mount. Это уже зависит от того, как вы привыкли управлять маунтами.#mergerfs #storage
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1
Иногда нужно быстро передать ссылку, Wi-Fi пароль, токен для теста или короткий текст с сервера на телефон. Можно не открывать браузер и не генерировать картинку. QR-код можно вывести прямо в терминале. В linux для этого удобно использовать
qrencode.
apt install qrencode
qrencode -t ANSIUTF8 "https://networkadmin.ru"
В терминале появится QR-код, который можно сразу отсканировать телефоном.
Если нужен PNG-файл:
qrencode -o qrcode.png "https://networkadmin.ru"
Можно передавать данные из pipe:
echo "ssh user@10.10.10.5" | qrencode -t ANSIUTF8
sudo apt install qrencode
qrencode -t ANSIUTF8 "https://networkadmin.ru"
Если WSL нет, можно использовать PowerShell-модуль или сторонние утилиты, но через WSL обычно быстрее и проще.
#terminal #qrcode
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
В Active Directory можно настроить длину пароля, срок действия, историю и требования к сложности. Но есть неприятный нюанс: стандартная политика сложности не понимает, что такое плохой, но формально сложный пароль. Например:
Qwerty123
P@ssw0rd
Company2026
Admin@123
Такие пароли могут проходить базовую проверку сложности: есть заглавные буквы, строчные буквы, цифры, иногда спецсимволы.
Формально все хорошо.
Фактически - это словарные и шаблонные пароли, которые легко угадываются или быстро подбираются. Проблема в том, что стандартная парольная политика AD проверяет структуру пароля, но плохо понимает его смысл. То есть пароль вида:
P@ssw0rd2026! может выглядеть сложным для политики, но быть очень слабым с точки зрения реальной безопасности.• входит в список запрещенных слов
• похож на название компании
• содержит имя пользователя
• совпадает с типовыми шаблонами
• найден в базе скомпрометированных паролей
• слишком предсказуемо меняется по годам или сезонам
• PassFiltEx
• Lithnet Password Protection for Active Directory
Оба варианта позволяют расширить стандартную проверку паролей и отсеивать то, что обычная политика AD пропускает. Например, можно запретить пароли, связанные с: названием компании, доменом, брендами, городами, типовыми словами вроде Password, Qwerty, Admin, годовыми шаблонами вроде 2025, 2026, известными утечками паролей. Это особенно полезно в инфраструктурах, где пользователи любят обновлять пароль по принципу:
Winter2025! Spring2026! Company2026!С точки зрения пользователя - пароль новый. С точки зрения атакующего - почти тот же самый шаблон.
#windows #activedirectory
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Как смотреть потоки процесса через ps, /proc и strace
Обычно при работе с ps мы смотрим процесс по PID или грепаем по имени:
Но часто удобнее сразу указать имя процесса через -C. Например, посмотрим потоки prometheus:
▪️ Посчитать количество потоков
Важно: количество потоков и количество процессов - не одно и то же. Например:
Результаты могут сильно отличаться.
▪️ Посмотреть нагрузку по потокам. Если приложение тормозит, полезно вывести CPU/MEM с разбивкой по потокам. Например, для процесса с PID 508:
Так можно увидеть, какой именно поток ест CPU.
▪️ Найти подробности потока через /proc. Если знаем PID процесса и SPID потока:
В начале строки можно увидеть имя потока, например:
У Fail2ban это может подсказать, какой jail сейчас нагружает систему. Больше информации:
▪️ Подключить strace к конкретному потоку. strace умеет подключаться не только к PID процесса, но и к SPID потока:
На нагруженном сервере вывод быстро завалит консоль, поэтому лучше писать в файл:
Можно ограничить типы системных вызовов. Открытие файлов и чтение:
Запись:
Сеть:
▪️ Через htop. В htop тоже можно смотреть трейсы. Выберите нужный процесс или поток и нажмите:
#linux #htop
🧑💻 NetworkAdmin
Обычно при работе с ps мы смотрим процесс по PID или грепаем по имени:
ps -p 524 -o %mem,%cpu,cmd
ps ax | grep prometheus
Но часто удобнее сразу указать имя процесса через -C. Например, посмотрим потоки prometheus:
ps -T -C prometheus
PID SPID TTY TIME CMD
525 525 ? 00:55:34 prometheus
525 803 ? 00:03:10 prometheus
525 808 ? 00:09:22 prometheus
525 1054 ? 00:08:44 prometheus
PID - ID основного процессаSPID - ID конкретного потока
ps -T -C zabbix_server | wc -l
Важно: количество потоков и количество процессов - не одно и то же. Например:
ps -T -C zabbix_server | wc -l
ps ax | grep zabbix_server | wc -l
Результаты могут сильно отличаться.
ps -L -o spid,%mem,%cpu,cmd 508
SPID %MEM %CPU CMD
1070 0.6 0.0 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
1071 0.6 0.1 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
1077 0.6 0.3 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
Так можно увидеть, какой именно поток ест CPU.
cat /proc/508/task/1077/stat
В начале строки можно увидеть имя потока, например:
1077 (f2b/f.wp-login)
У Fail2ban это может подсказать, какой jail сейчас нагружает систему. Больше информации:
cat /proc/508/task/1077/status
strace -p 1077
На нагруженном сервере вывод быстро завалит консоль, поэтому лучше писать в файл:
strace -p 1077 -o ~/strace.out
Можно ограничить типы системных вызовов. Открытие файлов и чтение:
strace -p 1077 -e trace=openat,read
Запись:
strace -p 1077 -e trace=write
Сеть:
strace -p 1077 -e trace=connect,recvfrom,sendto
s. Если strace установлен, htop покажет системные вызовы в реальном времени.#linux #htop
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
systemd.path: запуск действия при изменении файла
У systemd есть удобный механизм, о котором часто забывают - systemd.path. Он позволяет следить за событиями в файловой системе и запускать нужный .service, когда файл или каталог изменился.
Например:
изменился конфиг - перезапустить сервис
появился файл - запустить обработчик
обновился сертификат - скопировать его и сделать reload
▪️ Пример: реагируем на изменение конфига. Допустим, нужно выполнить действие при изменении файла:
Создаем unit:
Этот .path будет следить за изменением файла.
▪️ Сервис, который запустится по событию. Теперь создаем:
Для примера сервис просто пишет дату события в файл:
На практике в ExecStart можно указать любой скрипт:
• перезапуск сервиса;
• reload nginx;
• копирование файла;
• отправка алерта;
• валидация конфига.
▪️ Включаем и запускаем
Проверяем статус:
Теперь изменяем файл:
И смотрим результат:
Должна появиться новая строка с текущей датой.
▪️ Почему это удобно. Главный плюс- все работает через systemd. Значит, события и ошибки можно смотреть привычно:
Не нужно писать отдельный бесконечный watcher на bash или городить cron.
▪️ Полезные директивы
Срабатывает при изменении файла.
Срабатывает после закрытия файла, в который была запись.
Срабатывает, когда файл или каталог появился.
То же самое, но с маской.
Срабатывает, когда в пустом каталоге появился файл.
#linux #systemd
🧑💻 NetworkAdmin
У systemd есть удобный механизм, о котором часто забывают - systemd.path. Он позволяет следить за событиями в файловой системе и запускать нужный .service, когда файл или каталог изменился.
Например:
изменился конфиг - перезапустить сервис
появился файл - запустить обработчик
обновился сертификат - скопировать его и сделать reload
/etc/daemon/daemon.conf
Создаем unit:
/etc/systemd/system/daemon.path
[Unit]
Description=Watch /etc/daemon/daemon.conf for changes
[Path]
PathModified=/etc/daemon/daemon.conf
[Install]
WantedBy=multi-user.target
Этот .path будет следить за изменением файла.
/etc/systemd/system/daemon.service
[Unit]
Description=Run action after daemon.conf change
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'echo "daemon config changed at $(date)" >> /etc/daemon/restart.date'
Для примера сервис просто пишет дату события в файл:
/etc/daemon/restart.date
На практике в ExecStart можно указать любой скрипт:
• перезапуск сервиса;
• reload nginx;
• копирование файла;
• отправка алерта;
• валидация конфига.
systemctl daemon-reload
systemctl enable --now daemon.path
Проверяем статус:
systemctl status daemon.path
Теперь изменяем файл:
echo "# test" >> /etc/daemon/daemon.conf
И смотрим результат:
cat /etc/daemon/restart.date
Должна появиться новая строка с текущей датой.
journalctl -u daemon.path
journalctl -u daemon.service
Не нужно писать отдельный бесконечный watcher на bash или городить cron.
[Path]
PathModified=/path/file
Срабатывает при изменении файла.
PathChanged=/path/file
Срабатывает после закрытия файла, в который была запись.
PathExists=/path/file
Срабатывает, когда файл или каталог появился.
PathExistsGlob=/path/*.conf
То же самое, но с маской.
DirectoryNotEmpty=/path/dir
Срабатывает, когда в пустом каталоге появился файл.
#linux #systemd
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Обычная маршрутизация отвечает на вопрос: куда отправить пакет по адресу назначения? Но иногда этого мало. Например, на linux-сервере есть два провайдера, два интерфейса или несколько VPN. И нужно, чтобы часть трафика шла через один шлюз, а часть - через другой.
Вот здесь появляется Policy Based Routing. PBR позволяет маршрутизировать трафик не только по destination IP, но и по дополнительным условиям:
• source IP
• интерфейс
• fwmark
• отдельная таблица маршрутизации
• правила из ip rule
eth0 -> 192.168.1.10/24 -> gateway 192.168.1.1
eth1 -> 10.10.10.10/24 -> gateway 10.10.10.1
Обычный default route может быть только один основной. А нам нужно, чтобы ответы с адреса 10.10.10.10 уходили обратно через 10.10.10.1, а не через первый шлюз.
Создаем отдельную таблицу маршрутизации:
echo "100 isp2" >> /etc/iproute2/rt_tables
Добавляем маршрут в эту таблицу:
ip route add 10.10.10.0/24 dev eth1 src 10.10.10.10 table isp2
ip route add default via 10.10.10.1 dev eth1 table isp2
Теперь добавляем правило:
ip rule add from 10.10.10.10/32 table isp2
Смысл такой: если пакет идет от source IP 10.10.10.10, использовать таблицу isp2.
Проверить правила:
ip rule show
Проверить маршруты в таблице:
ip route show table isp2
Проверить, как ядро выберет маршрут:
ip route get 8.8.8.8 from 10.10.10.10
Это одна из самых полезных команд при отладке PBR.
• сервер с несколькими провайдерами
• отдельный выход в интернет для VPN-клиентов
• маршрутизация по source IP
• разделение служебного и пользовательского трафика
• multi-WAN
• асимметричные схемы
• маршрутизация через разные туннели
Можно использовать и fwmark, если нужно направлять трафик по меткам firewall. Например, пометить пакеты через iptables:
iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 10
И отправить такие пакеты в отдельную таблицу:
ip rule add fwmark 10 table isp2
Так можно строить более гибкую логику: не только от какого IP, но и какой именно трафик.
ip route # маршруты
ip rule # правила
Потому что решение принимает не одна таблица маршрутизации, а связка: rule -> table -> route
#network #routing
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Когда говорят про безопасность в linux, часто вспоминают SELinux и AppArmor. Оба инструмента решают похожую задачу: ограничивают, что процесс может делать в системе, даже если у него уже есть обычные Unix-права.
То есть если приложение скомпрометировали, оно не должно автоматически получить возможность читать все подряд, писать куда угодно и трогать чужие файлы. Это дополнительный слой защиты поверх пользователей, групп и прав доступа. Но подход у них разный.
Проверить статус:
sestatus
Посмотреть контексты файлов:
ls -Z
Посмотреть контекст процесса:
ps auxZ
SELinux мощный и гибкий, но за это приходится платить сложностью. Если что-то заблокировано, нужно разбираться в контекстах, политиках, AVC denial, boolean’ах и audit2allow.
Типичный разбор:
ausearch -m avc -ts recent
audit2why
audit2allow
Для больших корпоративных инфраструктур, особенно на RHEL-подобных системах, SELinux - сильный и зрелый вариант.
Проверить статус:
aa-status
Перевести профиль в complain-режим:
aa-complain /etc/apparmor.d/usr.sbin.nginx
Вернуть enforcement:
aa-enforce /etc/apparmor.d/usr.sbin.nginx
В complain-режиме AppArmor не блокирует действие, а только пишет, что было бы запрещено. Это удобно при внедрении: сначала наблюдаем, потом включаем реальные ограничения.
• быстрее понять логику профиля;
• проще привязка к путям;
• легче внедрять точечно;
• удобнее для небольших команд;
• меньше порог входа для обычного админа.
• более строгая модель через labels;
• лучше подходит для больших стандартизированных сред;
• глубже интегрирован в RHEL/CentOS/Rocky/Alma;
• мощнее при сложных многоуровневых политиках;
• меньше зависит от путей к файлам.
Самая плохая практика:
setenforce 0
или полное отключение SELinux/AppArmor просто потому, что сервис не стартует. Это не решение проблемы, а выключение защитного слоя. Лучше временно перевести в мягкий режим и разобраться:
setenforce 0
для диагностики SELinux, но не как постоянное состояние. Для AppArmor аналогично:
aa-complain /etc/apparmor.d/profile-name
#linux #security
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
Иногда нужно не просто писать общий access log, а отдельно сохранять конкретные типы запросов. Например: все 404, все 5xx, запросы от конкретных ботов, отдельные URI, нестандартные методы, трафик из определенных стран. Можно потом парсить общий лог через grep, awk или SIEM. Но в Nginx часто есть более аккуратный способ - использовать map.
map позволяет создать переменную на основе другой переменной Nginx. А потом использовать эту переменную в логах, блокировках, редиректах и другой логике.
map $status $to404log {
404 1;
default 0;
}
map $status $to5xxlog {
~^5 1;
default 0;
}
Теперь в нужном виртуальном хосте:
access_log /var/log/nginx/404.log combined if=$to404log;
access_log /var/log/nginx/5xx.log combined if=$to5xxlog;
• запросы со статусом 404 попадут в /var/log/nginx/404.log
• ответы 500, 502, 503, 504 и другие 5xx попадут в /var/log/nginx/5xx.log
• при этом общий access log можно оставить как есть
Например:
access_log /var/log/nginx/access.log combined;
• Nginx смотрит на встроенную переменную $status
• если статус 404, переменная $to404log получает значение 1
• если статус начинается с 5, переменная $to5xxlog получает значение 1
• директива access_log ... if= пишет запись только если переменная не равна 0
Так можно быстро отделить интересные события без внешней обработки логов.
map $http_user_agent $block_useragent {
default 0;
~*semrushbot 1;
~*ahrefs 1;
~*rss2tg 1;
~*seznambot 1;
~*AspiegelBot 1;
~*BLEXBot 1;
~*MASHIAH 1;
}
А в виртуальном хосте вернуть 403:
if ($block_useragent) {
return 403;
}
Да, if в Nginx часто ругают, но для простого return внутри server это нормальный и распространенный сценарий. По такой же логике можно делать условия по другим переменным:
$request_method
$request_uri
$remote_addr
$host
$geoip_country_code
$http_referer
Например:
• отдельно логировать POST-запросы
• блокировать конкретные URI для некоторых User-Agent
• писать подозрительные запросы в отдельный лог
• ограничивать доступ по стране через GeoIP
• включать разные upstream’ы по признакам запроса
#nginx #linux
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9