v0.3.0-prod

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-04 14:57:48 +01:00
1 parent 387933957e
commit 677a0e4d2c
20 files changed
+1239 -180

No files matched your search

+78 -9
View File
@@ -1,26 +1,81 @@
# 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.
## Zachowana funkcjonalność
## Funkcjonalność
- wykrycie klienta TeamSpeak po IP widzianym przez HTTP i ServerQuery;
- utworzenie permanentnego kanału z nazwą, tematem i opcjonalnym hasłem;
- 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;
- obecna baza RRD `online` i wykresy: 12h, 24h, 72h, tydzień, miesiąc, rok;
- 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.
## Uruchomienie
## 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`. To kopia dotychczasowej bazy z archiwum.
3. Zapewnij zapis do katalogu `data` dla UID 10001 przy Dockerze: `chown -R 10001:10001 data`.
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_<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:
```html
@@ -37,24 +92,38 @@ Przykład iframe:
## 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).
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 — `<zakres>.png`
(ciemna paleta) i `<zakres>-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;
- 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;
- 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.