Как восстановить работу директорий пользователей в Confluence

Имеем свеженастроенную пару JIRA 8.2 + Confluence 6.15. В Confluence пользователи должны браться из директории пользователей JIRA, но из-за неправильной настройки никто залогиниться не может. Имеем криворукого админа (меня то есть), который неловким движением руки отключил внутреннюю директорию Confluence, где, разумеется, и были все пользователи с админскими правами. Картинка “Приехали”.

Чинится проблема редактированием доступа к директориям пользователей напрямую в базе Posgtres:

rat@atlassian:~$ sudo -u postgres psql
[sudo] password for rat: 
psql (9.5.17)
Type "help" for help.
postgres=# \list
                                   List of databases
    Name    |   Owner    | Encoding |   Collate   |    Ctype    |   Access privileges   
------------+------------+----------+-------------+-------------+-----------------------
 confluence | confluence | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 jira       | jira       | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres   | postgres   | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 

postgres=# \connect confluence
You are now connected to database "confluence" as user "postgres".
confluence=# select id, directory_name, active from cwd_directory;
   id    |        directory_name         | active 
---------+-------------------------------+--------
  294913 | Confluence Internal Directory | F
 1736705 | JIRA Server                   | T
(2 rows)

confluence=# update cwd_directory set active='T' where id=294913;
UPDATE 1

confluence=# select id, directory_name, active from cwd_directory;
   id    |        directory_name         | active 
---------+-------------------------------+--------
 1736705 | JIRA Server                   | T
  294913 | Confluence Internal Directory | T
(2 rows)

confluence=# \quit

Осталось перезапустить Confluence и правильно настроить директории пользователей.

Доступ к удаленному FTPS-серверу в пакетном режиме

Для коммуникации с ftps-сервером хорошо подходит утилита lftp, которая поддерживает SSL в отличие от стандартной утилиты ftp. Есть поддержка считывания данных из ~/.netrc, так что в скриптах логин и пароль светить не придется.
Вызов работы из скрипта выглядит примерно так (Ubuntu Linux -> Microsoft FTP Server):

/usr/bin/lftp -e "set ftp:passive-mode off;
                  set ftp:ssl-protect-data true;
                  set ftp:ssl-force true;
                  set ssl:verify-certificate no;
                  lcd /data/processing/out;
                  cd /;
                  mput *;
                  bye" ftps.domain.tld

Установка NextCloud на Ubuntu 16.04 (Apache2, MariaDB)

Для небольшой компании установил NextCloud в качестве точки обмена со сторонней бухгалтерией и бизнес-партнерами. До этого они обменивались документами по электронной почте, что, учитывая требования GDPR, создавало сложности.
Установка почти полностью делалась по статье “Install NextCloud On Ubuntu 16.04 LTS With Apache2, MariaDB, PHP 7.1 And Let’s Encrypt SSL/TLS”, за мелкими исключениями.
В моем случае сервер находится на нестандартном порту (порт прокидывается за NAT, 8443->443), поэтому использовать сертификат Let’s Encrypt не получится, для запроса сертификата веб-сайт должен быть доступен извне по портам 80/tcp и 443/tcp. Использовался самоподписанный сертификат SSL/TLS.
В сети много инструкций по настройке NextCloud в связке с Nginx, но я несколько недолюбливаю этот отличный веб-сервер за его головоломные файлы конфигурации.
Сам сервер развернут в виде LXC-контейнера для повышения уровня изоляции данных. В качестве базы данных – MariaDB.

$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt-get install apache2

Сразу отключим листинг директорий:
$ sudo sed -i "s/Options Indexes FollowSymLinks/Options FollowSymLinks/" /etc/apache2/apache2.conf
$ sudo systemctl stop apache2.service
$ sudo systemctl start apache2.service
$ sudo systemctl enable apache2.service

База данных:
$ sudo apt-get install mariadb-server mariadb-client
$ sudo mysql_secure_installation

When prompted, answer the questions below by following the guide.

Enter current password for root (enter for none): Just press the Enter
Set root password? [Y/n]: Y
New password: Enter password
Re-enter new password: Repeat password
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y

Рестартуем сервер MariaDB:
$ sudo systemctl restart mysql.service

