Piszki Lab

Analiza przypadku w języku przodków…

Konfiguracja OpenLDAP i Kerberos w CentOS 7

| 0 comments

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.

ticket

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 :

hdc0

[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.

hdc1W logu zobaczymy prawidłowe połączenia między serwerami (dane jeszcze nie są replikowane, tylko konfiguracja).

hdc2

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).

hdc3Widzimy, że replikacja idzie zaszyfrowanym kanałem (TLS).

hdc4Podstawowa 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)

hdc5

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).

hdc6Od 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}

hdc7Na 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

hdc8Podajemy hasło dla bazy kerberosa i… mamy to! Pozostaje skopiować klucz główny z hasłem do bazy kerberosa na drugi węzeł:

hdc13

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:

hdc9

Następnie dodajemy uprawnienia dla hostów w klastrze (jako host i jako ldap):

hdc10

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”): hdc11

Tworzymy plik/var/kerberos/krb5kdc/kadm5.keytab (kopiujemy go na drugi węzeł):

hdc12

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

hdc15

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ę:

hdc16I 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.

Oceń ten artykuł:
[Głosów:2    Średnia:5/5]

Dodaj komentarz

Required fields are marked *.


.

Podobał się wpis? Wesprzyj Piszki Lab, kliknij w reklamę! :-)

.