OpenVPN как не принять предложение сервера о настройке интерфейсов

В повседневной работе я пользуюсь десятками включённых VPN клиентов. Эти клиенты соединяются с разными серверами, а серверы имеют разные настройки. Так например у меня возникала ситуация когда нужный мне роут пробрасывался через TUN интерфейс который PUSH-ился с сервера. Если бы сервер использовал только я стоило бы перенастроить только сервер чтобы это исправить. Однако сервером пользуются и другие люди и там этот PUSH необходим. Сначала я руками переписывал роут и забывал до следующего переподключения. Но последнее время часто приходилось переподключаться и вечное переписывание роута стало раздражать. В документации к OpenVPN написано что в настройках с недавнего времени появился ключ который поможет клиенту отвергнуть то что ему предлагает сервер.
Continue reading OpenVPN как не принять предложение сервера о настройке интерфейсов

Почему копипастить команды с web сразу в терминал это глупость?

Потому что bash/sh терминал выполнит команду если в ней будет перевод строки.

Во-первых когда команда набирается руками происходит запоминание, сначала вы внимательно читаете команду, затем вводите ту часть которую запомнили, затем дальше читаете и снова вводите. Работают глаза, голова и руки. Запоминается эффективнее.

Во-вторых вместе с командой можно скопипастить чужой зловредный код. Не верите?
Выделите код который приведён ниже скопируйте и затем вставьте в тектовый редактор. После этого вы всегда будете копипастить код через редактор, а короткие команды будете набирать руками.
Continue reading Почему копипастить команды с web сразу в терминал это глупость?

Однострочный скрипт вывода smart информации по нескольким дискам в виде таблицы

Буквы дисков нужно задать в операторе for in a b (ниже будет скрипт который сам выберет все диски)

# for idx in a b ; do smartctl -A /dev/sd$idx | sed -n '/^ID#/,/^$/p' | sed -e '/^ID#/d;s/^./'"$idx "'&/g'; done | sed -e '/^[ \t]*$/d' | awk '{disks[$1]=$1;keys[$3]=$2$3;valuelen=length($11);keylen=length($3);if(valuelen>maxvaluelen){maxvaluelen=valuelen}if(keylen>maxkeylen){maxkeylen=keylen};VALUES[$1,$3]=$11}END{printf("\n%-"maxkeylen"s","NAME");for(disk in disks){printf(" %"maxvaluelen"s",toupper("SD"disk))};for(key in keys){printf("\n%-"maxkeylen"s",key); for(disk in disks){printf(" %"maxvaluelen"s", VALUES[disk,key])}};printf("\n")}'

NAME                       SDA    SDB
Spin_Retry_Count             0      0
Seek_Error_Rate              0      0
Start_Stop_Count           101     21
Multi_Zone_Error_Rate        0      0
Offline_Uncorrectable        0      0
Spin_Up_Time              9216  10183
Power_Cycle_Count           95     12
Calibration_Retry_Count      0      0
Load_Cycle_Count        199966 111455
Reallocated_Sector_Ct        0      0
Raw_Read_Error_Rate          3      0
Current_Pending_Sector       0      0
Reallocated_Event_Count      0      0
Power_On_Hours            9259  11992
Temperature_Celsius         27     23
UDMA_CRC_Error_Count         0      0
Power-Off_Retract_Count     33      7

Одним движением руки этот скрипт преобразуется в вывод информации, например по 8-ми дискам в RAID контроллере 3ware.

for idx in `seq 0 7` ; do smartctl -A /dev/twa0 -d 3ware,$idx | sed -n '/^ID#/,/^$/p' | sed -e '/^ID#/d;s/^./'"$idx "'&/g'; done | sed -e '/^[ \t]*$/d' | awk '{disks[$1]=$1;keys[$3]=$2$3;valuelen=length($11);keylen=length($3);if(valuelen>maxvaluelen){maxvaluelen=valuelen}if(keylen>maxkeylen){maxkeylen=keylen};VALUES[$1,$3]=$11}END{printf("\n%-"maxkeylen"s","NAME");for(disk in disks){printf(" %"maxvaluelen"s",toupper("p:"disk))};for(key in keys){printf("\n%-"maxkeylen"s",key); for(disk in disks){printf(" %"maxvaluelen"s", VALUES[disk,key])}};printf("\n")}'

А такой скрипт сам выберет все диски с помощью lsblk и выведет состояние smart

