Przejdź do treści
Lekcja 81 z 117

Kurs › Moduł 9 › Lekcja 81

Konta z jednego miejsca: LDAP i Active Directory

⏱ ok. 12 min czytania· moduł 9: Użytkownicy i bezpieczeństwo

Po tej lekcji: kursant rozumie, czym jest katalog LDAP i Active Directory, przeszukuje go ldapsearch, wie, jak /etc/nsswitch.conf i SSSD sprawiają, że konta z katalogu działają jak lokalne, konfiguruje sssd.conf, dołącza serwer do domeny przez realm discover i realm join, ustawia profil przez authselect i włącza automatyczne zakładanie folderów domowych.

Wstęp

Firma ma pięćdziesiąt serwerów. Przychodzi Ania. Admin loguje się na każdy i robi useradd. Pięćdziesiąt razy. Za rok Ania odchodzi i trzeba ją usunąć z pięćdziesięciu maszyn. Admin zapomina o trzech. Za dwa lata audytor pyta, czemu była pracownica ma aktywne konto na serwerze z danymi klientów.

Dlatego w firmach konta trzyma się w jednym miejscu. Jedno konto Ani, jedno hasło, wszędzie działa. Wyłączasz ją w jednym miejscu, znika ze wszystkich serwerów naraz. Dziś podłączamy do tego Linuksa.

Katalog, czyli centralna kadrowa

Pamiętasz księgę meldunkową /etc/passwd z lekcji 78? Każdy serwer ma własną. To jak firma, w której każde piętro prowadzi swoją listę pracowników. Katalog to jedna centralna kadrowa dla całej firmy. Wszystkie piętra dzwonią tam i pytają: „czy Jan Kowalski u nas pracuje, jaki ma numer i do których działów należy?”

LDAP, czyli Lightweight Directory Access Protocol, to język tych telefonów. Sposób zadawania pytań katalogowi. Najpopularniejsze serwery mówiące po LDAP to OpenLDAP, 389 Directory Server, a w firmach z Windowsami przede wszystkim Active Directory Microsoftu. AD to katalog LDAP, do tego Kerberos do sprawdzania haseł i DNS, żeby się wzajemnie znajdować. W świecie Red Hata jest jeszcze FreeIPA, czyli Red Hat Identity Management, ale zasada jest ta sama.

Dane w katalogu układają się w drzewo, jak adres pocztowy czytany od końca:

uid=jkowalski,ou=ludzie,dc=firma,dc=lan

Czytaj od prawej: domena firma.lan (dc to domain component), w niej dział ludzie (ou to organizational unit), w nim Jan, którego login (uid) to jkowalski. Taki pełny adres to DN, distinguished name. Polska, Kraków, ulica, numer mieszkania.

Zaglądamy do katalogu: ldapsearch

Na potrzeby lekcji postawiłem na maszynie testowej mały serwer OpenLDAP na porcie 3890 i wrzuciłem do niego Jana Kowalskiego i grupę admini. Klient to pakiet [Ubuntu] ldap-utils, [RHEL] openldap-clients.

ldapsearch -x -H ldap://127.0.0.1:3890 -b dc=firma,dc=lan "(uid=jkowalski)" cn uidNumber homeDirectory loginShell
# jkowalski, ludzie, firma.lan
dn: uid=jkowalski,ou=ludzie,dc=firma,dc=lan
cn: Jan Kowalski
uidNumber: 10001
homeDirectory: /home/jkowalski
loginShell: /bin/bash

# search result
search: 2
result: 0 Success

# numEntries: 1

-x to proste logowanie zamiast Kerberosa. -H to adres serwera. -b to „base”, od którego miejsca drzewa szukać. W nawiasie filtr: szukam wpisu, którego uid to jkowalski. Na końcu lista pól, które chcę zobaczyć. Bez niej dostaniesz wszystko.

Znajome? uidNumber, homeDirectory, loginShell. To dokładnie te same pola co w /etc/passwd, tylko z nazwami zamiast pozycji między dwukropkami. Katalog jest księgą meldunkową na sterydach.

Grupy:

