ЗАДАЧА

Однажды, начальство потребовало круглосуточно вести запись происходящего в офисах. А также, в рабочее время — периодически публиковать фото из офисов на сайте.
От моего предшественника мне досталось:
- Несколько офисов с умными видеокамерами D-Link, которые фотографируют происходящее
- Сервер на FreeBSD
- Сайт организации, куда должен попадать снимок с каждой камеры
- Сетевая папка в локальной сети, где должны храниться архивы записей
Известно:
— Сервер FreeBSD не доступен извне
— Хостер не любит, когда к его FTP подключаются чаще, чем раз в минуту
— Учитывая качество и толщину каналов связи, камеры не пишут видео, а делают периодические фото
На момент постановки задачи, видеокамеры самостоятельно вразнобой подключались к FTP хостера и выкладывали снимки по расписанию каждую минуту. В результате чего, хостер периодически блокировал доступ по FTP к сайту.
РЕШЕНИЕ
Решение — теория
Решением этой задачки стал один из уроков по информационной безопасности, данный мне однажды в СПбГУ ИТМО. В уроке говорилось о зонах безопасности, об их уровнях, и о правилах чтения/записи между уровнями.
В двух словах: приложение с высшим уровнем безопасности, например — 1, может читать и писать в приложение уровня 2 или 3. Но приложение 2, не может ни читать, ни писать в приложение 1, хотя может читать и писать в приложение 3.
На самом деле правила несколько сложнее, но я упростил для облегчения восприятия.
Who is who
Схема взаимодействия всех вышеуказанных элементов выстроилась простая:
Камеры складывают фотографии на мой сервер по FTP, сервер их копирует в сетевую папку, а также публикует на хостинге.

