janol.pl_

Wdrożenie

OpenLDAP master-master z TLS i ppolicy

LDAP jest w firmie tak centralnym punktem, że jego awaria oznacza, że nikt się nigdzie nie zaloguje — ani do VPN-a, ani do Wildfly, ani do systemu, który akurat rano potrzebuje działu księgowego. Jeden serwer to jeden punkt awarii, którego nie akceptuję przy usłudze tej rangi. Replikacja master-master w OpenLDAP daje dwa równorzędne serwery, z których każdy przyjmuje zapisy — bez roli „master do zapisu, slave tylko do odczytu”, którą trzeba by ręcznie przełączać przy awarii.

Dlaczego master-master, nie master-slave

Konfiguracja master-slave (provider-consumer) jest prostsza i w wielu miejscach wystarczająca — jeden serwer zapisuje, reszta tylko czyta i serwuje zapytania. Problem pojawia się przy awarii mastera: coś musi ręcznie albo automatycznie przepiąć zapisy na inny węzeł, a to dodatkowa logika i dodatkowy punkt, który może zawieść w najgorszym momencie. Master-master eliminuje ten krok — obie strony przyjmują zapisy równolegle, a syncrepl w trybie mirrormode wymienia zmiany między nimi w obie strony.

Cena tej decyzji Master-master oznacza możliwość konfliktów zapisu — dwie zmiany na tym samym wpisie na obu serwerach niemal jednocześnie. OpenLDAP rozstrzyga je po numerze CSN (Change Sequence Number), czyli w praktyce „wygrywa późniejszy timestamp”. To akceptowalne przy ruchu, jaki generuje typowy katalog firmowy, ale nie nadaje się do środowiska z bardzo częstymi, współbieżnymi zapisami na tych samych obiektach.

syncrepl w trybie mirrormode

Każdy z dwóch serwerów ma w konfiguracji wpis syncrepl wskazujący na drugiego, plus mirrormode on, które mówi OpenLDAP-owi, że to nie jest zwykła replikacja provider-consumer:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001 provider=ldaps://ldap01.internal:636
  bindmethod=simple binddn="cn=repl,dc=example,dc=com"
  credentials="<hasło>" searchbase="dc=example,dc=com"
  type=refreshAndPersist retry="5 5 300 +"
  starttls=critical tls_cert=/etc/ldap/certs/ldap02.pem
  tls_key=/etc/ldap/certs/ldap02.key
  tls_cacert=/etc/ldap/certs/ca.pem
-
add: olcMirrorMode
olcMirrorMode: TRUE

Numer rid musi być unikalny na każdym serwerze i różny między obiema stronami — to częsty błąd przy kopiowaniu konfiguracji „z jednego na drugi”: zostawia się ten sam rid po obu stronach i replikacja milczy albo się duplikuje w logach bez jasnego komunikatu błędu.

TLS między replikami — kolejność certyfikatów

Ruch replikacyjny między serwerami niesie hashe haseł i pełną treść katalogu, więc szyfrowanie między nimi nie jest opcjonalne, nawet w sieci wewnętrznej, która teoretycznie jest „zaufana”. Tu trafiłem na pułapkę, która kosztowała mnie więcej czasu niż powinna: kolejność, w jakiej generuje się i wdraża certyfikaty, ma znaczenie.

  • Najpierw własne CA (albo firmowe, jeśli już istnieje) — bez tego każdy serwer wystawiałby sobie certyfikat samopodpisany, którego drugi nie zweryfikuje.
  • Certyfikat dla każdego serwera musi mieć w SAN zarówno FQDN, po którym łączy się drugi replika, jak i ewentualny alias, jeśli klienci LDAP używają innej nazwy niż replikacja.
  • CA cert musi trafić na oba serwery przed pierwszym uruchomieniem syncrepl — inaczej pierwsza próba połączenia kończy się cichym błędem weryfikacji TLS, a w logu slapd widać tylko ogólne „TLS: can't connect”, bez wskazania, że chodzi o brakujące CA.
  • Restart slapd po podmianie certyfikatu jest obowiązkowy — OpenLDAP nie podejmuje nowego certyfikatu na gorąco.