while read NAME ; do smartctl -A "${NAME}" | sed -n '/^ID#/,/^$/p' | sed -e '/^ID#/d' -e 's|^.|'"${NAME} "'&|g'; done < <(lsblk -d --noheadings --output name,type | awk '$2=="disk"{print "/dev/"$1}' | sort) | sed -e '/^[ \t]*$/d' | awk '{disks[$1]=$1;keys[$3]=$2$3;valuelen=length($11);keylen=length($3);if(valuelen>maxvaluelen){maxvaluelen=valuelen};if(keylen>maxkeylen){maxkeylen=keylen};VALUES[$1,$3]=$11}END{printf("\n%-"maxkeylen"s","NAME");for(disk in disks){valuelen=length(disk);if(valuelen>maxvaluelen){maxvaluelen=valuelen}};for(disk in disks){printf(" %"maxvaluelen"s",disk)};for(key in keys){printf("\n%-"maxkeylen"s",key); for(disk in disks){printf(" %"maxvaluelen"s", VALUES[disk,key])}};printf("\n")}'

Обновление базы дисков smartctl на CentOS 6.7

Чтобы обновить базу дисков smartctl на CentOS release 6.7 (Final) нужно использовать новый скрипт, так как старый, который идёт в дистрибутиве не работает.

update-smart-drivedb -v ./aa.txt
Download from branches/RELEASE_5_43_DRIVEDB
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
109   329  109   329    0     0   1108      0 --:--:-- --:--:-- --:--:--  2611
./aa.txt.error: rejected by /usr/sbin/smartctl, probably no longer compatible

С момента моего предыдущего обновления снова изменилась структура smartmontools линков для получения свежей базы. Поэтому я написал скрипт который загружает скрипт обновления и настраивает его так чтобы он стянул свежую базу дисков.
Continue reading Обновление базы дисков smartctl на CentOS 6.7

Мониториг почтовой очереди

Скрипт получает состояние почтовой очереди и подсчитывает количество заданий их общий размер и выводил даты самого молодого и старого задания в очереди.

mailq | sed -e 's/[\t]\+/ /g' | sed -e '/^ \+/d;/^ *$/d' | awk '{if(NR==1){qtimestr=gensub(/^.+ (-+Q-Time-+) .+$/,"\\1","g",$0);start=index($0,qtimestr);len=length(qtimestr);}else{printf("%d;%"len"s\n", $2, substr($0,start,len));}}' | while IFS=';' read size date ; do echo -e "`date -d\"${date}\" '+%F %T'`;${size}"; done | sort | awk -F';' '{count++;size=size+$2;if(NR==1){printf("oldest:%s\n", $1)}}END{if(count>0){printf("youngest:%s\ncount:%d\nsize:%dMB\n", $1, count, size/1024/1024)}}'
oldest:2015-10-31 04:21:00
youngest:2015-11-04 11:25:00
count:10
size:25MB

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

Значительно более быстро контролировать количество заданий в очереди можно контролировать если очередь заданий разместить на отдельной файловой системе.

df -iP /var/spool/mqueue
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/mapper/VG_MAIN-MQUEUE 10485760     120 10485640    1% /var/spool/mqueue
В данном случае интересует количество использованных inodes.
df -iP /var/spool/mqueue | awk '{if($3 ~ /^[0-9]+$/){print $2, $3, $4}}'
10485760 120 10485640

Здесь нужно мониторить второе поле которое показывает количество занятых inode. Их резкий рост и удержание на высоком уровне будет сигнализировать о вероятной спамерской активности с вашего сервера. А так как в отличии от первого (с mailq) такой скрипт работает очень быстро то мониторить можно ежесекундно.
Повторюсь, этот способ не подойдёт если каталог с почтовыми очередями находится в одной файловой системе с другими каталогами, например на корневой FS. Здесь начнут влиять inode временных файлов и всяких других создаваемых пользователями, программами и демонами. Впрочем вынести почтовые очереди на отдельную FS не такая уж и сложная задача. А для сервера полезная.

Запись терминальной сессии

Для записи терминальной сессии можно использовать обычный подход, записывать видео. Однако в этом есть два неудобства: во-первых видео занимает много места, во-вторых из видео нельзя сделать copy&paste текстовой информации.

Оба эти недостатка отсутствуют в программе asciinema. Её файлы маленькие и поддерживается copy&paste.

Галерею работ можно посмотреть на https://asciinema.org/.
И сразу можно сделать копию для себя так как исходный код прилагается.
Исходный код плеера
Исходный код для сайта

А с помощью другой утилиты можно устроить живую демонстрацию своей консоли.

Трансляция окна своей консоли как WEB приложения

