💀 Процесс намертво завис в проде? Ставим диагноз без kill -9
Если сервис перестал отвечать, рука сама тянется к
Базовая диагностика зависания
Расширенный мониторинг дескрипторов
Анализ блокировки (Read Blocking)
Такой подход превращает абстрактное «оно зависло» в понятную картину: процесс может просто ждать
❗️ ❗️ ❗️ Нравится формат? Ставь 👍
👉 Рубрика: #шпаргалка@LinuxSkill
#Linux #Strace #Troubleshooting #DevOps #SysAdmin
Если сервис перестал отвечать, рука сама тянется к
kill -9. Но перезапуск вслепую не решает проблему — он уничтожает контекст, и сбой повторится. Показываю, как заглянуть внутрь зависшего приложения на лету и поставить точный диагноз, не останавливая процесс.Базовая диагностика зависания
# 1. Находим PID проблемного процесса по имени.
pidof my_process
# 2. Подключаемся к процессу на лету и слушаем ввод-вывод.
# -t добавит таймстампы: видно, КОГДА процесс замер.
sudo strace -tt -p <PID> -e trace=read,write
Расширенный мониторинг дескрипторов
# 3. Отслеживаем ВСЕ файловые операции.
# ВАЖНО: не 'open', а группа %file — современный glibc зовёт openat(), а не open(),
# поэтому одиночный 'open' почти ничего не покажет. %file ловит open, openat, stat, access.
sudo strace -p <PID> -e trace=read,write,%file
# Если ждёшь именно сетевую блокировку (отвалившаяся БД, зависший сокет):
sudo strace -p <PID> -e trace=%net
Анализ блокировки (Read Blocking)
# 4. Если вывод strace замирает на строке вида:
# read(5,
# — процесс жив, но ждёт данных в дескриптор №5.
# Отключаемся (Ctrl+C НЕ убьёт процесс) и смотрим, что это за дескриптор:
sudo lsof -p <PID> -a -d 5
# Быстрая альтернатива без lsof — прямо из /proc:
sudo ls -l /proc/<PID>/fd/5
Такой подход превращает абстрактное «оно зависло» в понятную картину: процесс может просто ждать
read() из сокета из-за отвалившейся БД или недоступного пайпа, а не находиться в deadlock. Диагноз без правки кода и простоя на рестарт.👉 Рубрика: #шпаргалка@LinuxSkill
#Linux #Strace #Troubleshooting #DevOps #SysAdmin
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23
🧨 Ты ищешь в strace open() — а его там нет
Полез за выжимкой по strace в свою базу конспектов, получил бодрый совет: фильтруй по -e trace=open,openat, увидишь, где программа ищет конфиг. Решил проверить перед постом. Ubuntu 24.04, strace 6.8. Не увидел ничего.
Подопытный — скрипт, который проверяет конфиг через test -f:
Программе незачем открывать файл, чтобы понять, что его нет. Она зовёт newfstatat, и фильтр по open его срезает. Фильтр отрезает ровно то, ради чего ты запускал strace.
Заодно: open( в выводе cat — 0 вхождений, openat( — 3. glibc на x86_64 давно ходит через openat. Искать в трейсе open() бессмысленно.
Рабочий вариант
-o тут не для красоты. Без него 2>&1 | grep смешает трейс с выводом самого приложения, у меня в грепнутый поток прилетело cat: /nonexistent: No such file от подопытного.
Если процесс уже крутится
Честно: эту связку я проверял только по документации, systemd-хоста под рукой не было. Синтаксис --value живёт с systemd 230.
Что читать в ошибках
▪️ ENOENT — такого пути нет
▪️ EACCES — нет прав на файл или каталог
▪️ ENOTDIR — в середине пути не каталог
▪️ ELOOP — symlink закольцевался
Половина «config not found» в проде — это EACCES, а не отсутствие файла.
Грабли: -Z появился в strace 5.2 от 12 июля 2019. На старом RHEL 7 со strace 4.12 его нет, там остаётся grep по ENOENT. В контейнере нужен --cap-add=SYS_PTRACE. И трейс тормозит процесс в разы — на живом сервисе цепляйся коротко.
А вы чем ловите такие вещи — strace, ltrace или сразу lsof -p?
#Linux #strace #DevOps #Debug #SRE
Полез за выжимкой по strace в свою базу конспектов, получил бодрый совет: фильтруй по -e trace=open,openat, увидишь, где программа ищет конфиг. Решил проверить перед постом. Ubuntu 24.04, strace 6.8. Не увидел ничего.
Подопытный — скрипт, который проверяет конфиг через test -f:
# фильтр из конспекта
strace -f -e trace=open,openat ./finder.sh \
| grep myapp
# пусто
# весь файловый класс
strace -f -e trace=%file ./finder.sh | grep myapp
# newfstatat(AT_FDCWD, "/etc/myapp/config.ini",
# 0x7ffc0f32bee0, 0) = -1 ENOENT
Программе незачем открывать файл, чтобы понять, что его нет. Она зовёт newfstatat, и фильтр по open его срезает. Фильтр отрезает ровно то, ради чего ты запускал strace.
Заодно: open( в выводе cat — 0 вхождений, openat( — 3. glibc на x86_64 давно ходит через openat. Искать в трейсе open() бессмысленно.
Рабочий вариант
# -Z печатает только вызовы, вернувшие ошибку
strace -f -Z -e trace=%file \
-o /tmp/t.log ./myapp
grep -E 'ENOENT|EACCES' /tmp/t.log
-o тут не для красоты. Без него 2>&1 | grep смешает трейс с выводом самого приложения, у меня в грепнутый поток прилетело cat: /nonexistent: No such file от подопытного.
Если процесс уже крутится
PID=$(systemctl show -p MainPID --value nginx)
sudo strace -f -Z -e trace=%file -p $PID
Честно: эту связку я проверял только по документации, systemd-хоста под рукой не было. Синтаксис --value живёт с systemd 230.
Что читать в ошибках
▪️ ENOENT — такого пути нет
▪️ EACCES — нет прав на файл или каталог
▪️ ENOTDIR — в середине пути не каталог
▪️ ELOOP — symlink закольцевался
Половина «config not found» в проде — это EACCES, а не отсутствие файла.
Грабли: -Z появился в strace 5.2 от 12 июля 2019. На старом RHEL 7 со strace 4.12 его нет, там остаётся grep по ENOENT. В контейнере нужен --cap-add=SYS_PTRACE. И трейс тормозит процесс в разы — на живом сервисе цепляйся коротко.
А вы чем ловите такие вещи — strace, ltrace или сразу lsof -p?
#Linux #strace #DevOps #Debug #SRE
👍6
🩹 Прогнал 10 популярных регулярок. Четыре врут
Салют, дежурный по проду.
Попалась подборка «10 регулярок для админа» — та самая, что кочует по всем каналам. Вместо того чтобы репостнуть, прогнал её в песочнице: Ubuntu 24.04, GNU grep 3.11, GNU sed 4.9. Четыре пункта из десяти работают не так, как обещает комментарий рядом.
Поиск IP ловит версию ядра
На моём тестовом логе выдал
Мусор ушёл. Цена —
Пустые строки удаляются не все
URL глотает всё до пробела
На строке
Комментарий обещает «длиннее 100 символов», регулярка ловит и ровно стошную. Плюс зависит от локали: строка из 60 кириллических букв под
По мелочи:
Регулярки для email и извлечения доменов, кстати, оказались нормальными. Придираться не к чему.
Забирайте исправленный набор в закладки — пригодится, когда в три часа ночи будете грепать чужой лог.
#Linux #Bash #grep #sed #regex #DevOps
Салют, дежурный по проду.
Попалась подборка «10 регулярок для админа» — та самая, что кочует по всем каналам. Вместо того чтобы репостнуть, прогнал её в песочнице: Ubuntu 24.04, GNU grep 3.11, GNU sed 4.9. Четыре пункта из десяти работают не так, как обещает комментарий рядом.
Поиск IP ловит версию ядра
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' file.log
На моём тестовом логе выдал
5.15.0.91 (версия ядра), 999.999.999.999 и 1.2.3.4 — откушенный кусок от 1.2.3.4.5. Октеты не проверяются, границы тоже. -w не спасает: точка не словесный символ.
grep -oP '(?<![\d.])((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\
\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?![\d.])' file.log
Мусор ушёл. Цена —
-P, а это PCRE, на BSD-grep не заработает.Пустые строки удаляются не все
sed -i '/^$/d' file.txt
^$ — это строка нулевой длины. Строка из трёх пробелов останется, строка с табом останется. Проверил через cat -A. Правильно так:
# сначала посмотреть, что уйдёт
sed -n '/^[[:space:]]*$/p' file.txt
# и только потом править, с бэкапом
sed -i.bak -E '/^[[:space:]]*$/d' file.txt
-i без .bak переписывает файл на месте. Один раз ошибёшься с шаблоном — восстанавливать неоткуда.URL глотает всё до пробела
grep -Eo 'https?://[^ ]+' file.txt
На строке
см. (http://foo.org/x) и "https://bar.io/y" вернул адреса вместе со скобкой и кавычкой. А в строке с табуляцией утащил ещё и следующее слово — таб это не пробел.
grep -Eo 'https?://[^[:space:]<>"()]+[^[:space:]<>"(),.;:]' \
file.txt
.{100,} — это сто и большеКомментарий обещает «длиннее 100 символов», регулярка ловит и ровно стошную. Плюс зависит от локали: строка из 60 кириллических букв под
LC_ALL=C считается длинной (120 байт), под UTF-8 — нет. Проверил обе, результат разный. Если нужны символы и строго больше — берите awk 'length($0) > 100'.По мелочи:
^# не видит комментарии с отступом, -v 'ERROR' выкидывает строки со словом NOERROR, а \s в седьмом пункте — GNU-расширение, на macOS и busybox отвалится, там нужен [[:space:]].Регулярки для email и извлечения доменов, кстати, оказались нормальными. Придираться не к чему.
Забирайте исправленный набор в закладки — пригодится, когда в три часа ночи будете грепать чужой лог.
#Linux #Bash #grep #sed #regex #DevOps
👍13🔥1
🔬 Дали root и разрешили ломать. Сервис, который я хотел создать
Постоянные читатели знают, что у меня есть бот @gradeliftbot, в котором больше 200 задач по Linux. Работает он просто: кусок лога и вопрос по нему. Песочницы нет вообще. Всё скатывается в механику заданий вопрос-ответ. Кейсы генерит нейросеть, и местами это заметно, хотя там содержится достаточно много материала, чего стоит только полный гайд от Docker на 1195 страниц и 8500 страниц с командами Linux и их подробным описанием.
Изначально идея была создать песочницу с заданиями, где нужно было выполнять задания, но я так и не смог придумать, как адаптировать её под мобильный формат Telegram-бота. Поэтому получилась очень упрощённая версия от изначальной идеи.
На днях наткнулся на izzylab.ru — тренажёр по Linux и DevOps, где терминал живёт прямо в браузере. Открыл первую задачу «создай пользователя john» и вместо useradd набрал разведку. Интереснее было понять, куда меня пустили и насколько там можно наглеть.
uname -r; nproc; free -m | head -2
id; sudo -n true; echo $?
4.19.0-gvisor, 2 ядра, 256 МБ, uid=0. Под тобой не хост и не виртуалка, а gVisor — юзерспейсное ядро от Google, которое перехватывает системные вызовы и разбирает их само. Отсюда и щедрость: тебе спокойно дают root и разрешают крушить систему, потому что до настоящего ядра ты всё равно не дотянешься. У каждой задачи свой контейнер, сброс откатывает всё начисто.
Больше всего я боялся увидеть чекер, который сверяет введённую строку с эталоном. Проверил в лоб. Задание «закодируй /root/data.bin в base64» решил не той утилитой:
openssl base64 -A -in /root/data.bin \
-out /root/data.b64
Засчитано, хотя штатный base64 ломает вывод каждые 76 символов, а openssl -A пишет одной строкой. Проверка смотрит на результат, а не на способ. Обмануть тоже не вышло: подложил валидный base64 от постороннего текста — поймал и написал, какой критерий не сошёлся, решение при этом не выдал.
На момент, когда я смотрел сервис, уже было 437 заданий, что значительно больше чем в @gradeliftbot. 87 лёгких, 276 средних, 74 сложных. Разбивка не самая ожидаемая — Git 39, регулярки 30, скрипты 29, SQLite 25, Kubernetes 20, диагностика 20. Redis и Postgres поднимаются руками и отвечают, apt install работает, сеть наружу есть.
А дальше пошли находки, ради которых стоило лезть. ss -s возвращает get_sockstat: No such file or directory, при этом ip -br a работает нормально. Причина в netlink: gVisor держит NETLINK_ROUTE, но не NETLINK_SOCK_DIAG, на котором стоит ss. Я сам писал «netstat мёртв, бери ss» — вот вам среда, где канон переворачивается.
Со strace то же самое, только тоньше. Запуск программы под трейсом работает, а attach к чужому PID — нет:
ptrace(PTRACE_SEIZE, 261): Operation not permitted
PTRACE_TRACEME есть, PTRACE_SEIZE нет. Ещё ping даёт 100% потерь, а curl https://ya.ru возвращает 302 — ICMP не проброшен, TCP ходит. Я такие штуки люблю: за час разведки узнал про gVisor больше, чем за все статьи о нём.
Теперь честно. Если вы хотите просто научиться внимательно читать логи, что важно на собесах или когда за кем-то исправляешь ошибки, тогда подходит @gradeliftbot. А если цель — именно практика в живом терминале, то формат izzylab.ru на мой взгляд, подходит лучше.
Что скажете насчёт идеи живой песочницы в браузере телефона? Тут в MAX ботов разрешили создавать самозанятым, можно будет попробовать перенести бота из телеги в MAX.
#Linux #DevOps #gVisor #strace #Практика
Постоянные читатели знают, что у меня есть бот @gradeliftbot, в котором больше 200 задач по Linux. Работает он просто: кусок лога и вопрос по нему. Песочницы нет вообще. Всё скатывается в механику заданий вопрос-ответ. Кейсы генерит нейросеть, и местами это заметно, хотя там содержится достаточно много материала, чего стоит только полный гайд от Docker на 1195 страниц и 8500 страниц с командами Linux и их подробным описанием.
Изначально идея была создать песочницу с заданиями, где нужно было выполнять задания, но я так и не смог придумать, как адаптировать её под мобильный формат Telegram-бота. Поэтому получилась очень упрощённая версия от изначальной идеи.
На днях наткнулся на izzylab.ru — тренажёр по Linux и DevOps, где терминал живёт прямо в браузере. Открыл первую задачу «создай пользователя john» и вместо useradd набрал разведку. Интереснее было понять, куда меня пустили и насколько там можно наглеть.
uname -r; nproc; free -m | head -2
id; sudo -n true; echo $?
4.19.0-gvisor, 2 ядра, 256 МБ, uid=0. Под тобой не хост и не виртуалка, а gVisor — юзерспейсное ядро от Google, которое перехватывает системные вызовы и разбирает их само. Отсюда и щедрость: тебе спокойно дают root и разрешают крушить систему, потому что до настоящего ядра ты всё равно не дотянешься. У каждой задачи свой контейнер, сброс откатывает всё начисто.
Больше всего я боялся увидеть чекер, который сверяет введённую строку с эталоном. Проверил в лоб. Задание «закодируй /root/data.bin в base64» решил не той утилитой:
openssl base64 -A -in /root/data.bin \
-out /root/data.b64
Засчитано, хотя штатный base64 ломает вывод каждые 76 символов, а openssl -A пишет одной строкой. Проверка смотрит на результат, а не на способ. Обмануть тоже не вышло: подложил валидный base64 от постороннего текста — поймал и написал, какой критерий не сошёлся, решение при этом не выдал.
На момент, когда я смотрел сервис, уже было 437 заданий, что значительно больше чем в @gradeliftbot. 87 лёгких, 276 средних, 74 сложных. Разбивка не самая ожидаемая — Git 39, регулярки 30, скрипты 29, SQLite 25, Kubernetes 20, диагностика 20. Redis и Postgres поднимаются руками и отвечают, apt install работает, сеть наружу есть.
А дальше пошли находки, ради которых стоило лезть. ss -s возвращает get_sockstat: No such file or directory, при этом ip -br a работает нормально. Причина в netlink: gVisor держит NETLINK_ROUTE, но не NETLINK_SOCK_DIAG, на котором стоит ss. Я сам писал «netstat мёртв, бери ss» — вот вам среда, где канон переворачивается.
Со strace то же самое, только тоньше. Запуск программы под трейсом работает, а attach к чужому PID — нет:
ptrace(PTRACE_SEIZE, 261): Operation not permitted
PTRACE_TRACEME есть, PTRACE_SEIZE нет. Ещё ping даёт 100% потерь, а curl https://ya.ru возвращает 302 — ICMP не проброшен, TCP ходит. Я такие штуки люблю: за час разведки узнал про gVisor больше, чем за все статьи о нём.
Теперь честно. Если вы хотите просто научиться внимательно читать логи, что важно на собесах или когда за кем-то исправляешь ошибки, тогда подходит @gradeliftbot. А если цель — именно практика в живом терминале, то формат izzylab.ru на мой взгляд, подходит лучше.
Что скажете насчёт идеи живой песочницы в браузере телефона? Тут в MAX ботов разрешили создавать самозанятым, можно будет попробовать перенести бота из телеги в MAX.
#Linux #DevOps #gVisor #strace #Практика
👍5🔥2👀2👎1
Чек-лист как проверить софт из чужой подборки
На Хабре попалась подборка «Стек российского сисадмина в 2026». Идея хорошая, а вот что с ней делать дальше — вопрос. Такие списки выходят каждый год, и половина позиций в них кочует по инерции. Автор списал у прошлогоднего автора, а проект уже год как не двигается или тихо переехал на несвободную лицензию.
Вот шесть проверок, которые снимают вопрос за десять минут. До того, как ты выкатишь это в прод.
1. Паспорт репозитория
Три поля решают почти всё.
2. Сколько на самом деле прошло с последнего пуша
Глазами дату читать бесполезно — «2024-03-11» не выглядит страшно, пока не увидишь рядом число 881.
3. Bus factor
Если весь список — один ник, проект держится на одном человеке. Это не приговор, но знать надо заранее, а не когда он выгорит.
Тут спрятаны грабли, на которые я сам наступил. Без скобок вокруг
4. Лицензия
В первом же запросе смотри на
5. Есть ли пакет в репах твоего дистрибутива
Проверял на Ubuntu 24.04:
6. Помни про лимит
Анонимно GitHub API даёт 60 запросов в час на IP, и на общем адресе они кончаются мгновенно — я на этом словил 403 посреди проверки. С персональным токеном лимит 5000:
Честный минус метода: API показывает активность, а не качество. Проект может коммитить каждый день и при этом быть непригодным, а может годами лежать без изменений, потому что он просто дописан. Так что это фильтр первого уровня, а не вердикт.
#Linux #DevOps #GitHub #jq #Чеклист
На Хабре попалась подборка «Стек российского сисадмина в 2026». Идея хорошая, а вот что с ней делать дальше — вопрос. Такие списки выходят каждый год, и половина позиций в них кочует по инерции. Автор списал у прошлогоднего автора, а проект уже год как не двигается или тихо переехал на несвободную лицензию.
Вот шесть проверок, которые снимают вопрос за десять минут. До того, как ты выкатишь это в прод.
1. Паспорт репозитория
R=zabbix/zabbix
curl -s "https://api.github.com/repos/$R" \
| jq '{pushed_at, archived, license: .license.spdx_id}'
Три поля решают почти всё.
archived: true — проект заморожен официально, дальше можно не смотреть.2. Сколько на самом деле прошло с последнего пуша
P=$(curl -s "https://api.github.com/repos/$R" \
| jq -r .pushed_at)
echo $(( ($(date +%s) - $(date -d "$P" +%s)) / 86400 ))
Глазами дату читать бесполезно — «2024-03-11» не выглядит страшно, пока не увидишь рядом число 881.
3. Bus factor
S=$(date -u -d '90 days ago' +%Y-%m-%d)
curl -s "https://api.github.com/repos/$R/commits\
?since=$S&per_page=100" \
| jq -r '[.[] | (.author.login // "?")]
| group_by(.) | map("\(length) \(.[0])") | .[]' \
| sort -rn | head -5
Если весь список — один ник, проект держится на одном человеке. Это не приговор, но знать надо заранее, а не когда он выгорит.
Тут спрятаны грабли, на которые я сам наступил. Без скобок вокруг
.author.login // "?" конвейер молча теряет коммиты, у которых автор не привязан к аккаунту GitHub. На макете из пяти коммитов jq вернул четыре. Оператор `//` в jq применяется ко всему потоку, а не к каждому элементу — скобки обязательны.4. Лицензия
В первом же запросе смотри на
spdx_id. Значение NOASSERTION означает, что GitHub не смог сопоставить файл лицензии ни с одной стандартной. Чаще всего это BSL, SSPL или самописный текст с ограничениями на коммерческое использование. Открывай LICENSE руками.5. Есть ли пакет в репах твоего дистрибутива
for p in netdata zabbix-server-pgsql; do
v=$(apt-cache madison "$p" 2>/dev/null | head -1)
printf '%-22s %s\n' "$p" "${v:-НЕТ В РЕПАХ}"
done
Проверял на Ubuntu 24.04:
netdata нашёлся в noble/universe, zabbix-server-pgsql — нет, ставить придётся из репозитория вендора. Отдельная засада: apt-cache madison и apt-cache policy возвращают код 0, даже когда пакета не существует, просто печатают пустоту. Конструкция apt-cache policy X || echo "нет" не сработает никогда — проверяй пустоту переменной, как выше.6. Помни про лимит
Анонимно GitHub API даёт 60 запросов в час на IP, и на общем адресе они кончаются мгновенно — я на этом словил 403 посреди проверки. С персональным токеном лимит 5000:
curl -s -H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/rate_limit | jq .rate
Честный минус метода: API показывает активность, а не качество. Проект может коммитить каждый день и при этом быть непригодным, а может годами лежать без изменений, потому что он просто дописан. Так что это фильтр первого уровня, а не вердикт.
#Linux #DevOps #GitHub #jq #Чеклист
👍7
Попалась заметка про reptyr — утилиту, которая забирает уже запущенный процесс в новую сессию терминала. Команды из ходовых инструкций прогнал сам, на двух машинах с Ubuntu 24.04.
Ситуация: запустил в ssh дамп базы, а он затянулся. Надо было сразу в tmux, но уже поздно.
Сначала отвязываем процесс от текущей оболочки:
Вот тут первая шероховатость. В инструкциях пишут
Дальше открываем новую сессию в tmux и забираем процесс:
Работает. Прогнал на
При этом reptyr напечатал
А вот чего в инструкциях обычно нет. По умолчанию в Debian и Ubuntu
Запустил от обычного пользователя — получил отказ:
Дальше reptyr сам подсказывает посмотреть
С нулём тот же скрипт перенёс процесс на
Есть флаг
И про версии. На одной машине приехал reptyr 0.9.0-1, на другой при той же Ubuntu 24.04 — 0.8.0. Флаги в обеих одинаковые, но длинных опций нет ни там, ни там:
А вы reptyr вообще применяли в бою или проще перезапустить и не рисковать?
#Linux #Bash #tmux #reptyr #DevOps
Ситуация: запустил в ssh дамп базы, а он затянулся. Надо было сразу в tmux, но уже поздно.
Сначала отвязываем процесс от текущей оболочки:
# Ctrl-Z, потом
bg
jobs -l
disown %1
Вот тут первая шероховатость. В инструкциях пишут
disown top, по имени команды. Оно работает, но ровно пока такой job один. Проверил: один sleep снимается нормально, два — ambiguous job spec и выход с кодом 1. Пиши %1 или PID, не имя.Дальше открываем новую сессию в tmux и забираем процесс:
# PID берём из вывода jobs -l
reptyr 7972
Работает. Прогнал на
top из одной tmux-сессии в другую: нулевой дескриптор сменился с /dev/pts/0 на /dev/pts/2, вывод продолжился в новом окне.При этом reptyr напечатал
[-] Timed out waiting for child stop. — и всё равно перенёс. Причём это вылезло на обеих машинах и на разных версиях, так что предупреждение можно игнорировать.А вот чего в инструкциях обычно нет. По умолчанию в Debian и Ubuntu
kernel.yama.ptrace_scope равен 1: подцепиться можно только к своему потомку. Процесс из старой ssh-сессии новой оболочке не потомок. Запустил от обычного пользователя — получил отказ:
Unable to attach to pid 2162:
Operation not permitted
Дальше reptyr сам подсказывает посмотреть
ptrace_scope — за это спасибо автору. Лечится так:cat /proc/sys/kernel/yama/ptrace_scope
# 0 — классические правила ptrace
sysctl -w kernel.yama.ptrace_scope=0
С нулём тот же скрипт перенёс процесс на
/dev/pts/5. Но это ослабление защиты: любой процесс под тем же uid сможет подцепиться к любому другому. Держать так постоянно на боевой машине я бы не стал. Под root, кстати, всё работает и при единице — CAP_SYS_PTRACE обходит yama.Есть флаг
-T: крадёт весь терминальный сеанс и по описанию должен выручать, когда у процесса есть дети. Взял конвейер sleep 400 | cat, натравил reptyr -T — и ничего. Ни ошибки, ни переноса: лог пустой, tty прежний. В чём подвох, не разобрался.И про версии. На одной машине приехал reptyr 0.9.0-1, на другой при той же Ubuntu 24.04 — 0.8.0. Флаги в обеих одинаковые, но длинных опций нет ни там, ни там:
reptyr --help отвечает invalid option.А вы reptyr вообще применяли в бою или проще перезапустить и не рисковать?
#Linux #Bash #tmux #reptyr #DevOps
👍2🔥1
Ходовой набор для отладки bash-скрипта известен всем:
Сначала то, что работает без оговорок:
Но `set -e` замолкает в условиях. Вызвал функцию через
Трассировка. Обычно пишут просто
Теперь в каждой строке трассировки видно файл и номер:
Пошаговый режим. Типовой рецепт — функция с
В нём три поломки, и каждая тихая.
Первая:
Вторая: ловушка не заходит внутрь функций. Без
Третья самая злая.
Две строки съела отладка — отладчик изменил поведение отлаживаемого. Плюс при неинтерактивном stdin приглашение
С ним прошли все три строки, паузы работали даже при stdin из
И про
Прогонял на двух машинах, bash 5.2.21 и 5.2.37 — поведение одинаковое.
А вы
#Linux #Bash #Debug #DevOps #Автоматизация
set -euo pipefail сверху, set -x вокруг мутного места и ловушка DEBUG с read, чтобы останавливаться перед каждой командой. Прогнал этот набор на Ubuntu 24.04, bash 5.2. Три вещи из него ведут себя не так, как ожидаешь.Сначала то, что работает без оговорок:
# падаем на ошибке, пустой переменной и в пайпах
set -euo pipefail
pipefail не декорация: false | true без него даёт код 0, с ним 1. А set -u ловит то, до чего не доберётся проверка синтаксиса:
echo "путь: /${UNSET_VAR}/data"
# bash: UNSET_VAR: unbound variable
Но `set -e` замолкает в условиях. Вызвал функцию через
if — код возврата внутри перестал что-либо значить:
check() { false; echo "функция продолжила"; }
if check; then :; fi
# echo выполнился, rc=0
Трассировка. Обычно пишут просто
set -x, но дефолтный PS4 печатает голый плюс — в скрипте на 300 строк не поймёшь, где ты:
PS4='+ ${BASH_SOURCE}:${LINENO}: '
set -x
do_something_risky
set +x
Теперь в каждой строке трассировки видно файл и номер:
+ g.sh:4: mkdir -p /tmp/zz
Пошаговый режим. Типовой рецепт — функция с
read и ловушка DEBUG:
dbg() { read -p "$BASH_SOURCE:$LINENO? " _; }
trap 'dbg' DEBUG
В нём три поломки, и каждая тихая.
Первая:
$LINENO и $BASH_SOURCE внутри функции указывают на саму функцию. На всех командах печаталось line=3 — строка, где стоит read. Реальные 6 и 7 не появились ни разу. Координаты надо передавать аргументами:
dbg() { read -r -p "[$1:$2] $3? " _; }
trap 'dbg "$BASH_SOURCE" "$LINENO" "$BASH_COMMAND"' DEBUG
Вторая: ловушка не заходит внутрь функций. Без
set -T виден только вызов work, а команды в её теле пропадают — как раз там, где обычно и прячется баг.
set -T
Третья самая злая.
read в ловушке читает тот же stdin, что и скрипт. Подал в цикл три строки — дошла одна:
printf 'alpha\nbeta\ngamma\n' | bash f.sh
получил: beta
Две строки съела отладка — отладчик изменил поведение отлаживаемого. Плюс при неинтерактивном stdin приглашение
read -p не печатается вовсе, и скрипт молча проносится мимо пауз. Лечится перенаправлением:
dbg() { read -r -p "$1? " _ < /dev/tty; }
С ним прошли все три строки, паузы работали даже при stdin из
/dev/null.И про
bash -n, который советуют как «проверку перед запуском». Он про синтаксис и только: незакрытый if поймал с кодом 2, а несуществующую команду и rm -rf /$UNSET_VAR/data пропустил с кодом 0.Прогонял на двух машинах, bash 5.2.21 и 5.2.37 — поведение одинаковое.
А вы
set -T вообще используете или обходитесь set -x?#Linux #Bash #Debug #DevOps #Автоматизация
👀2👍1
Посмотреть, какие интерфейсы подняты, и не принять погасший порт за живой
Краткий статус интерфейсов
Нюанс:
Второй адрес без алиасов вида eth0:1
Второй адрес встаёт рядом с первым, при удалении линк не дёргается.
Узнать, куда реально пойдёт пакет
Не читает таблицу, а спрашивает у ядра готовое решение вместе с исходящим адресом. Для локального ответит
Увидеть все таблицы маршрутизации
Обычный
Посмотреть, кто есть в сети рядом
Нюанс:
Сбросить кэш соседей
Нюанс: без
Дать интерфейсу второе имя без даунтайма
Всё, что пишет, требует
А вы
#Linux #iproute2 #Networking #DevOps #Debug
Краткий статус интерфейсов
ip -4 -br address show up
Нюанс:
up фильтрует по состоянию линка, а не по наличию адреса. Погасил eth0 — с ключом он пропал из вывода совсем, без ключа виден как DOWN со всеми адресами на месте. Пустая строка тут не значит «адреса нет».Второй адрес без алиасов вида eth0:1
ip address add 10.0.0.50/24 dev eth0
ip address del 10.0.0.50/24 dev eth0
Второй адрес встаёт рядом с первым, при удалении линк не дёргается.
Узнать, куда реально пойдёт пакет
ip route get 8.8.8.8
# 8.8.8.8 via 192.0.2.1 dev eth0 src 192.0.2.2
Не читает таблицу, а спрашивает у ядра готовое решение вместе с исходящим адресом. Для локального ответит
local 192.0.2.2 dev lo.Увидеть все таблицы маршрутизации
ip route show table all
Обычный
show дал одну строку, table all — шесть, включая local с broadcast и host-маршрутами. С VPN и контейнерами разница уходит в десятки строк.Посмотреть, кто есть в сети рядом
ip -4 neigh show
ip -6 neigh show
Нюанс:
ip neigh — не ARP-таблица, как пишут в шпаргалках, а таблица соседей: ARP плюс IPv6 NDP. Без ключа -4 или -6 получишь обе вперемешку. arp -n показывал только первую, отсюда и путаница.Сбросить кэш соседей
ip -s -s neigh flush dev eth0
Нюанс: без
-s -s команда молчит и отдаёт код 0 — не отличишь «очистил десять записей» от «там было пусто». С ключом печатает удалённые записи и итог *** Round 1, deleting 1 entries ***.Дать интерфейсу второе имя без даунтайма
ip link property add dev eth0 altname eno2
altname eno2 появляется в ip link show eth0, и по новому имени интерфейс находится: ip address show dev eno2 отдаёт eth0. Есть с ядра 5.8 и iproute2 5.8, на RHEL 7 синтаксис не разберётся.Всё, что пишет, требует
CAP_NET_ADMIN — в контейнере без --cap-add=NET_ADMIN упадёт.А вы
ip route get в отладке применяете или сразу лезете в show?#Linux #iproute2 #Networking #DevOps #Debug
👍8🔥1
Закрыть системные файлы от правки и не получить молча сломанный useradd
Запретить правку файла даже руту
Нюанс:
Разрешить логу только дозапись
Нюанс: ротация делается переименованием, а его атрибут не пускает. Запустил logrotate на таком файле:
Снова ноль на выходе. Файл не ротировался, cron считает, что всё прошло, логи растут до конца места.
Найти файлы с SUID и SGID
Нюанс: без
Выдать права одному пользователю
Единственное место без подвоха. После первой команды в
Поставить наблюдение за файлом
Нюанс: правило живёт до перезапуска auditd. Постоянные пишутся в
Закрыть входящий трафик, оставшись в сессии
Порядок именно такой: политика последней.
У двух худших находок общее одно: команда не сработала, а код возврата ноль. Мониторингом такое не ловится.
А вы
#Linux #Security #Hardening #iptables #DevOps
Запретить правку файла даже руту
chattr +i /etc/passwd
Нюанс:
useradd после этого выдаёт cannot open /etc/passwd и завершается с кодом 0. Пользователь не создаётся, скрипт не падает, мониторинг молчит. Снял атрибут — создался.Разрешить логу только дозапись
chattr +a /var/log/secure
Нюанс: ротация делается переименованием, а его атрибут не пускает. Запустил logrotate на таком файле:
error: failed to rename: Operation not permitted
rc=0
Снова ноль на выходе. Файл не ротировался, cron считает, что всё прошло, логи растут до конца места.
Найти файлы с SUID и SGID
find / -xdev -perm /6000 -type f -ls 2>/dev/null
Нюанс: без
-xdev поиск по корню вернул ноль файлов и код 1 — захлебнулся на /proc. С ним нашлось 16 штук. Пустой вывод легко принять за чистую систему. Старый синтаксис +6000 GNU find уже не понимает, отвечает invalid mode.Выдать права одному пользователю
setfacl -m u:lisa:rw file
setfacl -b file
Единственное место без подвоха. После первой команды в
ls -l появляется плюс: -rw-rw-r--+, после сброса исчезает — по нему и замечают ACL.Поставить наблюдение за файлом
auditctl -w /etc/passwd -p wa -k pwd_change
Нюанс: правило живёт до перезапуска auditd. Постоянные пишутся в
/etc/audit/rules.d/audit.rules.Закрыть входящий трафик, оставшись в сессии
iptables -A INPUT -m conntrack \
--ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -P INPUT DROP
Порядок именно такой: политика последней.
iptables -P INPUT DROP первой командой на удалённом сервере обрывает соединение сразу. И два уточнения: -m state до сих пор принимается, но актуален -m conntrack --ctstate, а iptables -V на 24.04 отвечает v1.8.10 (nf_tables) — правила уезжают в nftables.У двух худших находок общее одно: команда не сработала, а код возврата ноль. Мониторингом такое не ловится.
А вы
chattr +i на боевых конфигах ставите или считаете это ловушкой для самого себя?#Linux #Security #Hardening #iptables #DevOps
👍5🔥1
Пробросить TCP через WebSocket мимо DPI и не собрать по дороге утечку сокетов
websocat — консольный клиент вебсокетов на Rust, авторства Виталия Шукелы. Соединение начинается с обычного HTTP-рукопожатия
Пробросить локальный TCP-порт в вебсокет
Нюанс: без
Держать сессию живой на нестабильной сети
Нюанс:
Проверил разрыв: поднял клиент раньше сервера, в логе
Отправить одно сообщение и выйти
Отправляет ровно один фрейм из stdin и закрывает сессию. Удобно дёргать из скрипта. Локальный эхо-сервер вернул
Пробить туннель на тестовый сервер с самоподписанным TLS
Флаг
Из полезного при отладке:
Честный минус — синтаксис. Аргументы это цепочки специферов вида
А чем вы пробиваете туннель, когда наружу открыт только 443?
#Linux #websocat #Networking #Tunneling #DevOps
websocat — консольный клиент вебсокетов на Rust, авторства Виталия Шукелы. Соединение начинается с обычного HTTP-рукопожатия
Upgrade, поэтому фильтр видит легитимный веб-трафик. Проверял на версии 1.14.0, скачанной статическим бинарником с релизов.Пробросить локальный TCP-порт в вебсокет
websocat --binary -E \
tcp-l:127.0.0.1:8080 ws://example.com/socket
Нюанс: без
-E websocat печатает предупреждение про утечку сокетов при обслуживании нескольких клиентов. У меня оно вылезало на каждом запуске туннеля, с флагом лог чистый.Держать сессию живой на нестабильной сети
websocat -t --ping-interval 10 \
- autoreconnect:ws://example.com/socket
Нюанс:
autoreconnect: требует двух аргументов. Вариант с одним, который ходит по подборкам, падает: Specify ws:// or wss:// URI to connect to a websocket. Первым аргументом идёт источник данных — - для stdin или tcp-l: для туннеля.Проверил разрыв: поднял клиент раньше сервера, в логе
Reconnecting failed, процесс жив. Сервер появился — соединение поднялось само.Отправить одно сообщение и выйти
echo "get_metrics" | websocat -1 ws://example.com/socket
Отправляет ровно один фрейм из stdin и закрывает сессию. Удобно дёргать из скрипта. Локальный эхо-сервер вернул
get_metrics и завершился с кодом 0.Пробить туннель на тестовый сервер с самоподписанным TLS
websocat -k wss://staging.example.com/socket
Флаг
-k отключает проверку сертификата. Для стенда годится, дальше стенда — нет: MITM на таком туннеле не отличить от обычной работы.Из полезного при отладке:
websocat -t ws-l:127.0.0.1:1234 mirror: поднимает локальный эхо-сервер, на котором можно проверить свою команду, не выходя наружу.Честный минус — синтаксис. Аргументы это цепочки специферов вида
overlay:addrtype:address, и с непривычки половина попыток заканчивается Invalid command-line parameters. Плюс инкапсуляция в кадры съедает скорость, а на больших объёмах приходится крутить -B вручную.А чем вы пробиваете туннель, когда наружу открыт только 443?
#Linux #websocat #Networking #Tunneling #DevOps
👍2👀2🔥1
Если гоняешь ansible по сотне серверов, SSH каждый раз делает хендшейк заново
Переиспользовать одно TCP-соединение
Замерил на локальном sshd: первое подключение 0.22 секунды, второе и третье — по 0.01. Разница в двадцать раз, и это на loopback, где сети как таковой нет. На реальном канале с задержкой выигрыш будет больше.
Каталог
Не упереться в лимит длины пути
Нюанс:
Проверил на пути в 109 байт — ровно на границе.
Проверить, жив ли мастер
Отвечает
Сбросить залипшую сессию
Печатает
Нюанс: если мастер-процесс умер не сам, а был убит, файл сокета остаётся сиротой. Прибил мастера через
Честный минус: ноутбук ушёл в сон с активным сокетом — при пробуждении получишь висящую сессию и таймаут вместо мгновенного входа. Лечится тем же
А вы ControlPersist держите глобально на
#Linux #SSH #Ansible #DevOps #Terminal
Переиспользовать одно TCP-соединение
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%C
ControlPersist 10m
Замерил на локальном sshd: первое подключение 0.22 секунды, второе и третье — по 0.01. Разница в двадцать раз, и это на loopback, где сети как таковой нет. На реальном канале с задержкой выигрыш будет больше.
Каталог
~/.ssh/sockets надо создать заранее, сам он не появится.Не упереться в лимит длины пути
Нюанс:
%C тут не для красоты. Классический %r@%h:%p на длинных именах хостов выходит за предел, и ssh отказывается работать:
ControlPath too long ('/root/.ssh/sockets/xxx...
-root@127.0.0.1:2222' >= 108 bytes)
Проверил на пути в 109 байт — ровно на границе.
%C даёт хеш фиксированной длины и снимает вопрос совсем.Проверить, жив ли мастер
ssh -O check user@host
Отвечает
Master running (pid=924). Пригодится, когда непонятно, идёт ли новая сессия через кеш или поднимает соединение с нуля.Сбросить залипшую сессию
ssh -O exit user@host
Печатает
Exit request sent. и удаляет файл сокета. Нужно, когда на сервере поменялись ключи или конфиг, а мастер держит старую сессию.Нюанс: если мастер-процесс умер не сам, а был убит, файл сокета остаётся сиротой. Прибил мастера через
kill -9 — -O check стал отвечать Connection refused, но новое подключение прошло нормально: ssh увидел мёртвый сокет и поднял соединение заново.Честный минус: ноутбук ушёл в сон с активным сокетом — при пробуждении получишь висящую сессию и таймаут вместо мгновенного входа. Лечится тем же
-O exit, но сначала надо догадаться, что дело в нём.А вы ControlPersist держите глобально на
Host * или включаете точечно?#Linux #SSH #Ansible #DevOps #Terminal
👍4🔥1
Как достать пароль из kdbx, когда графика легла и буфер обмена недоступен
Всё проверено на keepassxc-cli 2.7.6 из репозитория Ubuntu 24.04.
Посмотреть, что вообще есть в базе
Нюанс: без
Вытащить конкретный пароль в терминал
Нюанс: обычный
Открыть базу с файлом ключей
Тот же
Найти запись, когда не помнишь путь
Вернёт полный путь вида
Забрать всю базу разом
Отдаёт в stdout все записи вместе с паролями открытым текстом. Полезно при переезде, но перенаправлять это в файл на общей машине — плохая идея.
Про
И про совет закрыть GUI перед работой в консоли. Проверил: при открытой базе KeePassXC создаёт рядом файл
А вы держите пароли в локальных kdbx или переехали на self-hosted Vaultwarden?
#Linux #KeePass #Security #CLI #DevOps
Всё проверено на keepassxc-cli 2.7.6 из репозитория Ubuntu 24.04.
Посмотреть, что вообще есть в базе
keepassxc-cli ls -R database.kdbx
Нюанс: без
-R увидишь только верхний уровень. Вложенные записи в группах не покажет — легко решить, что база пустая.Вытащить конкретный пароль в терминал
keepassxc-cli show -a Password database.kdbx "Production/DB"
Нюанс: обычный
show без ключей выводит Password: PROTECTED вместо значения. Флаг -s раскрывает все поля, -a Password отдаёт только пароль одной строкой — удобно подставлять в скрипт.Открыть базу с файлом ключей
keepassxc-cli ls -k keyfile.key database.kdbx
Тот же
-k работает во всех подкомандах. Если пароля на базе нет вообще, добавь --no-password, иначе утилита будет ждать ввода.Найти запись, когда не помнишь путь
keepassxc-cli search database.kdbx "DB"
Вернёт полный путь вида
/Production/DB, который дальше подставляется в show.Забрать всю базу разом
keepassxc-cli export -f csv database.kdbx
Отдаёт в stdout все записи вместе с паролями открытым текстом. Полезно при переезде, но перенаправлять это в файл на общей машине — плохая идея.
Про
clip, который советуют для копирования пароля в буфер. Именно в сценарии «GUI лёг» он и не работает: без графической сессии команда отвечает All clipping programs failed. Tried xclip. Смысл в нём есть только в живой графике, где он же и чистит буфер через 10 секунд.И про совет закрыть GUI перед работой в консоли. Проверил: при открытой базе KeePassXC создаёт рядом файл
.kdbx.lock. Утилита о нём знает, так что «гарантированного повреждения» не будет — но менять базу из двух мест одновременно всё равно не стоит, последняя запись затрёт чужие правки.А вы держите пароли в локальных kdbx или переехали на self-hosted Vaultwarden?
#Linux #KeePass #Security #CLI #DevOps
👍2
Диски гостей раздулись, а внутри места полно. Вернуть его хосту — и не просадить запись
Проверить, поддерживает ли диск discard
Нюанс: в подборках советуют
Ненулевые
Отдать блоки хосту прямо сейчас
Сначала
Настроить регулярный TRIM
Нюанс: свой скрипт в
Если всё-таки делаешь через cron, помни про две вещи. Скрипт без строки
Не включать онлайн-TRIM в fstab
Опция
Всё это работает, только если discard включён в настройках диска на самом гипервизоре. Иначе гость честно шлёт команды, а хост их игнорирует и продолжает копить мусор.
А вы fstrim по таймеру гоняете или руками, когда место кончилось?
#Linux #Virtualization #Proxmox #fstrim #DevOps
Проверить, поддерживает ли диск discard
lsblk --discard
Нюанс: в подборках советуют
hdparm -I /dev/sda | grep -i trim. На virtio-диске это не сработает вовсе — у меня вернуло Operation not permitted, потому что hdparm говорит по протоколу ATA, которого у виртуального диска нет. lsblk --discard показывает нужное для любого типа:
NAME DISC-GRAN DISC-MAX
vda 4K 1G
vdb 0B 0B
Ненулевые
DISC-GRAN и DISC-MAX означают, что discard проходит. Нули — гипервизор его не отдаёт, дальше можно не настраивать.Отдать блоки хосту прямо сейчас
fstrim -av --dry-run
fstrim -av
Сначала
--dry-run: он делает всё то же самое, но без самого discard. У меня показал 0 B (dry run) trimmed on /dev/vda, а боевой запуск вернул 243.1 GiB trimmed.Настроить регулярный TRIM
systemctl enable --now fstrim.timer
systemctl list-timers fstrim.timer
Нюанс: свой скрипт в
/etc/cron.weekly писать не нужно — в пакете util-linux уже лежит готовый fstrim.timer с OnCalendar=weekly и разбросом запуска до 100 минут, чтобы виртуалки не пошли в discard одновременно.Если всё-таки делаешь через cron, помни про две вещи. Скрипт без строки
#!/bin/sh не выполнится: run-parts: failed to exec: Exec format error. И файл с точкой в имени, вроде fstrim.sh, run-parts молча пропустит.Не включать онлайн-TRIM в fstab
UUID=xxxx / ext4 defaults,discard 0 1
Опция
discard при монтировании шлёт запрос на очистку при удалении каждого файла. Пакетный TRIM раз в неделю делает то же самое, но одним заходом и в спокойное время.Всё это работает, только если discard включён в настройках диска на самом гипервизоре. Иначе гость честно шлёт команды, а хост их игнорирует и продолжает копить мусор.
А вы fstrim по таймеру гоняете или руками, когда место кончилось?
#Linux #Virtualization #Proxmox #fstrim #DevOps
🔥2
Сервис упал ночью, до утра гигабайт логов. Порядок, в котором в них лезть
Начать с ошибок текущей загрузки
Сузить окно до времени инцидента
Нюанс: форматы вроде
Посмотреть, не прибило ли сервис ядро
Проверить блокировки SELinux
Нюанс: на Debian и Ubuntu команда чаще всего вернёт пустоту — там AppArmor, а не SELinux, и
Найти, кто сканирует сайт
Нюанс: ходовой вариант
Для пары серверов этого хватает. Когда машин десятки, CLI перестаёт работать и нужен Loki или ELK — но и там первый вопрос будет тот же: какое окно и какой приоритет.
А с чего начинаете вы, когда прод упал и надо быстро локализовать причину?
#Linux #Logs #journalctl #Troubleshooting #DevOps
Начать с ошибок текущей загрузки
journalctl -p err -b
-b отсекает всё, что было до последней перезагрузки, -p err оставляет уровень Error и выше. Цифровой вариант -p 3 делает то же самое, но словом читается лучше.Сузить окно до времени инцидента
journalctl --since "2026-08-13 23:00:00" \
--until "2026-08-14 01:00:00"
Нюанс: форматы вроде
yesterday 23:00:00, которые кочуют по подборкам, systemd не разбирает — получишь Failed to parse timestamp. Проверил на systemd 255: отдельно yesterday работает, -2h работает, 23:00 работает, а вот их комбинация — нет. Надёжнее всегда писать полную дату.Посмотреть, не прибило ли сервис ядро
dmesg -T --level=err,warn
dmesg -T | grep -i oom-killer
-T переводит секунды аптайма в нормальную дату. Фильтр по уровню полезнее грепа: покажет и OOM, и ошибки диска, и отвалившийся сетевой интерфейс.Проверить блокировки SELinux
ausearch -m avc -ts recent
Нюанс: на Debian и Ubuntu команда чаще всего вернёт пустоту — там AppArmor, а не SELinux, и
/sys/fs/selinux просто нет. Шаг актуален для RHEL-семейства; на Debian смотри journalctl -t audit и dmesg | grep -i apparmor.Найти, кто сканирует сайт
awk -F'"' '{split($3,a," ");
if (a[1]==404) print $1}' access.log |
awk '{print $1}' | sort | uniq -c | sort -rn
Нюанс: ходовой вариант
awk '$9 == 404' разваливается, если в URL попал пробел — поле уезжает, и строка молча не считается. У меня из трёх запросов с 404 такой вариант нашёл два. Разбор по кавычкам берёт код ответа там, где он реально лежит.Для пары серверов этого хватает. Когда машин десятки, CLI перестаёт работать и нужен Loki или ELK — но и там первый вопрос будет тот же: какое окно и какой приоритет.
А с чего начинаете вы, когда прод упал и надо быстро локализовать причину?
#Linux #Logs #journalctl #Troubleshooting #DevOps
👍5🔥1
Прокинуть скрипт и каталог на сервер и не собрать по дороге чужие абсолютные пути
Всё проверено на OpenSSH 9.6p1, подключением к живому sshd.
Увидеть, какие параметры реально применились
Отдаёт итоговый конфиг после разбора всех
Выполнить локальный скрипт на сервере
Нюанс:
Убрать устаревший ключ хоста
Нюанс: для нестандартного порта нужен формат
Забрать каталог без промежуточных файлов
Нюанс:
Уйти в фон сразу после авторизации
Управление вернулось через 181 мс, при этом клиентский процесс продолжает висеть, пока команда не отработает. Удобно для проброса портов:
Держать ключ в агенте ограниченное время
Через 30 секунд ключ уйдёт из памяти агента сам. Нюанс: без запущенного
Про
А чем пробрасываете доступ к внутренним панелям —
#Linux #SSH #Networking #DevOps #Terminal
Всё проверено на OpenSSH 9.6p1, подключением к живому sshd.
Увидеть, какие параметры реально применились
ssh -G user@host
Отдаёт итоговый конфиг после разбора всех
Host и Match: порт, пользователя, список шифров, таймауты. Быстрее, чем перечитывать ~/.ssh/config глазами и гадать, какая секция сработала. Если сессия висит — ssh -vvv покажет, на каком этапе.Выполнить локальный скрипт на сервере
ssh user@host bash -s arg1 arg2 < ./script.sh
Нюанс:
-s обязателен, если передаёшь аргументы. Без него bash примет arg1 за имя файла и ответит No such file or directory. И второе: интерактивный read внутри такого скрипта работать не будет — stdin уже занят самим скриптом.Убрать устаревший ключ хоста
ssh-keygen -f ~/.ssh/known_hosts -R '[10.0.0.1]:2222'
Нюанс: для нестандартного порта нужен формат
[host]:port в кавычках. Просто -R 10.0.0.1 строку не найдёт и молча ничего не сделает. Старый файл сохраняется рядом как known_hosts.old.Забрать каталог без промежуточных файлов
ssh user@host "tar -cf - -C /data ." | tar -xC ~/bak/
Нюанс:
-C на удалённой стороне обязателен. Вариант tar -cf - /data утащит в архив весь абсолютный путь, и локально ты получишь ~/bak/data/... вместо содержимого. Проверил — распаковалось именно так.Уйти в фон сразу после авторизации
ssh -f user@host "sleep 300"
Управление вернулось через 181 мс, при этом клиентский процесс продолжает висеть, пока команда не отработает. Удобно для проброса портов:
ssh -f -N -L 9998:localhost:15672 user@host.Держать ключ в агенте ограниченное время
ssh-add -t 30 ~/.ssh/id_ed25519
Через 30 секунд ключ уйдёт из памяти агента сам. Нюанс: без запущенного
ssh-agent команда просто скажет Could not open a connection to your authentication agent.Про
sshpass из подборок: он кладёт пароль в аргументы процесса, где его видно в ps любому пользователю системы. Для закрытого стенда — ладно, дальше стенда лучше ProxyJump и ключи.А чем пробрасываете доступ к внутренним панелям —
-L руками или прописываете в конфиг?#Linux #SSH #Networking #DevOps #Terminal
👍5
🎥 Вебинар: «LVM без простоя: расширение тома, перенос данных и аварийный откат через snapshot»
На открытом уроке разберем, как использовать возможности LVM при работе с хранилищем в production-среде. Рассмотрим расширение логического тома, перенос данных на другое блочное устройство и применение snapshot для возврата к предыдущему состоянию системы.
О чем поговорим:
— Как устроен LVM и какие задачи он помогает решать
— Как расширять логические тома при увеличении объема данных
— Как перенести данные на другое блочное устройство
— Что такое snapshot и как он работает
— Как использовать snapshot для отката при возникновении проблем
🧠 Вебинар приурочен к старту курса «Администратор Linux. Продвинутый уровень». Программа постоянно обновляется с учётом современных требований рынка. Занятия проводят практикующие эксперты, которые ежедневно работают с производственной инфраструктурой. Вы изучите востребованные инструменты: Zabbix, Prometheus, Docker, Nginx, PostgreSQL, ELK, Ansible, SELinux, Bash и многие другие.
👉 Для участия зарегистрируйтесь https://otus.pw/T9OrR/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
На открытом уроке разберем, как использовать возможности LVM при работе с хранилищем в production-среде. Рассмотрим расширение логического тома, перенос данных на другое блочное устройство и применение snapshot для возврата к предыдущему состоянию системы.
О чем поговорим:
— Как устроен LVM и какие задачи он помогает решать
— Как расширять логические тома при увеличении объема данных
— Как перенести данные на другое блочное устройство
— Что такое snapshot и как он работает
— Как использовать snapshot для отката при возникновении проблем
🧠 Вебинар приурочен к старту курса «Администратор Linux. Продвинутый уровень». Программа постоянно обновляется с учётом современных требований рынка. Занятия проводят практикующие эксперты, которые ежедневно работают с производственной инфраструктурой. Вы изучите востребованные инструменты: Zabbix, Prometheus, Docker, Nginx, PostgreSQL, ELK, Ansible, SELinux, Bash и многие другие.
👉 Для участия зарегистрируйтесь https://otus.pw/T9OrR/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
👍5👎1
Обойти массив путей с пробелами и не получить вместо одной папки три несуществующих.
Перебрать элементы, не разорвав их по пробелам
Кавычки вокруг
Посчитать элементы
Нюанс:
Обойти разреженный массив
Нюанс: если удалить элемент через
Прочитать файл в массив
Нюанс:
Сделать словарь
Нюанс:
Общее у всех пяти: bash не ругается, а тихо делает не то. Поэтому
А вы конфиги в bash разбираете ассоциативными массивами или сразу отдаёте jq?
#Linux #Bash #Shell #Scripting #DevOps
Перебрать элементы, не разорвав их по пробелам
folders=("/var/log/nginx" "/var/log/app with space")
for d in "${folders[@]}"; do
ls -ld "$d"
done
Кавычки вокруг
${folders[@]} обязательны. Без них тот же цикл на моём тесте выдал четыре итерации вместо двух: путь распался на /var/log/app, with и space. И не путай @ с *: под кавычками "${folders[*]}" склеивает весь массив в одну строку.Посчитать элементы
echo "${#folders[@]}"
Нюанс:
${#folders} без [@] — это не размер массива, а длина первого элемента. У меня вернуло 8 вместо 3, и никакой ошибки.Обойти разреженный массив
for i in "${!sparse[@]}"; do
echo "$i = ${sparse[$i]}"
done
Нюанс: если удалить элемент через
unset, индексы останутся с дырками. Массив с индексами 0, 5, 9 имеет размер 3 — обход по seq 0 2 найдёт один элемент из трёх. Перебирать надо индексы через ${!arr[@]}, а не числа от нуля.Прочитать файл в массив
readarray -t lines < /var/log/app.log
readarray -t lines < <(grep -v '^$' /var/log/app.log)
Нюанс:
cat file | readarray -t lines не работает. Пайп запускает readarray в подоболочке, массив заполняется там и умирает вместе с ней. У меня получилось 0 элементов вместо 4, снова без ошибки. Нужен либо редирект, либо подстановка процесса < <(...).Сделать словарь
declare -A ports=([nginx]=80 [php-fpm]=9000)
Нюанс:
declare -A не формальность. Без него bash создаёт обычный индексный массив, все строковые ключи считаются нулём, и каждое присваивание перетирает предыдущее. Проверил: ${ports[nginx]} вернуло 9000 — значение от php-fpm.Общее у всех пяти: bash не ругается, а тихо делает не то. Поэтому
shellcheck на скриптах с массивами экономит больше времени, чем кажется.А вы конфиги в bash разбираете ассоциативными массивами или сразу отдаёте jq?
#Linux #Bash #Shell #Scripting #DevOps
👍2
Выкинуть из скрипта форки awk и cut и не нарваться на подстановку, которая молчит
Разобрать путь без basename и dirname
Замерил тысячу итераций в цикле:
Собрать файлы в массив вместо ls
Нюанс: без
Сравнить вывод двух команд без временных файлов
Задать дефолт и проверить обязательные переменные
Нюанс: двоеточие в
Собрать тайминги шагов скрипта
Нюанс: в подборках сюда пишут
А что вы чаще выносите из скриптов — cut и awk или всё-таки оставляете, потому что читается понятнее?
#Linux #Bash #Shell #Scripting #DevOps
Разобрать путь без basename и dirname
path="/var/log/nginx/access.log.tar.gz"
name="${path##*/}" # access.log.tar.gz
dir="${path%/*}" # /var/log/nginx
Замерил тысячу итераций в цикле:
basename — 1107 мс, подстановка — 5 мс. Разница в двести раз, и это не про скорость самой утилиты, а про форк процесса на каждый вызов.Собрать файлы в массив вместо ls
shopt -s nullglob
files=(/var/log/nginx/*.log)
cp -- "${files[@]}" /backup/logs/
Нюанс: без
nullglob массив по несуществующей маске не пустой. Проверил — в нём оказался один элемент, сама строка /var/log/nginx/*.nosuch, и cp уедет с несуществующим файлом. shopt -s nullglob заставляет пустой glob разворачиваться в ноль элементов.Сравнить вывод двух команд без временных файлов
diff <(ls -1 dir1) <(ls -1 dir2)
<(...) подставляет путь вида /dev/fd/63, за которым стоит обычный pipe — проверил через ls -l. Нюанс: код возврата команды внутри скобок теряется, diff его не увидит. И это только bash: в dash тот же синтаксис даёт Syntax error, так что для #!/bin/sh не годится.Задать дефолт и проверить обязательные переменные
: "${TIMEOUT:=30}"
: "${DB_USER:?не задана}"
Нюанс: двоеточие в
:? обязательно. Вариант ${DB_USER?...} из подборок срабатывает только когда переменная не объявлена вовсе — пустую строку он пропускает, и скрипт идёт дальше с пустым логином. С :? тот же случай падает с кодом 127. То же и с :-: он подставляет дефолт, но не присваивает, переменная остаётся пустой.Собрать тайминги шагов скрипта
PS4='+ ${EPOCHREALTIME} '
set -x
Нюанс: в подборках сюда пишут
PS4='+ $(date "+%s.%N") => ' — и это форк date на каждую строку трассировки, в посте про избавление от форков. Встроенная EPOCHREALTIME даёт то же время с микросекундами и без единого процесса.А что вы чаще выносите из скриптов — cut и awk или всё-таки оставляете, потому что читается понятнее?
#Linux #Bash #Shell #Scripting #DevOps
👍3🔥1
Найти на сервере скрытых рутов и не поверить проверке, которая их не видит.
Найти лишние учётки с нулевым UID
Нюанс: в подборках пишут
Убедиться, что root не пускают по SSH
Нюанс, и он главный.
Именно так выглядит типовой облачный образ.
Проверить sudoers перед сохранением
Найти SUID-бинарники
Нюанс:
Про
А вы root на серверах отключаете совсем или держите для аварийной консоли?
#Linux #Security #SSH #sudo #DevOps
Найти лишние учётки с нулевым UID
getent passwd | awk -F: '($3 == 0) {print $1}'
Нюанс: в подборках пишут
awk -F: '($3 == "0")' — со строковым сравнением. Оно пропускает UID, записанный как 00 или 0000. Проверил: завёл пользователя с -u 00, id показал uid=0(root), то есть для ядра это полноценный рут, а строковая проверка его не увидела. Без кавычек awk сравнивает числа и ловит все варианты.getent вместо cat /etc/passwd нужен, чтобы захватить пользователей из LDAP и SSSD.Убедиться, что root не пускают по SSH
sshd -T | grep -i permitrootlogin
Нюанс, и он главный.
grep по /etc/ssh/sshd_config показывает то, что написано, а не то, что действует. Собрал стенд: в основном конфиге PermitRootLogin no, а в /etc/ssh/sshd_config.d/99-cloud.conf — yes. Строка Include стоит выше, drop-in побеждает:
grep -> PermitRootLogin no
sshd -T -> permitrootlogin yes
Именно так выглядит типовой облачный образ.
sshd -T печатает итоговую конфигурацию после всех включений.Проверить sudoers перед сохранением
visudo -c -f /etc/sudoers.d/devops
visudo без аргументов знают все, а вот -c -f работает с любым файлом и годится для CI или проверки после Ansible. На кривом правиле показывает строку и место ошибки, на нормальном — parsed OK. Заодно sudo -l покажет, что реально разрешено текущему пользователю.Найти SUID-бинарники
find / -xdev -perm /6000 -type f -ls 2>/dev/null
Нюанс:
-perm -4000 -user root из чек-листов пропускает SGID. На моей машине разница 9 файлов против 12 — мимо проверки прошли chage, expiry, ssh-agent. Фильтр -user root тоже лишний: чужой SUID опаснее рутового, а он под этот фильтр не попадёт. И -xdev обязателен, иначе find захлебнётся на /proc.Про
sudo -i вместо su спорить не буду, но помнить стоит другое: жёсткие правила в sudoers обходятся через less, find, awk и десяток других утилит — у всех есть побег в шелл. Разрешать по одной команде безопаснее, чем кажется, только если это не интерпретатор.А вы root на серверах отключаете совсем или держите для аварийной консоли?
#Linux #Security #SSH #sudo #DevOps
👍3
🎥 Вебинар: Где Linux хранит настройки и логи: разбираем файловую структуру на практике.
На открытом уроке разберем структуру каталогов Linux и узнаем, где искать конфигурационные файлы, системные журналы и другие важные данные. Рассмотрим назначение основных директорий и вспомним, как права доступа влияют на работу с файлами.
Данный открытый урок проходит в рамках курса «Администратор Linux. Базовый уровень».
О чем поговорим:
— Как устроена файловая структура Linux
— Для чего предназначены основные системные каталоги
— Что такое конфигурационный файл и где обычно хранятся настройки сервисов
— Где Linux хранит системные логи и как найти нужный журнал
— Как права доступа влияют на чтение и изменение файлов
🧠 Вебинар приурочен к старту курса «Администратор Linux. Базовый уровень». Вы освоите Bash, TCP/IP, Nginx, Apache, MySQL, Docker, Git, Prometheus, Grafana и ELK. На живых занятиях с практикующими экспертами научитесь настраивать веб-серверы, работать с сетью, контейнерами, мониторингом и фильтрацией трафика.
👉 Для участия зарегистрируйтесь https://otus.pw/e3sf/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
На открытом уроке разберем структуру каталогов Linux и узнаем, где искать конфигурационные файлы, системные журналы и другие важные данные. Рассмотрим назначение основных директорий и вспомним, как права доступа влияют на работу с файлами.
Данный открытый урок проходит в рамках курса «Администратор Linux. Базовый уровень».
О чем поговорим:
— Как устроена файловая структура Linux
— Для чего предназначены основные системные каталоги
— Что такое конфигурационный файл и где обычно хранятся настройки сервисов
— Где Linux хранит системные логи и как найти нужный журнал
— Как права доступа влияют на чтение и изменение файлов
🧠 Вебинар приурочен к старту курса «Администратор Linux. Базовый уровень». Вы освоите Bash, TCP/IP, Nginx, Apache, MySQL, Docker, Git, Prometheus, Grafana и ELK. На живых занятиях с практикующими экспертами научитесь настраивать веб-серверы, работать с сетью, контейнерами, мониторингом и фильтрацией трафика.
👉 Для участия зарегистрируйтесь https://otus.pw/e3sf/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
👎1
Смонтировать сетевую шару и не подвесить процессы, когда сервер отвалится.
Убрать пароль из fstab
Файл
Отцепить зависшую шару
Проверил на tmpfs: после
Посмотреть, что отдаёт сервер
Нюанс:
Монтировать по требованию, а не при загрузке
Нюанс:
Не залипнуть на медленном NFS
Нюанс: man про
А вы сетевые шары монтируете через fstab или отдаёте systemd с automount?
#Linux #NFS #Samba #systemd #DevOps
Убрать пароль из fstab
mount -t cifs -o credentials=/etc/cifs-creds \
//10.0.0.10/share /mnt/samba
Файл
/etc/cifs-creds из трёх строк: username=, password=, domain=. Нюанс: chmod 600 на нём — целиком на вашей совести, mount права не проверяет и с файлом 644 отработает молча. Заодно проверьте, что каталог с ним не уезжает в бэкап открытым текстом.Отцепить зависшую шару
umount -l /mnt/nfs
Проверил на tmpfs: после
-l каталог пуст и монтирования в mount больше нет, но процесс, державший файл открытым, спокойно дочитал его по старому дескриптору. Нюанс: -l убирает точку из дерева, а не освобождает ресурсы — пока живы процессы, ФС остаётся. Ищите их через lsof +D до того, как отцепите, иначе искать будет негде.-f имеет смысл только для NFS и только вместе с недоступным сервером, для cifs он почти ничего не даёт.Посмотреть, что отдаёт сервер
showmount -e 10.0.0.10
Нюанс:
showmount говорит по протоколу MOUNT, которого в NFSv4 нет вообще. На сервере с отключёнными v2 и v3 получите clnt_create: RPC: Program not registered — и это не значит, что сервер мёртв. Для v4 просто монтируйте корень экспорта: mount -t nfs4 10.0.0.10:/ /mnt.Монтировать по требованию, а не при загрузке
//10.0.0.10/share /mnt/samba cifs
noauto,x-systemd.automount,_netdev,
credentials=/etc/creds 0 0
Нюанс:
_netdev нужен наравне с x-systemd.automount. Без него systemd не знает, что монтирование сетевое, и может дёрнуть его до поднятия интерфейсов. Плюс x-systemd.idle-timeout= отцепит шару после простоя — на нестабильном канале это лучше, чем висящая точка.Не залипнуть на медленном NFS
mount -t nfs -o soft,timeo=50,retrans=2 \
10.0.0.10:/data /mnt/nfs
timeo в децисекундах, так что 50 — это 5 секунд. По умолчанию для TCP стоит 600, то есть минута, да ещё с линейным ростом после каждой переотправки.Нюанс: man про
soft пишет прямо — silent data corruption. Приложение получит I/O error, и если оно его не обработает, запись потеряется тихо. Для баз и любой записи оставляйте hard. И не тащите из старых шпаргалок intr — опция игнорируется с ядра 2.6.25.А вы сетевые шары монтируете через fstab или отдаёте systemd с automount?
#Linux #NFS #Samba #systemd #DevOps
👍4