Tradycyjnie typowa aplikacja składa się z następujących elementów:
W tym prostym przypadku wystarczyłaby podstawowa konfiguracja:
- aplikacja używa prostego lokalnego mechanizmu uwierzytelniania dla swoich użytkowników
- aplikacja korzysta z prostej puli połączeń
- istnieje jeden użytkownik zdefiniowany jako dostęp do bazy danych
Jednak wraz z rozwojem organizacji i rozrostem dodawanych jest więcej komponentów:
- więcej aplikacji dzierżawy lub instancji aplikacji uzyskujących dostęp do bazy danych
- więcej usług i systemów uzyskujących dostęp do bazy danych
- centralne uwierzytelnianie/autoryzacja (AA) dla wszystkich (lub większości) usług
- oddzielenie komponentów dla łatwiejszego przyszłego skalowania
W powyższym schemacie wszystkie koncerny są rozdzielone na poszczególne komponenty, każdy komponent służy do określonego celu. Jednak nadal pula połączeń korzysta z jednego dedykowanego użytkownika bazy danych, jak w poprzedniej prostszej konfiguracji, którą widzieliśmy powyżej.
Oprócz nowych komponentów pojawiają się również nowe wymagania:
- lepsza, szczegółowa kontrola nad tym, co użytkownicy mogą robić na poziomie bazy danych
- audyt
- lepsze, bardziej przydatne logowanie systemowe
Zawsze możemy zaimplementować wszystkie trzy z większą ilością kodu aplikacji lub większą liczbą warstw w aplikacji, ale jest to po prostu kłopotliwe i trudne w utrzymaniu.
Ponadto PostgreSQL oferuje tak bogaty zestaw rozwiązań w wyżej wymienionych obszarach (bezpieczeństwo, Row Level Security, audyt itp.), że przeniesienie wszystkich tych usług do warstwy bazy danych ma sens. Aby pobrać te usługi bezpośrednio z bazy danych, musimy zapomnieć o pojedynczym użytkowniku w bazie danych i zamiast tego użyć prawdziwych indywidualnych użytkowników.
To prowadzi nas do schematu takiego jak poniżej:
W naszym przypadku użycia opiszemy typową konfigurację przedsiębiorstwa składającą się z powyższego schematu, w którym używamy:
- Serwer aplikacji Wildfly (przykłady pokazane dla wersji 10)
- Usługa uwierzytelniania/autoryzacji LDAP
- pulerze połączeń pgbouncera
- PostgreSQL 10
Wygląda to na typową konfigurację, ponieważ jboss/wildfly od wielu lat obsługuje uwierzytelnianie i autoryzację LDAP, PostgreSQL wspiera LDAP od wielu lat.
Jednak pgbouncer rozpoczął obsługę LDAP (i to przez PAM) dopiero od wersji 1.8 pod koniec 2017 roku, co oznacza, że ktoś do tego czasu nie mógł używać najgorętszego poolera połączeń PostgreSQL w takiej konfiguracji korporacyjnej (co nie brzmiało obiecująco pod żadnym wybranym przez nas kątem spojrzeć na to)!
W tym blogu opiszemy konfigurację potrzebną w każdej warstwie.
Konfiguracja Wildfly 10
Konfiguracja źródła danych będzie musiała wyglądać tak, pokazuję najważniejsze rzeczy:
<xa-datasource jndi-name="java:/pgsql" pool-name="pgsqlDS" enabled="true" mcp="org.jboss.jca.core.connectionmanager.pool.mcp.LeakDumperManagedConnectionPool">
<xa-datasource-property name="DatabaseName">
yourdbname
</xa-datasource-property>
<xa-datasource-property name="PortNumber">
6432
</xa-datasource-property>
<xa-datasource-property name="ServerName">
your.pgbouncer.server
</xa-datasource-property>
<xa-datasource-property name="PrepareThreshold">
0
</xa-datasource-property>
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
<driver>postgresql-9.4.1212.jar</driver>
<new-connection-sql>
SET application_name to 'myapp';
</new-connection-sql>
<xa-pool>
<max-pool-size>400</max-pool-size>
<allow-multiple-users>true</allow-multiple-users>
</xa-pool>
<security>
<security-domain>postgresqluser</security-domain>
</security>
</xa-datasource>
Pogrubiłem ważne parametry i wartości. Pamiętaj, aby zdefiniować adres IP (lub nazwę hosta), nazwę bazy danych i port zgodnie z konfiguracją serwera pgbouncer.
Ponadto zamiast typowej nazwy użytkownika/hasła będziesz musiał zdefiniować domenę bezpieczeństwa, którą należy określić w sekcji źródła danych, jak pokazano powyżej. Jego definicja będzie wyglądać następująco:
<security-domain name="postgresqluser">
<authentication>
<login-module code="org.picketbox.datasource.security.CallerIdentityLoginModule" flag="required">
<module-option name="managedConnectionFactoryName" value="name=pgsql,jboss.jca:service=XATxCM"/>
</login-module>
</authentication>
</security-domain>
W ten sposób wildfly przekaże kontekst bezpieczeństwa do pgbouncer.
UWAGA: na tym blogu omawiamy podstawy, tj. nie używamy ani nie wspominamy o TLS, jednak zdecydowanie zachęcamy do korzystania z niego w swojej instalacji.
Użytkownicy wildfly muszą uwierzytelnić się na serwerze LDAP w następujący sposób:
<login-module code="<your login module class>" flag="sufficient">
<module-option name="java.naming.provider.url" value="ldap://your.ldap.server/"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="principalDNPrefix" value="uid="/>
<module-option name="uidAttributeID" value="memberOf"/>
<module-option name="roleNameAttributeID" value="cn"/>
<module-option name="roleAttributeID" value="memberOf"/>
<module-option name="principalDNSuffix"
value=",cn=users,cn=accounts,dc=yourorgname,dc=com"/>
<module-option name="userSrchBase" value="dc=yourorgname,dc=com"/>
<module-option name="rolesCtxDN"
value="cn=groups,cn=accounts,dc=yourorgname,dc=com"/>
<module-option name="matchOnUserDN" value="true"/>
<module-option name="unauthendicatedIdentity" value="foousr"/>
<module-option name="com.sun.jndi.ldap.connect.timeout" value="5000"/>
</login-module>
Powyższe pliki konfiguracyjne dotyczą wildfly 10.0, w każdym przypadku radzimy zapoznać się z oficjalną dokumentacją swojego środowiska.
Konfiguracja PostgreSQL
Aby poinformować PostgreSQL o uwierzytelnieniu (UWAGA: nie autoryzuj!) na serwerze LDAP, musisz wprowadzić odpowiednie zmiany w postgresql.conf i pg_hba.conf. Interesujące wpisy są następujące:
W postgresql.conf:
listen_addresses = '*'
oraz w pg_hba.conf:
#TYPE DATABASE USER CIDR-ADDRESS METHOD
host all all ip.ofYourPgbouncer.server/32 ldap ldapserver=your.ldap.server ldapprefix="uid=" ldapsuffix=",cn=users,cn=accounts,dc=yourorgname,dc=com"
Upewnij się, że zdefiniowane tutaj ustawienia LDAP odpowiadają dokładnie tym, które zdefiniowałeś w konfiguracji serwera aplikacji. Istnieją dwa tryby działania, w których PostgreSQL może kontaktować się z serwerem LDAP:
- proste wiązanie
- wyszukaj, a następnie powiąż
Tryb prostego wiązania wymaga tylko jednego połączenia z serwerem LDAP, dlatego jest szybszy, ale wymaga nieco bardziej rygorystycznej organizacji słownika LDAP niż tryb drugi. Tryb wyszukiwania i wiązania zapewnia większą elastyczność. Jednak w przypadku przeciętnego katalogu LDAP pierwszy tryb (proste wiązanie) będzie działał dobrze. Musimy podkreślić pewne punkty dotyczące uwierzytelniania PostgreSQL LDAP:
- Ma to związek z tylko uwierzytelnianiem (sprawdzanie haseł).
- Członkostwo na role jest nadal wykonywane w PostgreSQL, jak zwykle.
- Użytkownicy muszą być utworzeni w PostgreSQL (poprzez CREATE użytkownika/role) jak zwykle.
Istnieje kilka rozwiązań pomagających w synchronizacji między użytkownikami LDAP i PostgreSQL (np. ldap2pg) lub możesz po prostu napisać własne opakowanie, które będzie obsługiwać zarówno LDAP, jak i PostgreSQL w celu dodawania lub usuwania użytkowników.
Pobierz oficjalny dokument już dziś Zarządzanie i automatyzacja PostgreSQL za pomocą ClusterControlDowiedz się, co musisz wiedzieć, aby wdrażać, monitorować, zarządzać i skalować PostgreSQLPobierz oficjalny dokumentKonfiguracja PgBouncera
Jest to najtrudniejsza część naszej konfiguracji, ponieważ pgbouncer nadal nie obsługuje natywnego LDAP, a jedyną opcją jest uwierzytelnianie przez PAM, co oznacza, że zależy to od poprawnej lokalnej konfiguracji PAM UNIX/Linux dla LDAP.
Tak więc procedura jest podzielona na dwa kroki.
Pierwszym krokiem jest skonfigurowanie i przetestowanie, czy pgbouncer współpracuje z PAM, a drugim krokiem jest skonfigurowanie PAM do współpracy z LDAP.
pgbouncer
pgbouncer musi być skompilowany z obsługą PAM. W tym celu musisz:
- zainstaluj libpam0g-dev
- ./configure --with-pam
- przekompiluj i zainstaluj pgbouncera
Twój pgbouncer.ini (lub nazwa pliku konfiguracyjnego pgbouncer) musi być skonfigurowany dla pam. Musi również zawierać poprawne parametry dla Twojej bazy danych i aplikacji zgodnie z parametrami opisanymi w powyższych sekcjach. Rzeczy, które będziesz musiał zdefiniować lub zmienić:
yourdbname = host=your.pgsql.server dbname=yourdbname pool_size=5
listen_addr = *
auth_type = pam
# set pool_mode for max performance
pool_mode = transaction
# required for JDBC
ignore_startup_parameters = extra_float_digits
Oczywiście będziesz musiał przeczytać dokumentację pgbouncera i dostroić swojego pgbouncera do swoich potrzeb. Aby przetestować powyższą konfigurację, wszystko, co musisz zrobić, to utworzyć nowego lokalnego użytkownika UNIX i spróbować uwierzytelnić się w pgbouncer:
# adduser testuser
<answer to all question, including password>
Aby pgbouncer działał z PAM podczas odczytu z lokalnych plików passwd, plik wykonywalny pgbouncer musi być własnością roota i mieć setuid:
# chown root:staff ~pgbouncer/pgbouncer-1.9.0/pgbouncer
# chmod +s ~pgbouncer/pgbouncer-1.9.0/pgbouncer
# ls -l ~pgbouncer/pgbouncer-1.9.0/pgbouncer
-rwsrwsr-x 1 root staff 1672184 Dec 21 16:28 /home/pgbouncer/pgbouncer-1.9.0/pgbouncer
Uwaga:Konieczność posiadania roota i setuid (co jest prawdziwe dla każdego testowanego przeze mnie systemu debian/ubuntu) nie jest nigdzie udokumentowana, ani w oficjalnej dokumentacji pgbouncera, ani nigdzie w sieci.
Następnie logujemy się (jako superużytkownik pgsql) do hosta postgresql (lub psql -h twój.pgsql.server) i tworzymy nowego użytkownika:
CREATE USER testuser PASSWORD 'same as the UNIX passwd you gave above';
następnie z hosta pgbouncer:
psql -h localhost -p 6432 yourdbname -U testuser
Powinieneś otrzymać monit i zobaczyć tabele tak, jakbyś był połączony bezpośrednio z serwerem bazy danych. Pamiętaj, aby usunąć tego użytkownika z systemu, a także usunąć go z bazy danych po zakończeniu wszystkich testów.
PAM
Aby PAM mógł współpracować z serwerem LDAP, potrzebny jest dodatkowy pakiet:libpam-ldap . Jego skrypt poinstalacyjny uruchomi okno dialogowe trybu tekstowego, na które należy odpowiedzieć, podając prawidłowe parametry dla serwera LDAP. Ten pakiet dokona niezbędnych aktualizacji w plikach /etc/pam.d, a także utworzy plik o nazwie:/etc/pam_ldap.conf. Jeśli coś zmieni się w przyszłości, zawsze możesz wrócić i edytować ten plik. Najważniejsze wiersze w tym pliku to:
base cn=users,cn=accounts,dc=yourorgname,dc=com
uri ldap://your.ldap.server/
ldap_version 3
pam_password crypt
Nazwa/adres serwera LDAP i bazy wyszukiwania muszą być dokładnie takie same, jak te określone w plikach PostgreSQL pg_hba.conf i Wildfly standalone.xml conf, które wyjaśniono powyżej. pam_login_attribute domyślnie to uid. Zachęcamy do przejrzenia plików /etc/pam.d/common-* i zobaczenia, co zmieniło się po instalacji libpam-ldap. Postępując zgodnie z dokumentacją, możesz utworzyć nowy plik o nazwie /etc/pam.d/pgbouncer i zdefiniować tam wszystkie opcje PAM, ale wystarczą domyślne pliki common-*. Zajrzyjmy do /etc/pam.d/common-auth:
auth [success=2 default=ignore] pam_unix.so nullok_secure
auth [success=1 default=ignore] pam_ldap.so use_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
Unix passwd zostanie sprawdzone jako pierwsze, a jeśli to się nie powiedzie, zostanie sprawdzony LDAP, więc pamiętaj, że będziesz musiał usunąć wszelkie lokalne hasła dla tych użytkowników, którzy są zdefiniowani zarówno w lokalnym linux/unix /etc/passwd, jak i w LDAP . Teraz nadszedł czas na ostatni test. Wybierz użytkownika, który jest zdefiniowany na twoim serwerze LDAP, a także utworzony w PostgreSQL i spróbuj uwierzytelnić się z bazy danych (poprzez pgsql -h twój.pgsql.server ), a następnie z pgbouncer (również przez psql -h twój.pgbouncer.server) , a na koniec za pośrednictwem Twojej aplikacji. Właśnie urzeczywistniłeś jeden system bezpieczeństwa dla aplikacji, puli połączeń i PostgreSQL!