Punkt startowy do wszystkich aplikacji uruchomionych na tej maszynie oraz skrócona instrukcja jej obsługi. Od lipca 2026 część ruchu przechodzi przez nową, centralną bramę logowania (oauth2-proxy + authz.py) — w pełni /targety/, (od 10.07.2026) /allegro_api/ oraz (od 14.07.2026) /generator_produktow/ oraz (od 15.07.2026) /autoprezentacja/; /karty/ ma logowanie własne. Od 14.07.2026 ta sama brama potrafi też dokładać apkom delegowany dostęp do Microsoft Graph/SharePoint (X-Graph-Token) — obecnie /allegro_api/ i /autoprezentacja/, patrz punkt 09. Pełny słownik pojęć i architektury niżej.
Od 17.07.2026 publiczny adres serwera to mokate-dabid-serwer.northeurope.cloudapp.azure.com — dawny allegro-test-mokate… już nie odpowiada (zmieniono etykietę DNS na tym samym Public IP). IP 74.234.19.63 oraz nazwa VM allegro-serwer-test — bez zmian.
Krótkie wyjaśnienie każdego elementu, który dokłada się do procesu logowania i routingu na tym serwerze — od lipca 2026.
Serwer WWW i reverse proxy. Obsługuje TLS (certyfikaty przez Certbot) i kieruje żądania do właściwej aplikacji po ścieżce URL. Konfiguracja: /etc/nginx/strony/.
Usługa Microsoft do logowania kontem firmowym @mokate.com.pl. Jedna apka „Logowanie DABiD” w Entra jest wspólnym rejestrem dla całego serwera.
Lokalny mikroserwis (port 4180). Przeprowadza logowanie do Entra ID i pamięta sesję w ciastku. Odpowiada na pytanie „czy to Ty”.
Nasz mikroserwis Flask (port 5098). Łączy sprawdzenie sesji (oauth2-proxy) ze sprawdzeniem uprawnień do konkretnej apki (SQL). Odpowiada na pytanie „czy Ty masz dostęp do TEJ apki”.
Mechanizm NGINX do pod-żądań autoryzacyjnych. Limit: tylko jedno wywołanie per lokalizacja — stąd authz.py, który sam odpytuje oauth2-proxy w środku.
Dane o zalogowanym użytkowniku (e-mail, imię, nazwisko, token SQL), które brama dokleja do żądania. Apka może im ufać bez własnego logowania.
Token w formacie header.payload.signature (base64url). Claimy (e-mail, imię, nazwisko) można odczytać bez weryfikacji podpisu — podpis zweryfikował już oauth2-proxy.
Token dostępowy konkretnego użytkownika (nie konto serwisowe), którym apki łączą się prosto do Azure SQL — z takimi uprawnieniami, jakie ma dany użytkownik.
Analogicznie do X-Sql-Token, ale do Microsoft Graph (SharePoint) — delegowany token konkretnego zalogowanego użytkownika. Zdobywa go authz.py osobnym, cichym logowaniem (prompt=none), niezależnym od sesji SQL/oauth2-proxy. Dziś dla /allegro_api/ i /autoprezentacja/.
Parametr OIDC każący Microsoft zalogować w tle, bez żadnego ekranu — działa, bo zgoda administratora już jest, a przeglądarka ma żywą sesję SSO z chwili głównego logowania chwilę wcześniej.
Tabela w Azure SQL: „ten e-mail ma dostęp do tej apki”. Jedyne miejsce, gdzie ręcznie zarządza się dostępami do bramy.
Terminal multiplexer. Każda aplikacja żyje w panelu jednej z dwóch sesji (mokate-server, app-targety-ph) — odłączenie się nie zatrzymuje apek.
Usługa mokate-server.service odpala jeden skrypt (auto-start.sh), który sam odtwarza sesje tmux po restarcie serwera.
Automatyczne certyfikaty TLS dla NGINX — dzięki nim aplikacje wymagające logowania Entra ID działają po HTTPS na porcie 443.
| Apka | Za nową bramą? | Uwagi |
|---|---|---|
| /targety/ | ✅ Tak | W pełni zmigrowana — NGINX i kod apki czytają nagłówki z bramy |
| /allegro_api/ | ✅ Tak | W pełni zmigrowana (od 10.07.2026) — NGINX i kod apki czytają nagłówki z bramy, bez podwójnego logowania. Od 14.07.2026 pierwsza apka z delegowanym dostępem do Microsoft Graph (X-Graph-Token, patrz punkt 09) |
| /generator_produktow/ | ✅ Tak (od 14.07.2026) | W pełni zmigrowana — wcześniej całkiem otwarta (bez logowania), teraz NGINX i kod apki czytają nagłówki z bramy. Bez X-Graph-Token (mają to /allegro_api/ i /autoprezentacja/) |
| /autoprezentacja/ | ✅ Tak (od 15.07.2026) | W pełni zmigrowana — NGINX i kod apki czytają nagłówki z bramy, bez własnego logowania. Delegowany dostęp do Microsoft Graph/SharePoint (X-Graph-Token) od 15.07.2026 — druga apka po /allegro_api/. Uwaga: nazwa w APPS_NEEDING_GRAPH musi być dokładnie „autoprezentacja" (= pierwszy segment URL-a), nie „autoryzacja" (katalog authz.py) — patrz punkt 09 |
| /karty/ | ❌ Nie | Własne logowanie .NET OIDC — trzeba je wyłączyć przed migracją |
SSH (konsola — CMD lub PowerShell). Użytkownik administracyjny: dabid.
tylko IP MOKATE Port 22 (SSH) jest osiągalny wyłącznie z sieci MOKATE — spoza niej połączenie nie przejdzie (dotyczy też tunelu RDP poniżej, bo idzie przez SSH).
Hasło administratora nie jest zapisane na tej stronie — ta strona jest publiczna (route / nie ma logowania Entra ID). Hasło trzymajcie w menedżerze haseł / prywatnej notatce.
RDP (pulpit zdalny) — MOKATE blokuje RDP spoza sieci, więc łączymy się przez tunel SSH. Najszybciej pobrać oba gotowe pliki:
Kolejność użycia:
Pliki możesz też stworzyć ręcznie — nazwa dowolna, ważne rozszerzenia .bat i .rdp.
Poprzednio jeden płaski plik. Od lipca 2026 konfiguracja jest rozbita na katalogi:
Ważne: główny include /etc/nginx/strony/*.conf; (w nginx.conf) wczytuje tylko pliki bezpośrednio w strony/. Pliki z strony/apps/ wczytuje osobny include .../apps/*.conf; wewnątrz bloku server{} w kartka.conf — bo zawierają gołe location{} bez własnego server{}.
Nauczone boleśnie: nigdy nie zostawiaj plików .bak w strony/ ani strony/apps/ — jeśli pasują do wzorca wildcard include, NGINX wczyta je i przy zduplikowanym server_name może po cichu wybrać stary, niezabezpieczony blok bez żadnego błędu w nginx -t. Backupy trzymamy poza /etc/nginx/ całkowicie (np. ~/nginx-backups/).
Po każdej zmianie przetestuj i przeładuj (test musi przejść, zanim reload wejdzie w życie):
Dodatkowa mapa potrzebna do obsługi WebSocketów w proxy:
NGINX obsługuje tylko jeden auth_request per lokalizację — druga deklaracja bezgłośnie nadpisuje pierwszą, bez błędu w nginx -t. Dlatego sprawdzenie sesji i sprawdzenie dostępu są połączone w jednym miejscu: authz.py (patrz punkt 03).
Każda nowa aplikacja musi mieć ROOT ustawiony nie na /, lecz na swój route (np. /raporty) — zarówno w samej aplikacji, jak i w konfiguracji NGINX.
Dwa niezależne pytania, dwa mechanizmy, jedna brama przed NGINX:
Przepływ dla chronionej apki (w pełni /targety/, /allegro_api/, /generator_produktow/ oraz /autoprezentacja/):
Jeśli sesja nieprawidłowa → 401 (przekierowanie na logowanie Microsoft). Jeśli sesja OK, ale e-mail nie ma wpisu w tabeli dostępów → 403. Błąd samego SQL (np. brak zmapowanego konta) jest traktowany fail-closed — jak brak dostępu, nie jak błąd 500. Wynik jest cache’owany 60s per (apka, e-mail), żeby nie odpytywać SQL przy każdym żądaniu.
Ryzyko: authz.py jest teraz punktem krytycznym logowania dla wszystkich apek za bramą — jeśli padnie, nikt się nie zaloguje, nawet jeśli sam oauth2-proxy działa. Monitoring (Uptime Kuma) jest jeszcze do wdrożenia.
Wspólne wylogowanie dla całego serwera (łączy wylogowanie z oauth2-proxy z sesją Entra):
Po przejściu bramy, aplikacja dostaje nagłówki, którym może ufać bez własnego logowania:
X-Name/X-Surname/X-UserName są percent-encoded, bo nagłówki HTTP nie dopuszczają polskich znaków diakrytycznych bezpośrednio — odbierająca apka musi zrobić unquote().
Szybkie zdekodowanie payloadu JWT do debugowania (bez weryfikacji podpisu — token już zweryfikował oauth2-proxy):
Podgląd żywych claimów w przeglądarce: /targety/diag/sql-token-claims (dekoduje X-Sql-Token i zwraca jako JSON).
Aplikacje bez logowania nie muszą przechodzić przez NGINX — mogą być dostępne bezpośrednio po publicznym IP i porcie (jak Generator zestawów czy Panel harmonogramu). /generator_produktow/ była wcześniej w tej sytuacji (stała za NGINX, ale bez żadnego logowania) — od 14.07.2026 jest już w pełni pod nową bramą, razem z /targety/ i /allegro_api/ (patrz „Stan migracji” wyżej).
Aby port był osiągalny z zewnątrz, trzeba go wystawić w Azure:
Prosty, płaski słownik dostępu: kto ma prawo wejść do której apki. Baza mkt-sls-p-sqldb2, kolumny id, email, aplikacja.
Dodanie dostępu dla kogoś:
Nazwa aplikacji w tej tabeli to po prostu jej główny route/URL — pierwszy segment ścieżki (np. /targety/… → targety), liczony automatycznie przez wspólną mapę /etc/nginx/conf.d/authz_app_map.conf (patrz punkt 07). Tabela jest odpytywana delegowanym tokenem SQL zalogowanego użytkownika, nie kontem serwisowym — więc osoba musi też mieć zmapowane konto AAD w tej bazie (CREATE USER ... FROM EXTERNAL PROVIDER) i GRANT SELECT na tę tabelę, inaczej sprawdzenie kończy się (bezpiecznie) odmową dostępu.
Wszystkie aplikacje startują automatycznie przez serwis systemd, który odpala jeden skrypt startowy:
Skrypt tworzy dwie sesje tmux — każdy panel podpisany nazwą aplikacji:
| Sesja | Panel | Start |
|---|---|---|
| mokate-server | Generator-Zestawow | python server.py |
| mokate-server | karta_produktow (dotnet) | dotnet run |
| mokate-server | allegro-web (wsgi) | python wsgi.py |
| mokate-server | allegro-bot (api) | python api.py |
| mokate-server | Generator produktów | python3 app.py |
| app-targety-ph | targety-ph (kalkulator) | python3 app.py |
| app-targety-ph | app-authz (nowy) | python authz.py |
Podłączenie się:
Nawigacja między panelami (domyślny prefix Ctrl+b): strzałka — przeskok do panelu; o — cyklicznie następny panel; d — odłącz się (apki dalej działają w tle).
Aktualizacja apki po zmianach w repo (git już autoryzowany na serwerze):
Nie trzeba restartować całego serwera ani systemctl restart mokate-server.service — to tylko odtworzyłoby wszystkie panele od zera. Do zwykłej aktualizacji kodu wystarczą powyższe kroki w jednym panelu.
Problem: niektóre apki muszą wołać Microsoft Graph (np. pobrać plik z SharePointa) W IMIENIU zalogowanego użytkownika. X-Sql-Token się do tego nie nadaje — jego „aud” to Azure SQL, Graph go odrzuci jako zły odbiorca. Azure AD v2 nie pozwala też zmieszać scope’ów z dwóch różnych zasobów w jednym logowaniu (AADSTS70011).
Wariant rozważony i odrzucony: token APLIKACYJNY Graph (client_credentials + uprawnienie Application „Sites.Read.All” + zgoda administratora). Działałby, ale dawałby appce dostęp do całego SharePointa tenanta, niezależnie od tego, kto jest zalogowany — świadomie zbyt szeroki zakres.
Rozwiązanie: authz.py sam prowadzi DRUGI, całkowicie niezależny od oauth2-proxy login OAuth2 (Authorization Code) do tej samej apki Entra „Logowanie DABiD”, scope’owany wyłącznie do Graph delegowanego (Sites.Read.All — ma już zgodę administratora sprzed migracji pod bramę, żadnej nowej zgody nie trzeba). Nowe endpointy (publiczne, NIE internal jak /authz/check):
Magazyn refresh tokenów — zaszyfrowany (cryptography.Fernet), bo authz.py sam go trzyma (oauth2-proxy swojego nie eksponuje przez żaden nagłówek):
Automatyzacja bez klikania: dla apek wymienionych w stałej APPS_NEEDING_GRAPH w authz.py (dziś allegro_api i autoprezentacja), /check zwraca 403 + nagłówek X-Needs-Graph-Bootstrap: 1, gdy user nie ma jeszcze połączonego Grafa (zamiast zwykłego 200) — nginx na tej podstawie cicho przekierowuje przez /authz/graph/connect i z powrotem. Dzięki prompt=none to 1-2 niewidoczne przeskoki (poniżej sekundy) — user tego nie zauważa. Dla apek spoza listy nic się nie zmienia.
Pułapek nginx, boleśnie znaleziony przy wdrożeniu — kolejność FAZ: if w TEJ SAMEJ lokalizacji co auth_request_set nie działa. if biegnie w fazie rewrite, PRZED fazą access (gdzie żyje auth_request/auth_request_set) — w momencie sprawdzania warunku zmienna jest jeszcze pusta, niezależnie od tego, co zwróci authz.py (potwierdzone empirycznie: add_header w tej samej lokalizacji widziało poprawną wartość, a if na tę samą zmienną i tak nie odpalał 302). Poprawka: przenieść if do NAZWANEJ LOKACJI wywoływanej przez error_page — internal redirect z error_page wykonuje się PO fazie access, więc zmienna ma już wartość (dokładnie ten sam mechanizm co error_page 401 = @oauth2_signin). Konsekwencja: auth_request rozróżnia tylko 2xx/401/403 (inny kod = 500) — więc /check musi zwracać 403 (nie 200+nagłówek) dla „trzeba podłączyć Graf”.
snippets/authz-gate.conf — dopisane obok istniejących linii z punktu 02 (i zamieniony error_page 403):
kartka.conf — nowa nazwana lokacja + nowa publiczna lokacja dla connect/callback:
Jak dodać kolejną apkę do X-Graph-Token:
Ważne zastrzeżenie znalezione przy wdrożeniu: delegowane Sites.Read.All daje dostęp TYLKO do tego, do czego zalogowany user REALNIE ma dostęp w samym SharePoincie (udostępnianie/ACL) — to nie jest blankietowe uprawnienie jak wersja aplikacyjna. Plik udostępniony tylko konkretnej osobie da 403 accessDenied innemu userowi mimo idealnie poprawnego tokenu/scope/zgody administratora — wygląda jak brak uprawnień w Entra, ale to zupełnie inna przyczyna (SharePoint). Diagnoza: otwórz plik bezpośrednio w SharePoincie (nie przez Graph) jako dany user — SharePoint pokaże czytelny ekran „poproś o dostęp”. Fix: doudostępnij plik „Osobom w organizacji z linkiem” zamiast linku „Określone osoby”.