# szybka weryfikacja, że replika faktycznie mówi TLS
openssl s_client -connect ldap02.internal:636 -CAfile /etc/ldap/certs/ca.pem

# log slapd przy problemach z replikacją — poziom -1 to wszystko
slapd -d sync -h "ldap:/// ldaps:///"
Konflikty replikacji Gdy oba serwery przez chwilę nie widzą się nawzajem (restart, blip sieciowy) i w tym czasie ktoś zmieni ten sam atrybut po obu stronach, po odzyskaniu łączności OpenLDAP rozstrzyga konflikt po CSN, ale starsza zmiana po prostu znika bez ostrzeżenia w interfejsie klienta. Jedyny ślad zostaje w logu slapd. Monitoring rozjazdu contextCSN między serwerami to jedyny sensowny sposób, żeby wyłapać to zanim zauważy użytkownik.

Polityki haseł: overlay ppolicy

Sam LDAP nie wymusza niczego w kwestii haseł — długość, rotacja, blokada konta po nieudanych próbach logowania to domena overlay ppolicy, zgodnego z RFC dotyczącym Password Policy dla LDAP. Włącza się go per baza, a konkretne reguły opisuje jako osobny wpis w katalogu:

dn: cn=default,ou=policies,dc=example,dc=com
objectClass: pwdPolicy
objectClass: pwdPolicyChecker
cn: default
pwdAttribute: userPassword
pwdMinLength: 12
pwdMaxAge: 7776000
pwdExpireWarning: 604800
pwdInHistory: 5
pwdMaxFailure: 5
pwdLockoutDuration: 900
pwdLockout: TRUE
pwdMustChange: TRUE
pwdGraceAuthNLimit: 0

W praktyce: hasło minimum 12 znaków, ważność 90 dni (7776000 sekund) z tygodniowym ostrzeżeniem przed wygaśnięciem, pięć ostatnich haseł zapamiętanych żeby nie dało się wracać do starego, blokada konta na 15 minut po pięciu nieudanych próbach. Te liczby dobieramy pod politykę bezpieczeństwa firmy — overlay tylko je egzekwuje, nie narzuca konkretnych wartości.

ppolicy w replikacji master-master

Tu jest druga pułapka. Atrybuty operacyjne ppolicy — liczba nieudanych prób, znacznik czasu blokady — też są replikowane jak zwykłe zmiany w katalogu. Jeśli użytkownik trafi na dwa różne serwery przy kolejnych próbach logowania (typowe za load balancerem), licznik nieudanych prób musi się zgadzać po obu stronach, inaczej konto zablokuje się „niespodziewanie” na jednym serwerze, mimo że administrator widzi inny stan na drugim. W praktyce oznacza to, że replikacja musi nadążać w czasie rzeczywistym — opóźnienie rzędu sekund jest tu nieszkodliwe, ale przy poważniejszych opóźnieniach sieciowych między lokalizacjami warto rozważyć, czy oba serwery faktycznie powinny obsługiwać ruch produkcyjny jednocześnie, czy jeden ma być czysto zapasowy.

Polityka haseł, która istnieje tylko w dokumencie PDF, nie istnieje. ppolicy to ten sam wymóg, ale egzekwowany przez serwer, a nie przez pamięć administratora.

Co bym zrobił inaczej na starcie

  • Ustawiłbym monitoring contextCSN od pierwszego dnia, nie po pierwszym niewyjaśnionym rozjeździe danych.
  • Testowałbym TLS między replikami openssl s_client przed włączeniem syncrepl, a nie po fakcie, kiedy błąd trzeba wyłuskać z logu.
  • Dokumentowałbym rid każdej relacji replikacji w jednym miejscu — przy więcej niż dwóch serwerach łatwo o kolizję numerów.

Oba serwery LDAP stoją u mnie za parą HAProxy w trybie active-active, z health checkiem sprawdzającym, czy dany węzeł w ogóle odpowiada na ldaps:// — bez tego klient mógłby trafić na serwer, który akurat traci połączenie z drugą repliką.

← wszystkie wpisy