Железо проекта: как мы строили комнату с хакерским квестом

ppz1hw9wz_7l_ho_cuwonadz3xm.png


Пару недель назад мы провели онлайн-квест для хакеров: построили комнату, которую заполнили умными устройствами и запустили из нее YouTube-трансляцию. Игроки могли управлять IoT-девайсами с сайта игры; целью было найти запрятанное в комнате оружие (мощную лазерную указку), хакнуть его и устроить в комнате короткое замыкание.

Чтобы добавить остросюжетности, мы поставили в комнате шредер, в который загрузили 200 000 рублей: шредер съедал по купюре в час. Выиграв игру, можно было остановить шредер и забрать все оставшиеся деньги.

Мы уже рассказывали прохождение игры, а также как был сделан бекенд проекта. Пришло время рассказать про хардвар и как он собирался.


Было очень много запросов показать момент уборки комнаты — показываем, как мы ее разбираем

Архитектура железа: управление комнатой


Проектировать аппаратное решение мы начали, когда был уже приблизительно понятен сценарий, готов бекенд и у нас стояла пустая комната, готовая для монтажа оборудования.
Вспоминая старый анекдот «The S in IoT stands for Security» («Буква S в аббревиатуре IoT обозначает Security») мы приняли решение, что в этот этот раз игроки по сценарию игры взаимодействуют только с фронтендом и бэкендом сайта, но не получают возможности добраться непосредственно к железу.

Это было сделано из соображений безопасности и зрелищности происходящего на экране: при непосредственном доступе игроков к железу, было бы значительно сложнее изолировать безопасные и потенциально опасные действия, например, ускоренную прокрутку шредера или управление пиротехникой.

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

Не использовать беспроводные решения


Все игровое пространство находится в одном кадре, до каждого уголка которого можно дотянуться. Реальной необходимости в беспроводных подключениях не было и они бы просто стали еще одной точкой отказа.

Не использовать какие-то специальные устройства от умного дома


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

Кроме того, нужно было придумать устройства, по которым будет явно видно, что именно игроки меняют его состояние: включили/выключили или проставили конкретный свет на буквах СОКОЛ.

Мы собрали все элементы из общедоступного железа, которое можно купить в обычных магазинах радиодеталей: между доставкой пиццы и диетической колы, на площадку постоянно приезжали курьеры Чип и Дип и Леруа.

Выбор собрать все самим упрощал отладку, масштабируемость, однако, требовал большей аккуратности при монтаже.

Все реле и арудино не должно быть видно в кадре


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

guhch4qid906y7cj8_hf986yjvk.png
В итоге все спрятали под столом, а камеру установили так, чтобы ниже стола ничего не было видно. Это была наша «слепая зона» для подползания инженера

В итоге у нас получилось фактически одно умное устройство: оно получало от бэкенда состояние каждой своей части и меняло его соответствующей командой.

С точки зрения аппаратной реализации это устройство управляло 6 элементами:

  1. Несколько настольных ламп, они имеют состояние вкл/выкл и управляются игроками
  2. Буквы на стене, они могут менять свой цвет по команде игроков
  3. Вентиляторы, которые крутятся и открывают флипчарт при нагрузке на сервер
  4. Лазер, который управляется через ШИМ
  5. Шредер который сжирал деньги по расписанию
  6. Дым-машина, которая срабатывала перед каждым выстрелом лазера



Тестируем дым-машину вместе с лазером

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

Что из себя представляло это умное устройство


zezvwnqpdu2ejmpnwznlcmbmzn8.png

Всю дорогу Юра, наш хардварщик, старался не усложнять и придумать самое простое, минималистичное решение из возможных.

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

К портам подключались:

  • 16 обычных реле (именно они издавали то щелканье, которое было слышно на видео. В основном мы выбрали их из-за этого звука)
  • 4 твердотельных реле для управления каналов с ШИМ, например вентиляторов,
  • отдельный ШИМ выход для лазера
  • вывод, формирующий сигнал на светодиодную ленту


Вот пример json-команды, которая приходила к реле от сервера

{"power":false,"speed":0,"period":null,"deviceIdentifier":"FAN"}


А это пример функции, с помощью которой команда попадала на арудино

def callback(ch, method, properties, body):    request = json.loads(body.decode("utf-8"))    print(request, end="\n")     send_to_serial(body)


Чтобы отследить момент, когда лазер наконец пережжет веревку и гиря полетит на аквариум, мы сделали небольшую кнопку, которая срабатывала на падение гири и давала сигнал системе.

jyrizg7eoizurrt2viqfegn5aje.png
Кнопка-мониторинг движения гири

При этом сигнале должны были загореться дымовые шашки, сделанные из шариков для пинг-понга. Мы вложили 4 дымовухи прямо в корпус сервера и подвели к ним нихромовую нить, которая должна была раскалиться и сработать как запал.

yyzqnjqsitvslrdvlpl0exrtcts.png
Корпус с дымовыми шашками и китайской гирляндой