PHP 7.1 не входит в поставку Ubuntu 16.04, ставим из стороннего репозитория:
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get update
$ sudo apt-get install php7.1 libapache2-mod-php7.1 php7.1-common libapache2-mod-php7.1 php7.1-mbstring php7.1-xmlrpc php7.1-soap php-apcu php-smbclient php7.1-ldap php-redis php7.1-gd php7.1-xml php7.1-intl php7.1-json php-imagick php7.1-mysql php7.1-cli php7.1-mcrypt php7.1-ldap php7.1-zip php7.1-curl

Немного правим дефолтные параметры PHP-FPM:
$ sudo vim /etc/php/7.1/apache2/php.ini
file_uploads = On
allow_url_fopen = On
memory_limit = 512M
upload_max_filesize = 256M
max_execution_time = 360
date.timezone = Europe/Riga

Настройка базы:
$ sudo mysql -u root -p
CREATE DATABASE nextcloud;
CREATE USER 'nextclouduser'@'localhost' IDENTIFIED BY 'new_password_here';
GRANT ALL ON nextcloud.* TO 'nextclouduser'@'localhost' IDENTIFIED BY 'user_password_here' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;

Скачиваем и устанавливаем NextCloud:
$ wget https://download.nextcloud.com/server/releases/latetst.zip
$ unzip latest.zip
$ sudo mv nextcloud /var/www/html/nextcloud/
$ sudo chown -R www-data:www-data /var/www/html/nextcloud/
$ sudo chmod -R 755 /var/www/html/nextcloud/

Конфиг нового виртуального хоста, нужен только для начальной отладки:
$ sudo vim /etc/apache2/sites-available/nextcloud.conf

<VirtualHost *:80>
ServerAdmin admin@domain.tld
DocumentRoot /var/www/html/nextcloud/
ServerName office.domain.tld
ServerAlias doc.domain.tld
Alias /nextcloud "/var/www/html/nextcloud/"
<Directory /var/www/html/nextcloud/>
Options +FollowSymlinks
AllowOverride All
Require all granted
<IfModule mod_dav.c>
Dav off
</IfModule>
SetEnv HOME /var/www/html/nextcloud
SetEnv HTTP_HOME /var/www/html/nextcloud
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Включим необходимые модули и рестартуем Apache:
$ sudo a2ensite nextcloud.conf
$ sudo a2enmod rewrite
$ sudo a2enmod headers
$ sudo a2enmod env
$ sudo a2enmod dir
$ sudo a2enmod mime
$ sudo systemctl restart apache2.service

Генерируем сертификат RSA со сроком 10 лет:
$ sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/ssl/private/apache-selfsigned.key -out /etc/ssl/certs/apache-selfsigned.crt
$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Подробная информация по параметрам в статье на Digital Ocean: “How To Create a Self-Signed SSL Certificate for Apache in Ubuntu 16.04”.

Создаем рабочий конфиг виртуального хоста:
$ sudo vim /etc/apache2/sites-available/nextcloud-le-ssl.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin admin@domain.tld
DocumentRoot /var/www/html/nextcloud/
ServerName office.domain.tld
ServerAlias doc.domain.tld

Alias /nextcloud "/var/www/html/nextcloud/"

<Directory /var/www/html/nextcloud/>
Options +FollowSymlinks
AllowOverride All
Require all granted
<IfModule mod_dav.c>
Dav off
</IfModule>
SetEnv HOME /var/www/html/nextcloud
SetEnv HTTP_HOME /var/www/html/nextcloud
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

SSLCertificateFile /etc/ssl/certs/apache-selfsigned.crt
SSLCertificateKeyFile /etc/ssl/private/apache-selfsigned.key

</VirtualHost>
</IfModule>

Рестартуем Apache, заходим через веб на сервер, заканчиваем настройку:
$ sudo systemctl restart apache2.service

После первоначальной настройки необходимо сделать еще несколько полезных вещей:
1. Настроить SMTP-сервер для отправки почты:
Settings -> Administration -> Basic settings -> Email server

2. Включить кеширование данных, для небольшого сервера достаточно APCu:
$ sudo vim /var/www/html/nextcloud/config/config.php
'memcache.local' => '\OC\Memcache\APCu',

3. Отключить быстрый набор контактов, это повышает приватность данных:
Settings -> Administration -> Sharing -> [ ]Allow username autocompletion in share dialog…

4. Ускорить чистку удаленных файлов.
Дело в том, что по умолчанию удаленные файлы хранятся как минимум 30 дней и удаляются из “корзины” только если в систем осталось мало места на диске. Нам это совсем не нужно, пусть удаляются через 35 дней:
$ sudo vim /var/www/html/nextcloud/config/config.php
'trashbin_retention_obligation' => '30, 35',

