2026-11-05 12:49:33 +01:00
2026-11-05 12:48:09 +01:00
2026-10-30 10:56:13 +01:00
2026-10-30 10:56:13 +01:00
2026-11-05 12:48:09 +01:00
2026-11-05 12:48:09 +01:00
2026-11-05 12:48:09 +01:00
2026-11-05 12:48:09 +01:00
2026-11-04 14:57:48 +01:00
2026-11-04 14:57:48 +01:00
2026-10-30 10:56:13 +01:00
2026-10-30 10:56:13 +01:00
2026-10-30 10:56:13 +01:00
2026-10-30 10:56:13 +01:00
2026-11-04 14:57:48 +01:00
2026-11-05 12:48:09 +01:00
2026-11-05 12:48:09 +01:00

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 — 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 — dane pobierane jako JSON i rysowane responsywnie w <canvas> po stronie przeglądarki (patrz sekcja RRD), z podglądem na powiększenie;
  • 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.
  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_<wersja>_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:<wersja z go.mod>-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:

<iframe
  src="https://app.example.org/embed/channel"
  title="Utwórz kanał TeamSpeak"
  loading="lazy"
  referrerpolicy="no-referrer"
  style="width:100%;min-height:620px;border:0">
</iframe>

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 na dashboardzie nie są PNG renderowanymi przez rrdtool graph. GET /stats/graph/{zakres} (internal/rrd/rrd.go, Manager.Fetch) woła rrdtool fetch i zwraca surowe próbki jako mały JSON ({"points":[{"t":..., "v":...},...],"current":...,"average":...,"max":...}); front-end (internal/web/assets/app.js) rysuje z tego wykres samodzielnie w <canvas>, kolorami wziętymi na żywo z CSS (--line/--muted/--accent), więc przy przełączeniu jasny/ciemny motyw wykres się po prostu przerysowuje z już pobranych danych — bez kolejnego zapytania do serwera i bez osobnych plików PNG per motyw. Dzięki temu wykres jest też w pełni responsywny (łącznie z telefonem) i powiększenie w lightboksie to kolejne przerysowanie tych samych danych w większym `, a nie osobny plik.

Fetch dobiera funkcję konsolidacji RRD do zakresu: LAST dla 12h/24h/72h (pełna rozdzielczość, RRA RRA:LAST:0.5:1:4500), AVERAGE dla tydzień/miesiąc/rok (te okna istnieją tylko w gęściej uśrednionych RRA — pytanie ich o LAST nie zwróciłoby nic starszego niż ok. 75h).

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.

Rate limiting

Domyślnie wyłączony (RATE_LIMIT_ENABLED=false) — endpointy mają limity zahardkodowane w kodzie od początku, ale same w sobie nie chronią niczego krytycznego (ServerQuery i tak ma własny timeout, channelcreate i tak wymaga poprawnego tokenu CSRF), a przy nietypowym ruchu (kilku odwiedzających za tym samym IP/NAT, kilka kart, lightbox z wykresami) dawały false-positive 429 bez realnej korzyści. Włącz (RATE_LIMIT_ENABLED=true), jeśli chcesz dodatkową warstwę ochrony przed nadużyciami na konkretnym endpoincie.

Każdy limit jest per-IP (po rozwiązaniu TRUSTED_PROXIES/X-Forwarded-For, patrz clientIP() w internal/web/server.go) i liczony w oknie czasowym podanym w nawiasie:

Zmienna Endpoint Domyślnie Co chroni
RATE_LIMIT_STATS GET /stats (na minutę) 60 render strony statystyk
RATE_LIMIT_STATS_LIVE GET /stats/live (na minutę) 60 auto-odświeżanie licznika online co 60 s
RATE_LIMIT_GRAPH GET /stats/graph/{zakres} (na minutę) 600 dane wykresów — każde trafienie to jedno tanie rrdtool fetch, limit jest wysoki, bo jeden widok strony sam zużywa 6 zakresów, a NAT/kilka kart to łatwo pomnoży
RATE_LIMIT_CHANNEL POST /channel/create (na minutę) 40 tworzenie kanału
RATE_LIMIT_VERIFY POST /verify/start (na minutę) 10 start weryfikacji nicku (NAT)
RATE_LIMIT_VERIFY_DONE POST /verify/complete (na minutę) 10 wpisanie 6-cyfrowego kodu
RATE_LIMIT_CREATE POST /channel/create po weryfikacji (na godzinę) 5 utworzenie kanału po weryfikacji nicku przez NAT

Przy przekroczeniu limitu endpoint odpowiada 429 Too Many Requests z nagłówkiem Retry-After: 60. Wartości domyślne są w .env.example i packaging/ts3monolith.default — dostosuj je do realnego ruchu na danej instalacji zanim włączysz RATE_LIMIT_ENABLED na produkcji.

S
Description
No description provided
Readme
19 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 52.3%
HTML 14.5%
JavaScript 11.6%
CSS 11.2%
Shell 9.2%
Other 1.2%