Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TermLearn

Nauka terminala przez wpisywanie prawdziwych komend w prawdziwym shellu.

To nie jest symulator. Każdy znak trafia do realnego bash w kontenerze Dockera, tworzonym na czas sesji i kasowanym po zamknięciu karty. Potoki |, przekierowania >, &&, pętle, dowolne komendy z obrazu — wszystko działa, bo to jest bash, a nie jego imitacja w JavaScripcie.

Learn the Linux terminal by typing real commands into a real bash — sandboxed in a per-session Docker container, no account, no data collected. 17 courses included; course content and UI are in Polish.

TermLearn — sesja nauki

Dlaczego nie symulator

Symulator uczy tego, co jego autor przewidział. Wpiszesz komendę w formie, o której nie pomyślał — dostajesz „błąd", choć zrobiłeś dobrze. TermLearn sprawdza efekt w kontenerze, więc każda droga do celu zalicza zadanie: mkdir backup i mkdir -p ./backup są tak samo poprawne, bo katalog istnieje.

Drugi powód jest ważniejszy: w prawdziwym shellu można się pomylić naprawdę. rm usuwa, fork bomba zawiesza, > nadpisuje plik. Uczysz się na tym samym narzędziu, którego użyjesz w pracy — tylko że w środowisku, w którym pomyłka nic nie kosztuje.

Jak sprawdzane są zadania

Backend uruchamia weryfikację w tym samym kontenerze, w którym pracujesz. Trzy tryby:

Typ Co sprawdza Do czego
script skrypt shellowy w kontenerze, exit 0 = zaliczone zadania zmieniające dysk (mkdir, cp, echo >)
cwd realny katalog roboczy interaktywnego basha nawigacja (cd)
lastcmd ostatnia wpisana komenda (regex) komendy wyświetlające (pwd, ls, grep) — nie zmieniają stanu, więc nie ma czego mierzyć na dysku

Zadania lastcmd zaliczają się same po Enterze, reszta po kliknięciu „Sprawdź zadanie". Rozwiązania nigdy nie trafiają do przeglądarki — check czytany jest z pliku kursu po stronie serwera, więc nie da się ich podejrzeć w DevTools.

Bezpieczeństwo

Uczeń wpisuje dowolne komendy, więc kontener traktowany jest jak środowisko wrogie:

Ustawienie Po co
--network none brak dostępu do sieci — z kontenera nie wyjdzie ani skan, ani pobranie
--cap-drop ALL + --security-opt no-new-privileges brak uprawnień systemowych, brak drogi do eskalacji
--read-only + tmpfs na /home/learner i /tmp zapis tylko w pamięci, kasowany razem z sesją
--memory 256m, --cpus 0.5, --pids-limit 128 ochrona przed fork bombą i wyczerpaniem zasobów hosta
USER learner uczeń nigdy nie pracuje jako root
kontener per sesja, docker rm -f przy rozłączeniu zero śladów na hoście
sprawdzanie nagłówka Origin terminal otwiera tylko strona serwowana przez ten backend
limit 8 równoległych sesji górna granica tego, ile kontenerów da się zamówić naraz

Dlaczego sprawdzamy Origin: połączenia WebSocket nie podlegają regule tego samego źródła — przeglądarka pozwoli dowolnej otwartej stronie połączyć się z localhost, więc to serwer musi odsiać obcych. Bez tej kontroli każda witryna w Twojej przeglądarce mogła po cichu zamawiać kontenery i wykonywać w nich komendy, dopóki TermLearn działał. Skutek byłby ograniczony do piaskownicy (bez sieci, bez dostępu do dysku hosta), ale wystarczał do zajechania maszyny liczbą kontenerów.

Serwer nasłuchuje wyłącznie na 127.0.0.1 i tak ma zostać. To narzędzie do pracy lokalnej: nie ma logowania ani kont. Wystawienie go na publiczny adres oznacza oddanie obcym powłoki w kontenerze — chcesz udostępnić to grupie, dołóż uwierzytelnienie, a limit sesji podnieś świadomie (MAX_SESJI w backend/main.py).

Uczciwie o granicach izolacji: kontener to nie maszyna wirtualna. Powyższe ustawienia zamykają drogę wyjścia przez sieć, uprawnienia i system plików, ale ucieczka przez błąd w jądrze albo w samym Dockerze pozostaje możliwa. Do nauki komend to w zupełności wystarcza; do uruchamiania kodu, któremu nie ufasz, użyj maszyny wirtualnej.