Enjoy!

Быстро перекодировать пачку видео для просмотра на iPad – ffmpeg в помощь

На Маке понадобилось залить сезон сериала жене на iPad. Тратить деньги на программы перекодирования видео как-то не хочется. Вспоминаем про старый добрый ffmpeg:
$ brew install ffmpeg
$ for i in *.avi; do ffmpeg -i "$i" "${i%.*}.mp4"; done

Для *.mkv иногда достаточно сменить контейнер. Операция происходит очень быстро, но есть шанс, что на планшете не заработает.
$ for i in *.mkv; do ffmpeg -i "$i" -codec copy "${i%.*}.mp4"; done

Получившиеся файлы слегка переименовываем – ставим индекс серии в начало названия, так как проигрыватель на iOS сильно обрезает имена файлов. Заливаем на устройство через iTunes.

Еще парочку нюансов:
1. Иногда при перекодировании avi в mp4 убивается звук. Можно попробовать звуковую дорожку просто скопировать:
$ for i in *.avi; do ffmpeg -i "$i" -acodec copy "${i%.*}.mp4"; done
2. Теги mp4 частенько не совпадают с названием файла, можно поправить:
$ for i in *.mp4; do mp4tags -s `basename "$i" .mp4` $i; done

Автоматизация резервного копирования сайта по расписанию. FTP

Некоторые провайдеры не дают доступ к размещенным сайтам по SFTP, поэтому всем удобный rsync работать не будет. Придется довольствоваться старым небезопасным ftp. Самый удобный инструмент – wget. Делаем примитивный однострочник:
$ wget --no-passive -r -l 0 -q 0 ftp://isp3.service.tld/web/ -P /tmp; zip -q -r /var/backup/sites/site1/backup_`date +\%Y\%m\%d\%H\%M\%S`.zip /tmp/isp3.service.tld; rm -fr /tmp/isp3.service.tld

Опция --no-passive включает активный режим. В пассивном режиме, который включается по умолчанию, траффик ftp может быть зарезан файерволом. Если все работает без этой опции, то в активном режиме режиме нет необходимости.
Комбинация опций -r -l 0 указывает рекурсивность и неограниченную глубину дерева каталогов.

Как видно, логин и пароль в командной строке отсутствуют. Их, конечно, можно указать как параметры wget, например --user="serviceftp" --password="$uperPa$$w0rd", но лучше так не делать.
Специально для хранения логинов и паролей ftp/http есть файл .netrc. И wget будет читать информацию из этого файла при получении запроса авторизации от сервера.

$ touch ~/.netrc
$ chmod 600 ~/.netrc
$ echo 'machine isp3.service.tld login serviceftp password $uperPa$$w0rd' >> ~/.netrc

Сайт в моем случае статичный и обновляется редко. Резервные копии нужны на случай, если пользователь сайт поломает, а у провайдера не будет рабочего бекапа. Бекап Шредингера, так сказать.

Добавим такую строчку в crontab (5:30 каждого первого числа):

30 5 1 * * /usr/bin/wget --no-passive -r -l 0 -q 0 ftp://isp3.service.tld/web/ -P /tmp; zip -q -r /var/backup/sites/site1/backup_`date +\%Y\%m\%d\%H\%M\%S`.zip /tmp/isp3.service.tld;rm -fr /tmp/isp3.service.tld > /dev/null

Debian 8 и старое ядро OpenVZ

UPD: конфигурация нерабочая – сервисы не стартуют! Так как проблема заключается в старой версии vzctl, а менять ядро на хост-системе никто не будет, то от идеи Debian 8 со старым ядром придется отказаться.

