Logowanie do narzędzi administracyjnych przez Active Directory: LDAPS czy OIDC
System CI, platforma GitOps, monitoring i baza wiedzy potrafią logować użytkowników kontem domenowym. Bezpośrednie LDAPS i OIDC przez pośrednika, takiego jak Dex lub Keycloak, dają ten sam efekt dla użytkownika i zupełnie inny dla bezpieczeństwa.
Przewodnik Dostęp uprzywilejowany Część 3 z 5
- Konta uprzywilejowane w Active Directory: model warstw bez wdrażania całego PAM-a
- LAPS: unikalne hasło lokalnego administratora na każdej maszynie
- Logowanie do narzędzi administracyjnych przez Active Directory: LDAPS czy OIDC
- Konto awaryjne: dostęp, gdy logowanie przez katalog przestaje działać
- Zdalny dostęp dostawców: bramka z nagrywaniem sesji zamiast VPN dla każdego
Administrator odchodzi z zespołu. Jego konto w Active Directory zostaje wyłączone tego samego dnia i sprawa wydaje się zamknięta. Miesiąc później przegląd pokazuje, że konto lokalne w systemie CI, w monitoringu i w panelu platformy GitOps nadal działa, bo każde z tych narzędzi miało własną listę użytkowników.
Podpięcie narzędzi pod katalog rozwiązuje ten problem, ale sposób podpięcia ma większe znaczenie, niż sugeruje formularz konfiguracji, w którym oba warianty zajmują po jednej zakładce. Różnica dotyczy tego, kto widzi hasło, gdzie można wymusić drugi składnik i ile miejsc trzeba sprawdzić przy odbieraniu dostępu.
Wariant pierwszy: bezpośrednie LDAPS
Narzędzie przyjmuje login i hasło użytkownika, łączy się z kontrolerem domeny i sprawdza je operacją bind. Członkostwo w grupach odczytuje zapytaniem LDAP i na tej podstawie nadaje role. Zalety to prosta konfiguracja, brak dodatkowych komponentów i obsługa w niemal każdym narzędziu.
Wady wynikają z tego, że hasło użytkownika trafia do narzędzia:
- każde narzędzie widzi hasła wszystkich logujących się osób, więc kompromitacja jednego z nich oznacza wyciek haseł domenowych,
- uwierzytelniania wieloskładnikowego nie da się wymusić, bo bind LDAP przyjmuje tylko hasło,
- każde narzędzie potrzebuje konta do zapytań o grupy, a wygaśnięcie jego hasła zatrzymuje logowanie wszystkim,
- zasady sesji, blokady i wylogowania są w każdym narzędziu inne.
Wariant drugi: OIDC przez pośrednika
Narzędzie przekierowuje użytkownika do pośrednika tożsamości przez OpenID Connect. Pośrednik, połączony z Active Directory, sprawdza hasło i zwraca do narzędzia podpisany token z nazwą użytkownika i listą grup. Narzędzie nigdy nie widzi hasła.
Najczęściej spotykane są dwa rozwiązania open source. Dex jest lekkim pośrednikiem bez własnej bazy użytkowników: tłumaczy LDAP na OIDC i nie przechowuje kont. Keycloak jest pełnym dostawcą tożsamości z federacją użytkowników z LDAP, własnymi zasadami sesji i drugim składnikiem (kody jednorazowe, klucze WebAuthn). Trzecią drogą jest chmurowy dostawca tożsamości, do którego konta są synchronizowane z katalogu.
| Cecha | Bezpośrednie LDAPS | OIDC przez Dex | OIDC przez Keycloak lub Entra ID |
|---|---|---|---|
| Kto widzi hasło | każde narzędzie | tylko Dex | tylko dostawca tożsamości |
| Drugi składnik | nie | nie, konektor LDAP sprawdza tylko hasło | tak, wymuszany w jednym miejscu |
| Konta do wiązania z katalogiem | po jednym na narzędzie | jedno | jedno lub żadne przy synchronizacji |
| Dodatkowe elementy | brak | jedna usługa bez bazy danych | serwer z bazą danych albo usługa chmurowa |
| Kiedy wybrać | jedno lub dwa narzędzia | kilka narzędzi, platforma Kubernetes | wymagane MFA, wiele narzędzi |
Dla jednego czy dwóch narzędzi w małym zespole bezpośrednie LDAPS jest akceptowalnym startem, pod warunkiem weryfikacji certyfikatu i osobnego konta do wiązania. Przy kilku narzędziach pośrednik zaczyna się opłacać, bo hasła trafiają do jednego miejsca, a nie do każdego narzędzia. Gdy wymagane jest MFA, potrzebny jest dostawca, który je obsługuje. Przejście z LDAPS na OIDC nie wymaga zmian w katalogu: grupy i konwencja nazw zostają te same, zmienia się tylko miejsce, w którym sprawdzane jest hasło.
Konektor LDAP w Dex
Poniższa konfiguracja łączy Dex z katalogiem przez LDAPS, wpuszcza tylko włączone konta użytkowników i przekazuje do narzędzi wyłącznie grupy z prefiksem GG-, żeby token nie zawierał setek nieistotnych grup.
connectors:
- type: ldap
id: ad
name: Active Directory
config:
# Nazwa domeny zamiast adresu jednego kontrolera
host: corp.example.com:636
# Certyfikat wewnętrznego urzędu certyfikacji
rootCA: /etc/dex/tls/corp-root-ca.pem
# Konto do wiązania, hasło ze zmiennej środowiskowej
bindDN: CN=svc-dex-bind,OU=Service Accounts,DC=corp,DC=example,DC=com
bindPW: $LDAP_BIND_PASSWORD
usernamePrompt: Login domenowy
userSearch:
baseDN: OU=Users,DC=corp,DC=example,DC=com
# Tylko włączone konta użytkowników
filter: "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
username: sAMAccountName
idAttr: DN
emailAttr: userPrincipalName
nameAttr: displayName
preferredUsernameAttr: sAMAccountName
groupSearch:
baseDN: OU=Groups,DC=corp,DC=example,DC=com
filter: "(&(objectClass=group)(cn=GG-*))"
userMatchers:
- userAttr: DN
groupAttr: member
nameAttr: cn
Niektóre platformy, na przykład Argo CD, mają Dex wbudowany. Konfiguracja konektora jest wtedy taka sama, tylko trafia do ich pliku ustawień, a hasło konta do wiązania do ich sekretu.
Uprawnienia z grup
Niezależnie od wariantu uprawnienia wynikają z członkostwa w grupach katalogu, a nie z ręcznych przypisań w narzędziu. Sprawdza się prosta konwencja: osobna grupa na każdą rolę w każdym narzędziu, z nazwą mówiącą, co daje, na przykład GG-GitOps-Admins, GG-GitOps-ReadOnly, GG-Grafana-Editors. Członkostwo w grupie jest jedynym sposobem uzyskania roli, a przegląd uprawnień sprowadza się do przeglądu grup.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
# Bez policy.default: kto nie należy do żadnej grupy, nie ma dostępu
scopes: '[groups]'
policy.csv: |
g, GG-GitOps-Admins, role:admin
g, GG-GitOps-ReadOnly, role:readonly
Konto do wiązania
Pośrednik potrzebuje konta, którym wyszukuje użytkowników i grupy. Wystarcza zwykłe konto domenowe, bo uwierzytelnieni użytkownicy domyślnie mogą czytać atrybuty potrzebne do logowania. Konto nie należy do żadnej grupy poza Domain Users, ma odmowę logowania lokalnego i przez pulpit zdalny, a w opisie właściciela i datę następnej zmiany hasła.
Hasło ma co najmniej 30 losowych znaków i jest zmieniane według planu, na przykład raz w roku, razem z sekretem w pośredniku. Konto z hasłem, które wygasa samo w nieprzewidzianym momencie, zatrzymuje logowanie do wszystkich narzędzi naraz.
Pułapki wdrożeniowe
- Certyfikat kontrolera. LDAPS działa tylko wtedy, gdy kontroler domeny ma certyfikat do uwierzytelniania serwera. Nazwa użyta w konfiguracji musi znaleźć się w polu SAN certyfikatu, inaczej weryfikacja się nie powiedzie.
- Adres kontrolera. Konfiguracja wskazuje nazwę domeny albo kilka kontrolerów, nigdy adres IP jednego z nich. Awaria tego kontrolera nie może zatrzymać logowania.
- Opóźnienie odebrania dostępu. Token wydany przez pośrednika jest ważny do wygaśnięcia. W Dex czas życia tokenu ustawia
expiry.idTokens(domyślnie 24 godziny). Dla narzędzi z dostępem do produkcji rozsądna wartość to godzina. - Konto lokalne. Lokalny administrator narzędzia zostaje, wyłączony z codziennej pracy i zabezpieczony jako dostęp awaryjny na wypadek niedostępności katalogu lub pośrednika.
Sprawdź u siebie
Dostępność portu 636 na każdym kontrolerze domeny. Dobry wynik to True przy każdym kontrolerze; False oznacza brak certyfikatu na kontrolerze albo zablokowany port.
foreach ($dc in Get-ADDomainController -Filter *) {
[pscustomobject]@{
DC = $dc.HostName
LDAPS = Test-NetConnection -ComputerName $dc.HostName -Port 636 -InformationLevel Quiet
}
}
Certyfikat widziany przez narzędzie. W polu subjectAltName musi być nazwa z konfiguracji, wystawcą powinien być wewnętrzny urząd certyfikacji, a data notAfter odległa o co najmniej miesiąc. Drugie polecenie sprawdza weryfikację z certyfikatem urzędu; dobry wynik to Verify return code: 0 (ok).
openssl s_client -connect corp.example.com:636 -servername corp.example.com </dev/null 2>/dev/null |
openssl x509 -noout -subject -issuer -enddate -ext subjectAltName
openssl s_client -connect corp.example.com:636 -CAfile corp-root-ca.pem </dev/null 2>/dev/null |
grep 'Verify return code'
Zapytanie kontem do wiązania przez LDAPS, z uwzględnieniem grup zagnieżdżonych. Wynik powinien zawierać wszystkich członków grupy. Błąd Can't contact LDAP server przy otwartym porcie zwykle oznacza brak zaufania do certyfikatu.
LDAPTLS_CACERT=corp-root-ca.pem ldapsearch -x -H ldaps://corp.example.com:636 \
-D 'svc-dex-bind@corp.example.com' -W \
-b 'OU=Users,DC=corp,DC=example,DC=com' \
'(memberOf:1.2.840.113556.1.4.1941:=CN=GG-GitOps-Admins,OU=Groups,DC=corp,DC=example,DC=com)' \
sAMAccountName
Stan konta do wiązania. Dobry wynik to puste MemberOf (Domain Users jest grupą podstawową i się tu nie pokazuje) oraz PasswordLastSet w okresie zaplanowanej rotacji.
Get-ADUser -Identity svc-dex-bind -Properties PasswordLastSet, PasswordNeverExpires, MemberOf |
Select-Object SamAccountName, Enabled, PasswordLastSet, PasswordNeverExpires, MemberOf