# TS3 monolith (Go) Jedna aplikacja zastępująca stare `sk.linuxiarz.pl` i `ts3stats.linuxiarz.pl`. Wygląd dashboardu (topbar + sidebar, karty, panele, jasny/ciemny motyw) jest celowo spójny ze stylem [PostfixAdminRS](https://github.com/postfixadmin/postfixadmin) — ten sam układ tokenów kolorów, te same komponenty UI. ## Funkcjonalność - wykrycie klienta TeamSpeak po IP widzianym przez HTTP i ServerQuery; - utworzenie permanentnego kanału z nazwą, tematem, opcjonalnym hasłem, limitem użytkowników (`channel_maxclients`) i jakością kodeka (`channel_codec_quality`, 1–10); - `setclientchannelgroup` dla wskazanego `client_database_id`; - `clientmove` wskazanego `clid` do nowego kanału; - dashboard statystyk: liczba online na żywo (auto-odświeżanie co 60 s przez `GET /stats/live`), uptime/wersja/platforma serwera (`serverinfo`), wykresy RRD: 12h, 24h, 72h, tydzień, miesiąc, rok — w kolorystyce dopasowanej do motywu aplikacji (ciemne tło, akcentowa linia, watermark); - przełącznik jasny/ciemny motyw (zapamiętywany w `localStorage`); - formularz pod `/embed/channel`, przeznaczony do osadzenia na innej domenie. Przy jednym kliencie pod danym IP wszystko działa automatycznie. Jeżeli kilka klientów TS3 ma ten sam publiczny IP (NAT), aplikacja prosi o wybór nicku i wysyła 6-cyfrowy kod prywatną wiadomością TS3. Dzięki temu nie przenosi przypadkowej osoby i nie nadaje jej Channel Admina. ## Szybki development ``` cp .env.example .env # uzupełnij sekrety make quick # najszybszy build (bez testów/trimpath) -> bin/ts3monolith make dev # quick build + start z lokalnym .env make test # go test ./... make build # pełny build produkcyjny (testy + trimpath + bez symboli) ``` ## Uruchomienie — Docker 1. Skopiuj `.env.example` do `.env` i ustaw sekrety oraz dozwoloną domenę iframe. 2. Zachowaj `data/linuxiarz.rrd` (kopia dotychczasowej bazy z archiwum) albo zainicjuj nową bazę — patrz sekcja [RRD](#rrd). 3. Zapewnij zapis do katalogu `data` dla UID 10001: `chown -R 10001:10001 data`. 4. Uruchom: `docker compose up -d --build`. 5. Reverse proxy kieruje HTTPS do `127.0.0.1:8080`. ## Uruchomienie — pakiet .deb + systemd Alternatywa dla Dockera, zalecana na dedykowanym hoście Debian/Ubuntu: ``` make deb # scripts/build-deb.sh -> packages/ts3monolith__amd64.deb sudo dpkg -i packages/ts3monolith_*.deb sudo nano /etc/default/ts3monolith # APP_SECRET, TS_QUERY_USER, TS_QUERY_PASSWORD, ... sudo -u ts3monolith /usr/lib/ts3monolith/init-rrd # tylko przy świeżej instalacji, bez starej bazy sudo systemctl enable --now ts3monolith ``` `scripts/build-deb.sh` buduje wszystko w kontenerze Docker — dokładnie tak samo jak odpowiadający skrypt w PostfixAdminRS — więc host nie potrzebuje lokalnie zainstalowanego Go ani żadnych zależności poza samym Dockerem. Wersja obrazu (`golang:-bookworm`) jest odczytywana z `go.mod`, a sam moduł nie ma żadnych zewnętrznych zależności, więc build jest w pełni offline po ściągnięciu obrazu. Wynikowy pakiet trafia do `packages/`. Pakiet instaluje: - `/usr/bin/ts3monolith` — binarka; - `/etc/default/ts3monolith` — zmienne środowiskowe (conffile); - `/lib/systemd/system/ts3monolith.service` — jednostka z pełnym hardeningiem (`ProtectSystem=strict`, `NoNewPrivileges`, `RestrictAddressFamilies`, `MemoryDenyWriteExecute`, `SystemCallFilter=@system-service`, ...), takim samym duchem jak w PostfixAdminRS; - `/usr/lib/ts3monolith/init-rrd` — jednorazowy skrypt tworzący świeżą bazę RRD z RRA pokrywającymi wszystkie zakresy wykresów (12h…rok), gdy nie ma archiwalnego pliku do skopiowania; - dedykowanego użytkownika systemowego `ts3monolith` (bez loginu, bez home). `install.sh` z korzenia repo zostaje jako prostszy, ręczny installer bez budowania pakietu `.deb` (np. do szybkiego postawienia na hoście bez uprawnień do `dpkg`). Przykład iframe: ```html ``` `FRAME_ANCESTORS` musi zawierać origin strony nadrzędnej, np. `https://www.example.org`. ## TeamSpeak ServerQuery Najbezpieczniej uruchamiać aplikację na tym samym hoście lub w prywatnej sieci z TS3 i ustawić `TS_QUERY_HOST=127.0.0.1`/adres prywatny. Nie wystawiaj portu 10011 publicznie. Query powinien mieć tylko prawa potrzebne do: `clientlist -ip -uid`, `channelcreate`, `setclientchannelgroup`, `clientmove`, `sendtextmessage`, `channeldelete` (rollback), `serverinfo` (karty uptime/wersji na dashboardzie). ## RRD Aplikacja używa systemowego `rrdtool`, a nie biblioteki Go, więc istniejący plik RRD pozostaje bez migracji. Co 60 s liczona jest liczba klientów `client_type=0` na wybranym virtual serverze i zapisywana do DS `online`. Błąd ServerQuery nie zapisuje sztucznego zera. Świeża instalacja bez archiwalnej bazy: `packaging/ts3monolith-init-rrd` (instalowany jako `/usr/lib/ts3monolith/init-rrd` w pakiecie `.deb`) tworzy nową bazę `rrdtool create` z RRA pokrywającymi 12h/24h/72h/tydzień/miesiąc/rok przy kroku 60 s — ten sam layout logiczny co istniejące wykresy aplikacji. Wykresy renderowane są w kolorach dopasowanych do motywu dashboardu (`--panel`/`--field`/`--accent` z `internal/web/assets/app.css`), z cienkim wypełnieniem pod linią, watermarkiem z datą generacji i `--slope-mode` dla gładszej linii. Każdy zakres jest renderowany dwukrotnie — `.png` (ciemna paleta) i `-light.png` (jasna paleta) — a front-end (`internal/web/assets/app.js`) przełącza `src` na właściwy wariant przy zmianie motywu, więc wykresy zawsze dopasowane są do aktywnego trybu jasny/ciemny. Jeżeli docelowy serwer ma inną architekturę/endianness niż źródłowy x86_64 little-endian, przenieś RRD przez `rrdtool dump`/`rrdtool restore` zamiast kopiowania binarnego pliku. ## Bezpieczeństwo / fingerprinting - brak PHP, frameworków frontendowych, CDN i nagłówka `X-Powered-By`; - standardowy `net/http` nie dodaje nagłówka `Server`; - CSP z kontrolowanym `frame-ancestors`, HSTS, `nosniff`, restrykcyjne Permissions-Policy, `style-src`/`script-src 'self'` (bez inline — cały CSS/JS w plikach statycznych); - nagłówki proxy są ufane wyłącznie z `TRUSTED_PROXIES`; - stateless HMAC tokeny formularzy i klientów, sprawdzanie `Origin`, limity request body i rate limiting; - sekrety tylko przez środowisko, brak danych dostępowych w repo; - rollback kanału, jeśli nadanie grupy lub przeniesienie klienta się nie powiedzie; - kontener non-root, `cap_drop: ALL`, `no-new-privileges`, read-only root filesystem (Docker) albo analogiczny hardening jednostki systemd (pakiet `.deb`); - build z `-trimpath` i usuniętymi symbolami/debug info. Nie da się zagwarantować, że stos technologiczny będzie całkowicie nierozpoznawalny na podstawie zachowania sieci/TLS. Celem jest brak prostych bannerów, wersji i sygnatur frameworka.