ldapsearch -x -LLL -H ldap://127.0.0.1:3890 -b dc=firma,dc=lan "(objectClass=posixGroup)" cn memberUid
dn: cn=admini,ou=grupy,dc=firma,dc=lan
cn: admini
memberUid: jkowalski

-LLL wycina komentarze. Czysty wynik do skryptów.

Sprawdzenie hasła, czyli „bind”, zalogowanie się do katalogu jako Jan:

ldapwhoami -x -H ldap://127.0.0.1:3890 -D uid=jkowalski,ou=ludzie,dc=firma,dc=lan -W
dn:uid=jkowalski,ou=ludzie,dc=firma,dc=lan

Ze złym hasłem:

ldap_bind: Invalid credentials (49)

-W pyta o hasło, zamiast brać je z linii poleceń, gdzie zostałoby w historii. W Active Directory pytasz tak samo, tylko login wygląda jak jan@firma.lan, a drzewo jak dc=firma,dc=lan z działami w stylu ou=Users.

Recepcjonistka z listą: /etc/nsswitch.conf

Mamy katalog. Ale ls, id czy sshd nie umieją mówić po LDAP. One wołają bibliotekę systemową: „podaj mi użytkownika o numerze 10001”. A biblioteka sprawdza w /etc/nsswitch.conf, gdzie szukać. To recepcjonistka z kartką: „najpierw szukaj w segregatorze na biurku, potem zadzwoń do centrali”.

Przed podłączeniem katalogu:

passwd:         files
group:          files
shadow:         files

Po zainstalowaniu na Ubuntu pakietu libnss-sss:

passwd:         files sss
group:          files sss
shadow:         files sss

files to segregator, czyli /etc/passwd. sss to telefon do SSSD, o którym za chwilę. Kolejność ma znaczenie: lokalne konta wygrywają. Dlatego lokalny root zawsze działa, nawet gdy katalog leży.

[Ubuntu] Pakiet dopisał sss sam. [RHEL] Tego pliku nie edytujesz ręcznie, robi to authselect.

Efekt:

getent passwd jkowalski
id jkowalski
grep jkowalski /etc/passwd; echo "grep: $?"
jkowalski:*:10001:10000:Jan Kowalski:/home/jkowalski:/bin/bash
uid=10001(jkowalski) gid=10000(admini) groups=10000(admini)
grep: 1

Jan istnieje dla systemu, ma numer, grupę, folder i powłokę, a w /etc/passwd go nie ma. Teraz widzisz, czemu w lekcji 78 męczyłem Cię getent zamiast grep. grep przeszukuje segregator. getent pyta recepcjonistkę, a ona dzwoni, gdzie trzeba.

SSSD: sekretarka z notesem

Każdy program mógłby dzwonić do katalogu sam. Ale wtedy każde ls -l to kilkaset zapytań przez sieć, a jak sieć padnie, nikt się nie zaloguje. Potrzebny jest pośrednik.

SSSD, System Security Services Daemon, to sekretarka, która rozmawia z centralą w imieniu całego piętra. I ma notes. Raz zapytała o Jana, zapisała odpowiedź i przez jakiś czas nie dzwoni ponownie. A jak centrala nie odbiera, zagląda do notesu.

Konfiguracja to /etc/sssd/sssd.conf. Wersja dla zwykłego LDAP:

[sssd]
services = nss, pam
domains = firma.lan

[domain/firma.lan]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldap://127.0.0.1:3890
ldap_search_base = dc=firma,dc=lan
cache_credentials = true

services to okienka, które sekretarka obsługuje: nss odpowiada na pytania „kto to jest”, pam na pytania „czy hasło się zgadza”. id_provider mówi, skąd brać dane o kontach, auth_provider, kto sprawdza hasła. cache_credentials pozwala zalogować się z zapamiętanym hasłem, kiedy katalog jest nieosiągalny.

Pakiety: [Ubuntu] sssd-ldap sssd-tools libnss-sss libpam-sss, [RHEL] sssd sssd-ldap, a do Active Directory sssd-ad. Potem sudo systemctl enable --now sssd.

Narzędzia diagnostyczne:

sudo sssctl domain-status firma.lan
Online status: Online

