KERNEL SHADOWS: курс по Linux, где ни один тест не требует root, сети и железа

Три недели назад я рассказывал про курс языка C, который собрал себе сам с помощью ИИ. Тем же способом собрался второй — про Linux. Идея была записана через три дня после первой, с открытым вопросом: приквел это или спин-офф. Вышел спин-офф: та же вселенная и те же персонажи, но там ты пишешь программы, а здесь управляешь системами, на которых они работают.
Сразу, чтобы не тратить ничьё время. Это не видеокурс и не платформа. Это git-репозиторий с текстом: 101 папка, в каждой — README с теорией и заданием, каркас файла с TODO, тесты и эталонное решение. Проходится в редакторе с терминалом. Видео нет, регистрации нет, прогресс считает make progress по твоим же файлам. Если ждёшь кнопку «Начать урок» — её тут не будет.
101 серия в восьми сезонах, 98 часов 25 минут задач, 2393 автоматические проверки. Бесплатно, GPL v3, на GitHub.
Как это выглядит в первые пять минут
git clone https://github.com/gfazzz/kernel-shadows.git
cd kernel-shadows/season-01-shell-foundations/s01e01-terminal-awakening
less README.md # сцена, теория, задание
cp starter/whereami.sh artifacts/
bash tests/test.sh
В starter/ лежит не решение, а каркас:
#!/usr/bin/env bash
# whereami.sh — s01e01 «Terminal Awakening» (СТАРТЕР)
#
# Задача: инструмент ориентации. Скрипт должен ответить на вопрос
# «где я сейчас в дереве файловой системы?».
set -euo pipefail
# TODO 1: получи текущий абсолютный путь (подсказка: команда из 3 букв)
# и сохрани его в переменную here.
here="" # <-- замени на вызов нужной команды
# TODO 2: выведи текущую директорию и домашнюю директорию ($HOME).
echo "Текущая директория (pwd): ${here}"
# TODO 3: определи, находишься ли ты в корне "/", и напечатай понятный статус.
# TODO 4 (бонус): сравни here с $HOME и сообщи, дома ли ты.
Тест на нём сразу красный, и это не сбой установки:
════════════════════════════════════════════════════════════
s01e01 tests — subject: artifacts/whereami.sh
════════════════════════════════════════════════════════════
PASS: whereami.sh найден
PASS: синтаксис bash корректен
PASS: есть shebang
FAIL: в выводе нет текущего пути …/home/max/ops/deep/place (скрипт должен печатать pwd)
FAIL: в выводе нет $HOME (…/home/max)
FAIL: похоже, путь захардкожен: вывод не меняется при смене директории
════════════════════════════════════════════════════════════
Итог: 3 passed, 3 failed
════════════════════════════════════════════════════════════
Вот это и есть весь «интерактив» курса. Тест — не экзаменатор в конце, а спецификация задания: он перечисляет, чего не хватает, и пересчитывается за секунду после каждой правки. Третий FAIL заслуживает отдельного слова: он ловит не текст вывода, а попытку сжульничать — запускает скрипт из двух разных директорий и смотрит, меняется ли ответ. Каждая проверка в курсе написана так, чтобы падать на правдоподобно неверном решении, иначе она бесполезна.
Эталон лежит рядом, в solution/. Он нужен, чтобы после своей попытки сравнить подходы, и открывать его раньше — единственный способ испортить себе курс. make progress эталон не засчитывает: серия считается пройденной, только если тест зелёный на твоём файле в artifacts/.
Что нужно из софта: bash, git, make, python3. Ни один тест не просит root, сеть, Docker или железо — про это ниже отдельная глава, она оказалась самой интересной частью работы. Линукс под рукой удобен, но для первых сезонов достаточно и macOS. Если Linux ты видишь впервые, docs/GETTING_STARTED.md разбирает установку Ubuntu на виртуалку и типичные грабли.
Что внутри
Восемь сезонов, восемь стран, шестьдесят дней операции. Ты — системный администратор, которого наняли построить и защитить инфраструктуру на пятьдесят узлов; выясняется, что защищать придётся всерьёз.
Сезон | Тема | Локация | Серий | Время |
|---|---|---|---|---|
1 | Оболочка и основы | Новосибирск 🇷🇺 | 16 | 14 ч 05 |
2 | Сети | Москва → Стокгольм 🇸🇪 | 12 | 10 ч 35 |
3 | Администрирование | Петербург → Таллин 🇪🇪 | 13 | 12 ч 05 |
4 | DevOps и автоматизация | Амстердам → Берлин 🇩🇪 | 12 | 11 ч 20 |
5 | Безопасность | Цюрих + Женева 🇨🇭 | 12 | 10 ч 55 |
6 | Встраиваемый Linux | Шэньчжэнь 🇨🇳 | 12 | 11 ч 50 |
7 | Эксплуатация | Рейкьявик 🇮🇸 | 12 | 13 ч 05 |
8 | Финальная операция | глобально 🌐 | 12 | 14 ч 30 |
Названия сезонов мало что говорят, поэтому вот третий целиком — по одной теме на серию:
s03e01 Список жильцов: «Кто есть на этой машине»
s03e02 Три буквы: «Кто что может»
s03e03 Заряженный пистолет: «Каждому ровно то, что нужно»
s03e04 PID 6623: «Имя, которое пишет себе сам»
s03e05 Служба, которая вернётся: «Кто их запускает»
s03e06 Пять минут по расписанию: «Кто догонит пропущенное»
s03e07 Вечер 14 октября: «Всё это было записано»
s03e08 Место, которого нет: «df говорит одно, du — другое»
s03e09 Пятьсот гигабайт, которых не видно: «Диск есть, места нет»
s03e10 Машина, которая не поднялась: «Кто скажет ей, что монтировать»
s03e11 Копия, которой не было: «Когда вы восстанавливались в последний раз»
s03e12 Строка, которой не было: «Кто скажет службе, что журнал повернули»
s03e13 03:47: «Третий вопрос»
Единица курса — серия: один концепт, одна задача, один проверяемый артефакт, 45–90 минут. Каждый сезон собирает работающую систему, а не набор упражнений: shadow_toolkit → netshield → fleet_admin → shadow_iac → hardening_kit → shadow_mesh → aurora → shadow_core. Финал не вводит новой темы, он принимает всё построенное: двенадцать фаз, по одной на навык каждого сезона. Курс заканчивается не поздравлением, а прогоном, который возвращает ноль или не возвращает.
Подсказок по ходу становится меньше. Сезоны 1–3 дают полный каркас и эталон, 4–5 — каркас и эталон после попытки, 6–7 — только интерфейсы, 8 — только договор вызова. Это встроено в сюжет, а не введено молча.
Дальше — две инженерные задачи, до которых пришлось додумываться, потому что курс по Linux устроен иначе, чем курс по языку.
Проблема первая: курс по Linux сам собой становится курсом по bash
Если собирать курс по Linux наивно — а именно так его соберёт любая языковая модель, и именно так он у меня сначала и собрался, — получится курс по шелл-скриптингу. Причина простая: скрипт легко проверить. Запустил, сравнил вывод, поставил галочку. Всё остальное проверять неудобно, поэтому всё остальное вытесняется.
Результат — выпускник, который бодро пишет .sh и беспомощен там, где ничего не надо автоматизировать. А администратор большую часть времени не автоматизирует: он читает конфигурацию, смотрит, что запущено, и понимает, почему система ведёт себя так, а не иначе.
Лечится это принудительно — таксономией типов серий, которая стала частью критериев приёмки сезона:
Тип | Что делаешь | Что проверяет тест |
|---|---|---|
| пишешь | поведение скрипта на фикстуре |
| правишь конфигурацию | свойства конфигурации, идемпотентность |
| работаешь в CLI, собираешь отчёт | воспроизводимость находки |
| пишешь | поведение программы |
Сезон, целиком состоящий из Type A, не принимается. Это правило, а не пожелание. По факту вышло так:
A — автоматизация | B — конфигурация | C — разбор | D — код |
|---|---|---|---|
25 | 38 | 30 | 8 |
Скрипты — четверть курса. Конфигурация и разбор вместе дают две трети, и это ровно та пропорция, в которой администратор работает.
Проблема вторая: как это проверить, если тесту нельзя ничего
Ограничение курса: ни один тест не требует root, сети, Docker, кластера или железа.
Причина не в аскетизме. Курс, для которого нужен кластер Kubernetes, не пройдёт никто. А курс, чьи тесты дёргают живую сеть, будет падать через раз — и человек решит, что сломан он, а не тест.
Отсюда три приёма.
1. Мок вместо внешней команды
Серия про проверку доступности узлов. Настоящий ping в тесте недопустим: без сети он даст ложное падение, с сетью — зависимость от чужих серверов.
Тест кладёт в начало PATH свой ping:
cat > "${FAKEBIN}/ping" <<'MOCK'
#!/usr/bin/env bash
host="${!#}"
case "${host}" in
up-*|10.0.0.1) exit 0 ;;
*) exit 1 ;;
esac
MOCK
Проверяется не связность, а логика решения: сохранил ли код возврата в переменную, не захардкодил ли статус, различает ли «недоступен» и «ответил без поля time=». То же сделано для ss, dig, ufw и dpkg.
2. Разбор конфигурации по правилам той программы, которая её читает
Серия про юнит systemd. Запускать systemd в тесте нельзя. Но юнит — это текст с известной семантикой, и её можно воспроизвести:
Проверяет НЕ скрипт, а свойства unit-файла: читает его так, как это делает systemd (секции значимы, побеждает последнее присваивание, пустое значение сбрасывает настройку, комментарии не в счёт) и сверяет эффективные значения с требованиями к службе.
Отдельно ловятся: конвейер в
ExecStart(оболочки нет),AfterбезWants, настройка, сброшенная пустым присваиванием ниже.
Это оказалось не обходным путём, а более честной проверкой, чем запуск. Запущенная служба отвечает на вопрос «стартовала ли она сейчас». Разбор отвечает на «что здесь написано на самом деле» — а это и есть навык. Так же проверяются sshd_config, fstab, logrotate, compose.yaml, манифесты Kubernetes и правила auditd.
Побочный эффект приятный: тест ловит то, что в жизни обнаруживается через недели. Директиву, оставшуюся в комментарии. Директиву, заданную дважды. Host * наверху ~/.ssh/config, из-за которого все частные настройки перестают действовать.
3. Снимок вместо живой системы
Серии про разбор инцидентов работают на снятых состояниях: вывод ps, содержимое /proc, журналы, дампы /proc/net/tcp. Надо найти скрытый процесс, стёртый бинарник и адрес управляющего сервера, закодированный little-endian.
Это тоже не компромисс. Криминалистика и в жизни работает со снимком: живой системе, на которой стоит руткит, нельзя задавать вопросы о ней самой.
Чем проверялся сам курс
make check # ссылки + инструменты + хронология + тесты
make test-repeat # два прогона подряд: воспроизводимость
make test-locale # прогон под LC_ALL=C и чужим часовым поясом
make clean-clone # приёмка на чистом клоне
Последние три цели появились не от любви к процессу. test-repeat ловит тесты, зависящие от порядка. test-locale — те, что сравнивают отсортированный вывод и ломаются в немецкой локали. clean-clone — файлы, съеденные .gitignore: в курсе по Linux логи, ключи и .env — это учебные материалы, и типовые правила игнорирования вычёркивают ровно их.
Числа в README не проставлены руками: 101 серия, 98 ч 25 мин и 2393 проверки считаются обходом шапок серий и логов последнего прогона. Поэтому они не расходятся с фактом — и поэтому, пока я готовил эту статью, цифра проверок выросла с 2335 до 2393, а я об этом узнал от скрипта, а не от себя.
Про ИИ, раз уж курс собран им
Оба курса я собрал себе сам, с помощью ИИ, почти без ручной работы. И вывод из этого получился не тот, который обычно ожидают.
ИИ здесь не пишет код вместо тебя. Он делает две другие вещи.
Составляет программу обучения под конкретного человека. Не «сгенерируй курс по Linux» — так получится оглавление учебника. А разговор о том, чему я хочу научиться, в каком порядке, на каком материале и что должно получиться на выходе. Раньше это было привилегией тех, у кого есть наставник.
Помогает во время прохождения. В сюжете есть наставник LILITH — и это чат Cursor, открытый рядом с кодом. Курс проходится в режиме Ask, а не Agent: в Agent ассистент правит файлы сам, серия окажется решённой, а ты не наберёшь ни одной команды. Первый раздел .cursorrules прямо запрещает помощнику писать в artifacts/, выдавать решение серии и пересказывать solution/. Что он делает вместо этого: объясняет концепт, показывает приём на другой задаче, разбирает сообщение об ошибке, задаёт наводящий вопрос.
Поэтому в конце каждой серии стоят зелёные запросы и красный анти-паттерн — вопрос, который выглядит совершенно разумным и вредит именно здесь:
«Разбери заголовок за меня.» — зашитая длина IP-заголовка работает на девяти пакетах из десяти. Десятый ты должен найти сам.
«Оптимизируй мой код.» — единственный запрещённый порядок действий в этой серии. Сначала профиль, потом оптимизация; наоборот — суеверие.
Сто восемьдесят одна такая пара на два курса, каждая привязана к ловушке конкретной серии.
И вот главное. Разговоры про ИИ обычно сводятся к тому, что он сделает программистов и админов ненужными. По моему опыту — скорее наоборот. То, что он реально удешевил, — это стоимость персональной программы обучения. Раньше между «хочу разобраться в Linux» и «разбираюсь» стояли поиск нормального курса, чужой темп и чужой порядок тем. Теперь между ними стоит вечер разговора с ассистентом.
Сам навык от этого не подешевел ни на копейку. Человек, который умеет прочитать sshd_config и увидеть, что директива не действует, нужен ровно так же, как был нужен. Просто дорога к нему стала короче.
Чего у курса нет
Видео, платформы, прогресс-бара и сертификата. Проверки строго внутренние: 2393 зелёных — это про то, что код курса согласован сам с собой, а не про то, что по нему кто-то научился.
И главное: его ни разу не прошли целиком снаружи — как и сестринский. Ни одна проверка не ловит того, что видит человек, читающий подряд.
Возьмёшься за серию и что-то не сойдётся — issue с выводом теста будет лучшей помощью.
Репозитории: KERNEL SHADOWS — Linux, GPL v3 OPERATION MOONLIGHT — C, MIT
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.