jak zabezpieczyć otwarty port PostgreSQL

29

Taka jest sytuacja. Wydaje się, że musimy mieć otwarty port TCP 5432 dla świata, na którym klient ma dostęp do swojej bazy danych PostgreSQL.

Z oczywistych powodów nie możemy po prostu powiedzieć „nie”, tylko w ostateczności.

Jakie są największe problemy? Jak mogę bronić naszej infrastruktury?

W każdym razie: dlaczego nie miałby być otwarty na świat? Myślę, że może jest bardziej bezpieczny niż jakiś 20-letni, nieobsługiwany serwer FTP.

PS VPN nie jest w porządku. Może jakieś szyfrowanie (jeśli mogę mu podać adres URL połączenia JDBC, który działa ).

Josip Rodin
źródło
4
Tunele SSH nie są opcją? W tym artykule instruktażowym faktycznie wykorzystano PostgreSQL jako przykład. Możesz dostarczyć klientowi wstępnie skonfigurowanego klienta SSH, aby ułatwić połączenie.
Lucyfer Sam
@LuciferSam Nie. Baza danych będzie używana przez wewnętrznie opracowaną aplikację Java na około 100 maszynach firmy. Naszym jedynym sposobem na ich skonfigurowanie jest podanie adresu URL połączenia jdbc do administracji lokalnej, każda inna jest bardzo, bardzo problematyczna.
@milkman co robi aplikacja? może zamiast tego mógłby wysłać zapytanie do serwera RESTful? Oczywiście przekazywanie SQL do REST nic nie zyskuje, ale przy założeniu, że jest to CRUD ..
tedder42
@ tedder42 Manipuluje bazą danych użytkowników CMS, który również jest hostowany przez nas. Nie mamy pozwolenia na zmianę jego źródła.

Odpowiedzi:

41

Wymagaj SSL, miej włączony SELinux, monitoruj logi i używaj aktualnej wersji PostgreSQL .

Po stronie serwera

Wymagaj SSL

W postgresql.confzestawie ssl=oni upewnij się, że plik klucza i plik certyfikatu są odpowiednio zainstalowane (zobacz dokumenty i komentarze w postgresql.conf).

Może być konieczne wykupienie certyfikatu od urzędu certyfikacji, jeśli chcesz, aby klienci ufali mu bez specjalnej konfiguracji na kliencie.

W pg_hba.confużyciu coś takiego:

hostssl theuser thedatabase 1.2.3.4/32 md5

... prawdopodobnie z „all” dla użytkownika i / lub bazy danych oraz ewentualnie z szerszym źródłowym filtrem adresów IP.

Ogranicz użytkowników, którzy mogą się zalogować, odmów logowania do zdalnego administratora

Nie zezwalaj użytkownikom na „wszystko”, jeśli to możliwe; nie chcesz zezwalać na logowanie superużytkownika zdalnie, jeśli możesz tego uniknąć.

Ogranicz prawa użytkowników

Ogranicz prawa użytkowników, którzy mogą się zalogować. Nie udzielaj im CREATEDBani CREATEUSERpraw.

REVOKECONNECTprawo od PUBLICwszystkich baz danych, a następnie oddać tylko do użytkowników / ról, które powinny mieć dostęp do tej bazy danych. (Grupuj użytkowników w role i przyznawaj uprawnienia rolom, a nie bezpośrednio poszczególnym użytkownikom).

Upewnij się, że użytkownicy z dostępem zdalnym mogą łączyć się tylko z tymi DB, których potrzebują, i mają prawa tylko do schematów, tabel i kolumn w obrębie, których faktycznie potrzebują. Jest to dobra praktyka również dla lokalnych użytkowników, to po prostu rozsądne bezpieczeństwo.

Konfiguracja klienta

W PgJDBC przekaż parametrssl=true :

Aby poinstruować sterownik JDBC o próbie nawiązania połączenia SSL, należy dodać parametr adresu URL połączenia ssl = true.

... i zainstaluj certyfikat serwera w magazynie zaufania klienta lub użyj certyfikatu serwera, któremu ufa jeden z urzędów certyfikacji we wbudowanym magazynie zaufanych certyfikatów Java, jeśli nie chcesz, aby użytkownik musiał zainstalować certyfikat.

Trwająca akcja

Teraz upewnij się, że aktualizujesz PostgreSQL . PostgreSQL ma tylko kilka luk w zabezpieczeniach przed uwierzytelnieniem, ale jest to więcej niż zero, więc bądź na bieżąco. W każdym razie powinieneś, poprawki błędów to dobra rzecz.

Dodaj zaporę z przodu, jeśli istnieją duże bloki / regiony, o których wiesz, że nigdy nie potrzebujesz dostępu.

Rejestruj połączenia i rozłączenia (patrz postgresql.conf). Rejestruj zapytania, jeśli jest to praktyczne. Uruchom system wykrywania włamań lub fail2ban lub podobny z przodu, jeśli jest to praktyczne. W przypadku fail2ban z postgresiem jest wygodny poradnik tutaj

Monitoruj pliki dziennika.

Bonusowa paranoja

Dodatkowe kroki, aby pomyśleć o ...

Wymagaj certyfikatów klienta

Jeśli chcesz, możesz także użyć pg_hba.confżądania, aby klient przedstawił certyfikat klienta X.509 zaufany przez serwer. Nie musi używać tego samego urzędu certyfikacji co certyfikat serwera, możesz to zrobić za pomocą homebrew openssl CA. Użytkownik JDBC musi zaimportować certyfikat klienta do swojego magazynu kluczy Java keytooli ewentualnie skonfigurować niektóre właściwości systemu JSSE, aby wskazywał Javę w swoim magazynie kluczy, więc nie jest to całkowicie przezroczyste.

Poddaj kwarantannie instancję

Jeśli chcesz być naprawdę paranoikiem, uruchom instancję dla klienta w osobnym kontenerze / maszynie wirtualnej lub przynajmniej na innym koncie użytkownika, używając tylko potrzebnej bazy danych.

W ten sposób, jeśli skompromitują instancję PostgreSQL, nie dostaną się dalej.

Użyj SELinux

Nie powinienem tego mówić, ale ...

Uruchom maszynę z obsługą SELinux, taką jak RHEL 6 lub 7, i nie wyłączaj SELinuksa ani nie włączaj go w tryb zezwolenia . Trzymaj go w trybie wymuszania.

Użyj portu innego niż domyślny

Bezpieczeństwo tylko przez zaciemnienie to głupota. Bezpieczeństwo, które wykorzystuje trochę niejasności po wykonaniu rozsądnych czynności, prawdopodobnie nie zaszkodzi.

Uruchom Pg na porcie innym niż domyślny, aby utrudnić życie automatycznym atakującym.

Umieść serwer proxy z przodu

Możesz także uruchomić PgBouncer lub PgPool-II przed PostgreSQL, działając jako pula połączeń i proxy. W ten sposób możesz pozwolić proxy obsługiwać SSL, a nie prawdziwego hosta bazy danych. Serwer proxy może znajdować się na osobnej maszynie wirtualnej lub maszynie.

Korzystanie z serwerów proxy pul połączeń jest ogólnie dobrym pomysłem w PostgreSQL, chyba że aplikacja kliencka ma już wbudowaną pulę. Większość serwerów aplikacji Java, Railsów itp. Ma wbudowane buforowanie. Nawet wtedy serwer proxy po stronie serwera jest w najgorszym wypadku nieszkodliwy.

Craig Ringer
źródło
3
Jeśli klient ma statyczny adres IP $, zezwól tylko na zaporę na port $.
user9517 obsługuje GoFundMonica
Dziękuję Ci bardzo! Pgjdbc ma ten parametr, ale mogę podać mu tylko adres URL połączenia jdbc i nie jestem pewien, czy będzie działał z jego aplikacją Java (zastrzeżona, niepodlegająca debugowaniu). Ok, jeśli nie, zadam nowe pytanie. Dziękuję twoją szczegółową odpowiedź!
1
@lesto Właściwie myślę, że odsłonięcie VPN znacznie zwiększa powierzchnię ataku w porównaniu z tylko jedną ograniczoną usługą. Ludzie zapominają, że VPN staje się kanałem ataku dla każdego złośliwego oprogramowania na zdalnych maszynach, aby przebić się przez wszystkie zabezpieczenia obwodowe i dostać się do wnętrzności sieci. Uważam je za dopuszczalne tylko wtedy, gdy łączą się z DMZ, która traktuje je jako równie toksyczne jak hosty internetowe.
Craig Ringer
1
@CraigRinger Nie mówię o usunięciu pozostałej części ochrony, ale o enkapsulacji usługi w VPN
Lesto
1
@lesto Pewnie, zgadzam się, VPN może być użyteczną dodatkową warstwą, jeśli nie jest traktowany jak Magic Security Sauce, jak wielu administratorów niestety.
Craig Ringer
2

Proste rozszerzenie imponującego planu działania Craigs:

Być może użytkownik korzysta z relatywnie niewielkiego zestawu dostawców sieci (na przykład jego operator sieci mobilnej podczas przeprowadzki, jego sieć kablowa z domu i miejsce pracy wychodzące z pracy przez IP).

Większość dostawców sieci ma wiele adresów IP, ale tak naprawdę niewiele podsieci. Możesz więc podać filtr iptables, który ogranicza dostęp postgresql do segmentów sieci używanych przez klienta. To znacznie zmniejszyło możliwości ataku losowo wybranych źródeł problemów w sieci.

Prosty scenariusz wsparcia:

  1. Twój klient dzwoni do Ciebie: „Nie mogę się zalogować” .
  2. Dowiesz się z tcpdump -i eth0 -p tcp port 5432rozkazem, skąd on pochodzi.
  3. Za pomocą whois 1.2.3.4można uzyskać adres IP używany przez ten adres IP. Na przykład może być 1.2.3.0/24.
  4. Z iptables -A INPUT -s 1.2.3.0/24 -p tcp --dport 5432 -j ACCEPT(lub podobnym) pozwalasz na połączenia TCP z jego nową podsiecią.

Istnieje bardzo dobry skrypt o nazwie Perl, uifktóry może zapewniać trwałe i intuicyjne zestawy deklarowanych reguł iptables. (Google dla „uif iptables”).

Peter mówi, że przywraca Monikę
źródło
1
Ciekawy pomysł, ale brzmi to trochę krucho.
nishantjr
@nishantjr Oczywiście nie jest to samodzielne rozwiązanie, tylko możliwość poprawy sytuacji.
Peter mówi, że przywróć Monikę
Bardziej praktycznym podejściem może być dodanie do białej listy poszczególnych dostawców usług internetowych i / lub krajów, aby dowiedzieć się, jak to zrobić, np. Stackoverflow.com/questions/16617607/...
Josip Rodin
1

Oto dość prosta konfiguracja Fail2ban dla PostgreSQL na podstawie powyższego HOWTO, ale dostosowana do faktycznej pracy z pakietami Ubuntu, przechwytywania kolejnego błędu i pomijania różnych komunikatów debugowania, aby przyspieszyć:

/etc/fail2ban/filter.d/local-postgresql.conf:

[Definition]

failregex = <HOST>\(\d+\) FATAL:  password authentication failed for .+$
            <HOST>\(\d+\) FATAL:  no pg_hba.conf entry for host .+$

ignoreregex = duration:

/etc/fail2ban/jail.d/local-postgresql.conf:

[local-postgresql]
enabled  = true
filter   = local-postgresql
action   = iptables[name=PostgreSQL, port=5432, protocol=tcp]
           sendmail-whois[name=PostgreSQL, dest=root@localhost]
logpath  = /var/log/postgresql/postgresql-9.3-main.log
maxretry = 3
Josip Rodin
źródło
1

Fail2ban to potężne narzędzie, ale nie ufaj, że filtr będzie działał tak, jak jest. Przetestuj wszystkie filtry za pomocą narzędzia failregex i pamiętaj, aby uciec od cudzysłowów (np. „Admin” to \ „admin \”). Na przykład testowanie następującej linii filtru failregex z mojego /etc/log/postgresql/postgresql-9.3-main.log nie działało dla mnie.

fail2ban-regex '2016-09-20 14:30:09 PDT FATAL:  password authentication failed for user "admin"' '<HOST>\(\d+\) FATAL:  password authentication failed for .+$'

Powyższe dało mi

Failregex: łącznie 0

Musiałem zaktualizować failregex, aby pasował do formatu dziennika.

fail2ban-regex '2016-09-20 14:30:09 PDT FATAL:  password authentication failed for user "admin"' 'FATAL:  password authentication failed for user \"<HOST>\"'

To dało mi pozytywny wynik.

Failregex: 1 ogółem

Test fail2ban-regex można również zaimplementować w całych plikach dziennika.

fail2ban-regex /var/log/postgresql/postgresql-9.3-main.log /etc/fail2ban/filter.d/postgresql.local

Powyższe dało mi następujący pozytywny wynik ze zaktualizowanym failregex.

Failregex: 169 ogółem

metale
źródło