Сервер здесь выступает уровнем 1, имеющим доступ ко всем остальным уровням. Нетрудно заметить, что здесь есть как-бы нарушение правила: камерам, имеющим более низкий 2й уровень, дано право писать на 1й уровень. По-хорошему, следовало сервер научить читать картинку с камер, а не открывать им доступ, но в данном случае, общая безопасность системы не пострадала. Объясняю: хитрость в том, что камеры пишут в FTP-папку, имеющую тот же уровень безопасности, что и у камер, т.е. 2й. Папка находится отдельно от всех остальных и содержит только текущие изображения с камер. Любой кто подключится к FTP используя логин и пароль с камеры, получит только несколько фотографий офисов, но не сможет увидеть всех архивов.
Пока расписывал это все, подумал, что злоумышленник может выгрузить таким образом свою фотографию, содержащую какую-нибудь непристойность, и она автоматически попадет на сайт. Надо подумать, как этого избежать. Можно, например, ограничить доступ к FTP по статическим IP-адресам камер и усложнить пароли для доступа к камерам и FTP. Но лучше, чем научить сервер считывать изображение с камер самостоятельно, боюсь, ничего не придумать. Что поделать — технические ограничения камер.
Но не будем унывать. Камеры висят не в самом удобном месте для физического доступа, а чтобы в них покопаться — нужно знать пароль,… а еще нужно надеть маску или выключить свет, прежде чем камеру свинтить, но и эти действия спалят злоумышленника, т.к. известно кто в какое время где был.
Вернемся к нашим баранам
Итак, камеры будут дружно скидывать фотографии на сервер, а он в свою очередь, при помощи хитроумного скрипта на SH, будет копировать их в папку архива с текущей датой и выкладывать копии на сайте, если текущее время совпадает с режимом работы учреждения. А на следующий день, сервер почистит уже ставшую вчерашней папку архива от файлов с 0-вым размером, соберет все фотографии в видеоролик при помощи утилиты ffmpeg, и удалит их.
Решение — практика
- Устанавливаем и настраиваем на FreeBSD-сервере ProFTP (о вкусах не спорят, я просто предлагаю понятный вариант) и ffmpeg.
- Создаем папку для видеокамер и папку для архива (например, где-нибудь в папке /usr/)
- Создаем в ProFTP учетную запись для видеокамер и отдаем ей созданную папку
- Настраиваем камеры, используя их веб-интерфейс, таким образом, чтобы они круглосуточно и, к примеру, ежеминутно (кому какой период подойдет), подключались к нашему серверу по FTP и сохраняли фотографию.
- Очень важно также, настроить камерам правильную дату, время и автоматическую синхронизацию с местным NTP-сервером точного времени.
- Вспоминаем параметры подключения к FTP нашего хостинга.
- Пишем хитроумный SH-скрипт и заносим его в cron с частотой выполнения, аналогичной частоте выкладывания фотографий камерами, т.е. раз в минуту.
- Наслаждаемся и идем докладывать начальству.
И наконец сердце решения — скрипт, собственной наработки:
#!/bin/sh
## Запоминаем дату. Требуется, чтобы делать
## архив отправленных файлов, на будущее
day="`date +%Y-%m-%d`"
archive="/store/STORE/cam_archive"
## Данные для подключения по FTP
## Имя пользователя
FTPUSER='*********'
## IP-адрес или доменное имя хоста
FTPHOST='***.***.***.***'
## Пароль FTP
FTPPASS='************'
## Удалённый каталог, в который будем пересылать файл
REMOTEDIR="/www/img"
## Локальный каталог откуда будем копировать файлы
LOCALDIR="/store/STORE/cam"
## Входим в локальный каталога, откуда будем копировать файлы
cd $LOCALDIR
# Пишем в лог новую дату
echo .
echo -================================================-
date
## Создаём каталог, куда будем ложить копии отправленных файлов
mkdir $archive/$day >/dev/null 2>&1
## Запоминаем время для имени файла.
time1=`date +%H_%M_%S`
## Копируем файлы в созданную нами директорию, текущего дня.
# Для каждой камеры создается своя папка (по имени файла, который создает камера)
echo Сохраняем снимки в архив.
for file in $(ls -1 *.jpg)
do
camfolder=`echo ${file} | sed 's/\.jpg//'`
mkdir $archive/$day/$camfolder >/dev/null 2>&1
cp $file $archive/$day/$camfolder/$time1\_$file
done
# Топорный метод для определения подходящего времени, чтобы выложить снимки на сайт
# grep дает ответ, есть ли в строке minutes1 текущая минута времени minutes2
minutes1='01 03 05 07 09 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59'
minutes2=`date +%M`
result=`echo ${minutes1} | grep ${minutes2}`
# Публикуем снимки на сайте, только если время в пределах режима работы учреждения (>=10 and <=18 and !result)
if [ `date +%H` -ge 10 ] && [ `date +%H` -le 18 ] && [ -n "$result" ]
then {
echo Публикуем снимки на FTP.
## Здесь начинается подключение к FTP-серверу и выгрузка снимков
ftp -n $FTPHOST <<EOF
quote USER $FTPUSER
quote PASS $FTPPASS
binary
cd $REMOTEDIR
dir
mput *
a
EOF
}
fi
# Если настал новый день (часы=00 and минуты=00), тогда делаем видео из папки вчерашнего дня.
if [ `date +%H` -eq '00' ] && [ `date +%M` -eq '00' ]
then {
# вычислим вчерашний день
LASTDAY=`date -v-1d +"%Y-%m-%d"`
## заходим в эту папку
cd $archive/$LASTDAY/
## проходим по всем подпапкам и чистим их от пустых и недогруженных файлов
## фотографии с моих камер всегда имеют размер более 10k, поэтому все что меньше - однозначно недогружено
find $archive/$LASTDAY/ -size -10k -print | {
while read files
do
rm "${files}"
done
}
## Проходим по всем папкам камер по очереди, выполняя три действия
for camfolders in $(ls -1 -U)
do
## 1. Переименование файлов в ряд с именами в виде порядковых номеров
y=0
ls -1 $archive/$LASTDAY/$camfolders/*.jpg | {
while read files2
do
y=`expr ${y} + 1`
mv ${files2} $archive/$LASTDAY/$camfolders/img`printf %04d ${y}`.jpg
done
}
## 2. Конвертируем все в видеоряд
## TODO: требуется настройка высокого качества видео
ffmpeg -f image2 -i $archive/$LASTDAY/$camfolders/img%04d.jpg -r 6 -s 720x576 $archive/$LASTDAY/$camfolders\_$LASTDAY.avi
## 3. Удаляем все изображения из папки
rm -rf $camfolders
done
}
fi
exit 0
Работа для напильника:
- Качество сжатия видеозаписи, мягко говоря, страдает ужасно. Не могу разобраться в хитроумных параметрах ffmpeg. А возможно, что не хватает кодеков. Прошу тех кто сталкивался — подсказать подходящий перечень ключей.
Злоумышленник-инсайдер, прикинувшись камерой, может устроить некислый дефейс уважаемому сайту, если конечно узнает пароль от FTP. Нужно либо закрыться как-то, либо научить сервер заходить в веб-интерфейс камер и скачивать с них текущее изображение (у камеры для этого предусмотрен статичный адрес cam1/snapshot.php, а аутентификация производится на базе .htaccess). Посоветуйте, пожалуйста, при помощи каких команд и утилит это можно решить.
UPD: решение этого момента подсказал Akint. Чуть позже добавлю в код.