-bjlo9qnjqjf4jpktrdumlh-o70.png

Ардуино


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

Первое — при получении нового запроса, запрос парсился с помощью библиотеки ArduinoJson. Далее каждому управляемому устройству сопоставлялись два его свойства:

  • состояние питания «включен» или «выключен» (стандартное состояние)
  • период, на который устройство включено — время в микросекундах от старта платы, когда уже пора ее выключать, то есть приводить состояние к стандартному


Последнее время задавалось при получении соответствующего параметра в JSON, однако его можно было и не передавать, тогда значение устанавливалось в 0 и обнуления не происходило.

Второе действие, которое производила ардуинка каждый цикл — это актуализация состояний, то есть проверка, нет ли необходимости что-то включить или не пришло ли время выключать какое-либо устройство.

Лазерная указка — тот самый Мегатрон 3000


klcqpsneque13szc-ceochcu7bq.png

Это обычный лазерный модуль для резки и маркировки LSMVR450–3000MF 3000 мВт 450нм с ручной фокусировкой.

Буквы Сокол


Сделаны очень просто — мы просто скопировали буквы с логотипа, вырезали их из картона, а потом обклеили led-лентой. При это пришлось спаивать куски ленты между собой, по 4 контакта на каждом шве, но результат того стоил. Наш бекендер Паша показал чудеса умелости, сделав это меньше чем за несколько часов.


Первые тесты iot-устройства и допиливание


Мы сделали первые тесты и заодно нам подъехали новые задачи. Дело в том, что в середине процесса к команде присоединился настоящий кинопродюсер и оператор из ВГИКа Илья Серов — он выстроил кадр, добавил дополнительное киношное освещение и немного изменил сценарий игры, чтобы сюжет получался более эмоциональным, а картинка более драматичной и театральной.

Это существенно увеличило качество, но появились элементы, которые тоже понадобилось подключать к реле и прописывать алгоритм работы.

Другой проблемой оказался лазер: мы провели несколько экспериментов с разными типами веревки и лазерами различной мощности. Для теста мы просто вертикально подвешивали груз на веревке.

При запуске с тестовым токеном мощность регулируемая через шим составляла менее 10% и веревку не повреждала даже при длительной экспозиции.

Для боевого режима лазер расфокусировали примерно до пятна диаметром 10 мм и он уверенно пережигал веревку с грузом, с расстояния около метра.

vnzngn0souwynpa6x9sydshnt-q.jpeg
Так лазер срабатывал отлично на тестах

Когда мы уже начали тестировать все прямо в комнате на подвешенной гире, оказалось, что надежно закрепить лазер не так-то просто. Затем, что когда веревка горит — она плавится, растягивается и смещается из под первоначального фокуса.

rk0s9sqluiuiflagxkukft_9sxs.png
А вот так он уже не работал: веревка смещалась

Илья переместил лазер на противоположной от веревки конец комнаты, чтобы лазерный луч шел через всю сцену и красиво смотрелся в кадре, из-за чего расстояние увеличилось вдвое.

Проведя ещё несколько экспериментов с пережиганием веревки уже в бою, мы решили не пытать судьбу и подстраховать разрезание веревки с помощью нихромовой проволоки. Она уничтожала нить через 120 секунд после включения лазера в боевом режиме. Это, а также отключение проволоки и запал дымовых шашек при срабатывания контакта отрыва мы решили жестко прописать прямо в железе микроконтроллера.

p2k6_f8c8dokm3kmfpjdtthqubk.png
Нить, которая в итоге пережигала веревку за кадром

Таким образом появилась третья задача которую решала ардуинка — отработать последовательности, связанные с исполнением этих команд.

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

Но на тестовом прогоне Илья отсмотрел сцену и предложил на самом большом экране показывать игровой баланс: сколько денег еще осталось, сколько съедено и обратный отсчет до следующего запуска шредера.

Ардуино мы завязали на текущее время: каждый полный час запускался шредер. Картинка на телевизор выдавалась с помощю распберри, которая в тот момент уже получала с сервера запросы и пересылала их на исполнение в ардуинку. Картинки с денежными показателями рисовались с помощью вызова консольной утилиты fim примерно вот так

image = subprocess.Popen(["fim", "-q", "-r", "1920×1080", fim_str]), где fim_str

И формировалась исходя из нужной суммы или времени.

Картинки мы сгенерировали заранее: просто взяли готовое видео с таймером и экспортировали 200 картинок.

Вот такая механика была запрограммирована в кресте. К моменту, как запустился финальный отсчет, мы все выехали на площадку, вооружились огнетушителями и сели ждать пожар (который вовсю пылал только в дискорде)

Как сделать трансляцию, которая работает неделю: выбор камеры


Для квеста нам нужна была непрерывная трансляция на ютубе в течение 7 дней — именно столько мы закладывали как максимальную продолжительность игры. Было две вещи, которые нам могли помешать:

  1. Перегрев камеры от непрерывной работы
  2. Обрыв интернета