Aplikacja nie ma kont, nie zbiera danych i nie woła żadnego zewnętrznego API. Postęp, XP i streak żyją w localStorage przeglądarki (przycisk „Resetuj postęp" czyści je). Strona nie wysyła ani jednego żądania poza localhost — xterm.js leży w repo (backend/vendor/, licencja MIT w vendor/xterm-LICENSE.txt), a nie na CDN. Po zbudowaniu obrazu całość działa bez internetu i nikt z zewnątrz nie widzi, że się uczysz. Aktualizacja biblioteki = podmiana plików w backend/vendor/ (wersje: @xterm/xterm@5.5.0, @xterm/addon-fit@0.10.0).

Wymagania

  • Linux z działającym Dockerem (użytkownik w grupie docker, bez sudo)
  • Python 3 z modułem venv — start.sh sam tworzy środowisko i instaluje FastAPI + uvicorn
  • internet tylko przy pierwszym uruchomieniu — do pobrania obrazu Debiana; potem całość działa offline

Uruchomienie

git clone https://github.com/Tyr9102/TermLearn.git
cd TermLearn/backend
./start.sh

Skrypt buduje obraz sesji (debian:bookworm-slim + bash, coreutils, grep, findutils, nano, less, procps, man, zip/unzip, git — ok. 270 MB, jednorazowo) i startuje serwer. Potem otwórz http://localhost:8002. Inny port: PORT=8080 ./start.sh.

Kursy

17 kursów, od orientacji w systemie po skrypty bashowe:

Podstawowe komendy Linuksa · Przekierowania i strumienie · Potoki · Ekspansje i cudzysłowy · Uprawnienia: rwx, chmod, umask · Procesy, zadania i sygnały · Środowisko shella · Dokumentacja, wildcards i find · Archiwa i kompresja · Dowiązania i dysk · Wyrażenia regularne · Przetwarzanie tekstu: grep, awk, sed · Sortowanie i duplikaty · Narzędzia tekstowe II · xargs i find -exec · Skrypty bash · Git od podstaw

Każde zadanie ma panel wyjaśnień (co robi komenda, składnia, ważne flagi, przykłady w stylu tldr) i wielopoziomową pomoc: wskazówka → wyjaśnienie → gotowe rozwiązanie. Gotowca dostajesz dopiero, gdy sam o niego poprosisz trzeci raz.

Własny kurs

  1. Skopiuj lessons/_TEMPLATE.json na lessons/<temat>.json i wypełnij (pola _comment usuń).
  2. Dopisz wpis do lessons/index.json (file, id, title, icon, description).
  3. Odśwież stronę — bez buildu i bez restartu serwera; kurs czytany jest z dysku przy każdym sprawdzeniu.

Ćwiczebne pliki, na których pracuje uczeń (readme.txt, users.csv, access.log…), leżą w backend/seed/ i są kopiowane do świeżego kontenera przy starcie sesji.

Szkielet zadania:

{
  "id": "unikalne-id",
  "prompt": "Utwórz nowy katalog o nazwie 'backup'.",
  "check": { "type": "script", "test": "[ -d backup ]" },
  "command": {                      // panel wyjaśnień (opcjonalny, ale zalecany)
    "name": "mkdir", "fullName": "make directory",
    "what": "Tworzy katalog...", "syntax": "mkdir [opcje] nazwa...",
    "flags": [{ "flag": "-p", "desc": "twórz też katalogi nadrzędne" }],
    "examples": [{ "cmd": "mkdir backup", "desc": "utwórz katalog backup" }]
  },
  "hints": ["Wskazówka...", "Wyjaśnienie...", "Wpisz: mkdir backup"]
}

Regexy w lastcmd warto pisać tolerancyjnie na warianty flag — "ls\\s+(-[a-zA-Z]*a[a-zA-Z]*|--all)" łapie ls -a, ls -la i ls --all.

Czego jeszcze nie ma

  • Jedno środowisko dla wszystkich kursów. Obraz jest wspólny, więc kurs wymagający python, docker czy nmap nie zadziała, dopóki nie powstanie „env per kurs".
  • Brak kont i synchronizacji — postęp zostaje w przeglądarce, na innym urządzeniu zaczynasz od zera.
  • Brak uwierzytelnienia — projekt zakłada jednego użytkownika na localhost; szczegóły w sekcji o bezpieczeństwie.

Architektura

przeglądarka (xterm.js) ──WebSocket──► FastAPI ──PTY──► docker exec -it bash
                                          │
                                          └── /api/check ──► docker exec (weryfikacja zadania)

Backend to jeden plik (backend/main.py, ~340 linii), frontend to jeden plik (backend/app.html). Bez frameworków frontendowych i bez kroku budowania.

Licencja

MIT — patrz LICENSE.

About

Learn the Linux terminal by typing real commands into a sandboxed bash container — not a simulator. xterm.js + FastAPI/PTY + Docker, 17 courses (Polish-language content), runs offline, no accounts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages