- Technická specifikace požadavků na úlohu spusitelnou v platformě Haxagon
 
Systém Haxagon zprostředkovává soutěžícím přístup k soutěžním úlohám. Automatizovaně tyto úlohy spouští a vytváří vzdálené připojení pro soutěžící. Aby úloha byla nasaditelná v systému, je nutné aby splňovala určité technícké specifikace.
V systému Haxagon mohou být úlohy spouštěny pomocí dvou druhů virtualizace:
Vagrant je nástroj pro správu virtuálních strojů, který umožňuje vytvořit, spravovat a automatizovat virtuální prostředí. Je nutné použít libvirt provider pro spouštění úloh v systému Haxagon.
Docker-compose je nástroj pro spouštění a správu aplikací pomocí Dockeru. Umožňuje spouštět více kontejnerů jako jeden celek a řídit je pomocí jednoho konfiguračního souboru.
Formát úlohy je velice jednoduchý - stačí systému poskytnout následující set souborů v gitovém repozitáři:
challenge.yaml- Soubor ve formátu YAML, který definuje základní parametry úlohy- potřebné zadání pro virtualizačního providera, který vytvoří infrastrukturu pro každou instanci úlohy (podle druhu virtualizace):
- Vagrantfile
 - docker-compose.yaml
 
 docker-compose.yaml/docker-compose.yml-DESCRIPTION.md-Markdown soubor obsahující zadání pro plniteleHANDBOOK.md- Markdown soubor obsahující obsah, který slouží jako příručka pro učitele
Tyto soubory jsou dále detailněji rozebírány v následujících sekcích
| název parametru | popis parametru | příklad | 
|---|---|---|
| title | pojmenování úlohy | Uživatelské oprávnění v linuxu | 
| description | relativní cesta k Markdown souboru, který obsahuje zadání úlohy | DESCRIPTION.md | 
| handbook | relativní cesta k Markdown souboru, který obsahuje příručku pro učitele | HANDBOOK.md | 
| flags | pole objektů definující vlajky, které budou součástí úlohy, platforma rozeznává 5 druhů vlajek, definujíse v systému následovně | 
společné parametry objektů v poli flags:
| název parametru | popis parametru | příklad | 
|---|---|---|
| name | pojmenování vlajky | oprávnění souboru file-1 | 
| description | bližší info o úkolu | Změň oprávnění souboru ~/file-1 na 742 | 
| points | bodové ohodnocení vlajky | 20 | 
| identifier | unikátní (v rámci souboru challenge.yaml) identifikátor pro vlajku | file-perms-check1 | 
| type | číslo označující druh vlajky | "4" | 
Každá instance má unikatně vygenerované vlajky, tak aby se zamezilo podvádění. Jejich unikátnost je zaručena vygenerováním náhodného řetězce, kterým jsou nahrazeny všechny výskyty placeholderu v repozitáři scénáře. Tak aby nedošlo k nechtěné záměně, jsou všechny místa určená k nahrazení ohraničena znaky #@{{ a }}@#. Právě placeholder slouží autorovi úlohy k označení a odlišení jednotlivých míst. Při spuštění se tedy z #@{{vlajka1}}@# stane např. haxagon{897316929176464ebc9ad085f31e7284} a v jiné instanci s úplně stejným scénářem zase haxagon{99bd2e29f6b569bb880f601815cd77ef}.
Pozor! K nahrazení řetězců docházi až v runtime instance. Tzn. Nahrazení probíhá těsně předtím, než systém zavolá
docker compose up. Je potřeba rozdělitbuildfázi kontejnerů a jejichruntimefázi. Například tento kod v souboru Dockerfile některého z kontejnerů ulohy:RUN echo #@{{vlajka1}}@# > /tmp/test, nesplní tížené očekávání, protože příkaz RUN v Dockerfile je spouštěn při buildění obrazů kontejnerů. Pro zpřístupnění dynamické vlajky vruntimefázi kontejneru, je třeba vytvořit složku, v ní vytvořit soubor s placeholderem a tu složku namountovat pomocí docker compose definicevolumes
specifika pro objekt vlajky tohoto typu
| název parametru | popis parametru | příklad | 
|---|---|---|
| type | 1 | |
| placeholder | zástupný řetězec znaků sloužící pro označení místa, do kterého se vloží unikátní vlajka pro instanci (viz. detailní popis níže) | flag2 | 
| maximumTries | maximální možný počet pokusů o odpověď | 3 | 
Tento druh vlajek má ve všech instancích a pro všechny uživatele stejnou hodnotu
specifika pro objekt vlajky tohoto typu | název parametru | popis parametru | příklad
| název parametru | popis parametru | příklad | 
|---|---|---|
| type | 2 | |
| answer | odpověď na úkol | flag{1234} | 
| maximumTries | maximální možný počet pokusů o odpověď | 3 | 
specifika pro objekt vlajky tohoto typu | název parametru | popis parametru | příklad |
| název parametru | popis parametru | příklad | 
|---|---|---|
| type | 3 | |
| maximumTries | maximální možný počet pokusů o odpověď | 3 | 
| options | pole objektů možných odpovědí | |
| objekt odpovědi má následující strukturu: | 
- value: "chybná odpověď"
  correct: falsePomocí docker compose exec se v definovaném intervalu spouští command a podle exitCodu procesu se určí, zda-li byla vlajka splněna. container určuje, ve kterým z možných kontejnerů se proces spustí.
