Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.
Подойдет для разработчиков, системных администраторов и DevOps
Реклама: @dad_admin
Post #300
1.18K
BA @bash_tex
Showing posts older than #301 · Back to latest
logrotate умеют ротацию по времени или по размеру, но что если нужно оба условия? Например: хочешь сохранять логи по дате, но если они перевалили за 100 МБ - сразу ротация, не дожидаясь полночи.
#!/bin/bash
LOG_DIR="/var/log/myapp"
MAX_SIZE=$((100 * 1024 * 1024)) # 100 MB
TODAY=$(date +%Y%m%d)
LOG_FILE="${LOG_DIR}/myapp_${TODAY}.log"
mkdir -p "$LOG_DIR"
touch "$LOG_FILE"
# Проверка размера
if [[ -f "$LOG_FILE" ]]; then
FILE_SIZE=$(stat -c%s "$LOG_FILE")
if (( FILE_SIZE > MAX_SIZE )); then
TS=$(date +%H%M%S)
mv "$LOG_FILE" "${LOG_FILE%.log}_overflow_${TS}.log"
touch "$LOG_FILE"
echo "[INFO] Перемещен переполненный лог-файл: ${LOG_FILE%.log}_overflow_${TS}.log"
fi
fi
*/10 * * * * /usr/local/bin/rotate-mixed.sh
sed -i решает задачу, но как это сделать рекурсивно, да еще и аккуратно?
find . -type f -name "*.conf" -exec sed -i 's|http://|https://|g' {} +
find . -type f -name "*.conf" -exec perl -pi -e 's|http://|https://|g' {} +
find . -type f -name "*.conf" -print -exec grep -H 'http://' {} \;
PATTERN="TODO"
REPLACE="Done"
find ./src -type f -name "*.txt" -exec sed -i "s/${PATTERN}/${REPLACE}/g" {} +
mkdir -p /opt/.honeypot
chmod 700 /opt/.honeypot
#!/bin/bash
WATCH_DIR="/opt/.honeypot"
LOG="/var/log/honeypot_access.log"
inotifywait -m -e access,modify,create,delete "$WATCH_DIR" --format '%T %w %f %e' --timefmt '%F %T' |
while read event; do
echo "[ALERT] $event" >> "$LOG"
echo "[ALERT] Honeypot accessed: $event"
# Можно добавить: уведомление в TG, systemd-notify и т.д.
done
script -q -c "top -n 1" session.log
socat -d -d pty,raw,echo=0 pty,raw,echo=0
/dev/pts/X и /dev/pts/Y, которые можно использовать, например, чтобы связать скрипт и программу, как будто они "болтают" друг с другом.
#!/usr/bin/expect -f
spawn ssh user@host
expect "password:"
send "supersecret\r"
interact
/etc/hosts из надежных источников, таких как someonewhocares.org или StevenBlack. И все это можно автоматизировать с bash.
#!/bin/bash
set -euo pipefail
# Источник обновленного hosts-файла
URL="https://someonewhocares.org/hosts/hosts"
# Временный файл
TMP="/tmp/hosts_blocklist"
# Бэкап текущего hosts
cp /etc/hosts "/etc/hosts.bak.$(date +%s)"
# Скачиваем и фильтруем список
curl -s "$URL" | grep -vE '^#|^$' > "$TMP"
# Объединяем с локальной частью
{
grep -E "^127\.0\.0\.1|^::1" /etc/hosts.bak.* | head -n 2
echo -e "\n# 🔒 AdBlock entries:"
cat "$TMP"
} > /etc/hosts
echo "/etc/hosts обновлен. Всего $(wc -l < "$TMP") заблокированных адресов."
(crontab -l; echo "0 4 * * 0 /usr/local/bin/update-hosts.sh") | crontab -
/etc/hosts требует root-прав, а некоторые адреса могут ломать нужные функции (whitelist нужно продумывать).getconf, ulimit, vmstat, iostat - системные команды, которые можно встраивать прямо в bash для более "умных" решений.
echo "Page size: $(getconf PAGESIZE)"
echo "Максимум аргументов в командной строке: $(getconf ARG_MAX)"
echo "Открытых файлов можно: $(ulimit -n)"
ulimit -c 0 # Запретить core dumps
vmstat 1 2 | tail -1 | awk '{print "Свободная память: " $4 " KB, ожидание I/O: " $16}'
iostat -dx 1 2 | grep sda | tail -1 | awk '{print "IO util: " $NF "%"}'
wait_for_idle() {
while :; do
util=$(iostat -dx 1 2 | awk '/sda/ {u=$NF} END{print int(u)}')
[[ $util -lt 50 ]] && break
echo "Диск занят ($util%), ждём..."
sleep 5
done
}

