Dzisiaj zajmiemy się kerberyzacją LDAP, brzmi to trochę dziwnie, ale sprowadza się do zainstalowania i skonfigurowania klastra składającego się z wielu węzłów (N+) działającego w trybie aktywnym. Klaster ten będzie serwował usługi LDAP i Kerberos dla systemów Linuks. Baza danych kerberosa będzie przechowywana w OpenLDAP, dzięki temu ta usługa również będzie działała jako tzw. multi master. Oprócz samego klastra, skonfigurujemy także system kliencki aby uwierzytelnienie użytkownika odbywało się na poziomie LDAP oraz aby taki użytkownik mógł, korzystając z bileciku (kerberos ticket) poruszać się swobodnie pomiędzy systemami. Ćwiczenie to ma na celu przygotowanie systemu autoryzacji do wykorzystania przy późniejszej instalacji Hadoop.
Mimo, że nie ma ograniczeń co do ilości węzłów, to ja tutaj zastosuję tylko dwa, działające w pełnej replikacji. W ramach przygotowań musicie posiadać serwer DNS oraz mieć spójny czas w całym środowisku (NTP). Jako system operacyjny wybrałem CentOS 7, ale dla samej konfiguracji klastra nie ma to większego znaczenia, równie dobrze może to być Ubuntu 18. Na potrzeby tej instalacji wygenerowałem nową domenę hdfs.lab oraz dwa serwery HDC1 i HDC2 o IP 192.168.30.11,12. Nowa domena wzięła się stąd, że Active Directory to też Kerberos, i w sieci nie mogą funkcjonować dwa ośrodki obsługujące tę samą domenę (u mnie: piszki.lab). Zaczynamy od zainstalowania wszystkich potrzebnych pakietów (na obydwu węzłach):
yum install krb5-server krb5-server-ldap krb5-workstation pam_krb5 openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel nss-pam-ldapd cyrus-sasl-gssapi
Do tego dojdzie kilka dodatkowych pakietów dla spełnienia zależności. Zaczniemy oczywiście od skonfigurowania OpenLDAP w trybie multimaster, proponuję przygotować sobie katalog o nazwie /root/ldap w którym utworzymy szereg plików ldif. Będziemy stawiać tylko i wyłącznie OpenLDAP w wersji zabezpieczonej TLS (i dostęp ogólny i replikacja), dlatego na starcie potrzebujecie certyfikatu. Ja wygenerowałem jeden certyfikat z nazwami SAN dla obu serwerów (o certyfikatach będzie jeszcze dalej). Jako załącznik do tego artykułu znajdziecie mój skrypt camenu za pomocą którego możecie stworzyć własne CA oraz wygenerować dowolny certyfikat. Od tego momentu konfigurację tworzymy jednocześnie na obydwu węzłach, w chwili gdy włączymy replikację, dalsza konfiguracja będzie odbywała się tylko na węźle pierwszym.
Zawartość pliku /etc/sysconfig/slapd :
SLAPD_URLS=”ldapi:/// ldap:/// ldaps:///”
SLAPD_LDAPS=yes
Do pliku /etc/rsyslog.conf dodajemy linię:
local4.* /var/log/openldap/ldap.log
Zawartość pliku /etc/logrotate.d/openldap
/var/log/openldap/ldap.log {
daily
rotate 10
missingok
notifempty
sharedscripts
postrotate
/bin/kill -HUP `cat /var/run/rsyslogd.pid 2> /dev/null` 2> /dev/null || true
endscript
}
Kopiujemy plik:
cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
chown ldap:ldap /var/lib/ldap/DB_CONFIG
Zawartość pliku /etc/openldap/ldap.conf :
BASE dc=hdfs,dc=lab
URI ldap://hdc1.hdfs.lab ldaps://hdc1.hdfs.lab #(i odpowiednio dla hdc2.hdfs.lab)
TLS_CACERTDIR /etc/openldap/certs
TLS_CACERT /etc/openldap/certs/ca.crt
TLS_REQCERT allow
SASL_NOCANON on
Restartujemy usługę rsyslog, startujemy usługę slapd (w /var/log/openldap powinien pojawić się plik ldap.log). Certyfikaty kopiujemy do /etc/openldap/certs (w moim przypadku są to dc.crt, dc.key i ca.crt). Jesteśmy gotowi, możemy działać dalej (slapd będzie nasłuchiwał na portach 88 i 636, odpowiednio skonfigurujcie firewalld).
Tworzymy pierwszy plik LDIF (ładujemy moduł do synchronizacji):
[root@hdc1 ldap]# cat 01_syncprov.ldif
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib64/openldap
olcModuleLoad: syncprov.la
Tworzymy drugi plik LDIF (olcServerID jest inne na każdym serwerze!):
[root@hdc1 ldap]# cat 02_config.ldif
dn: cn=config
changetype: modify
add: olcServerID
olcServerID: 1
Generujemy hasło poleceniem slappasswd i wgrywamy je do trzeciego pliku LDIF :
[root@hdc1 ldap]# cat 03_rootpw.ldif
dn: olcDatabase={0}config,cn=config
add: olcRootPW
olcRootPW: {SSHA}AyqlgqpsP6ITRWdZlqDBMWj/rP/Xy20W
Tworzymy czwarty plik LDIF (zastępujemy istniejące certyfikaty naszymi własnymi, uwaga: plik z kluczem (dc.key) musi mieć własność grupy “ldap” oraz uprawnienia 640):
[root@hdc1 ldap]# cat 04_ssl.ldif
dn: cn=config
changetype: modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/dc.key
–
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/dc.crt
–
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/ca.crt
–
replace: olcTLSCipherSuite
olcTLSCipherSuite: TLSv1+RSA:!EXPORT:!NULL
–
replace: olcTLSVerifyClient
olcTLSVerifyClient: never
Tworzymy piąty plik LDIF (konfiguracja replikacji pomiędzy węzłami, hasło XXXXX to oczywiście te, które wygenerowaliście wyżej poleceniem slappasswd):
[root@hdc1 ldap]# cat 05_configrep.ldif
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1 ldaps://hdc1.hdfs.lab
olcServerID: 2 ldaps://hdc2.hdfs.lab
dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001 provider=ldaps://hdc1.hdfs.lab binddn=”cn=config”
bindmethod=simple credentials=”XXXXX” searchbase=”cn=config”
type=refreshAndPersist retry=”5 5 300 5″ timeout=1
olcSyncRepl: rid=002 provider=ldaps://hdc2.hdfs.lab binddn=”cn=config”
bindmethod=simple credentials=”XXXXX” searchbase=”cn=config”
type=refreshAndPersist retry=”5 5 300 5″ timeout=1
–
add: olcMirrorMode
olcMirrorMode: TRUE
W dalszej kolejności wgrywamy po kolei pliki na obydwu serwerach, w rezultacie otrzymamy klaster OpenLDAP replikujący pomiędzy sobą dane.
W logu zobaczymy prawidłowe połączenia między serwerami (dane jeszcze nie są replikowane, tylko konfiguracja).
Od tego momentu wszystkie kroki wykonujemy na jednym węźle (!!!), ustawienia zostaną zreplikowane.
Tworzymy szósty plik LDIF (wskazanie bazy do replikacji, ja używam {2}hdb):
[root@hdc1 ldap]# cat 06_syncdb.ldif
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
Tworzymy siódmy plik LDIF (hasło olcRootPW i credentials to to samo co w plikach 03 i 05):
[root@hdc1 ldap]# cat 07_ldapdomain.ldif
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=hdfs,dc=lab
–
replace: olcRootDN
olcRootDN: cn=ldapadm,dc=hdfs,dc=lab
–
replace: olcRootPW
olcRootPW: {SSHA}AyqlgqpsP6ITRWdZlqDBMWj/rP/Xy20W
–
add: olcSyncRepl
olcSyncRepl: rid=004 provider=ldaps://hdc1.hdfs.lab binddn=”cn=ldapadm,dc=hdfs,dc=lab” bindmethod=simple
credentials=”XXXXX” searchbase=”dc=hdfs,dc=lab” type=refreshOnly
interval=00:00:00:10 retry=”5 5 300 5″ timeout=1
olcSyncRepl: rid=005 provider=ldaps://hdc2.hdfs.lab binddn=”cn=ldapadm,dc=hdfs,dc=lab” bindmethod=simple
credentials=”XXXXX” searchbase=”dc=hdfs,dc=lab” type=refreshOnly
interval=00:00:00:10 retry=”5 5 300 5″ timeout=1
–
add: olcDbIndex
olcDbIndex: entryUUID eq
–
add: olcDbIndex
olcDbIndex: entryCSN eq
–
add: olcMirrorMode
olcMirrorMode: TRUE
W kolejnym kroku wgrywamy obydwa pliki, od tego momentu mamy włączoną replikację bazy danych OpenLDAP (będzie to wyraźnie widać w logu, gdzie pojawią się cyklicznie wpisy z replikacji).
Widzimy, że replikacja idzie zaszyfrowanym kanałem (TLS).
Podstawowa konfiguracja klastra OpenLDAP za nami, teraz skonfigurujemy domenę oraz wszystko to co jest nam potrzebne.
Tworzymy ósmy plik LDIF (ograniczenie replikacji do konta ldapadm):
[root@hdc1 ldap]# cat 08_monitor.ldif
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base=”gidNumber=0+uidNumber=0,cn=peercred,cn=external, cn=auth” read by dn.base=”cn=ldapadm,dc=hdfs,dc=lab” read by * none
W kolejnym kroku wgramy podstawowe schematy do OpenLDAP (definicje parametrów):
ldapadd –Y EXTERNAL –H ldapi:/// –f /etc/openldap/schema/cosine.ldif (nis, inetorgperson, java, misc)
Tworzymy dziewiąty plik LDIF (zawartość katalogu, administrator oraz grupy, zawartość może być dowolna):
[root@hdc1 ldap]# cat 09_base.ldif
dn: dc=hdfs,dc=lab
dc: hdfs
objectClass: top
objectClass: domain
dn: cn=ldapadm,dc=hdfs,dc=lab
objectClass: organizationalRole
cn: ldapadm
description: LDAP Manager
dn: ou=Users,dc=hdfs,dc=lab
objectClass: organizationalUnit
ou: Users
dn: ou=Groups,dc=hdfs,dc=lab
objectClass: organizationalUnit
ou: Groups
dn: ou=Services,dc=hdfs,dc=lab
objectClass: organizationalUnit
ou: Groups
Tworzymy dziesiąty plik (jest to definicja memberOf, ten parametr jest wymagany często przy przeszukiwaniu katalogu przez usługi zewnętrzne):
[root@hdc1 ldap]# cat 10_memberof.ldif
dn: cn=module,cn=config
cn: module
objectclass: olcModuleList
objectclass: top
olcmoduleload: memberof.la
olcmodulepath: /usr/lib64/openldap
dn: olcOverlay={0}memberof,olcDatabase={2}hdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
dn: cn=module,cn=config
cn: module
objectclass: olcModuleList
objectclass: top
olcmoduleload: refint.la
olcmodulepath: /usr/lib64/openldap
dn: olcOverlay={1}refint,olcDatabase={2}hdb,cn=config
objectClass: olcConfig
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
objectClass: top
olcOverlay: {1}refint
olcRefintAttribute: memberof member manager owner
Plik ten wgrywamy poleceniem ldapadd –x –w ‘XXXXX’ –D “cn=ldapadm,dc=hdfs,dc=lab” –f 09_base.ldif (z hasłem).
Od tego momentu mamy funkcjonalny klaster OpenLDAP z pełną synchronizacją danych. W kolejnych krokach przejdziemy do konfiguracji Kerberos.
Ten prosty skrypt bash wygeneruje w katalogu /root/ldap jedenasty plik LDIF:
cp /usr/share/doc/krb5-server-ldap-1.15.1/kerberos.schema /etc/openldap/schema
mkdir /tmp/ldap-kerberos/
echo „include /etc/openldap/schema/kerberos.schema” > /tmp/ldap-kerberos/schema_convert.conf
mkdir /tmp/ldap-kerberos/krb5_ldif
slaptest -f /tmp/ldap-kerberos/schema_convert.conf -F /tmp/ldap-kerberos/krb5_ldif
cp /tmp/ldap-kerberos/krb5_ldif/cn=config/cn=schema/cn\=\{0\}kerberos.ldif /root/ldap/11_kerberos.ldif
FILE=/root/ldap/11_kerberos.ldif
sed -i „s@dn: cn={0}kerberos.*@dn: cn=kerberos,cn=schema,cn=config@g” ${FILE}
sed -i „s@cn: {0}kerberos.*@cn: kerberos@g” ${FILE}
sed -i '/structuralObjectClass: /d’ ${FILE}
sed -i '/creatorsName: cn=config/d’ ${FILE}
sed -i '/modifiersName: cn=config/d’ ${FILE}
sed -i '/createTimestamp: /d’ ${FILE}
sed -i '/modifyTimestamp: /d’ ${FILE}
sed -i '/entryUUID: /d’ ${FILE}
sed -i '/entryCSN: /d’ ${FILE}
Na koniec tworzymy dwunasty plik LDIF (zawierający definicje indeksów):
[root@hdc1 ldap]# cat 12_index.ldif
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq
–
add: olcDbIndex
olcDbIndex: member eq
–
add: olcDbIndex
olcDbIndex: dc eq
–
add: olcDbIndex
olcDbIndex: uidNumber eq
–
add: olcDbIndex
olcDbIndex: gidNumber eq
–
add: olcDbIndex
olcDbIndex: memberUid eq
–
add: olcDbIndex
olcDbIndex: uniqueMember eq
–
add: olcDbIndex
olcDbIndex: krbPrincipalName eq,pres,sub
–
add: olcDbIndex
olcDbIndex: krbPwdPolicyReference eq
Wgranie: ldapmodify -Y EXTERNAL -H ldapi:/// -f 12_index.ldif
Zawartość pliku /etc/krb5.conf (definicja naszej domeny):
includedir /etc/krb5.conf.d/
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
proxiable = true
pkinit_anchors = /etc/openldap/certs/ca.crt
udp_preference_limit = 1
default_realm = HDFS.LAB
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
HDFS.LAB = {
admin_server = hdc1.hdfs.lab
kdc = hdc1.hdfs.lab
kdc = hdc2.hdfs.lab
database_module = openldap_ldapconf
}
[dbdefaults]
ldap_kerberos_container_dn = cn=Kerberos,dc=hdfs,dc=lab
[appdefaults]
pam = {
debug = false
ticket_lifetime = 3600
renew_lifetime = 3600
forwardable = true
krb4_convert = false
}
[dbmodules]
openldap_ldapconf = {
db_library = kldap
ldap_kdc_dn = „cn=ldapadm,dc=hdfs,dc=lab”
ldap_kadmind_dn = „cn=ldapadm,dc=hdfs,dc=lab”
ldap_service_password_file = /var/kerberos/krb5kdc/service.keyfile
ldap_servers = ldaps://hdc1.hdfs.lab ldaps://hdc2.hdfs.lab
ldap_conns_per_server = 5
}
[domain_realm]
.hdfs.lab = HDFS.LAB
hdfs.lab = HDFS.LAB
Zawartość pliku /var/kerberos/krb5kdc/kdc.conf :
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
HDFS.LAB = {
max_life = 12h 0m 0s
max_renewable_life = 7d 0h 0m 0s
master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
}
Zawartość pliku /var/kerberos/krb5kdc/kadm5.acl :
*/admin@HDFS.LAB *
Generujemy plik z hasłem dla ldapadm którym kerberos będzie się łączył z OpenLDAP, na obydwu serwerach robimy to samo.
kdb5_ldap_util -D cn=ldapadm,dc=hdfs,dc=lab stashsrvpw -f /var/kerberos/krb5kdc/service.keyfile cn=ldapadm,dc=hdfs,dc=lab
Generujemy strukturę kerberosa w ldap (robimy to tylko na jednym serwerze):
kdb5_ldap_util -D „cn=ldapadm,dc=hdfs,dc=lab” -H ldap://hdc1.hdfs.lab create -subtrees „cn=Kerberos,dc=hdfs,dc=lab” -r HDFS.LAB -s
Podajemy hasło dla bazy kerberosa i… mamy to! Pozostaje skopiować klucz główny z hasłem do bazy kerberosa na drugi węzeł:
W następnym kroku startujemy usługi krb5kdc (na obydwu serwerach) i kadmin na pierwszym. Doszliśmy do etapu, w którym możemy przeprowadzić konfigurację “klienta”. W moim przypadku będzie to CentOS 7, konfiguracja klienta który jest serwerem jest nieco inna od “zwykłego” klienta, postaram się to rozróżnić. Zaczniemy jednak od przetestowania kerberosa, musimy dodać właściwe uprawnienia:
kadmin.local -q „addprinc admin/admin”
kadmin.local -q „addprinc root/admin”
Oraz dla użytkownika root:
Następnie dodajemy uprawnienia dla hostów w klastrze (jako host i jako ldap):
Zapisujemy uprawnienia hostów do pliku /etc/krb5.keytab (plik kopiujemy też na drugi węzeł, właścicielem pliku powinien być użytkownik “ldap”):
Tworzymy plik/var/kerberos/krb5kdc/kadm5.keytab (kopiujemy go na drugi węzeł):
Na tym etapie mamy działający OpenLDAP i Kerberos, teraz dokonamy kerberyzacji ldap, czyli skonfigurujemy w systemie możliwość zalogowania się kontem z ldap, wygenerowania uprawnień kerberos i użycia ich z ssh. Robimy to na razie na węzłach klastra, na samym końcu pokażę procedurę podłączenia serwera klienckiego do całego systemu (będzie zdecydowanie prościej). Do zarządzania OpenLDAP polecam klienta LDAP Admin, prosty i skuteczny. w moim katalogu utworzę użytkownika ppisz którego użyję do dalszych testów (nadajemy mu uprawnienia w domenie: kadmin.local –q “addprinc ppisz”).
Włączamy możliwość zalogowania się kontem OpenLDAP:
authconfig –updateall –enableldap –enableldapauth –enablemkhomedir
Autentykację ldap zapewnia usługa NSCD:
[root@HDC2 ~]# cat /etc/nslcd.conf
uid nslcd
gid ldap
uri ldaps://hdc1.hdfs.lab ldaps://hdc2.hdfs.lab
base dc=hdfs,dc=lab
bind_timelimit 30
timelimit 30
idle_timelimit 3600
ssl on
tls_reqcert demand
tls_cacertdir /etc/openldap/cacerts
tls_cacertfile /etc/openldap/cacerts/ca.crt # To jest ten sam plik co w /etc/openldap/certs/ca.crt
tls_ciphers HIGH:TLSv1.2:!aNULL:!eNULL
Restartujemy usługę nscd i możemy się logować kontem z OpenLDAP oraz generować uprawnienia kerberos
Aby wszystko spiąć razem, musimy jeszcze skonfigurować połączenie kerberosa z ldap za pomocą GSSAPI.
Tworzymy trzynasty plik LDIF:
[root@hdc1 ldap]# cat 13_sasl.ldif
dn: cn=config
changetype: modify
add: olcAuthzRegexp
olcAuthzRegexp: uid=([^,]+),cn=hdfs.lab,cn=gssapi,cn=auth
uid=$1,ou=Users,dc=hdfs,dc=lab
–
add: olcSaslSecProps
olcSaslSecProps: noanonymous,noplain
–
add: olcSaslRealm
olcSaslRealm: HDFS.LAB
Wgranie: ldapmodify -QY EXTERNAL -H ldapi:/// -f 13_sasl.ldif
Uzupełniamy plik /etc/openldap/ldap.conf o dyrektywy GSSAPI:
[root@hdc1 ~]# cat /etc/openldap/ldap.conf
BASE dc=hdfs,dc=lab
URI ldaps://hdc1.hdfs.lab ldaps://hdc2.hdfs.lab
TLS_CACERTDIR /etc/openldap/cacerts
TLS_CACERT /etc/openldap/cacerts/ca.crt
TLS_REQCERT allow
SASL_NOCANON on
SASL_MECH GSSAPI
SASL_REALM HDFS.LAB
Jak widać w obu powyższych przypadkach, jako URI są wskazane obydwa serwery OpenLDAP. O ile działa, to pytany zawsze będzie pierwszy serwer, i tak jest ok, bileciki kerberosa są przechowywane per węzeł klastra (mimo, że baza jest współdzielona), więc lepiej aby były w jednym miejscu (inaczej po przeskoku trzeba będzie wygenerować nowy ticket). Jeśli chodzi o konfigurację SSH w CentOS 7 to nie trzeba nic robić, tak skonfigurowany system pozwoli wykonać pełną sekwencję:
I to już właściwie koniec, pozostaje przygotowanie klienta który nie jest serwerem, w takim przypadku instalujemy następujące pakiety:
yum install nss-pam-ldapd pam_krb5 krb5-workstation openldap-clients cyrus-sasl-gssapi
I wkopiować (z węzła klastra) pliki /etc/openldap/ldap.conf /etc/krb5.conf /etc/nslcd.conf i ca.crt wkopiować do /etc/openldap/certs i cacerts
I na koniec TIP:
Taki komunikat: bdb_equality_candidates: (uid) not indexed wskazuje na brak potrzebnego indeksu (w nawiasie będzie podany parametr).
Aby założyć index tworzymy plik LDIF (wgrywamy standardowo, ldapmodify):
dn: olcDatabase={2}hdb,cn=config
add: olcDbIndex
olcDbIndex: uid eq
I to by było na tyle, post wyszedł bardzo długi, ale wyczerpuje temat maksymalnie jak się dało, jeśli ktoś ma większe doświadczenie w temacie i chciałby coś podpowiedzieć to zachęcam do komentowania.