specifika pro objekt vlajky tohoto typu | název parametru | popis parametru | příklad
| název parametru | popis parametru | příklad | 
|---|---|---|
| type | 4 | |
| command | příkaz, který se spustí pro ověření splnění úkolu | bash -c '[ "$(cat /tmp/test.txt)" == "ahoj" ]' | 
| container | cílový kontejner, ve kterém se příkaz bude spouštět | server | 
| shell | shell, ve kterém je příkaz spouštěn | sh | 
| user | uživatel, pod kterým je příkaz spoštěn | root | 
| internval | interval, ve kterém bude docházet ke spuštění příkazu | 2000 | 
| exitCode | v případě, že příkaz bude ukončet s touto hodnotou exit kodu, bude vlajka splněna | 0 | 
ukázkový soubor challenge.yaml
title: Ukázková úloha
# relativni cesta k souboru obsahujici zadani ulohy
description: ./DESCRIPTION.md
# relativni cesta k souboru obsahujici ucitelskou prirucku k uloze
handbook: ./HANDBOOK.md
# vlajky, ktere jsou s ulohou spojeny
flags:
  - name: Obsah souboru /tmp/file1
    description: 
    points: 10
    type: "1"
    identifier: "file-content-1"
    placeholder: flag1
    maximumTries: 3
  - name: Heslo uživatele adam
    description: Najdi způsob jak získat heslo uživatele adam v plaintextu
    points: 20
    type: "2"
    identifier: "password-dump1"
    answer: flag{adamisbest}
    maximumTries: 2
  - name: Vyber správnou odpověď
    description: 
    points: 30
    type: "3"
    identifier: "choice-flag1"
    maximumTries: 2
    options:
      - value: správná odpověd
        correct: true
      - value: chybná odpověd
        correct: false
  - name: /tmp/test.txt
    description: Do souboru /tmp/test.txt zapiš text "ahoj"
    points: 30
    type: "4"
    identifier: "file-content-check1"
    command: "`bash -c '[ "$(cat /tmp/test.txt)" == "ahoj" ]'`"
    interval: 1000
    container: "server"
    exitCode: 0Pomocí tohoto souboru jsme schopni definovat, jaká infrastruktura se pro úlohu spustí. Dokumentace formátu je dostupná zde: https://docs.docker.com/compose/
V souboru docker-compose není možné:
- eskalovat práva kontejneru:
- vytvářet privilegované kontejnery
 - přidávat kontejnerům systémové schopnosti (SYStem capabilities)
 
 - mountovat adresáře a soubory (vše potřebné by do kontejneru mělo být předáno v build fázi)
 
Je nutné systém informovat o tom, že scénář je spuštěný a vše je připraveno. Tuto informaci je možné systému sdělit tím, že do stdout entrypointu/commandu libovolného commandu definovaného v docker-compose souboru vypíšete řetězec "SCENARIO_IS_READY". Ukázkový docker-compose.yml:
version: "3"
services:
    webserver:
        image: nonbusybox
        container_name: webserver
        command: sh -c '/setup.sh && echo SCENARIO_IS_READY && sleep infinity'
        ports:
            - "80:80"Vagrantfile je konfigurační soubor pro Vagrant, který se používá k nastavení virtuálního prostředí pro vaši úlohu. Tento soubor by měl být umístěn ve stejné složce jako challenge.yaml.
V Vagrantfile může být nastaveno např.:
- image operačního systému, který se má použít pro virtuální stroj (např. Ubuntu nebo CentOS)
 - konfigurace sítě pro virtuální stroj
 - provisioning
 - omezení prostředků (RAM, CPU, různé IO atp.)
 
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
  # Nastavení libvirt provider
  config.vm.provider :libvirt do |libvirt|
    libvirt.driver = "kvm"
  end
  # Nastavení obrayu operačního systému pro virtuální stroj
  config.vm.box = "ubuntu/focal64"
  # Konfigurace privátní sítě pro virtuální stroj
  config.vm.network "private_network", ip: "192.168.33.10"
  # Nastavení RAM a CPU pro virtuální stroj
  config.vm.provider "libvirt" do |vb|
    vb.memory = "512"
    vb.cpus = 1
  end
  # Konfigurace shell provisioningu
  config.vm.provision "shell" do |s|
    s.inline = <<-SHELL
      apt-get update
      apt-get install -y apache2
      echo SCENARIO_IS_READY
    SHELL
    s.env = {
      "VARIABLE_NAME" => "value"
    }
  end
endZadání úlohy by mělo splňovat následující konvence:
Obsah zadání se dělí na teoretickou část, kde jsou řešiteli předávány teoretické znalosti bez vazby na obsah úlohy a zadání. Tato konzistentní struktura napříč úlohami, kde v první řadě v nejdříve v souboru uvedena sekce # Teorie a poté až sekce # Zadání zlepšuje orientaci řešitelů a zadavatelů v úlohách.
příklad
## Teorie
### Enumerace neznámé sitě
informace o tom, jak funguje průzkum sítě, může obsahovat odkaz na asciinema.org, který bude vyrenderovat
## Zadání
Úvodní text zadání
### Přístup do úlohy
Potřebné informace k připojení se do úlohy. Např. port k SSH službě a přístupové údaje uživatele v systému. Může obsahovat placeholder %%INSTANCE_IP%%, ktery bude nahrazen IP adresou instance ulohy
### Vlajka č. 1: Domovský adresář
info k vlajce č. 1
### Vlajka č. 2
info k vlajce č. 2- části textu obsahující nějakou technickou informaci (např. definici subnet - 
192.168.40.0/24, příkazyfind --name file, parametr ---service-scanatp.) zaobalíme do code highlight bloku pomocí znaku ` - konzistence termínů napříč zadáním a příručkou k úloze