Шрифт
Разбор всех заданий отборочной игры Yandex.Root
Play

Разбор всех заданий отборочной игры Yandex.Root

Сегодня ночью завершилась первая игра отборочного тура Yandex.Root — олимпиады для Unix-инженеров и системных администраторов. В ней приняло участие 456 человек из 229 команд, 194 из которых выполнили хотя бы одно задание. Со всеми девятью справилось 38 команд.

Мы проводим Root в четвёртый раз, но впервые решили опубликовать на Хабре разбор тасков. Задачи, которые мы даём на олимпиаде, сопоставимы с теми, что регулярно решают наши системные администраторы. В Яндексе почти каждый день что-то выкатывается и, когда что-то идёт не так, нужно оперативно распознать это и эффективно отреагировать.

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

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

Игра Shannon

Мы решили назвать все игры в память о людях, которые внесли вклад в современные технологии, которые используются в нашей работе. Эта посвящена Клоду Шеннону, инженеру и математику, который среди прочего подарил нам слово «бит». Кстати, сам сервис root.yandex.ru запущен на compute узлах private cloud Яндекса. Цель отборочной игры — решить несколько проблем на виртуальной машине, изменив конфигурацию установленной ОС: например, запустить службу или скорректировать работу программы. Задачи могут быть как связанными, так и независимыми. Необходимо «зачистить мониторинг», то есть решить проблемы со статусом Critical (отмечены красным цветом) внутри виртуальной машины. Ее образ можно скачать заранее, образ зашифрован — ключ для расшифровки публикуется и рассылается всем участникам по электронной почте в начале игры.

После расшифровки образа необходимо установить соединение с игровым VPN. Для этого всем участникам выдается config-файл: капитан получает его по почте в момент одобрения заявки, члены команды — когда принимают приглашение капитана. В случае утраты файл можно скачать повторно командой «download». Каждый игрок может подключиться к VPN со своей виртуальной машины, поэтому задачи можно распределять внутри команды и решать их параллельно.

В качестве операционной системы в этой игре используется ArchLinux. После загрузки ОС пользователь видит приглашение «shannon login:». Никаких подсказок относительно реквизитов доступа у игрока нет. Как правило, в таких случаях нужно воспользоваться наличием «физического» доступа к машине и сбросить пароль суперпользователя:

  • перезагружаемся, останавливаем таймер обратного отсчёта в загрузчике Grub;
  • нажимаем 'e', находим строчку linux …, дописываем в конец init=/bin/bash;
  • нажимаем Ctrl-X, ждём загрузки;
  • вводим команду passwd , устанавливаем новый пароль;
  • вводим команду exec /sbin/init , загрузка продолжается.

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

  • синхронизируемся с внешним зеркалом: pacman-Sy , pacman-key --populate ;
  • обновляем пакетный менеджер: pacman -S pacman , pacman-db-upgrade;
  • для решения некоторых заданий нам понадобится tcpdump и strace: pacman -S tcpdump strace.

Внутри игрового образа находится программа game, которая запускает проверочные скрипты (далее — проверки) на удалённом сервере. В случае успешного выполнения проверки, игра засчитывает выполнение задания.

Чтобы обеспечить связность с удалённым сервером, нужно настроить OpenVPN. Организаторы уже подготовили всё необходимое — достаточно скопировать файл конфигурации (он прикреплён к письму от Yandex.Root) в /etc/openvpn/openvpn.conf и выполнить команду systemctl start openvpn@openvpn .

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

Here is a private key and a certificate for a CA to generate your certificate: -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQCjKwGnBHUwQtTzLb5uhrh+eRRAQyQwGzCg+n4XWzt8M+iX/OGx 4QCG4GjKhi9Nqzhm41+AjPB5cndU3Oe5j1LrcvWvxe2n15FG7hPSLG5dHe97pzpj KVma8OkcrUc6WWIccZ48FlV21ZCeUFukthtqEDDEEw1CxEnwHgIydnynlwIDAQAB AoGADTAfrREmK6VrMtCCsMpAxTAiG+ORXDYGYyx73oVoNGy5ovc0gr0N3tjqf1wD HML3BxHfmTNLCHXhAUHtlMjpya7kkJELurrFgEQ9gkcdogcf8Iw/J6GjBpJG2WlX vVL4zEiYw0T5TULGI54Iest0ZQx88EX8r+6x1jI668RHCtECQQDYUPLf2K/0FUyk csXoKq1ECseSVpfhG5NITqsLOc93jh3xAQFYtSuM7E3CeHkP+ZoKY/SGd9QkWrhd QQFoGL5vAkEAwRoCwNqlUWwTVayGdgw/D/mxtFelKRYl8kj50MeMraBqHM/ijXZt +wF5exUmuPio+nF64UIqLA1VCYhnqJ49WQJAL3DJY0hdhnVpYqN9PeamK0cF79Un 6AmpKnF+V67tDjZP4LwstGy/SV/FygGr41IFc4Pqa9c54mM3DdSk31SV5wJAHW9f mBI8PQsib17bKEd5nW/MfNcXYAn2QtaI7iBc+2KGilnOCQ5SeX6iC/cPbgbJi1Od DZVOZGSr38YhNvzYEQJBALoFJQEg6Xj44ClcJFIjbA+xyipk4h5JcmGvpUeKfaKF EBSJMECLR8wIa5XUkeRuM30JhTkd0s3WPUFaoBAvcvs= -----END RSA PRIVATE KEY-----