Камера должна была отдавать картинку как минимум Full HD, чтобы играть и наблюдать за комнатой было комфортно.

Изначально мы смотрели в сторону веб-камер, которые выпускают для стримеров. Мы резали бюджет, поэтому покупать камеру не хотелось, а в аренду, как оказалось, их не дают. В этот же момент мы чудом нашли лежащую у меня дома камеру Xbox Kinect, поставили в комнате и запустили тестовую трансляцию на неделю.

Камера справлялась нормально и не перегревалась, но Илья почти сразу заметил, что в ей не хватает настроек, в частности нельзя выставить экспозицию.

Илья стремился приблизить вид трансляции к стандартам кино-видео производства: передавать динамично меняющуюся световую сцену с яркими источниками света, затемнённым фоном и предметами в кадре. При этом хотелось сохранить проработку изображения как в светах так и в тенях, с минимальным цифровым шумом.

Поэтому, хоть кинект и показал себя надежным при тестах и ему не требовалась плата видеозахвата (еще одна точка отказа), от него мы решили отказаться. После трёх дней тестов разных камер, Илья выбрал Sony FDR-AX53 — небольшой надежный камкордер, бюджетный в аренде, но при этом обладающий достаточной надежностью и изобразительными характеристиками.

Мы арендовали камеру, включили ее на неделю в связке с платой видеозахвата и поняли, что с ней можем рассчитывать на непрерывную трансляцию в течении всего квеста.

Делаем кино: постановка сцены и света


Работа над освещением требовала определенного изящества, нам требовалось минимальными средствами выстроить световую партитуру:

1. Подсветка предметов, когда их находят игроки (лазер, гиря), а также постоянный свет на шредер. Здесь использовались dedolight 150 — надежные и компактные осветительные киноприборы с низковольтными галогенными лампами, позволяющие акцентно сфокусировать луч на конкретном предмете, не задевая фон и остальные предметы.

2. Практический игровой свет — настольная лампа, торшер, звезда, гирлянда. Весь практический свет был гармонично распределён в кадре, чтоб освещать свою область изображения, внутри стояли led лампы с цветовой температурой 3200К, лампа в торшере была закрыта красным фолиевым фильтром Rosco, для создания необычного цветового акцента-подсказки.

x2als9g7vcfvljzwhe2sp3c__hw.png
Я у мамы инженер или запуск завтра

Как мы резервировали интернет и электричество


К вопросу отказоустойчивости подошли почти как в дата-центре: решили не отступать от основных принципов и зарезервировали по привычной схеме N+1.

Если на ютубе трансляция прекращается, это значит, что заново подсоединиться по той же ссылке и продолжить стрим уже будет невозможно. Это был критический момент, к тому же комната находилась в обычном офисе.

Для этого мы использовали роутер на базе OpenWRT и пакет mwan3. Он автоматически тестировал каждые 5 секунд доступность канала и, в случае обрыва, переключался на резервный модем с Yota. В итоге переключение на резервный канал происходило меньше чем за минуту.
rwzqewcbo_-vq7srhir5nyny4vs.png
Также не менее важно было исключить перебои с электричеством, ведь даже кратковременный скачок напряжения вызвал бы перезагрузку всех компьютеров.

Поэтому мы взяли бесперебойник ippon innova g2 3000, который резервировал бы все игровые устройства: суммарная потребляемая мощность нашей системы была в районе 300 Ват. Его хватило бы на 75 минут, вполне достаточно для наших целей.

Мы решили пожертвовать дополнительным освещением в случае, если в помещении отключится электричество — его к бесперебойнику не подключали.

Благодарности


  • Спасибо Tilda Publishing и отдельно Михаилу Карпову за то, что он не просто пошел навстречу и разрешил нарушить Terms of use, но даже подарили нам бизнес-аккаунт на год, когда мы рассказали о проекте.
  • Лучшему боссу ntsaplin за то, что в ответ на звонок «есть идея: мы возьмем сервер, на него поставим аквариум, а над ним подвесим гирю, бум, бах, все залило водой, короткое замыкание, пожар!» он всегда уверенно говорит «делайте!»
  • Илье Серову за то, что он присоединился и стал сопродюсером проекта, готовым полночи ползать, приклеивая светодиодную ленту, искать технические решения и сделать все, чтобы у нас получилось настоящее кино.
  • zhovner за то, что всегда был готов спасти ситуацию, когда другие разводили руками, борщик, моральную поддержку и разговоры до утра.
  • samat за то, что связал нас с лучшим пентестером страны, который проконсультировал нас и помог с задачками.
  • daniemilk за крутой видеопродакшен всех роликов.
  • delfphe за твердую руку и готовность работать до последнего.
  • Ну Dodo Pizza Engineering за почти всегда теплую пиццу.


И самая большая благодарность — игрокам за все эмоции, которые мы пережили, пока вы двое суток штурмовали квест без сна и даже отложив работу.

Остальные статьи про квест с уничтожением сервера


oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru