Wdrożenie najlepszych praktyk Postgres

21

Ludzie,

Przydałaby mi się pomoc w ulepszeniu projektu kontroli dostępu użytkownika Postgres i lepszym dostosowaniu go do najlepszych praktyk. Pomagam wdrożyć niewielki produkcyjny serwer Postgres, ale nie jestem administratorem bazy danych, więc wiem wystarczająco dużo, aby być niebezpiecznym.

Jest jeden serwer z jedną instalacją Postgres v9.2. Ta instalacja obsługuje wiele baz danych, z których każda w pełni obsługuje innego „klienta”. Innymi słowy, klient1 nie będzie, nie powinien korzystać z bazy danych2 i tak dalej. Podczas normalnych operacji dostęp do baz danych jest uzyskiwany z pamięci podręcznej przez pasującą instancję CakePHP, wszystkie zlokalizowane na tym samym serwerze co Postgres. Chociaż mogą istnieć możliwe optymalizacje tego wdrożenia, najbardziej interesują mnie role Psql.

Na podstawie tego, co przeczytałem, wydaje się, że sensowne byłyby trzy typy ról:

  • Posty superużytkownika z domyślnym hasłem
  • Rola administratora, która nie ma uprawnień administratora do rutynowej konserwacji, tworzenia bazy danych, tworzenia kopii zapasowych, przywracania. Powinien być w stanie zrobić wszystko ze wszystkimi bazami danych klientów.
  • Role użytkowników z możliwością CRUD w ich odpowiedniej bazie danych. Więcej praw do własnej bazy danych może być tolerowane, jeśli wyczyści wdrożenie.

Wdrażanie tego projektu jest tam, gdzie jestem mniej pewny siebie. Własność DB kontra tabela i kto powinien odziedziczyć po tym, kto jest trochę błotnisty. Poniżej znajdują się moje bazy danych i moi użytkownicy. Czy to wystarczające informacje, aby ocenić wdrożenie?

     Role name |                   Attributes                   |     Member of     
    -----------+------------------------------------------------+-------------------
     admin     | Create role, Create DB                         | {user1, user2}
     postgres  | Superuser, Create role, Create DB              | {}
     user1     |                                                | {}
     user2     |                                                | {}

    postgres=# \l
                                 List of databases
       Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges   
    -----------+----------+----------+---------+-------+-----------------------
     admin     | postgres | UTF8     | en_US   | en_US | =Tc/postgres         +
               |          |          |         |       | postgres=CTc/postgres+
               |          |          |         |       | admin=CTc/postgres
     postgres  | postgres | UTF8     | en_US   | en_US | 
     template0 | postgres | UTF8     | en_US   | en_US | =c/postgres          +
               |          |          |         |       | postgres=CTc/postgres
     template1 | postgres | UTF8     | en_US   | en_US | =c/postgres          +
               |          |          |         |       | postgres=CTc/postgres
     user1     | admin    | UTF8     | en_US   | en_US | =Tc/admin            +
               |          |          |         |       | admin=CTc/admin      +
               |          |          |         |       | user1=CTc/admin
     user2     | admin    | UTF8     | en_US   | en_US | =Tc/admin            +
               |          |          |         |       | admin=CTc/admin      +
               |          |          |         |       | user2=CTc/admin

Aby zapobiec zewnętrznym połączeniom i hasłom w postaci wyczyszczenia, pg_hba.conf jest taki:

local   all             all                                     md5
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5
JP Beaudry
źródło
1
Z mojego doświadczenia wynika, że ​​najlepszą separacją, która przynosi także wiele innych korzyści, jest uruchomienie osobnych klastrów PostGreSQL (np. Usług) dla każdego klienta. To właśnie robimy obecnie dla dużego środowiska produkcyjnego i nie zrobiłbym tego inaczej, chyba że liczba baz danych byłaby naprawdę duża, a każda z nich byłaby naprawdę niewielka. Oczywiście aplikacja musi również wiedzieć, jak połączyć się z innym źródłem danych dla każdego najemcy (klienta).
Florin Asăvoaie
Obok @ FlorinAsăvoaie jego uwaga. Czy każda baza danych nie powinna mieć własnego właściciela i użytkownika zapytań? Ułatwi to umieszczenie niektórych użytkowników w przechowalni haseł w celach konserwacyjnych.
hspaans

