Pull to refresh
2730.06
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

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

Reading time10 min
Views7.3K

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

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

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


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

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


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

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

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

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

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


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

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


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

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

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

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

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


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


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

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

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


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

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

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




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

Предполагалось, что на компьютере будет крутиться простой скрипт, который получает 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)

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


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

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


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



Ардуино


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

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

  • состояние питания «включен» или «выключен» (вернулся в стандартное состояние)
  • время в милисекундах, на которое устройство включено (при обработке JSON это время прибавлялось к текщему, и сохранялся момент когда уже пора выключать, то есть приводить состояние к стандартному)
  • у вентиляторов и PWM выходов задавалась глубина ШИМ, которая определяла скорость вращения
  • для светодиодной ленты передавались значения цвета, и с какого и по какой пискель этот цвет применить. Таким образом любой из 587 светодиодов можно было зажечь своим цветом. Но, чтобы надпись смотрелась на камере равномерно, цвет корректировался так, чтобы яркость ближних букв была меньше чем у более далеких. Для этого использовался ешё один параметр.

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

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

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




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

Буквы Сокол


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


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


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

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

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

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

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


Так лазер срабатывал отлично на тестах

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


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

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

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


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

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

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

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

Raspberry мы завязали на текущее время: каждый полный час запускался шредер. Картинка на телевизор выдавалась с помощю распберри, которая в тот момент уже получала с сервера запросы и пересылала их на исполнение в ардуинку. Картинки с денежными показателями рисовались с помощью вызова консольной утилиты 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, для создания необычного цветового акцента-подсказки.


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

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


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

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

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

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

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

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

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


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

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

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


Tags:
Hubs:
Total votes 65: ↑64 and ↓1+63
Comments33

Articles

Information

Website
ruvds.com
Registered
Founded
Employees
11–30 employees
Location
Россия
Representative
ruvds