-----BEGIN CERTIFICATE----- MIIDHzCCAoigAwIBAgIJALEwbIlKhnreMA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV BAYTAlJVMQ8wDQYDVQQIEwZNb3Njb3cxDzANBgNVBAcTBk1vc2NvdzEPMA0GA1UE ChMGWWFuZGV4MQ0wCwYDVQQLEwRSb290MRgwFgYDVQQDEw9yb290LnlhbmRleC5j b20wHhcNMTUwNDA2MTY0MzA5WhcNMTYwNDA1MTY0MzA5WjBpMQswCQYDVQQGEwJS VTEPMA0GA1UECBMGTW9zY293MQ8wDQYDVQQHEwZNb3Njb3cxDzANBgNVBAoTBllh bmRleDENMAsGA1UECxMEUm9vdDEYMBYGA1UEAxMPcm9vdC55YW5kZXguY29tMIGf MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjKwGnBHUwQtTzLb5uhrh+eRRAQyQw GzCg+n4XWzt8M+iX/OGx4QCG4GjKhi9Nqzhm41+AjPB5cndU3Oe5j1LrcvWvxe2n 15FG7hPSLG5dHe97pzpjKVma8OkcrUc6WWIccZ48FlV21ZCeUFukthtqEDDEEw1C xEnwHgIydnynlwIDAQABo4HOMIHLMB0GA1UdDgQWBBQG+ykV13EVW9XxCTncLjLV YVX83TCBmwYDVR0jBIGTMIGQgBQG+ykV13EVW9XxCTncLjLVYVX83aFtpGswaTEL MAkGA1UEBhMCUlUxDzANBgNVBAgTBk1vc2NvdzEPMA0GA1UEBxMGTW9zY293MQ8w DQYDVQQKEwZZYW5kZXgxDTALBgNVBAsTBFJvb3QxGDAWBgNVBAMTD3Jvb3QueWFu ZGV4LmNvbYIJALEwbIlKhnreMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD gYEAmvNk8iAbV4+YMq/9oxkMeB6RxLs9m6jhYyAPuAI/dUhWSX+D+BnRcbsHWK4r a9G/riM1zerb5BD1apMz3faON2ydFJGB0thjlgr/KXfgaUXjp15QslEhsyhZIgEB Tak+0BQkkh5+cFAvJhGCZqajr6m2I8Dix3mF3Ey7nSx1GDU= -----END CERTIFICATE-----

Запишем данные из задания в файлы ca.crt и ca.key соответственно. Теперь посмотрим на сертификат удостоверяющего центра:

Запросим новый сертификат с теми же значениями:

Подготовим структуру для работы удостоверяющего центра:

Следующая команда вернёт ошибку, текст которой не столь очевиден:

На самом деле, проблема заключается в том, что строки в сертификате удостоверяющего центра и запросе написаны в разных кодировках. Чтобы обойти эту проблему, следует отредактировать файл /etc/ssl/openssl.cnf и изменить значение параметра string_mask в секции [req] на pkix .

Осталось подготовить файлы для веб-сервера:

Теперь установим веб-сервер (pacman -S nginx) и включим SSL в /etc/nginx/nginx.conf%, раскомментировав соответствующую секцию server.

Но решение не принимается проверяющей системой с диагностикой «SSLv3 is weak». Изменим конфигурацию на рекомендуемую Mozilla:

  • отметим опции Nginx и Modern, вставим конфигурацию в nginx.conf;
  • сгенерируем параметры Диффи-Хеллмана: openssl dhparam -out dhparam.pem -outform PEM -2 2048 ;
  • перезапустим nginx: systemctl restart nginx.
2.MariaDB repair

