Bash Days | Linux | DevOps
Авторский блог от действующего девопса Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу. Автор: Роман Шубин Реклама: @maxgrue MAX: https://max.ru/bashdays Курс: @tormozilla_bot Блог: https://bashdays.ru
Больше📈 Аналитический обзор Telegram-канала Bash Days | Linux | DevOps
Канал Bash Days | Linux | DevOps (@bashdays) языкового сегмента Русский является активным участником. Сейчас сообщество объединяет 23 794 подписчиков, занимая 5 701 место в категории Технологии и приложения и 28 128 место в регионе Россия.
📊 Показатели аудитории и динамика
С момента создания невідомо проект демонстрирует стремительный рост, собрав аудиторию из 23 794 подписчиков.
Согласно последним данным от 17 июня, 2026, канал показывает стабильную активность. За последние 30 дней изменение числа участников составило -202, а за последние 24 часа — -5, при этом общий охват остаётся высоким.
- Статус верификации: Не верифицирован
- Уровень вовлечённости (ER): Средний показатель вовлечённости аудитории составляет 21.91%. В первые 24 часа после публикации контент обычно набирает 12.48% реакций от общего числа подписчиков.
- Охват публикаций: В среднем каждый пост получает 5 213 просмотров. В течение первых суток публикация набирает 2 971 просмотров.
- Реакции и взаимодействия: Аудитория активно поддерживает контент: среднее количество реакций на один пост — 21.
- Тематические интересы: Контент сосредоточен на ключевых темах, таких как bashdays, linux, bash, docker, скрипт.
📝 Описание и контентная политика
Автор описывает ресурс как площадку для выражения субъективного мнения:
“Авторский блог от действующего девопса
Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.
Автор: Роман Шубин
Реклама: @maxgrue
MAX: https://max.ru/bashdays
Курс: @tormozilla_bot
Блог: https://bashdays.r...”
Благодаря высокой частоте обновлений (последние данные получены 18 июня, 2026) канал поддерживает актуальность и высокий уровень охвата публикаций. Аналитика показывает, что аудитория активно взаимодействует с контентом, что делает его важной точкой влияния в категории Технологии и приложения.
def respond_to_telnet_commands(sock, data):
"""Отвечает на IAC-команды, отправляя WONT на все DO и DONT на все WILL."""
i = 0
while i < len(data):
if data[i] == 0xff and i + 2 < len(data): # IAC
command, option = data[i + 1], data[i + 2]
if command == 0xfd: # DO
sock.send(bytes([0xff, 0xfc, option])) # WONT
elif command == 0xfb: # WILL
sock.send(bytes([0xff, 0xfe, option])) # DONT
i += 3
else:
i += 1
respond_to_telnet_commands - функция понадобится только если в ответе от сервера получаем такое b"\xff\xfb%\xff\xfb&\xff\xfd\x18\xff\xfd \xff\xfd#\xff\xfd'\xff\xfd$".
Если же в конце этой кишки будет login то всё ок можно сразу отправлять логин sock.send(b'login\r\n')
Подключение через ipython:
import socket
sock = socket.socket()
sock.connect(('192.168.1.100', 5000))
data = sock.recv(1024)
print(data)
respond_to_telnet_commands(sock, data)
data = sock.recv(1024)
print(data)
respond_to_telnet_commands(sock, data)
data = sock.recv(1024)
print(data)
sock.send(b'<login>\r\n')
data = sock.recv(1024)
print(data)
sock.send(b'<pass>\r\n')
data = sock.recv(1024)
print(data)
sock.send(b'<command>\r\n')
data = sock.recv(1024)
print(data.decode())
Подключение к telnet без аутентификации
ㅤ
В /etc/inetd.conf добавить:
5000 stream tcp nowait root /usr/local/bin/telnet_noauth
telnet_noauth - 5000 можно заменить на любой порт или написать просто telnet будет использоваться для подключения порт 23
<service_name>: имя службы (например, telnet), соответствует записи в /etc/services.
<socket_type>: обычно stream (для TCP) или dgram (для UDP).
<protocol>: протокол, например, tcp или udp.
<wait/nowait>: wait (ждёт завершения процесса) или nowait (многопоточный).
<user>: пользователь, от имени которого запускается служба (например, root).
<server_program>: путь к исполняемому файлу службы (например, /usr/sbin/in.telnetd).
<server_args>: аргументы для программы (обычно имя программы).
Добавить скрипт telnet_noauth с содержимым:
#!/bin/bash
/bin/bash -i
Дать права на исполнения:
chmod +x /usr/local/bin/**telnet_noauth**
Подключение через ipython:
import socket
sock = socket.socket()
sock.connect(('192.168.1.100', 5000))
data = sock.recv(1024)
print(data)
sock.send(b'<command>\r\n')
data = sock.recv(1024)
print(data.decode())
Такие дела!
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blog🤖 AI&ML — узнаете, как устроены RAG-системы и мультиагентные системы и как начать их использовать. ☁️ Cloud Infrastructure — нюансы сетевой архитектуры, проектирование IaaS‑кластеров на K8s, возможности балансировщиков и производительность SDN. 📈 Data&Analytics — про современные подходы к Big Data: тренды, интеграцию с AI-агентами и инструменты для хранения, обработки и анализа. ⚙️ Dev Platform Services — заглянем «под капот» решений, чтобы облегчить повседневную рутину разработки и настройки сервисов.А еще вас ждут демо, воркшопы, карьерные консультации, кастомный мерч и яркое afterparty. Не пропустите🖱
p1p0 у нас адрес 10.0.0.1/31 (у провайдера соответственно 10.0.0.0/31)
- На интерфейсе p1p1 у нас адрес 10.0.0.3/31 (у провайдера соответственно 10.0.0.2/31)
- На интерфейсе dummy-moew у нас висит адрес 188.222.33.1/32
- На сервере стоит гордый птыц bird, который делает bgp с провайдером
Зачем 30e и 31е маски? Потому что могу, да и ну его, айпишники тратить на 30е маски...
Делаем докериную сетку (bridge) и запускаем контейнер (например alpine:3), проверяем сетку:
docker network create -d bridge br-dzhigurda
docker run -it --rm --network br-dzhigurda alpine:3
ping -c 1 8.8.8.8
Получаем:
PING 8.8.8.8 (8.8.8.8): 56 data bytes
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
Херня, товарищи! Идём смотреть правила в iptables:
-A POSTROUTING -s 198.18.66.0/24 ! -o br-dzhigurda -j MASQUERADE
Видим, что Docker делает MASQUERADE. Это обычный, всем привычный NAT, но! оно работает только с физическими сетевыми интерфейсами. И весь трафик от сервера выходит с IP именно физических интерфейсов. Нам такое не подходит.
Имеем, то что мы смотрим
Закапываемся в доку Docker и трём глаза в изумлении, ничего не нашли. Отлично, полезем в кишочки разбираться. Нам надо попробовать сделать вместо MASQUERADE - SNAT.
Идём в сырцы Docker (а точнее в moby/moby), грепаем, копаемся, и видим
// Файл integration/network/network_linux_test.go
ipv4SNATAddr := "172.0.0.172"
// Create a bridge network with --opt com.docker.network.host_ipv4=172.0.0.172
bridgeName := "hostIPv4Bridge"
network.CreateNoError(ctx, t, c, bridgeName,
network.WithDriver("bridge"),
network.WithOption("com.docker.network.host_ipv4", ipv4SNATAddr),
network.WithOption("com.docker.network.bridge.name", bridgeName),
)
Ага. В доке этого не было. Поехали, попробуем!
docker network create -d bridge br-fuckingsnat --opt com.docker.network.bridge.name=br-fuckingsnat --opt com.docker.network.host_ipv4=188.222.33.1
docker run -it --rm --network br-dzhigurda alpine:3
ping -c 1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=108 time=16.5 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 16.497/16.497/16.497/0.000 ms
Отлично, связь есть. Смотрим в iptables:
-A POSTROUTING -s 198.18.66.0/24 ! -o br-fuckingsnat -j SNAT --to-source 188.222.33.1
Поразительно. Не, я точно слепой, такую же фичу не могли в документации пропустить... Или могли? Идём гуглим, и получаем всего один результат! И тот в Release Notes...
Итоги
Мы имеем работающие Docker контейнеры, которые SNAT-ятся с нужного нам IP, кровь из глаз после вида исходников, и непреодолимое желание написать разработчикам, что бы поправили доку.
🛠 #балансбатл #networks
—
✅ @bashdays ✅ @linuxfactory ✅ @blog192.168.0.87) который пулял куда-то API-запросы, с такой особенность, что работал он не как все машины через шлюз 192.168.0.1.
ㅤ
А пулял запросы в другую сеть которая была за шлюзом 192.168.0.176 и далее в дивный неведомый интернет, хуй знает куда он там чего отправляет, это в данном случае не важно.
Важно что запросы должны летать в шлюз 192.168.0.176 а общий шлюз в сети 192.168.0.1 с внешним X.X.X.X.
Все было хорошо, пока манагеры не захотели видеть этот сайт снаружи сети (IP X.X.X.X, всякие DNS опустим, они тут не важны).
Разраб тоже сказал что ему нужна 22 дырка снаружи, и понятно что проброс порта на шлюзе не сработает. Надо выдумывать маршруты.
Присказка окончена, понеслася сказка.
Пишем маршрутизацию
/etc/netplan/00-installer-config.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses: [192.168.0.87/24]
routes:
- to: 0.0.0.0/0 # Весь интернет-трафик через 192.168.0.176
via: 192.168.0.176
metric: 100
- to: 192.168.0.1/32 # Явный маршрут до шлюза 192.168.0.1
via: 192.168.0.1
metric: 50 # Более высокий приоритет
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
Применяем изменения (тут если накосячил сообщат)
sudo netplan apply
Создаем отдельную таблицу маршрутизации (table 100)
sudo ip route add default via 192.168.0.1 dev eth0 table 100
Создаем правила маршрутизации для каждого порта:
sudo ip rule add iif eth0 dport 22 lookup 100
sudo ip rule add iif eth0 dport 8080 lookup 100
Что делает:
iif eth0 — трафик, пришедший на интерфейс eth0.
dport 22 и dport 8080 — если это SSH (порт 22) или HTTP (порт 8080).
lookup 100 — использовать таблицу 100 для маршрутизации этого трафика.
Пометка исходящего трафика (iptables mangle + MARK)
sudo iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 1
sudo iptables -t mangle -A OUTPUT -p tcp --sport 8080 -j MARK --set-mark 1
Что делает:
-t mangle — таблица mangle для изменения (marking) пакетов.
--sport 22 и --sport 8080 — если исходящий трафик идёт с портов 22 (SSH) или 8080 (HTTP).
--set-mark 1 — помечает такие пакеты меткой 1.
Зачем:
Метка (mark) позволяет позже применить к пакетам особые правила маршрутизации.
Правило маршрутизации по метке (fwmark)
sudo ip rule add fwmark 1 lookup 100
Что делает:
fwmark 1 — если пакет помечен меткой 1 (как в iptables выше).
lookup 100 — использовать таблицу 100 для маршрутизации.
Зачем:
Это гарантирует, что ответы на SSH/HTTP-запросы (исходящие с портов 22/8080) будут направляться через шлюз 192.168.0.1 (из таблицы 100), а не через основной маршрут.
Общий смысл всей настройки:
Для входящих запросов на eth0 (порты 22 и 8080): Ответный трафик маркируется (mark=1).
Маркированный трафик направляется через таблицу 100 (шлюз 192.168.0.1).
Для исходящих ответов (SSH/HTTP с портов 22/8080): Трафик также маркируется и направляется через таблицу 100.
Результат:
Весь трафик, связанный с SSH (22) и HTTP (8080), всегда идёт через шлюз 192.168.0.1, даже если в системе есть другие маршруты по умолчанию.
Проверяем:
ssh -p 1422 user@Х.Х.Х.Х
curl ident.me #должен вернуть внешний адрес шлюза
Если все отработало, идем дальше.
При перезагрузке вся настроенная маршрутизация слетит.
Надо восстанавливать при каждой перезагрузке. Тут на выбор. Пойдем по пути systemd.
В комментах содержимое portforward-routes.service, в пост не влезло.Включаем
sudo systemctl daemon-reload
sudo systemctl enable portforward-routes.service
Перегружаемся\проверяем.
🛠 #балансбатл #networks
—
✅ @bashdays ✅ @linuxfactory ✅ @blogaccess.log пропал и больше не появляется? Nginx то в данный момент работает, запросы на него идут." (с)
б) "Где карта Билли? Нам нужна карта!" (с)
в) "Внятного ответа не получил, что-то на уровне — он появится спустя сутки, когда logrotate отработает. Дада… будем сутки без логов сидеть." (с)
Отвечаем на вопросы и решаем проблемы:
a) удаление файлов в Linux лишь убирает название файла из файла каталога, но inode с данными остаётся, пока его держит открытым хоть один процесс.
Так что это не проблема. Идем дальше.
б) и в) Помогаем Билли с картой и не сидим сутки без логов.
Пока процесс открыт он продолжает держать fd (файловые дескрипторы), даже если файл удален.
1. Проверим утверждение. Пишем скрипт:
#!/bin/bash
echo $$ > /tmp/nginx.pid
exec 3> /tmp/access.log
while true; do
echo "$(date) Где карта Билли? Нам нужна карта!!!" >&3
sleep 1
done
2. Запускаем, читаем логи:
$ ./simple.sh &
[2] 13502
$ tail -f /tmp/access.log
Sun Aug 10 23:15:42 MSK 2025 Где карта Билли? Нам нужна карта!!!
Sun Aug 10 23:15:43 MSK 2025 Где карта Билли? Нам нужна карта!!!
3. Удалим файл.
rm /tmp/access.log
4. Процесс продолжает писать в файл. Смотрим (PID знаем, смотри выше):
$ ls -l /proc/13502/fd
total 0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 0 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 1 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 2 -> /dev/pts/0
lr-x------ 1 aflw aflw 64 Aug 10 23:23 255 -> /home/aflw/planka-backup/simple.sh
l-wx------ 1 aflw aflw 64 Aug 10 23:23 3 -> '/tmp/access.log (deleted)'
или так (тут PID схватим по другому), через watch наглядно:
watch -n1 -d "lsof -p $(cat /tmp/nginx.pid) | grep deleted"
Every 1.0s: lsof -p 13502 | grep deleted
simple.sh 13502 aflw 3w REG 8,32 188825 17988 /tmp/access.log (deleted)
5. Билли возвращает карту:
cp /proc/13502/fd/3 /tmp/access.log.restored
Вуаля! Дескриптор НЕ потерялся, процесс не сошел с ума, логи восстановлены.
—
Что дальше? Файл то удален!
Представим, что у нас серьезный PROD, тяжеленный "Билли"нговый демон СверхБербанка, где перезапуск = потеря соединений и часовое восстановление сессий!
Подкинем новый файл /tmp/access_new.log без перезапуска процесса. Будем использовать отладчик gdb.
1. Запускаем
sudo gdb -p $(cat /tmp/nginx.pid)
2. Выполняем внутри gdb:
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
# открываем новый файл для логов
(gdb) call (int) open("/tmp/access_new.log", 66, 0644)
$1 = 4 # новый файловый дескриптор
(gdb) call (int) dup2(4, 3) # дублируем fd 4 в позицию 3.
$2 = 3
(gdb) call (int) close(4) # Закрываем временный fd 4
$3 = 0
(gdb) detach
Detaching from program: /usr/bin/bash, process 13502
[Inferior 1 (process 13502) detached]
(gdb) quit
3. Проверяем файловые дескрипторы процесса и лог:
$ ls -l /proc/13502/fd
total 0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 0 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 1 -> /dev/pts/0
lrwx------ 1 aflw aflw 64 Aug 10 23:23 2 -> /dev/pts/0
lr-x------ 1 aflw aflw 64 Aug 10 23:23 255 -> /home/aflw/planka-backup/simple.sh
lrwx------ 1 aflw aflw 64 Aug 10 23:23 3 -> /tmp/access_new.log
$ tail -f /tmp/access_new.log
Mon Aug 11 00:17:54 MSK 2025 Где карта Билли? Нам нужна карта!!!
На этом все. Процесс пишет в новый файл с тем же дескриптором.
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blogЖдем твоих лайков и комментариев.🛠 #балансбатл — ✅ @bashdays ✅ @linuxfactory ✅ @blog
/usr/local/bin/run.sh, который запускает другой bash скрипт на удаленном сервере, и получает его выхлоп:
#!/bin/bash
SSH_USER="root"
SSH_HOST="123.45.67.89"
SSH_KEY="/usr/local/share/ssh_keys/my_node" # Путь к приватному ключу
REMOTE_SCRIPT="/usr/local/bin/send_node_stat.sh" # месторасполдожение удаленного скрипта
# проверка наличия ключа необязательна, но неплохо помогает при отладке
if [[ ! -f "$SSH_KEY" ]]; then
echo "Ошибка: ssh ключ не найден по пути $SSH_KEY"
exit 1
fi
ssh -i "$SSH_KEY" "$SSH_USER@$SSH_HOST" "$REMOTE_SCRIPT" # собственно само выполнение удаленного скипта
Но, вся фича в том, что нам его нужно выполнить от непривилегированного пользователя - бомжа, а у бомжей как известно - нет домашней директории =(.
# создаем бомжа
useradd -r -s /bin/false bomj
usermod -aG sudo bomj
Так как нам нужно выполнять скрипт на удаленном сервере, справедливо настроить для ssh authentication key.
Но, это нужно сделать в каталоге, к которому будет иметь доступ пользователь bomj (также предполагается, что наш бомжара вход в группу run_scripts). В данном случае создаем от пользователя root:
mkdir -p /usr/local/share/ssh_keys
chown -R root:run_scripts /usr/local/share/ssh_keys
chmod -R 750 /usr/local/share/ssh_keys
chmod g+r /usr/local/share/ssh_keys/
ssh-keygen -t ed25519 -f /usr/local/share/ssh_keys/my_node
ssh-copy-id -i /usr/local/share/ssh_keys/my_node.pub root@123.45.67.89
Вроде бы все норм, в скрипте указан верный ключ, но, если мы попробуем запустить этот скрипт под бездомным пользователем, то каждый раз при запуске будем получать предложение ввести yes.
sudo -u bomj /usr/local/bin/run.sh
The authenticity of host '123.45.67.89 (123.45.67.89)' can't be established.
ED25519 key fingerprint is SHA256:or+sERSXpC0EsZZALxphJFxtN9Lt3tTtgr2KGO/xikI.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Could not create directory '/home/bomj/.ssh' (No such file or directory).
Failed to add the host to the list of known hosts (/home/bomj/.ssh/known_hosts).
Вся штука в том, что у пользователя bomj нет домашней директории, и он не может сделать запись в /home/bomj/.ssh/known_hosts.
Поэтому, рассмотрим возможность, хранить known_hosts в альтернативном месте. Для этого воспользуемся утилитой ssh-keyscan которая идет в стандартном наборе OpenSSH
Одно из возможностей ssh-keyscan - автоматическое добавления ssh-ключей в файл known_hosts, избегая ручного подтверждения ключа при первом подключении.
# Первый раз нужно будет явно записать fingerprint во сюда вот:
ssh-keyscan 123.45.67.89 >> /usr/local/share/ssh_keys/known_hosts
chown root:run_scripts /usr/local/share/ssh_keys/known_hosts
chmod 640 /usr/local/share/ssh_keys/known_hosts
И немного подправим изначальный скрипт
#!/bin/bash
SSH_USER="root"
SSH_HOST="123.45.67.89"
SSH_KEY="/usr/local/share/ssh_keys/my_node"
KNOWN_HOSTS_FILE="/usr/local/share/ssh_keys/known_hosts" # Альтернативный known_hosts
REMOTE_SCRIPT="/usr/local/bin/send_node_stat.sh"
if [[ ! -f "$SSH_KEY" ]]; then
echo "Ошибка: ключ SSH не найден по пути $SSH_KEY"
exit 1
fi
# тут проверяем known_hosts, если отсутствует — создадим пустой
if [[ ! -f "$KNOWN_HOSTS_FILE" ]]; then
touch "$KNOWN_HOSTS_FILE"
chown root:bot_scripts "$KNOWN_HOSTS_FILE"
chmod 640 "$KNOWN_HOSTS_FILE"
fi
ssh -i "$SSH_KEY" -o UserKnownHostsFile="$KNOWN_HOSTS_FILE" "$SSH_USER@$SSH_HOST" "$REMOTE_SCRIPT"
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blogТретий пост для #балансбатл, ставим котиков, комментируем.Привет. Есть российская Астра Линух 1.7.7 🔤🔤🔥🔤🔤🔤 🔤🔤🔤🔤🔤🔤🔤🔤 Есть у нее встроенный админ
administrator. Понадобилось мне создать учетку для периодического сканирования на уязвимости и сбор конфигурации.
ㅤ
Захожу под administrator, проваливаюсь в sudo -i, создаю учетку:
useradd -d /home/user -m -s /bin/bash -G astra-admin user
Создаю пароль:
passwd user
Настраиваю сканирование и.... ничего. Ошибка.
Начинаю искать проблему.
Захожу по user на хост, sudo -i (для сканирования требуется повышение привилегий), хочу проверить /etc/apt/sources.list, чтоб telnet-клиента поставить для пробития портов.
nano /etc/apt/sources.list
Снизу меня встречает красная строчка: "File '/etc/apt/sources.list' is unwritable"
Стоп! Я же root!
Захожу под встроенным админом, sudo -i, могу редактировать файл спокойно. Вспоминаю про мандатную политику, решаю проверить её:
astra-mic-control is-enabled
ВКЛЮЧЕНО!
Ок. Проверяю мандатные метки на пользователях:
pdpl-user administrator
pdpl-user user
У обоих пользователей минимальная метка 0, максимальная 63. Доступа все равно нет.
Решение:
Не знаю, глюк это мандатной политики или так было задумано, но несмотря на то, что команда pdpl-user user выдала нам максимальную метку 63, её фактически там нет. Нужно принудительно добавить 63 метку пользователю:
pdpl-user -i 63 user
После этого все заработало. Такие дела.
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blogВторой пост для #балансбатл, ставим котиков, комментируем.Продолжаю мучать "Жорика", как организовать автоматическую проверку здоровья RAID массива и жёстких дисков, уведомления если проблемы. 🔤🔤🔥🔤🔤🔤🔤 🔤🔤🔤🔤 ㅤ Для уведомления о проблем с жёсткими дисками пригодиться
smartmontools.
sudo apt update
sudo apt install smartmontools
sudo nano /etc/smartd.conf
В конец файла для каждого диска добавляю строки.
/dev/sda -a -m your_email@example.com
/dev/sdb -a -m your_email@example.com
/dev/sdc -a -m your_email@example.com
/dev/sdd -a -m your_email@example.com
Будут проводится все проверки и в случае сбоев прилетит сообщение на почтовый ящик.
Подробнее о настройках можно почитать man smartd.conf.
После этого smartd нужно активировать:
sudo systemctl start smartd
sudo systemctl enable smartd
Далее нужно настроить мониторинг самого массива. Тут до меня дошло что нужно же настроить почтовый сервер, иначе как будут отправляться письма.
Сначала думал устанавливать и настраивать postfix, потом после поисков нашёл легковесное решение - msmtp.
geek@zhorik:~$ sudo apt-get install msmtp msmtp-mta
Второй пакет позволяет прозрачно заменить sendmail на msmtp, что позволит легко отправлять уведомления от любых других служб используя стандартные механизмы.
Выбираю <OK> включаю AppArmor.
Создаю пароль приложения для почты яндекс инструкция
Далее создаю конфиг msmtp
sudo nano /etc/msmtprc
defaults
auth on
tls on
tls_starttls on
tls_certcheck off
keepbcc on
account yandex
host smtp.yandex.ru
port 587
protocol smtp
from your_email@example.com
user user
password password
account default: yandex
Синтаксис предельно понятен и в комментариях не нуждается. Порт 587 используется для подключений клиентских агентов (MUА) и ретрансляции почты от них. Также можно использовать стандартный порт 25.
В одном конфигурационном файле можно создать несколько почтовых аккаунтов, в конце добавить запись, которая будет указывать аккаунт по умолчанию, в нашем случае Яндекс: account default: yandex
Сохраняю содержимое файла. Через неделю пробую отправить почту echo "test" | msmtp -d your_email@example.com и получаю своё тестовое письмо.
Теперь открою конфигурационный файл /etc/mdadm/mdadm.conf и укажу в нем адрес на который следует отсылать уведомления:
MAILADDR your_email@example.com
Специально для яндекса нужно ещё добавить иначе он будет ругаться
MAILFROM jhon-mosk@yandex.ru
Сохраняю файл и обновляю initramfs sudo update-initramfs -u
Для проверки выполню команду mdadm --monitor --scan --test --oneshot
Мне пришло письмо с темой "TestMessage event on /dev/md127:zhorik" и телом:
This is an automatically generated mail message.
TestMessage event detected on md device /dev/md127
The /proc/mdstat file currently contains the following:
Personalities : [raid6] [raid5] [raid4] [raid0] [raid1] [raid10]
md127 : active raid5 sdc[1] sdd[3] sdb[0]
1953260544 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]
bitmap: 0/8 pages [0KB], 65536KB chunk
unused devices: <none>
Нужно будет ещё проверить что smartd тоже корректно шлёт письма.
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blogНу что, пошла жара. Первый пост для #балансбатл, ставим котиков, комментируем.А как ты снимаешь дамп postgres? 🔤🔤🔥🔤🔤🔤🔤🔤🔤🔤🔤🔤6️⃣3️⃣ Итак, настраиваем автоматическое бэкапирование бд, с блек-джеком и логами. Периодически возникает такая задача, проектов много, нужно автоматически снимать дампы. В случае чего слать уведомления и дебажить проблемы в случае их присутствия. В определённый момент пришёл к достаточно простому способу, скрипт в кроне такого вида:
DATE=$(date '+%Y-%m-%d')
echo "save dump db"
pg_dump -h <ip> -p <port> -U <user> -v -F c -f db_$DATE.tar.gz <db name> 2> /opt/backup-db/dump_log_$DATE && [[ $? -eq 0 ]] && curl -i -X \
GET "https://api.telegram.org/bot<bot id>:<bot token>/sendMessage?chat_id=<chat id>&text=Дамп базы данных снят успешно, на сервере <servername>" \
|| curl -i -X GET "https://api.telegram.org/bot<bot id>:<bot token>/sendMessage?chat_id=<chat id>&text=Во время снятия дампа произошла ошибка. Смотри лог на сервере <servername>"
Собственно, что он делает:
- Ну, естественно, снимает дамп бд, вывод процесса кладёт в определённую директорию, прикручивая к названию файла $DATE.
- В случае успеха (&& [[ $? -eq 0 ]] &&) дёргает телеграм бота, чтоб он написал в заданный чат сообщение об этом.
- В случае неуспеха (||, то есть если код выполнения не равен 0) - дёргает тот же бот чтобы он в чат сообщил об ошибке и где она произошла.
Ну и экономим место на дисках, после выполнения этого скрипта, запускаем:
find /opt/backup-db -type f -mtime +10 -delete
Это удалит все файлы в директории, старше заданного количества дней +1.
🛠 #балансбатл
—
✅ @bashdays ✅ @linuxfactory ✅ @blogЗаваливать постами НЕ нужно, максимум 1-2 поста в неделю от одного человека.🛠 #людипишут — ✅ @bashdays ✅ @linuxfactory ✅ @blog
Уже доступно! Исследование Telegram 2025 — ключевые инсайты года 