GoTTY позволяет устроить демонстрацию окна своей консоли как web приложения.

Для эмуляции терминала на JavaScript в интернет браузерах Gotty использует hterm. Gotty имеет свой websocket сервер перебрасывает PTY веб-клиентам и принимает ввод от клиентов и перебрасывает его на PTY. Идея hterm + websocket была навеяна Wetty.

Для каждого клиента подсоединяющегося к websocket GoTTY порождает новый процесс и это значит, что напрямую нельзя расшаривать терминал многим клиентам. Однако можно использовать мультиплексор терминала tmux. Но в этом случае клиенты будут находиться в режиме viewonly без передачи нажатий клавиатуры в терминал, а тот кто будет подключён локально сможет управлять терминалом.

Распространяется под лицензией MIT.
Исходники на https://github.com/yudai/gotty

Также имеется и утилита которая позволяет сделать легковесные файлы с записью работы в терминальной сессии и воспроизводить их в браузере.

Получение списка NS серверов для домена

Однострочный скрипт для получения списка ns сероверов домена.

DOMAIN="youngblog.hoster-ok.com"; TLD="${DOMAIN##*.}"; FLD="${DOMAIN%.*}"; FLD="${FLD##*.}"; echo -e "TLD:${TLD}\nFLD:${FLD}"; ROOTNS=`dig +short NS ${TLD}. | head -n 1 | sed -e 's/\.$//g'`; dig +authority +noadditional +nocomments +nostats +nocmd ${FLD}.${TLD}. @${ROOTNS} | sed -e 's/[;#%].*//g;s/^[ \t]*//g;s/[ \t]*$//g;/^$/d'
TLD:com
FLD:hoster-ok
hoster-ok.com.          172800  IN      NS      ns1.vds-ok.com.
hoster-ok.com.          172800  IN      NS      ns2.vds-ok.com.
hoster-ok.com.          172800  IN      NS      ns1.hoster-ok.com.

Принцип следующий:
Первая команда  dig +short NS ${TLD}.  (обратите внимание на точку на конце) получает корневые NS сервера которые обслуживают конкретный TLD (Top Level Domain) например .com .net .org .ru и т.д., а вторая команда  dig +authority +noadditional +nocomments +nostats +nocmd ${FLD}.${TLD}. @${ROOTNS}  получает из определённого NS @${ROOTNS} информацию по домену ${FLD}.${TLD}. (снова обратите внимание на точку на конце) информация, которую нужно включить или подавить, перечислена ключами.

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

Обновление виртуальных квот Pure-FTP

Случается скопировать файлы в папку ftp пользователя или удалить в обход ftp сервера. Тоесть прямо с диска сервера, а не через FTP соединение. После этого наблюдается несоответствие суммарного размера файлов на диске в каталоге ftp с теми размерами которые вычисляет pureftp. В корне каждого ftp каталога при включённых виртуальных квотах размещаются файлы .ftpquota. Эти файлы содержат два числа: количество файлов и их суммарный размер. При каждом добавлении обновлении или удалении файла через FTP демон pure-ftpd вносит измненение в этот файл. И если каталоги FTP изменять (добавлять удалять файлы) в обход pure-ftpd изменения остаются неучтёнными в этом файле. Для того чтобы восстановить соответствие сожержимого папок ftp сервера и файла .ftpquota предназначается команда pure-quotacheck. Работает она только для одного выбранного пользователя. А если необходимо пробежаться по всем пользователям то можно воспользоваться таким скриптом:

#!/bin/bash

PUREPW="/usr/bin/pure-pw"
PUREQUOTACHECK="/usr/sbin/pure-quotacheck"
AWK="/bin/awk"
SED="/bin/sed"
XARGS="/usr/bin/xargs"

# update all virtual quotas with no messages
# "${PUREPW}" list | "${AWK}" '{print $2}' | "${SED}" -e 's|/\./$||g' | "${XARGS}" -I{} "${PUREQUOTACHECK}" -u pureftp -d "{}"

# update all virtual quotas with progress info
"${PUREPW}" list | "${AWK}" '{print gensub(/\.\/$/,"","g",$2)}' | while read FTPHOME ; do
  [ -e "${FTPHOME}" ] && (
    echo -en "${FTPHOME}:";
    [ -e "${FTPHOME}.ftpquota" ] && BEFORE=`cat "${FTPHOME}.ftpquota"` || BEFORE="";
    "${PUREQUOTACHECK}" -u pureftp -d "${FTPHOME}";
    AFTER=`cat "${FTPHOME}.ftpquota"`;
    [ "${BEFORE}" == "${AFTER}" ] && (echo -en " no changes ") || (
      echo -en " changed\n"
      echo -e "1 ${BEFORE}\n2 ${AFTER}" | "${AWK}" '{printf("files:%10d size:%14d", $2, $3); if($1==1){printf("\n");}}'
    )
    echo " [Done]"
  );