/etc/shadow, /etc/ssh/sshd_config, /etc/fstab), это не исключает попыток обхода через эксплойты или скомпрометированные процессы. Решение: AppArmor + аудит + реакция.sshd_config сторонними процессами:
# /etc/apparmor.d/usr.local.block-ssh-access
#include <tunables/global>
/usr/local/bin/* {
deny /etc/ssh/sshd_config r,
}
/usr/local/bin, но явно запрещаем доступ к sshd_config.
sudo apparmor_parser -r /etc/apparmor.d/usr.local.block-ssh-access
aa-status
dmesg или /var/log/kern.log / /var/log/syslog:
audit: type=1400 audit(…): apparmor="DENIED" operation="open" profile="/usr/local/bin/myscript" name="/etc/ssh/sshd_config"
#!/bin/bash
LOG="/var/log/syslog"
ALERT="/var/log/apparmor_violations.log"
grep "apparmor=\"DENIED\"" "$LOG" | grep "/etc/ssh/sshd_config" >> "$ALERT"
if [[ -s $ALERT ]]; then
echo "[ALERT] Попытка доступа к sshd_config:"
cat "$ALERT"
> "$ALERT" # Очистка после отчёта
fi
#!/bin/bash
set -euo pipefail
CONFIG="/etc/myapp/config.conf"
BACKUP="/tmp/config.conf.bak.$RANDOM"
# Создаём резервную копию
cp "$CONFIG" "$BACKUP"
# Обработчик при ошибке
rollback() {
echo "Ошибка! Откатываем изменения..."
cp "$BACKUP" "$CONFIG"
echo "Конфиг восстановлен."
}
trap rollback ERR
echo "Обновляем конфиг..."
# Имитация изменения
echo "enable_feature=true" >> "$CONFIG"
# Имитация ошибки
false # или команда, которая может упасть
echo "Конфиг успешно обновлён"
rm "$BACKUP"
trap rollback ERR - ловит любую ошибку set -euo pipefail - строгий режим, ошибки не скрываютсяОбновление конфигов
Копирование и изменение сервисных файлов
Манипуляции с crontab, systemd, hosts, fstab
Авторазвертывание с откатом при сбое
mytool/
├── usr/
│ └── local/
│ └── bin/
│ └── mytool
└── DEBIAN/
└── control
Package: mytool
Version: 1.0.0
Section: utils
Priority: optional
Architecture: all
Maintainer: You <admin@bashtex.ru>
Description: Утилита для всего
generate_changelog() {
git log -n 10 --pretty=" * %s (%h)" > changelog.txt
echo "mytool (1.0.0) stable; urgency=low" | cat - changelog.txt > DEBIAN/changelog
rm changelog.txt
}
build_deb() {
chmod -R 755 mytool/DEBIAN
dpkg-deb --build mytool
}
#!/bin/bash
set -e
PKG_DIR="mytool"
VERSION="1.0.0"
PKG_NAME="mytool_${VERSION}.deb"
generate_changelog() {
git log -n 10 --pretty=" * %s (%h)" > "$PKG_DIR/DEBIAN/changelog"
}
build_deb() {
chmod -R 755 "$PKG_DIR/DEBIAN"
dpkg-deb --build "$PKG_DIR" "$PKG_NAME"
echo "Пакет создан: $PKG_NAME"
}
generate_changelog
build_deb
dpkg -i mytool_1.0.0.deb, а в /usr/local/bin/ появится твой скрипт.
#!/bin/bash
# 💥 Включаем строгий режим
set -euo pipefail
#Список зависимостей
REQUIRED_CMDS=("curl" "jq" "awk")
#Каталоги, которые должны существовать и быть доступны
REQUIRED_DIRS=("/var/log/myapp" "/etc/myapp")
#Проверка утилит
for cmd in "${REQUIRED_CMDS[@]}"; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "Требуется утилита '$cmd', но она не найдена"
exit 1
}
done
#Проверка директорий
for dir in "${REQUIRED_DIRS[@]}"; do
[[ -d "$dir" ]] || {
echo "Каталог $dir не существует"
exit 1
}
[[ -w "$dir" ]] || {
echo "Нет прав на запись в $dir"
exit 1
}
done
#Проверка прав запуска от root (если нужно)
if [[ $EUID -ne 0 ]]; then
echo "Скрипт должен быть запущен от root"
exit 1
fi
echo "Self-test пройден успешно. Продолжаем выполнение..."
#!/bin/bash
LOG_FILE="script.log"
PIPE="/tmp/pipe.$$"
# создаем именованный канал
mkfifo "$PIPE"
# логгер: читает из канала, добавляет таймштамп и пишет в файл
tee "$LOG_FILE" < "$PIPE" |
while IFS= read -r line; do
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"
done &
# перенаправим stdout и stderr скрипта в канал
exec > "$PIPE" 2>&1
# ———————————————
echo "Скрипт стартовал"
sleep 1
echo "Работаем..."
ls /nonexistent/path
echo "Завершено"
# ———————————————
wait
rm "$PIPE"
📍 Вывод в терминале с метками времени
📍 Лог сохраняется в файл без stdout-мусора
📍 Работает асинхронно: скрипт не ждёт tee/while
📍 Поддерживает stderr
parse_yaml() {
grep '^[^#[:space:]].*:' "$1" | awk -F': ' '
NF == 2 { gsub(/"/, "", $2); print $1 "=" $2 }
'
}
# config.yaml
port: 8080
host: localhost
debug: true
eval $(parse_yaml config.yaml)
echo "$host:$port" # → localhost:8080
parse_toml() {
grep -v '^\s*#' "$1" | grep '=' | while IFS='=' read -r key val; do
key=$(echo "$key" | xargs)
val=$(echo "$val" | sed -E 's/^["'\'']//; s/["'\'']$//' | xargs)
echo "$key=$val"
done
}
# config.toml
user = "admin"
port = 9000
eval $(parse_toml config.toml)
echo "$user:$port" # → admin:9000
stack=()
# push
stack+=("первый")
stack+=("второй")
# pop
last_index=$(( ${#stack[@]} - 1 ))
echo "POP: ${stack[$last_index]}"
unset 'stack[$last_index]'
queue=()
# enqueue
queue+=("alpha")
queue+=("beta")
# dequeue
echo "DEQUEUE: ${queue[0]}"
queue=("${queue[@]:1}")
queue=("job1" "job2" "job3")
while [[ ${#queue[@]} -gt 0 ]]; do
task="${queue[0]}"
echo "Processing $task"
queue=("${queue[@]:1}")
done
push() { stack+=("$1"); }
pop() { echo "${stack[-1]}"; unset 'stack[-1]'; }
peek() { echo "${stack[-1]}"; }
sudo visudo
myuser ALL=(targetuser) NOPASSWD: /usr/bin/systemctl restart my-service
sudo -u targetuser /usr/bin/systemctl restart my-service
sudo -u deployuser /home/deployuser/scripts/deploy.sh
sudo su - otheruser -c 'echo $HOME'
#!/bin/bash
USER=deploy
CMD="/opt/app/update.sh"
sudo -u "$USER" bash -c "$CMD"
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 54256 /usr/bin/passwd
find / -perm -4000 -type f 2>/dev/null
find / -perm -4000 -type f -exec ls -lh {} \; 2>/dev/null
find / -perm -4000 -type f -exec sha256sum {} \; 2>/dev/null > /var/log/suid_baseline.sha
sha256sum -c /var/log/suid_baseline.sha 2>/dev/null | grep -v ': OK'
@daily root /usr/local/bin/suid_check.sh >> /var/log/suid_monitor.log
#!/bin/bash
BASELINE="/var/log/suid_baseline.sha"
TMP="/tmp/suid_check.$$"
find / -perm -4000 -type f -exec sha256sum {} \; 2>/dev/null > "$TMP"
diff -u "$BASELINE" "$TMP" && cp "$TMP" "$BASELINE"
rm "$TMP"