Odpowiedzi:

5

Wiem, że to stare pytanie, ale spróbuję na nie odpowiedzieć nawet teraz, ponieważ muszę przeprowadzić badania związane z tym zagadnieniem.

To, co próbujesz zrobić, nazywa się wielodostępem na poziomie bazy danych. Można to osiągnąć na dwa sposoby:

  1. Jednak w jednym klastrze baz danych, tak jak opisał PO, moim osobistym wyborem byłoby:

    • Użytkownik postgres korzysta z uwierzytelniania równorzędnego i nie może nawiązywać połączeń za pomocą hasła. Moim zdaniem uwierzytelnianie MD5 to zła praktyka. Jeśli napotkasz jakiekolwiek problemy ze spójnością bazy danych lub tego typu rzeczami, nadal będziesz mógł się zalogować, jeśli pozwolisz Postgresowi na używanie uwierzytelniania równorzędnego.
    • Każdy klient powinien otrzymać własny schemat, a nie bazę danych. Istnieje wiele przyczyn:
      • Posiadanie całej bazy danych dawałoby wiele przywilejów.
      • Posiadanie tylko określonych tabel przyniosłoby problemy programistom i zawsze wymagałoby poproszenia administratorów o dodanie uprawnień i innych rzeczy.
      • W związku z tym w normalnej konfiguracji każdy z nich uzyskałby dostęp do tworzenia elementów w swoim schemacie, w tym tabel, widoków, wyzwalaczy itp.
      • Wszystkie używają tego samego ciągu połączenia oprócz nazwy użytkownika. W Postgres domyślnie, jeśli masz schemat z nazwą użytkownika, jest on automatycznie w ścieżce wyszukiwania.
    • Jako środek bezpieczeństwa wybrałbym brak administratora, który mógłby uzyskać dostęp do każdego schematu. Kopie zapasowe należy wykonywać albo przez zrzucenie każdego schematu z jego własnym użytkownikiem, albo przy użyciu techniki PITR PostgreSQL. Nadal będziesz musiał użyć użytkownika postgres do stworzenia nowych schematów, wybrałbym regułę sudo i skrypt do tego.
    • Wiele dobrych praktyk bezpieczeństwa zaleca porzucenie domyślnego schematu, więc - zaczynamy.
    • To rozwiązanie jest wyjątkowo odpowiednie, jeśli DB dla każdego klienta jest mały i masz mnóstwo klientów.
    • Jeśli aplikacja obsługuje wiele dzierżawców, może używać jednej puli połączeń dla wszystkich klientów. Oczywiście eliminuje to wiele powyższych ulepszeń bezpieczeństwa, ale może przynieść korzyści w zakresie wydajności, szczególnie gdy masz dużą liczbę klientów (jeśli masz 500-1000 oddzielnych źródeł danych i korzystasz z puli połączeń, będzie to dość przytłaczające).
  2. Każdy klient otrzymuje własny klaster bazy danych. Jest to moje preferowane rozwiązanie, zwłaszcza że zwykle pracuję z aplikacjami, które mają duże bazy danych dla każdego klienta.

    • Ten zapewnia bardzo dobrą separację danych. Możesz użyć osobnych woluminów pamięci dla każdego klienta, przydzielić ograniczenia procesora i pamięci (używając dokera?).
    • Naprawdę dobra elastyczność w zakresie potrzeb każdego klienta w jego instancji. Mogą być podobne lub mieć wyraźne cechy.
    • Bardzo łatwo skalować w obu kierunkach (w górę i na zewnątrz).
    • Korzystam również z oddzielnych wirtualnych adresów IP, w których każdy klaster nasłuchuje połączeń, dzięki czemu skalowanie w górę nie wymaga rekonfiguracji źródła danych.
    • Kopie zapasowe PITR są tworzone dla poszczególnych klientów, więc łatwiej będzie przywrócić pojedynczego klienta w porównaniu z wieloma dzierżawcami według schematu.
    • W złożonych konfiguracjach każdy klient może potrzebować wielu baz danych, schematów, użytkowników i ról itp., Więc jest to o wiele lepsze rozwiązanie w takich przypadkach.

Możesz także użyć kombinacji powyższych i użyć pgBouncer jako routera.

Florin Asăvoaie
źródło