В далекой-далекой Галактике… не, не так… на далеком-далеком хостинге, на древнем-древнем сервере…
Короче, на действительно древнем сервере, корый я настраивал для клиента в 2009 году и обновлял в 2014, понадобилось поднять для экспериментов контейнер OpenVZ. Точнее, он уже был создан в 2014 на шаблоне Debian 6.0, но с тех пор не обновлялся. Самое простое – уничтожить контейнер и создать заново с новым шаблоном. Здесь надо быть особенно внимательным – Debian 9 со старым ядром (2.6.32-5-openvz-amd64 #1 SMP Tue May 13 17:16:50 UTC 2014 x86_64 GNU/Linux) не дружит(!) и напрочь теряет сеть. Поэтому ставим Debian 8, для работы более чем достаточно.
$ sudo /usr/sbin/vzctl stop 1009
$ sudo /usr/sbin/vzctl destroy 1009
$ wget http://download.openvz.org/template/precreated/debian-8.0-x86_64-minimal.tar.gz
$ sudo mv debian-8.0-x86_64* /var/lib/vz/template/cache/
$ sudo /usr/sbin/vzctl create 1009 --ostemplate debian-8.0-x86_64-minimal

Старый конфиг сохраняем, необходимо только поменять название шаблона (OSTEMPLATE=”debian-6.0-amd64-minimal” -> OSTEMPLATE=”debian-8.0-x86_64-minimal”)

Пытаемся запустить и обламываемся:
$ sudo /usr/sbin/vzctl start 1009
Starting container ...
Container is unmounted
Container is mounted
Failed to set upstart as init: No such file or directory

Проблема в том, что vzctl пропускает init и использует upstart напрямую, а его у нас в системе нет.
Не мешает добавить DNS-сервер в /etc/resolv.conf, если там пусто. 8.8.8.8 подойдет.
$ sudo -i
# cd /var/lib/vz/root/1009
# chroot .
# vim /etc/resolv.conf
# apt-get update
# apt-get install upstart

Теперь другое дело:
$ sudo /usr/sbin/vzctl start 1009
Starting container ...
Container is unmounted
Container is mounted
Adding IP address(es): 123.45.67.89
Setting CPU units: 1000
Container start in progress...

Автоматизация для бедных. Часть 3 – обработка входящей почты. Fetchmail, procmail и другие

Понадобилось автоматизировать прием некоторой специфической почты. Содержание тела письма не важно, вложения необходимо сохранить на сервере для дальнейшего использования.

Выбор связки fetchmail / procmail в качестве решения вполне очевиден.
$ sudo apt-get install fetchmail procmail incron

Дальше начинается интересное – fetchmail не работает для отдельного пользователя, если нет глобальной конфигурации в /etc/fetchmailrc. И права тоже должны быть установлены корректно.

$ ls -l /etc/fetchmailrc
-rw------- 1 fetchmail root 271 Jan 11 22:08 /etc/fetchmailrc

Нам необходимо забирать всю почту, протокол pop3s с использованием SSL, отдать на обработку юзеру mailman. Период обращения к почтовому серверу – 3 минуты:

$ sudo cat /etc/fetchmailrc
#### .fetchmailrc

set daemon 180
set logfile /var/log/fetchmail.log

poll mail.domail.tld proto POP3
user "robot@domain.tld" pass "password" is "mailman" preconnect "date >> /var/log/fetchmail.log"
ssl
fetchall
no keep
no rewrite
mda "/usr/bin/procmail -f %F -d %T";

На стороне mailman находится конфигурация procmail. Сохраняем все входящие сообщения и отдельно сохраняем вложения, передавая их uudeview для распаковки:
$ cat .procmailrc
:0
*^content-Type:
{
:0c
$HOME/emailbackup
:0fw
| uudeview -i +a +o -p $HOME/attachments -
}

Каталоги ~/emailbackup и ~/attachments необходимо создать заранее.

После создания файлов на диске incron их положит на сервер. Это гораздо быстрее и эффективнее, чем обработка по crontab.
$ incrontab -l
/home/mailman/attachments/ IN_CREATE mv $@/$# /mnt/files/INBOX/

Подробная информация по incron на IBM Developer: “Планирование операций обработки событий в файловой системе”

Теперь начинаются костыли.
Иногда, по какой-то непонятной причине, incron не перемещает файлы на сервер. Приходится их подбирать по расписанию, каждые 5 минут:
*/5 * * * * /usr/bin/find /home/mailman/attachments -mmin +1 -type f -exec mv '{}' /mnt/files/INBOX/ \; > /dev/null

Еще один костыль для латышского языка. Файлы с неправильными названиями на национальных языках иногда не обрабатываются скриптами. Есть замечательная утилита для переименования файлов со слетевшими кодировками:
*/5 * * * * /usr/bin/find /home/mailman/attachments -mmin +1 -type f -exec /usr/bin/convmv -f cp1257 -t utf8 -r --notest '{}' \; > /dev/null

Может, не самое оптимальное решение, но работает. Буду рад советам и предложениям.

Кусочки скриптов Bash. Обработка строк.

Несколько примеров обработки строк на Bash.
Была задача проверять содержимое шифрованных ZIP-архивов, описание создания которых дано в предыдущей заметке, доставать каким-то образом оттуда референс-код партнера, вставлять в заголовок письма и писать отдельно в лог.
Код партнера должен быть в названии документа, но, так как документы создаются вручную и собираются со всего мира, то никакой системы нет. Хорошо, что все партнеры используют собственную систему кодов, это позволяет вести поиск по шаблону.

В общем виде для кода фиксированной длинны L-M999999.99 это выглядит примерно так:
str1=`zipinfo -1 $filenamе | grep "L-M" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -b -o "L-M" | cut -d: -f1`
bgn=$(( $idx + 1 ))
end=$(( $idx + 12 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

[ -z "$str1" ] – проверка определена ли и не пустая ли переменная
grep -b -o "L-M" | cut -d: -f1 – определение после какой позиции начинается искомая подстрока

Вообще, grep очень мощная и гибкая утилита. Но не все параметры очевидны.
Например, wildcard обозначается точкой: echo $str1 | grep -b -o "XZ.-"
Очень полезный туториал по grep можно найти на Digital Ocean: “Using Grep & Regular Expressions to Search for Text Patterns in Linux”.

Более сложный вариант со “славянской спецификой”. Мало того, что начальная часть кода переменной длины (99999-DDMMYY-AAA / 99999А-DDMMYY-AAA), так она еще может содержать как латинскую, так и кириллическую букву “А”, какая раскладка была включена, так и вбили.

str1=`zipinfo -1 $filename | sed 's/\xd0\x90/A/g'|grep -E "[[:digit:]]{6}-[[:alnum:]]{3}" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -E -b -o "[[:digit:]]{6}-[[:alnum:]]{3}" | cut -d: -f1`
# if reference number contains letter 'A' it is one character longer
char=`echo $str1 | grep -E -b -o "[[:digit:]]{6}-[[:alnum:]]{3}" | cut -c $(( $idx - 1 ))`
if [ "$(echo $char | grep "^[ [:digit:] ]*$")" ]
then
bgn=$(( $idx - 5 ))
else
bgn=$(( $idx - 6 ))
fi
end=$(( $idx + 10 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

grep -E "[[:digit:]]{6}-[[:alnum:]]{3}" – поиск по шаблону 6 цифр “-” 3 цифро-буквенных символа
[ "$(echo $char | grep "^[ [:digit:] ]*$")" ] – проверка является ли символ цифрой
sed 's/\xd0\x90/A/g' – замена кириллической “А” на латинскую “A”, используются шестнадцатиричные коды.

Более продвинутый вариант, так как партнеры все делают вручную и никак не могут определиться в каком регистре будут символы и надо ли ставить в коде дефис. Код вида 99-YYlv, где первая часть варьируется по длине от 2 до 3 цифр:

str1=`zipinfo -1 $filename | tr [A-Z] [a-z]| sed 's/-//g' | grep -E "[[:digit:]]{4}lv" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | tr [A-Z] [a-z]| sed 's/-//g' | grep -E -b -o "[[:digit:]]{4}lv" | cut -d: -f1`
char=`echo $str1 | tr [A-Z] [a-z]| sed 's/-//g' | grep -E "[[:digit:]]{4}lv" | cut -c $idx`
if [ "$(echo $char | grep "^[ [:digit:] ]*$")" ]
then
bgn=$idx
else
bgn=$(( $idx + 1 ))
fi
end=$(( $idx + 6 ))
ref=`echo $str1 | cut -c $bgn-$(( $end -4 ))`-`echo $str1 | cut -c $(( $end -3 ))-$end`
fi

tr [A-Z] [a-z] – самый простой способ поменять регистр строки.

По идее, проверяя не цифра ли символ перед началом искомой строки, мы вылезаем за пределы массива. За такое в серьезном программировании отрывают руки, но в bash все работает без проблем.

Самый извращенный вариант обработки, первая часть кода варьируется от 3 до 6 цифр – простой счетчик без заполняющих нулей вида 999999-YYYY.

str1=`zipinfo -1 $filename | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | head -1`
if [ -z "$str1" ]
then
ref='NONREF'
else
idx=`echo $str1 | grep -E -b -o "[[:digit:]]{3}-[[:digit:]]{4}" | cut -d: -f1`
# partner code left part can be up to 6 digits long
char1=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $idx`
char2=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $(( $idx - 1 ))`
char3=`echo $str1 | grep -E "[[:digit:]]{3}-[[:digit:]]{4}" | cut -c $(( $idx - 2 ))`
if [ "$(echo $char1 | grep "^[ [:digit:] ]*$")" ]
then
if [ "$(echo $char2 | grep "^[ [:digit:] ]*$")" ]
then
if [ "$(echo $char3 | grep "^[ [:digit:] ]*$")" ]
then
bgn=$(( $idx - 2 ))
else
bgn=$(( $idx - 1 ))
fi
else
bgn=$idx
fi
else
bgn=$(( $idx + 1 ))
fi

end=$(( $idx + 8 ))
ref=`echo $str1 | cut -c $bgn-$end`
fi

Полный код высчитывается итерационно. Ничего более приличного в голову не приходит.

P.S.Краткий справочник по арифметическим операциям в bash: “Bash:Perform arithmetic operations”

Автоматизация для бедных. Часть 2 – отсылка файлов на SFTP

Первая часть была о том, как простенько сделать автоматическую отправку документов с приложением в виде шифрованных архивов ZIP. Пока заметка писалась, один из партнеров согласился открыть доступ по SFTP.
Офисные сотрудники и протоколы Интернета по определению не совместимы, поэтому даже такую несложную операцию лучше автоматизировать.
Конфигурация серверов остается полностью той же, добавляется один скрипт пересылки файлов, дополнительная задача в crontab и пару рабочих каталогов. Для использования SFTP в скриптах необходимо, чтобы вход на удаленный сервер проходил без пароля, по ключу. Как настроить такую удобную штуку, я писал здесь 10 лет назад.

1. Структура каталогов и рабочие файлы
/var/transfer/partner2/
/var/transfer/partner2/out/ <- очередь на пересылку
/var/transfer/partner2/backup/ <- копии отправленных файлов
/var/transfer/partner2/transfer <- скрипт отсылки
/var/transfer/partner2/sftp.job <- батник для sftp

2. Скрипт отсылки
#!/bin/sh

# Find and deliver files to Partner2

src=/mnt/company/#secure_transfer_partner2/
dst=/var/transfer/partner2/out/
bkp=/var/transfer/partner2/backup/

# Только файлы старше 3 минут, игнорировать каталоги, подкаталоги и скрытые dot-файлы
/usr/bin/find $src -maxdepth 1 -not -path '*/\.*' -mmin +3 -type f -exec mv '{}' $dst \;
/usr/bin/sftp -b /var/transfer/partner2/sftp.job company@ftp.partner2.com
if [ $? -eq 0 ]
then
mv $dst/* $bkp/
fi

3. Батник для sftp
rat@assistant:~$ cat /var/transfer/partner2/sftp.job
lcd /var/transfer/partner2/out
cd /in
put *

Кстати, одним бантиком можно файлы положить, забрать, перенести. Причем, из множества каталогов.

4. Crontab
rat@assistant:~$ cat /etc/cron.d/securetransfer
# /etc/cron.d/securetransfer: crontab entry to deliver files to Partner2 SFTP

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

*/5 * * * * root /usr/bin/test -x /var/transfer/partner2/transfer && /var/transfer/partner2/transfer >/dev/null 2>&1

Логирование, нотификации передающей и принимающей сторонам не включены, но в планах.

Теперь небольшой хак как использовать sftp в скриптах, но на принимающей стороне нет возможности прописать свой публичный ключ. Есть утилитка sshpass, которая позволяет подать пароль для ssh, sftp или scp из переменной, из файла или (для безмозглых) в открытом виде.
rat@assistant:~$ sudo apt-get update && sudo apt-get install sshpass

Тогда вызов SFTP из скрипта без ручного ввода пароля может выглядеть так:
rat@assistant:~$ /usr/bin/sshpass -f ~/.partner2 /usr/bin/sftp -b ~/partner2_sftp.job customer@ftp.partner2.com, где:
~/.partner2 – пароль к аккаунту customer@ftp.partner2.com, -f – чтение из файла;
~/partner2_sftp.job – батник со списком sftp-команд, например:
lcd /var/transfer/partner2/out
cd /in
put *

SCP в таком случае выглядит предпочтительнее, но, если удаленный сервер сконфигурирован “sftp only”, scp и ssh работать не будут.

Еще пара любопытных примеров как положить или скачать отдельный файл с удаленного сервера по sftp, не вводя дополнительных команд:
Скачать: sftp {user}@{host}:{remoteFileName} {localFileName}
Положить: sftp {user}@{host}:{remote_dir} <<< $'put {local_file_path}'

Пример взят со StackOverflow в "Single Line sftp from Terminal".

Автоматизация для бедных. Часть 1 – отсылка почты с шифрованными приложениями

В соответствии с требованиями европейской регулы по защите данных GDPR данные частных лиц не могут передаваться в публичной сети в открытом виде. Это в первую очередь относится к операторам данных. Если необходимо передать информацию третьей стороне, то она должна быть в защищенном виде.
Пришлось помочь одной дружественной фирме. У них обмен данными с партнерами идет по электронной почте, и теперь вся информация, содержащая персональные данные клиента, должна быть зашифрована. Самое простое – пересылка шифрованных архивов ZIP. Несмотря на слабость алгоритмов шифрования ZIP, использование длинных сложных паролей делает атаку перебором малопривлекательной – данные не стоят ресурсов, необходимых для взлома. Но шифровать файлы вручную с правильным ключом (партнеров > 10) – то еще занятие. Уровень ошибок и время, уходящее на вроде простую операцию – неадекватно высокие.
Идея автоматизации очень простая – на сервере документов (Windows Server) делаются папки для каждого партнера, куда сотрудники складывают документы, которые необходимо зашифровать и отослать партнерам, а все автоматические задачи выполняет маленький офисный “сервер” под Linux.

Как поставить и настроить сервер (в данном случае подойдет и старый ноутбук, и Raspberry Pi) написаны тысячи статей разной степени полезности. Поэтому примем, что настроенный сервер у нас есть, дистрибутив Linux – Debian или Ubuntu. Так же у нас есть сторонний SMTP сервер, через который будем отправлять почту.

Задача – периодически заглядывать в папку и, если там что-то есть, зазимовать это “что-то” с паролем и отправить заданному адресату по электронной почте. Так как файловая система удаленная (cifs), мониторить ее события по inotify не получится, придется опрашивать по расписанию.

1. Как подмонтировать файловую систему с Windows Server
Необходимо добавить информацию о новой файловой системе в /etc/fstab:
# Company documents
//172.16.171.10/company /mnt/company cifs credentials=/etc/.credentials 0 0

Раньше тип файловой системы указывался как smbfs, но эти времена прошли.
Файл /etc/.credentials содержит всю информацию для подключения к “шаре” Windows Server:
rat@assistant:~$ sudo cat /etc/.credentials
domain=domainname
username=username
password=userpassword

Пользователь должен иметь права на запись и удаление файлов.

После того, как информация добавлена, подключим новую файловую систему:
rat@assistant:~$ sudo mount -a

Если все настроено правильно, новая файловая система будет доступна:
rat@assistant:~$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 989M 0 989M 0% /dev
tmpfs 202M 22M 180M 11% /run
/dev/sda5 9.1G 6.2G 2.5G 72% /
tmpfs 1007M 124K 1007M 1% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 1007M 0 1007M 0% /sys/fs/cgroup
/dev/sda6 136G 25G 105G 19% /var
tmpfs 202M 8.0K 202M 1% /run/user/1000
//172.16.171.10/company 80G 30G 51G 38% /mnt/company

2. Как настроить компоненты почтовой системы.
Для отправки почты понадобятся mutt и ssmtp.
rat@assistant:~$ sudo apt-get update && sudo apt-get install ssmtp mutt

Mutt – отличный консольный почтовый клиент, а sSMTP – хитрая штука, которая прикидывается полноценным почтовым сервером, но для операций использует ресурсы стороннего сервера (Google, Yandex, etc.). Настраивать еще один почтовый сервер и заботиться о его безопасности в наши планы не входит. Тем более, что наш сервер “assistant” находится далеко за брандмауэром и NAT:
rat@assistant:~$ ifconfig enp1s0
enp1s0 Link encap:Ethernet HWaddr 00:0f:1f:ab:c3:9c
inet addr:192.168.88.3 Bcast:192.168.88.255 Mask:255.255.255.0

sSMTP настраивается тривиально, вся конфигурация в одном файле, обычно /etc/ssmtp/ssmtp.conf. Реверсные алиасы не трогаем.
rat@assistant:~$ sudo cat /etc/ssmtp/ssmtp.conf|grep -v '#'
root=postmaster
rewriteDomain=domainname.lv
FromLineOverride=YES
mailhub=mail.provider.lv:587
AuthUser=robot@domainname.lv
AuthPass=strongpassword

Mutt тоже потребует настройки, иначе не получится корректно указывать желаемый адрес отправителя.
Файл настройки находится в каталоге пользователя, от которого будут запускаться скрипты. В данном случае /root/.muttrc
rat@assistant:~$ sudo cat /root/.muttrc
set content_type="text/html"
set realname="CompanyName Accounting"
set from="accounting@domainname.lv"

Первый параметр указывает на формат сообщения. Если форматирование и фирменный стиль не важны, можно оставить plain text, не указывая параметр content_type.

3. Структура каталогов и скрипты запуска.

Рабочие каталоги и файлы конфигурации разместим в /var/transfer/email:
/var/transfer/email/
/var/transfer/email/fetch <- скрипт обработки
/var/transfer/email/message_body <- тело письма
/var/transfer/email/partner1/ <- файлы и конфигурация для партнера
/var/transfer/email/partner1/recipient <- файл с адресом получателя
/var/transfer/email/partner1/.pswd <- понятно
/var/transfer/email/partner1/out/ <- очередь архивов на отсылку
/var/transfer/email/partner1/spool/ <- локальный спулер файлов для архивирования
/var/transfer/email/partner1/backup/ <- архив полученных и отосланных файлов (в основном на время отладки)

В текстовом редакторе создадим содержимое тела письма (простейший HTML):
rat@assistant:~$ sudo vim /var/transfer/email/message_body

Файлы recipient и .pswd содержат адрес получателя и пароль шифрования соответственно.

Скрипт архивирования и отсылки:
rat@assistant:~$ sudo vim /var/transfer/email/fetch
#!/bin/sh

# Find and fetch files for processing over email

# Partner1 #################################
pswd=`cat /var/transfer/email/partner1/.pswd`
rcpt=`cat /var/transfer/email/partner1/recipient`
src='/mnt/company/#encrypted_email_partner1/'
spool='/var/transfer/email/partner1/spool/'
out='/var/transfer/email/partner1/out/'
bkp='/var/transfer/email/partner1/backup/'

# Files only, no subfolders, older than 3 min, ignore hidden files
/usr/bin/find $src -maxdepth 1 -not -path '*/\.*' -mmin +3 -type f -exec mv '{}' $spool \;

# Files should be moved from spool to backup only on successful archiving
if [ "$(ls -A $spool)" ]
then
/usr/bin/zip -j -P $pswd $out/company_transfer_`date +\%Y\%m\%d\%H\%M\%S`.zip $spool/*
if [ $? -eq 0 ]
then
mv $spool/* $bkp
fi
fi

# Archives should be removed from outbox only on successful transfer
if [ "$(ls -A $out)" ]
then
cd $out
for i in `ls -A $out`
do
/usr/bin/mutt -s "CompanyName secure document transfer `date +\%Y\%m\%d\%H\%M`" $rcpt -a "$i" < /var/transfer/email/message_body if [ $? -eq 0 ] then mv $i $bkp fi done fi

Запуск скрипта по расписанию, каждые 5 минут:
rat@assistant:~$ cat /etc/cron.d/securemail
# /etc/cron.d/fetch: crontab entry to deliver files for encryption and sending over email

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

*/5 * * * * root test -x /var/transfer/email/fetch && /var/transfer/email/fetch >/dev/null

Если все настроено правильно, файлы будут забираться с сервера, архивироваться с паролем и отсылаться получателю, указанному в файле recipient, с адреса, указанного в .muttrc.

Логгинг добавить по вкусу. Отправку почты на CC, BCC и список адресов смотрим в документации к Mutt.
Хотя, отсылка на список адресов в Mutt выглядит так:
a="address1@mail.com, address2@mail.com"
mutt -s "subject" -a file.pdf -- $a

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

P.S. Скрипты в данной конфигурации работают от root'a, но, так как сторонний контент не выполняется, решение достаточно безопасное. При желании можно создать отдельного пользователя под задачи автоматизации.
P.S.S. Mutt сохраняет отосланные сообщения в файле sent в каталоге того пользователя, от которого работает Mutt. Так что при серьезном потоке сообщений место на диске съедается очень заметно. Отключение сохранения отосланных сообщений в Mutt делается директивой в .muttrc:
set copy=no

←Older