В этом задании требуется восстановить работу базы данных.

Из названия очевидно, что используется база данных MariaDB, известный форк MySQL.

Установим СУБД: pacman -S mariadb и попробуем запустить systemctl start mysqld . Из логов видно, что mysqld ищет файлы в неправильном месте. Из файла конфигурации /etc/mysql/my.cnf видно, что работа системы в сетевом режиме нарушена — добавлены параметры skip-networking, bind-address, указано неправильное значение datadir. Чтобы сэкономить время, не будем пытаться чинить файл конфигурации; вместо этого заменим его на заведомо рабочий:

Поправим владельца файлов — chown -R mysql:mysql /var/lib/mysql , и попробуем запустить базу данных ещё раз — systemctl restart mysqld .

Ура, mysqld работает. Попробуем подключиться:

Видим, что установлена парольная защита, а пароль нам неизвестен/ Значит, как и в случае с операционной системой, придётся сбросить его:

  • останавливаем mysqld: systemctl stop mysqld ;
  • подготавливаем файл с инструкцией сброса: echo "UPDATE mysql.user SET password = PASSWORD('root') where user = 'root';" > /tmp/reset.sql ;
  • запускаем mysqld с загрузкой нашего файла: mysqld --user=mysql --init-file=/tmp/reset.sql ;
  • теперь остановим mysqld штатным образом, для этого следует нажать Ctrl-\ ;
  • запустим unit снова — systemctl start mysqld

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

Создать таблицу с именем data не получится, так как системные таблицы всё ещё содержат её упоминание. Теперь подключим файл данных к нашей новой таблице. Для этого отключим «пустой» ibd-файл от таблицы: alter table data2 discard tablespace; , подменим его заполненным mv data.ibd data2.ibd и подключим обратно alter table data2 import tablespace; .

Так как db.data до сих пор числится в системных таблицах, то drop table db.data мы сделать не сможем. Придётся создать временную базу данных, перенести в неё новую таблицу, удалить старую, а затем создать заново:

Осталось лишь выдать доступ пользователю: grant all privileges on db.* to 'checker'@'%' identified by 'masterkey'; . К сожалению, проверка так и не проходит из-за ошибки подключения. Проверка с помощью tcpdump показывает, что mysqld не отвечает. Проверим фаервол с помощью iptables-save и обнаружим проблему, оставленную злодеем, — в таблицу nat добавлены ошибочные правила. Однако, их удаление ненадолго восстанавливает работу сети — правила появляются снова.

