Bezpieczeństwo i uwierzytelnianie

5 min czytania

Bezpieczeństwo i uwierzytelnianie

PgArachne opiera się całkowicie na systemie uprawnień PostgreSQL. Nie tworzy własnych list kontroli dostępu (ACL). PgArachne wspiera cztery metody uwierzytelniania. Metody 1–3 wykorzystują SET LOCAL ROLE do przełączania tożsamości; metoda 4 (bezpośrednie dane logowania) łączy się bezpośrednio jako dany użytkownik, więc przełączanie roli nie jest potrzebne.

Dotyczy to endpointów JSON-RPC i MCP. Endpoint SSE jest jedynym wyjątkiem — uwierzytelnia wywołującego, ale nie przełącza roli ani nie sprawdza uprawnień na poziomie kanału, ponieważ kanały LISTEN/NOTIFY PostgreSQL nie są obiektami bazy danych z własnymi egzekwowalnymi GRANTami. Zobacz stronę SSE, aby dowiedzieć się, co to oznacza w praktyce.

1. Logowanie interaktywne (JWT)

Użytkownicy uwierzytelniają się za pomocą swojej rzeczywistej nazwy użytkownika i hasła PostgreSQL przez uwierzytelnianie HTTP Basic w POST /{prefix}/{database}/token. Jeśli logowanie powiedzie się, otrzymują krótkotrwały JWT. Kolejne żądania przekazują ten token w nagłówku Authorization: Bearer <token>, a PgArachne przełącza aktywną rolę na tego użytkownika na czas trwania każdego żądania.

curl -X POST http://localhost:8080/db/my_database/token -u demo_user:user_password
→ {"token":"eyJhbGciOi...","token_type":"Bearer","expires_in":28800}

Dane logowania podróżują w nagłówku Authorization, nigdy w treści JSON, a logowanie ma własny adres URL, więc reverse proxy może ograniczać jego częstotliwość bez odczytywania treści żądań. Odpowiedź nigdy nie może być cache’owana.

Wymaga JWT_SECRET. Gdy nie jest skonfigurowany, endpoint zwraca HTTP 404 (-32601), a klienci korzystają zamiast tego z bezpośrednich danych logowania (metoda 4) lub tokenów API (metoda 2).

2. Konta serwisowe (tokeny API)

Dla systemów automatycznych lub skryptów zalecane są długoterminowe tokeny API.

  • Tokeny są przechowywane w tabeli pgarachne.api_tokens.
  • Każdy token jest przypisany do konkretnej roli w bazie danych.
  • Token wysyła się w nagłówku Authorization: Bearer <token>.

Generowanie tokenów API wymaga roli pgarachne_admin. Użyj pgarachne.add_api_token(...) z rolą, która jest członkiem pgarachne_admin.

3. Zewnętrzny dostawca identyfikacji (własny JWT)

Jeśli użytkownicy są uwierzytelniani poza PgArachne (na przykład przez zewnętrzną usługę uwierzytelniania), możesz generować tam JWT i wysyłać je bezpośrednio do PgArachne.

  • Format nagłówka: Authorization: Bearer <jwt>.
  • Podpisywanie: tylko HMAC (HS256 / HS384 / HS512) z użyciem tego samego JWT_SECRET skonfigurowanego w PgArachne.
  • Wymagane claimy: db_role (ciąg znaków, niepusty) oraz db_name (ciąg znaków, musi odpowiadać segmentowi ścieżki :database).
  • Wymagany claim: exp (znacznik czasu Unix) do określenia wygaśnięcia tokenu.

Wymaga JWT_SECRET; bez niego wartości Bearer są sprawdzane wyłącznie jako tokeny API.

Minimalny przykład payloadu:

{
  "db_role": "demo_user",
  "db_name": "my_database",
  "exp": 1767225600
}

Uwaga: asymetryczne algorytmy JWT (np. RS256 / ES256) nie są wspierane.

4. Bezpośrednie dane logowania do bazy danych (Basic Auth)

Najprostsza opcja: wysyłaj nazwę użytkownika i hasło PostgreSQL z każdym żądaniem, używając standardowego HTTP Basic Authentication. PgArachne otwiera dedykowany pool połączeń uwierzytelniony bezpośrednio jako ten użytkownik — SET LOCAL ROLE nie jest wykonywane.

Authorization: Basic <base64(username:password)>

Przykład z curl:

curl -X POST http://localhost:8080/db/my_database/jsonrpc \
  -u demo_user:user_password \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"api.hello_world","params":{},"id":1}'