Active servers:
LDAP: 127.0.0.1
sudo sssctl user-checks jkowalski
pam_acct_mgmt: Success
...
SSSD nss user lookup result:
 - user name: jkowalski
 - user id: 10001
 - group id: 10000
 - home directory: /home/jkowalski
 - shell: /bin/bash

A teraz test notesu. Wyłączam serwer LDAP:

sudo sssctl domain-status firma.lan -o
getent passwd jkowalski
Online status: Offline
jkowalski:*:10001:10000:Jan Kowalski:/home/jkowalski:/bin/bash

Centrala nie odbiera, a Jan dalej istnieje. Przez PAM zalogował się nawet zapamiętanym hasłem. Laptop w pociągu bez sieci, a Ty logujesz się kontem firmowym. To właśnie dzięki temu.

Notes ma też wadę: dopisałeś Jana w katalogu do nowej grupy, a serwer dalej pokazuje starą listę. Wtedy:

sudo sss_cache -E

Unieważnia cały notes. Następne pytanie pójdzie do centrali.

Folder domowy przy pierwszym logowaniu

Jan ma w katalogu wpisany folder /home/jkowalski. Ale na tym serwerze nikt go nie założył. Pięćdziesiąt serwerów, pięćdziesiąt folderów do założenia ręcznie? Nie. Moduł PAM pam_mkhomedir tworzy go przy pierwszym logowaniu, z wyprawką z /etc/skel, którą znasz.

ls -d /home/jkowalski
echo 'haslo-jana' | sudo pamtester proba-ldap jkowalski authenticate acct_mgmt open_session
ls -la /home/jkowalski
ls: cannot access '/home/jkowalski': No such file or directory
pamtester: successfully authenticated
pamtester: account management done.
Creating directory '/home/jkowalski'.
pamtester: successfully opened a session
drwx------  2 jkowalski admini 4096 Nov  3 16:33 .
-rw-------  1 jkowalski admini  220 Nov  3 16:33 .bash_logout
-rw-------  1 jkowalski admini 3771 Nov  3 16:33 .bashrc
-rw-------  1 jkowalski admini  807 Nov  3 16:33 .profile

proba-ldap to znowu moja testowa usługa PAM, żeby nie psuć prawdziwego logowania. Hasło z katalogu, sesja otwarta, folder założony.

Na prawdziwym serwerze włączasz to tak:

sudo pam-auth-update --enable mkhomedir                          # Ubuntu
sudo authselect enable-feature with-mkhomedir                    # RHEL
sudo systemctl enable --now oddjobd                              # RHEL

[RHEL] Na RHEL folder zakłada mała usługa oddjobd z pakietu oddjob-mkhomedir, dlatego musi działać.

authselect: gotowe zestawy na RHEL

Na Ubuntu klocki PAM i NSS dokładają pakiety i pam-auth-update. Na RHEL rządzi authselect. To jak menu w restauracji: zamiast samemu składać danie z dwudziestu składników, wybierasz zestaw i dodatki.

authselect list
authselect current

Zestawy, czyli profile, są trzy: local dla kont tylko lokalnych, domyślny, sssd dla kont z katalogu przez SSSD i winbind dla bezpośredniej integracji z AD przez Sambę. Dodatki to features: with-mkhomedir, with-faillock z lekcji 80, with-sudo do reguł sudo trzymanych w katalogu.

sudo authselect select sssd with-mkhomedir with-faillock

Ta jedna komenda przepisuje /etc/nsswitch.conf i pliki /etc/pam.d/system-auth oraz password-auth. A w tych plikach na górze stoi:

# Do not modify this file manually, use authselect instead. Any user changes will be overwritten.

Traktuj to dosłownie. Ręczne poprawki znikną przy następnym authselect. Ten sam los spotyka ręczną poprawkę w /etc/nsswitch.conf.

Active Directory: realm

Ręczne pisanie sssd.conf dla AD jest długie: Kerberos, konto komputera w domenie, mapowanie identyfikatorów. Dlatego jest realm, z pakietu realmd. Ten robi to wszystko za Ciebie.

Pakiety: [Ubuntu] sssd-ad sssd-tools realmd adcli, [RHEL] realmd oddjob oddjob-mkhomedir sssd adcli krb5-workstation samba-common-tools.