Как правило, периодические действия вызваны работой crontab. Проверим (crontab -l, cat /etc/cron.* /etc/crontab /etc/cron.d/*) и удалим все задания текущего пользователя (crontab -r) .

3.Binary

В данном задании нужно запустить «необычную» программу. Из задания нам известно имя файла — «1.exe». Найдём файл на файловой системе и проверим его содержимое:

Для запуска .NET-приложений под GNU/Linux существует среда mono. Установим её (pacman -S mono) и попробуем запустить нашу программу:

Но это задание не такое простое, как кажется. В ответ, game вернёт:

Попробуем понять, что же делает программа 1.exe. Запуск mono под strace покажет, что программа слушает TCP-сокет и выполняет команды, переданные проверкой. Поверхностный анализ трафика с помощью tcpdump показывает, что программа умеет читать файлы, обращаться к встроенной базе данных и выполнять вычисления. Проверка заканчивает работу после выполнения вычислений, значит, проблема скорее всего заключается в них.

Часто можно извлечь дополнительную информацию, поискав текстовые строки внутри исполняемых файлов. Попробуем воспользоваться этим приёмом — установим пакет программ binutils и применим программу strings на файл 1.exe. Один фрагмент вывода похож на список библиотек, используемых программой:

Все эти библиотеки, кроме dnAnalytics, входят в состав mono — это несложно проверить с помощью пакетного менеджера (pacman -Ql mono) .

Установим недостающую библиотеку, скачав и распаковав архив с официального сайта (bin/*.dll нужно положить в /root/1). После перезапуска, программа успешно проходит проверку.

4.Mongo

В этом задании нужно развернуть шардированное хранилище MongoDB, записав в него данные, предложенные организаторами.

There is a database in /var/lib/db.tar.gz.

Make a root.features collection with 2 shards and make it available on the standard port.

Для начала установим mongodb: pacman -S mongodb . Организаторы оставили архив с базой, распакуем его и сделаем архивную копию:

Для шардирования понадобится специальная служебная база, называемая configdb. Создадим её:

Теперь запустим «шардировщик» mongos: mongos --configdb localhost & . В задании требуется поднять два шарда, поэтому подготовим два экземпляра mongod:

И подключим их к mongos:

Теперь осталось загрузить дамп обратно: mognorestore --port 30001 dump/ .

Однако этого недостаточно для решения задачи — коллекция root.features не будет равномерно расшардирована между двумя шардами. Решим эту проблему, создав индекс по идентификатору документа и включив балансировщик:

Подождав, пока коллекция перераспределится между шардами, запустим проверку повторно.

5.Strange Protocol

На этот раз нам нужно запустить эхо-сервер на порту 13000.

Это задание кажется простым — действительно, простейшая реализация эхо-сервера уже встроена, например, в xinetd. Запуск tcpdump port 13000 показывает, что обмен происходит по протоколу UDP, но настройка echo-dgram в xinetd не даёт ожидаемого результата.

Посмотрим на трафик внимательнее — снова запустим tcpdump, но уже с параметром -X. Последний пакет кажется интересным:

Поиск по слову enet приводит к сайту, описывающему реализацию протокола enet, который позволяет передавать потоки данных через UDP, не заботясь о потере пакетов (как в TCP).

Дальнейший поиск приводит к библиотеке pyenet , привязке к enet для языка Python, который как раз подойдёт для нашей задачи. Напишем несложную программу:

Осталось установить эти библиотеки:

Запускаем нашу программу, и проверка на этот раз выполняется успешно.

6.File

Попробуем понять, чем же является /root/file:

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

Так как внутри образа диска находится том LVM, подключим его штатными средствами:

Посмотрим, что внутри:

Видим root.txt.gz, распаковываем: gunzip /mnt/root.txt.gz .

Так как мы уже установили nginx для задания SSL, воспользуемся им для раздачи файла по HTTP:

К сожалению, проверка не проходит — мы нашли не тот root.txt. Будем смотреть дальше. Посмотрим, какая у нас файловая система:

Поскольку в btrfs есть понятние subvolume, посмотрим на их список:

Оказывается, есть ещё один subvolume — root_1. Смонтируем именно его:

Теперь мы нашли другой файл root.txt.gz. Распакуем его: .

Это и будет решением задачи.

7.MariaDB Tuning

Во время решения задачи MariaDB repair мы починили базу данных, но она работает слишком медленно. Настало время исправить это.

Посмотрим, где тормозит наша база. Включим slow query log, куда будут попадать все запросы, выполняющиеся дольше секунды:

Запускаем проверку и смотрим в лог: tail /var/lib/mysql/shannon-slow.log .

Видим запрос SELECT COUNT(*) FROM db.data WHERE size < 10; .

Посмотрим на план запроса:

Конечно же, такой запрос выполяется слишком медленно — у нас нет индексов для этого поля. Добавим индекс: mysql> create index data_size on data(size); .

Повторный запуск проверки покажет нам ту же проблему для data(hits), которую мы решаем аналогичным образом.

В /root/repo лежит mercurial репозиторий, для которого нам надо поправить историю и сделать его доступным через http.

There is a HG repository in /root/repo.

Drop all .gz files in all revisions and make it available via ip:8000/

Первым делом, установим mercurial: pacman -S mercurial . Изменить историю можно с помощью модуля convert, который по умолчанию отключён. Включим его:

Этот модуль умеет применять правила сопоставления файлов в исходном и целевом репозитории. Такие правила называются filemap . Напишем и применим правило, которое выкинет файл 2.osm.gz:

После выполнения команды, получим репозиторий /root/repo1, который лишён файла 2.osm.gz во всех ревизиях. Осталось сделать его доступным снаружи. В mercurial есть встроенный веб-сервер, которым мы и воспользуемся:

9.Strange File

С этим заданием справилось самое большое количество команд — 151. На файловой системе находится странный файл tester/file, который никто не может изменить.

tester/file. No one can change it. Fix it.

И действительно, файл изменить не получается — даже из-под рута:

Для начала посмотрим, что у нас за файловая система такая:

Из руководства по ext4 (man 5 ext4) можно узнать, что файлы на этой ФС могут иметь следующие атрибуты:

The ext2, ext3, and ext4 filesystems support setting the following file attributes on Linux systems using the chattr(1) utility:

A — no atime updates

D — synchronous directory updates

S — synchronous updates

In addition, the ext3 and ext4 filesystems support the following flag:

j — data journaling

Finally, the ext4 filesystem also supports the following flag:

e — extents format

For descriptions of these attribute flags, please refer to the chattr(1) man page.

📎📎📎📎📎📎📎📎📎📎