Kiedy używać bezpośrednich danych logowania:
  • Szybkie prototypowanie i lokalny rozwój — zarządzanie tokenami nie jest wymagane.
  • Usługi wewnętrzne, które już bezpiecznie zarządzają danymi logowania.
  • Scenariusze, w których wcześniejsze wystawianie JWT dodaje niepotrzebną złożoność.

Kluczowe różnice względem metod opartych na tokenach:

  • GRANT demo_user TO pgarachne nie jest wymagane — PgArachne nie przełącza ról.
  • Row-Level Security PostgreSQL oraz uprawnienia GRANT są wymuszane jak zwykle, ponieważ połączenie działa jako rzeczywisty użytkownik.
  • Poole połączeń są utrzymywane osobno dla każdego użytkownika, z maksymalnym czasem życia 5 minut. Po zmianie hasła stare połączenia wygasają w tym okresie.
  • Aby klucze idempotencji działały z bezpośrednimi danymi logowania, przyznaj użytkownikowi uprawnienie EXECUTE ON FUNCTION pgarachne.save_idempotency_key(text, text).

Uprawnienia proxy: wymagane tylko dla metod 1–3

Dla uwierzytelniania JWT i tokenem API, PgArachne łączy się jako użytkownik systemowy zdefiniowany w DB_USER (np. pgarachne) i przełącza tożsamość za pomocą SET LOCAL ROLE. Użytkownik systemowy musi być członkiem każdej docelowej roli.

-- Wymagane tylko dla uwierzytelniania JWT / tokenem API:
GRANT demo_user TO pgarachne;

To uprawnienie nie jest potrzebne przy użyciu bezpośrednich danych logowania (metoda 4).

Porównanie metod uwierzytelniania

MetodaNagłówekSET LOCAL ROLEGRANT … TO pgarachneNajlepsze dla
JWT (/token)Bearer <jwt>✅ TakWymaganeSesje użytkowników końcowych
Token APIBearer <token>✅ TakWymaganeUsługi automatyczne
Zewnętrzny JWTBearer <jwt>✅ TakWymaganeIntegracja z zewnętrznym IdP
Bezpośrednie dane logowaniaBasic <b64>❌ NieNiewymaganeRozwój, usługi wewnętrzne

Które funkcje można wywołać

PgArachne nie ma własnej listy dozwolonych schematów — o tym, co rola może wywołać, decydują wyłącznie uprawnienia PostgreSQL. Funkcję można wywołać (i jest wymieniana przez capabilities, MCP tools/list oraz eksport OpenAPI), gdy przyjmuje jeden parametr jsonb, rola ma do niej EXECUTE, a rola ma USAGE do jej schematu. Funkcje w pg_catalog, information_schema, pgarachne oraz funkcje należące do rozszerzeń są pomijane na liście dla czytelności, ale obowiązują je te same uprawnienia.

Domyślne ustawienia PostgreSQL są liberalne

Domyślnie PostgreSQL nadaje EXECUTE do każdej nowej funkcji roli PUBLIC, a każda rola ma USAGE do schematu public. Funkcja pomocnicza public.something(jsonb) może więc zostać wywołana przez API przez dowolną uwierzytelnioną rolę, chyba że odbierzesz uprawnienie. Zalecana konfiguracja:

-- Nowe funkcje nie są wykonywalne przez wszystkich (uruchom jako rola, która je tworzy):
ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

-- Trzymaj funkcje API w dedykowanym schemacie i nadawaj uprawnienia jawnie:
GRANT USAGE ON SCHEMA api TO demo_user;
GRANT EXECUTE ON FUNCTION api.hello_world(jsonb) TO demo_user;

Ochrona przed atakami brute-force

LOGIN_RATE_LIMIT i LOGIN_RATE_LIMIT_PER_IP dotyczą każdej metody uwierzytelniania, na każdym endpoincie (JSON-RPC, MCP, SSE, OpenAPI):

  • /token: każda próba jest wliczana do limitów na (IP, nazwę użytkownika) i na IP.
  • Bezpośrednie dane logowania (Basic Auth): nieudane próby są wliczane do limitów na (IP, nazwę użytkownika) i na IP. Udane żądania nie są liczone, więc prawidłowy klient wysyłający dane logowania w każdym żądaniu nigdy nie zostanie ograniczony.
  • Tokeny Bearer (JWT lub token API): nieudane próby są wliczane do limitu na IP.

Po wyczerpaniu limitu kolejne próby są odrzucane z HTTP 429 aż do upływu okna (LOGIN_RATE_WINDOW) — bez jakiegokolwiek sprawdzania danych logowania.