Krok pierwszy: znaleźć domenę.

realm discover firma.lan

U mnie, bez prawdziwego kontrolera domeny:

realm: No such realm found: firma.lan

realm szuka kontrolera przez DNS, w specjalnych rekordach SRV, które AD sam publikuje. Jeśli Twój serwer nie używa DNS-a domeny, nic nie znajdzie. To najczęstsza przyczyna kłopotów: najpierw DNS, potem domena.

Z prawdziwym AD dostaniesz między innymi:

ad1.example.com
  type: kerberos
  realm-name: AD1.EXAMPLE.COM
  configured: no
  server-software: active-directory
  client-software: sssd

configured: no, czyli jeszcze nie jesteśmy członkiem. Krok drugi:

sudo realm join -U administrator ad1.example.com

-U to konto w AD, które może dołączać komputery do domeny. Pyta o jego hasło. Potem realm zakłada w AD konto komputera, zapisuje klucz Kerberosa tego serwera, pisze sssd.conf i podłącza SSSD do NSS i PAM. [RHEL] Również przez authselect. [Ubuntu] Nie włącza zakładania folderów, więc pam-auth-update --enable mkhomedir robisz sam.

Sprawdzenie:

realm list
id administrator@ad1.example.com
getent passwd jan@ad1.example.com

Zwróć uwagę na @domena w loginie. realm join ustawia use_fully_qualified_names = True, więc użytkownicy AD mają pełne nazwy, a foldery domyślnie trafiają do /home/login@domena. To chroni przed sytuacją, w której lokalny jan i domenowy jan to dwie różne osoby z tym samym loginem.

Krok trzeci, najważniejszy dla bezpieczeństwa: kto może się logować. Po join zwykle mogą wszyscy z domeny. Cała firma, z księgowością i stażystami, na serwerze z bazą danych. Zawężasz:

sudo realm deny --all
sudo realm permit -g admini-linux@ad1.example.com

Najpierw nikt, potem tylko grupa. Zasada najmniejszych uprawnień z lekcji 79 w wersji dla całej firmy. A sudo dla adminów z domeny dajesz jak zwykle w /etc/sudoers.d, grupą: %admini-linux@ad1.example.com ALL=(ALL) ALL.

Jak to działa pod spodem

Wpisujesz id jkowalski. Program woła funkcję biblioteki C getpwnam(). Biblioteka czyta /etc/nsswitch.conf, widzi files sss, szuka w /etc/passwd, nie znajduje, ładuje moduł libnss_sss i przez gniazdo lokalne pyta proces sssd_nss. Ten zagląda do pamięci podręcznej w /var/lib/sss/db. Jeśli wpis jest świeży, odpowiada od razu. Jeśli nie, prosi proces sssd_be, czyli backend domeny, który pyta serwer LDAP albo AD przez sieć i odświeża notes.

Logowanie idzie drugą drogą: sshd pyta PAM, PAM w stosie auth trafia na pam_sss.so, ten pyta sssd_pam, a backend sprawdza hasło. W czystym LDAP przez bind, czyli to, co zrobił ldapwhoami. W AD przez Kerberosa.

Ciekawostka: lista wszystkich użytkowników z getent passwd nie pokazuje Jana. SSSD domyślnie nie wylicza całego katalogu, bo w firmie z dziesięcioma tysiącami ludzi każde takie pytanie pobierałoby wszystkich. Pyta się o konkretne osoby po nazwie albo numerze.

Ściąga flag

KomendaFlagaCo robi
ldapsearch-xproste logowanie (bez Kerberosa)
ldapsearch-H ldap://serweradres serwera (ldaps:// szyfrowany)
ldapsearch-b dc=firma,dc=lanod którego miejsca drzewa szukać
ldapsearch-D dn -Wzaloguj się jako ten DN, zapytaj o hasło
ldapsearch-LLLwynik bez komentarzy
ldapwhoami-x -D dn -Wsprawdza login i hasło w katalogu
getentpasswd loginpyta przez NSS, także katalog
sssctldomain-status domenaczy SSSD widzi katalog
sssctluser-checks loginpełna diagnostyka użytkownika
sssctlconfig-checksprawdza sssd.conf
sss_cache-Eunieważnia notes SSSD
authselectlist / currentprofile / aktualny profil (RHEL)
authselectselect sssd with-mkhomedirprofil SSSD z folderami domowymi
authselectenable-feature with-…włącza dodatek
pam-auth-update--enable mkhomedirfoldery domowe (Ubuntu)
realmdiscover domenaznajduje domenę przez DNS
realmjoin -U admin domenadołącza serwer do domeny
realmlistdo jakich domen należymy
realmdeny --all / permit -g grupakto może się logować
realmleaveodłącza od domeny