done

pureftp – это локальный пользователь для демона pure-ftpd
Этот скрипт отчитается о изменении файлов .ftpquota для каждого пользователя ftp и выведет информацию из этого файла перед и после обновления, так что будет ясно где была разница. Внутри также есть закоментарованный “однострочник” которые обновить файлы .ftpquota без вывода каких либо сообщений.

В документации по PureFTP рекомендуется регулярно обновлять виртуальные квоты. Поэтому скрипт можно добавить в крон и в зависимости от количества и объёма всех FTP файлов запускать его один раз в сутки или в неделю.

А этот скрипт выводит текущие квоты всех пользователей.

pure-pw list | awk '{print gensub(/\.\/$/,".ftpquota","g",$2)}' | while read FN ; do [ -e "${FN}" ] && (echo -en "${FN}:"; cat "${FN}" | tr -d '\n'; echo ) ; done

Получить список соответствия LV = dm-N

При мониторинге производительности дисковой подсистемы возникает необходимость воспользоваться командой iostat. Она выводит результат по утилизации дисков с использованием символических имёт из ядра вида vd[a-z]N sd[a-z]N и dm-N. Когда речь касается физических дисков например /dev/sdaN тут всё понятно. А вот когда используется LVM то чтобы узнать какоe имя dm-N соответствует какому логическому тому нужно посмотреть какие lv_kernel_major и lv_kernel_minor соответствуют данному dm устройству в ядре. Следующая команда делает это автоматически.

lvs -o lv_name,lv_kernel_major,lv_kernel_minor | while read LVNAME LVKMAJOR LVKMINOR ; do grep '^[ \t]*'${LVKMAJOR}'[ \t]\+'${LVKMINOR}'[ \t]\+' /proc/partitions | awk -v name="${LVNAME}" '{print $4" "name}'; done
dm-7 lv_log
dm-0 lv_root
dm-1 lv_swap
dm-5 empty
dm-6 lv_server_fm-disk0
dm-8 lv_server_fm-disk1
dm-2 lv_vm1_disk0
dm-3 lv_vm2_disk0
dm-4 lv_vm3_disk0

Если нужно получить полные имена то нужно использовать опцию lv_path

# lvs -o lv_path,lv_kernel_major,lv_kernel_minor | while read LVNAME LVKMAJOR LVKMINOR ; do grep '^[ \t]*'${LVKMAJOR}'[ \t]\+'${LVKMINOR}'[ \t]\+' /proc/partitions | awk -v name="${LVNAME}" '{print $4" "name}'; done
dm-7 /dev/vg_v03t/lv_log
dm-0 /dev/vg_v03t/lv_root
dm-1 /dev/vg_v03t/lv_swap
dm-5 /dev/vg_vm/empty
dm-6 /dev/vg_vm/lv_server_fm-disk0
dm-8 /dev/vg_vm/lv_server_fm-disk1
dm-2 /dev/vg_vm/lv_vm1_disk0
dm-3 /dev/vg_vm/lv_vm2_disk0
dm-4 /dev/vg_vm/lv_vm3_disk0

А если полные имена через /dev/mapper то опцию lv_dm_path

# lvs -o lv_dm_path,lv_kernel_major,lv_kernel_minor | while read LVNAME LVKMAJOR LVKMINOR ; do grep '^[ \t]*'${LVKMAJOR}'[ \t]\+'${LVKMINOR}'[ \t]\+' /proc/partitions | awk -v name="${LVNAME}" '{print $4" "name}'; done
dm-7 /dev/mapper/vg_v03t-lv_log
dm-0 /dev/mapper/vg_v03t-lv_root
dm-1 /dev/mapper/vg_v03t-lv_swap
dm-5 /dev/mapper/vg_vm-empty
dm-6 /dev/mapper/vg_vm-lv_server_fm--disk0
dm-8 /dev/mapper/vg_vm-lv_server_fm--disk1
dm-2 /dev/mapper/vg_vm-lv_vm1_disk0
dm-3 /dev/mapper/vg_vm-lv_vm2_disk0
dm-4 /dev/mapper/vg_vm-lv_vm3_disk0