⚠ Typowy błąd

Piszesz sssd.conf w edytorze, zapisujesz, uruchamiasz. I nic:

sudo chmod 644 /etc/sssd/sssd.conf
sudo sssd -i
[sssd] [sss_ini_read_sssd_conf] (0x0020): Permission check on config file failed.
[sssd] [confdb_init_db] (0x0020): Cannot convert INI to LDIF [1432158324]: [File ownership and permissions check failed]
[sssd] [main] (0x0010): SSSD couldn't load the configuration database [1432158324]: File ownership and permissions check failed

SSSD odmawia startu, bo plik konfiguracyjny może przeczytać każdy. A w takim pliku często leży hasło konta, którym serwer łączy się z katalogiem. SSSD woli nie ruszyć, niż pracować z sejfem otwartym na korytarz. Dokumentacja Red Hata i Ubuntu mówi to samo: uprawnienia 0600, właściciel root:root.

sudo chown root:root /etc/sssd/sssd.conf
sudo chmod 600 /etc/sssd/sssd.conf
sudo sssctl config-check
sudo systemctl restart sssd

Na systemie z systemd ten sam błąd zobaczysz w journalctl -u sssd. A sssctl config-check przy okazji wyłapie literówki w nazwach opcji, zanim SSSD w ogóle wstanie.

✎ Zadanie

Załóż użytkownika stazysta w grupie praktykanci, wymuś zmianę hasła przy pierwszym logowaniu i pozwól mu przez sudo tylko na restart jednej usługi. Sprawdź sudo -l -U stazysta i getent passwd stazysta.

Pokaż rozwiązanie

Stażysta, grupa, hasło do zmiany i sudo tylko na jedną rzecz. U mnie wyszło tak:

sudo groupadd praktykanci
sudo useradd -m -s /bin/bash -g praktykanci stazysta
sudo passwd stazysta          # hasło startowe, podajesz je stażyście osobnym kanałem
sudo chage -d 0 stazysta      # data ostatniej zmiany hasła = 1970, więc musi zmienić od razu
sudo visudo -f /etc/sudoers.d/stazysta

W pliku jedna linijka:

stazysta ALL=(root) /usr/bin/systemctl restart nginx.service

I sprawdzenie:

sudo chage -l stazysta | head -2
sudo -l -U stazysta
getent passwd stazysta
Last password change					: password must be changed
Password expires					: password must be changed
User stazysta may run the following commands on vm:
    (root) /usr/bin/systemctl restart nginx.service
stazysta:x:1001:1001::/home/stazysta:/bin/bash

UID i GID wyszły 1001, bo to pierwsze konto i pierwsza grupa założone po moim. Jeśli masz już więcej kont, u Ciebie będzie wyższy numer. Ważne są trzy rzeczy: pełna ścieżka do systemctl, pełna nazwa usługi i visudo, a nie zwykły edytor. Bez pełnej ścieżki stażysta podłoży własny program o nazwie systemctl i dostanie roota. Bez visudo jedna literówka i nikt już nie dostanie roota, łącznie z Tobą. Stażysta może teraz zrestartować nginx o trzeciej w nocy. I nic więcej. Czyli dokładnie tyle, ile powinien móc stażysta o trzeciej w nocy.

Co dalej

Konta, grupy, sudo i katalog masz za sobą. W następnej lekcji drugi strażnik Linuksa, który pilnuje nawet roota: SELinux. Tryby, etykiety, restorecon, semanage i czytanie odmów bez strachu. I bez wyłączania go przy pierwszym problemie.