LDAP Linux Auth
Введение
Везде в документе приняты следующие соглашения:
- пароль, если не указан - r00tme
- все пароли заведомо простые так как использовались для отладки. В реальной инсталляции рекомендую использовать более сложные пароли.
- изначально настройка не безопасна (используется "админская" учетная запись, и перенастройка на более безопасную конфигурацию описываетсяв отдельной секции.
- использование шифрования описывается в отдельной секции, изначальная настройка не использует шифрование.
- OS: Ubuntu 14.04
Для простоты и скорости работы я создаю алиасы для утилит, что бы не вписывать логин и пароль каждый раз.
По этой причине команды указаны БЕЗ пароля.
alias ldappasswd='ldappasswd -D "cn=admin,dc=fuel_domain" -w r00tme' alias ldapsearch='ldapsearch -D "cn=admin,dc=fuel_domain" -w r00tme' alias ldapmodify='ldapmodify -D "cn=admin,dc=fuel_domain" -w r00tme' alias ldapadd='ldapadd -D "cn=admin,dc=fuel_domain" -w r00tme'
Если требуется ввести другой пароль то команда обычно предваряется символом \ (unalias), например
\ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"
LDAP Server Installation
"Тихая" установка
DEBIAN_FRONTEND=noninteractive apt-get install slapd ldap-utils
Современные дистрибутивы OpenLDAP используют сам LDAP как хранилище конфигурации. cn=config. По-умолчанию настроен доступ без пароля для локального рута.
Посмотреть содержимое можно так:
ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"
Создать отдельную базу данных для своих объектов.
Внимательно проверить права на директорию - она должна существовать и быть доступна серверу на запись (/var/lib/ldap_fuel_domain/)
dn: olcDatabase={2}hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {2}hdb olcDbDirectory: /var/lib/ldap_fuel_domain/ olcSuffix: dc=fuel_domain olcAccess: {0}to * by * write olcLastMod: TRUE olcRootDN: cn=admin,dc=fuel_domain olcRootPW: {SSHA}bxQpFzYmIkILSbDEL3cVl+nf03mdra/t olcDbCheckpoint: 512 30 olcDbConfig: {0}set_cachesize 0 2097152 0 olcDbConfig: {1}set_lk_max_objects 1500 olcDbConfig: {2}set_lk_max_locks 1500 olcDbConfig: {3}set_lk_max_lockers 1500 olcDbIndex: objectClass eq
Обратить внимание на
olcAccess: {0}to * by * write
Изначально я даю полный доступ всем на все, изменю его позже.
Создание: (подключение через -H ldapi:/// в установке по умолчанию доступно руту без пароля.
ldapadd -Y EXTERNAL -H ldapi:/// < database SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "olcDatabase={2}hdb,cn=config"
Если права на директорию неправильные ИЛИ не настроен apparmor возникает ошибка:
root@node-3:~/ldap# ldapadd -Y EXTERNAL -H ldapi:/// < database SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "olcDatabase={2}hdb,cn=config" ldap_add: Other (e.g., implementation specific) error (80) additional info: olcDbDirectory: value #0: invalid path: Permission denied
Исправить:
/etc/apparmor.d/usr.sbin.slapd ## /var/lib/ldap** rwk,
Для работы создаем свой домен куда будем помещать все объекты.
- domain
# fuel_domain dn: dc=fuel_domain objectClass: top objectClass: dcObject objectClass: organization o: fuel_users
- domain_admin
# admin dn: cn=admin,dc=fuel_domain objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword: {SSHA}bxQpFzYmIkILSbDEL3cVl+nf03mdra/t
Пароль пользователя генерируется утилитой ldappasswd, в моем примере это 'r00tme'
Загрузить объекты:
ldapadd -Y EXTERNAL -H ldapi:/// < domain ldapadd -Y EXTERNAL -H ldapi:/// < domain_admin
Проверить, подключившись с ново-созданным пользователем
ldapsearch -D "cn=admin,dc=fuel_domain" -w 'r00tme' -b 'dc=fuel_domain' '(objectclass=*)'
- -D - "логин"
- -w - пароль
- -b - base, ветка дерева в которой искать
- '(objectclass=*)' - "что искать", в примере - "все"
Результат поиска:
# extended LDIF # # LDAPv3 # base <dc=fuel_domain> with scope subtree # filter: (objectclass=*) # requesting: ALL # # fuel_domain dn: dc=fuel_domain objectClass: top objectClass: dcObject objectClass: organization o: fuel_users dc: fuel_domain # admin, fuel_domain dn: cn=admin,dc=fuel_domain objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword:: e1NTSEF9YnhRcEZ6WW1Ja0lMU2JERUwzY1ZsK25mMDNtZHJhL3Q= # search result search: 2 result: 0 Success # numResponses: 3 # numEntries: 2
Исправить права на базу:
dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=admin,dc=fuel_domain" write by * read
ldapmodify -Y EXTERNAL -H ldapi:/// < 03_fix_permissions SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={2}hdb,cn=config"
Проверить права можно например скачав весь конфиг:
ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" > all_config
PHP LDAP Addmin
Опционально можно поставить phpldapadmin
apt-get install phpldapadmin
Настройки минимальны и ограничиваются указанием домена пользователя
diff /etc/phpldapadmin/config.php /etc/phpldapadmin/config.php.original 300c300 < $servers->setValue('server','base',array('dc=fuel_domain')); --- > $servers->setValue('server','base',array('dc=example,dc=com')); 326c326 < $servers->setValue('login','bind_id','cn=admin,dc=fuel_domain'); --- > $servers->setValue('login','bind_id','cn=admin,dc=example,dc=com');
LDAP Configuration
Organization
Добавить организацию Обратить внимание - dn: dc=customer_organization,dc=fuel_domain - порядок важен. dc В КОТОРЫЙ добавляется должен быть последним.
# 04_customer_organization dn: dc=customer_organization,dc=fuel_domain dc: customer_organization o: Example Organization objectClass: dcObject objectClass: organization
Добавить организацию:
ldapadd < 04_customer_organization
Результат:
ldapadd < 04_customer_organization adding new entry "dc=customer_organization,dc=fue_domain" ldap_add: Server is unwilling to perform (53) additional info: no global superior knowledge root@node-3:~/ldap# ldapadd < 04_customer_organization adding new entry "dc=customer_organization,dc=fuel_domain"
Manager Role
# 05_manager dn: cn=Manager,dc=customer_organization,dc=fuel_domain cn: Manager description: LDAP administrator objectClass: organizationalRole objectClass: top roleOccupant: dc=customer_organization,dc=fuel_domain
ldapadd < 05_manager adding new entry "cn=Manager,dc=customer_organization,dc=fuel_domain"
Organization Units People and Groups
# 06_people dn: ou=People,dc=customer_organization,dc=fuel_domain ou: People objectClass: top objectClass: organizationalUnit
# 07_groups dn: ou=Group,dc=customer_organization,dc=fuel_domain ou: Group objectClass: top objectClass: organizationalUnit
ldapadd < 06_people adding new entry "ou=People,dc=customer_organization,dc=fuel_domain" root@node-3:~/ldap# ldapadd < 07_groups adding new entry "ou=Group,dc=customer_organization,dc=fuel_domain"
"Живые Люди"
Добавить пользователя. Обратить внимание - пароль такой же как в /etc/shadow
(Как его генерировать я не знаю пока)
dn: uid=sirmax,ou=People,dc=customer_organization,dc=fuel_domain objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: sirmax cn: Max Mazur sn: Mazur givenName: Mazur title: Max Mazur telephoneNumber: +38 067 341 80 70 mobile: +38 067 341 80 70 postalAddress: AddressLine1$AddressLine2$AddressLine3 userPassword: {CRYPT}$6$DS/mzad5$EB.cNCLE7KB7OCPK1nU6aEA8HnQDLY1FPd3KaWPVqaNBtWhmh/4cOUgD1I8tQSFu41yy7jMXDrg9TDqlAbuLX. labeledURI: http://wiki.sirmax.noname.com.ua/ loginShell: /usr/bin/sudosh uidNumber: 9999 gidNumber: 9999 homeDirectory: /home/sirmax/ description: This is an example user
ldapadd < 08_user1 adding new entry "uid=sirmax,ou=People,dc=customer_organization,dc=fuel_domain"
Обратить внимание на loginShell: /usr/bin/sudosh - если не используется запись сессий то шелл будет другим.
Остальные пользователи создаются полностью аналогично.
Groups
2 группы
- группа пользователя (для каждого пользователя)
- группа fuel_users которой будет разрешено делвть sudo
dn: cn=sirmax,ou=Group,dc=example_organization,dc=fuel changetype: add cn: fuel users objectClass: posixGroup gidNumber: 9999 description: Fuel Users memberUid: sirmax
dn: cn=fuel_users,ou=Group,dc=example_organization,dc=fuel changetype: add cn: fuel users objectClass: posixGroup gidNumber: 109999 description: Fuel Users memberUid: sirmax
Добавить:
ldapadd < 09_group_for_user1 adding new entry "cn=sirmax,ou=Group,dc=customer_organization,dc=fuel_domain" ldapadd < 10_group_fuel_users adding new entry "cn=fuel_users,ou=Group,dc=customer_organization,dc=fuel_domain"
Настройка клиента
Вынесено в отдельный документ:
http://wiki.sirmax.noname.com.ua/index.php/LDAP_Linux_Auth_Client
Securing LDAP
В этой секции описывается создание служебных пользователей и групп и ограничение их привелегий.
Эти пользователи будут использоваться на node-1 .. node-4 в файлах
- /etc/ldap.conf (node-3)
uri ldap://10.20.0.3/ base dc=fuel_domain ldap_version 3 rootbinddn uid=nssproxy-node3,ou=service_users,dc=customer_organization,dc=fuel_domain pam_password md5
- /etc/ldap.secret (пароль для node-1 - "node1", аналогично для остальных нод)
node3
- /etc/sudo-ldap.conf
BASE dc=customer_organization,dc=fuel_domain URI ldap://10.20.0.3 BINDDN uid=nssproxy-node3,ou=service_users,dc=customer_organization,dc=fuel_domain BINDPW node3 TIMELIMIT 15 TIMEOUT 20 SUDOERS_BASE ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain
Служебные пользователи
Для того что бы не делать анонимных запросов я создаю для каждого из своих серверов своего пользователя nssproxy-node{X}, например nssproxy-node1. Все пользователи помещаются в отдельный OU.
Отдельный юнит для этих пользователей
dn: ou=service_users,dc=customer_organization,dc=fuel_domain ou: service_users objectClass: top objectClass: organizationalUnit
Пользователь:
dn: uid=nssproxy-node1,ou=service_users,dc=customer_organization,dc=fuel_domain uid: nssproxy-node1 gecos: Network Service Switch Proxy User objectClass: top #objectClass: account objectClass: posixAccount objectClass: shadowAccount objectClass: organizationalPerson objectClass: inetOrgPerson userPassword: {SSHA}RsAMqOI3647qg1gAZF3x2BKBnp0sEVfa cn: node1 sn: node1 shadowLastChange: 15140 shadowMin: 0 shadowMax: 99999 shadowWarning: 7 loginShell: /bin/false uidNumber: 801 gidNumber: 801 homeDirectory: /home/nssproxy
и т. д. до node4
Обратить внимаение - нужно менять не только имя но и UID
Установка паролей
Меняем пароли для всех пользователей, задавая соответвенно node1, node2, node3, node4.
ldappasswd -S uid=nssproxy-node1,ou=service_users,dc=customer_organization,dc=fuel_domain ldappasswd -S uid=nssproxy-node2,ou=service_users,dc=customer_organization,dc=fuel_domain ldappasswd -S uid=nssproxy-node3,ou=service_users,dc=customer_organization,dc=fuel_domain ldappasswd -S uid=nssproxy-node4,ou=service_users,dc=customer_organization,dc=fuel_domain
Служебные Группы
Для служебных пользователей создаем отдельную группу, на которую будем назначать привелегии.
Важно отметить, что эта группа имеет тип groupOfNames а не posixGroup, и содержит не список ID а список dn=
Для posixGroup не работает конструкция by group.exact="cn= ... " read в списках контроля доступа.
dn: cn=nssproxy,ou=Group,dc=customer_organization,dc=fuel_domain cn: nssproxy1 objectClass: groupOfNames objectClass: top member: uid=nssproxy-node1,ou=service_users,dc=customer_organization,dc=fuel_domain member: uid=nssproxy-node2,ou=service_users,dc=customer_organization,dc=fuel_domain member: uid=nssproxy-node3,ou=service_users,dc=customer_organization,dc=fuel_domain member: uid=nssproxy-node4,ou=service_users,dc=customer_organization,dc=fuel_domain
Добавлять пользователей в группу отдельно не надо - группа изначально создается содержащей всех необходимых пользователей.
Назначение прав
Пока у пользователя нет прав то он не может получить криптованый пароль так как настройка ACL по умолчанию это запрещает:
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by * none
Тут можно видеть, что просмотр паролей (хешей) разрешен только администратору "cn=admin,dc=fuel_domain"
Соответвенно утилита getent не может получить доступа к паролю так-как использует пользователя nssproxy-node{N}
getent shadow sirmax sirmax:*:::::::
Вообще-то для того что бы работал логин пароль знать не обязательно - с ним можно авторизоваться через PAM (pam_ldap).
но для того что бы работал nss без pam_ldap а только с pam_unix нужно что бы можно было получить хеш пароля.
Проверим текущие разрешения:
Обратить внимание - у меня 2 базы данных потому {2}
\ldapsearch -Y EXTERNAL -H ldapi:/// -b "olcDatabase={2}hdb,cn=config"
Вывод поформатирован:
# {2}hdb, config dn: olcDatabase={2}hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {2}hdb olcDbDirectory: /var/lib/ldap_fuel_domain/ olcSuffix: dc=fuel_domain olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=admin,dc=fuel_domain" write by * read olcLastMod: TRUE olcRootDN: cn=admin,dc=fuel_domain olcRootPW: {SSHA}bxQpFzYmIkILSbDEL3cVl+nf03mdra/t olcDbCheckpoint: 512 30 olcDbConfig: {0}set_cachesize 0 2097152 0 olcDbConfig: {1}set_lk_max_objects 1500 olcDbConfig: {2}set_lk_max_locks 1500 olcDbConfig: {3}set_lk_max_lockers 1500 olcDbIndex: objectClass eq
Нас интересует секция
olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=admin,dc=fuel_domain" write by * read
Тут видно что права на чтения и запись пароля есть только у dn="cn=admin,dc=fuel_domain
Требуется добавить разрешение для группы nsswitch
# 21_acl dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by group.exact="cn=nssproxy,ou=Group,dc=customer_organization,dc=fuel_domain" read by * none olcAccess: {1}to dn.base="" by * read olcAccess: {2}to * by dn="cn=admin,dc=fuel_domain" write by * read
\ldapmodify -Y EXTERNAL -H ldapi:/// < 21_acl
После назначение прав можно увидеть что getent может получить хеш пароля:
getent shadow sirmax sirmax:$6$DS/mzad5$EB.cNCLE7KB7OCPK1nU6aEA8HnQDLY1FPd3KaWPVqaNBtWhmh/4cOUgD1I8tQSFu41yy7jMXDrg9TDqlAbuLX.
Следующим шагом следует разрешить чтение всего что касается sudo всем кроме группы nssproxy
dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to attrs=userPassword,shadowLastChange by self write by anonymous auth by dn="cn=admin,dc=fuel_domain" write by group.exact="cn=nssproxy,ou=Group,dc=customer_organization,dc=fuel_domain" read by * none olcAccess: {1}to dn.subtree="ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain" by dn="cn=admin,dc=fuel_domain" write. by group.exact="cn=nssproxy,ou=Group,dc=customer_organization,dc=fuel_domain" read by anonymous auth by * none olcAccess: {2}to dn.base="" by * read olcAccess: {3}to * by dn="cn=admin,dc=fuel_domain" write by * read
dn.subtree="ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain" - указать что все поддерево доступно только группе cn=nssproxy,ou=Group,dc=customer_organization,dc=fuel_domain
Проверить то, что доступ к поддереву ограничен можно следующим образом:
- Создать алиас для простоты использования, который использует "обычного" пользователя.
alias ldapsearch_sirmax='\ldapsearch -D "uid=sirmax,ou=People,dc=customer_organization,dc=fuel_domain" -w r00tme'
ldapsearch -x -b 'dc=fuel_domain' '(objectclass=*)' | grep sudo
Вывод (часть строк удалена так как вывод длиный)
loginShell: /usr/bin/sudosh dn: cn=defaults,ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain objectClass: sudoRole description: Default sudoOption's go here sudoOption: env_reset sudoOption: mail_badpass sudoOption: secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sb sudoOrder: 1 ... dn: cn=root,ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain objectClass: sudoRole sudoUser: root sudoHost: ALL sudoRunAsUser: ALL sudoRunAsGroup: ALL sudoCommand: ALL sudoOrder: 2 # %admin, sudo, services, customer_organization.fuel_domain dn: cn=%admin,ou=sudo,ou=services,dc=customer_organization,dc=fuel_domain objectClass: sudoRole sudoUser: %admin sudoHost: ALL sudoRunAsUser: ALL sudoCommand: ALL sudoOrder: 3
Для сравнения тот же запрос с использованием "обычного" пользователя не показывает информации о sudo
ldapsearch_sirmax -x -b 'dc=fuel_domain' '(objectclass=*)' | grep sudo
loginShell: /usr/bin/sudosh loginShell: /usr/bin/sudosh loginShell: /usr/bin/sudosh
SUDO
http://wiki.sirmax.noname.com.ua/index.php/LDAP_Linux_LDAP_SUDO
Шифрование
Ссылки
- ВВедение в PAM https://www.ibm.com/developerworks/ru/library/l-pam/
PAM
PAM vs NSS
http://serverfault.com/questions/538383/understand-pam-and-nss
http://pro-ldap.ru/tr/zytrax/ch6/slapd-config.html
https://wiki.archlinux.org/index.php/LDAP_authentication
RUS
http://www.bog.pp.ru/work/LDAP.html
http://pro-ldap.ru/tr/zytrax/ch6/slapd-config.html
http://xgu.ru/wiki/man:ldapmodify
https://rtfm.co.ua/openldap-migraciya-s-slapd-conf-na-cnconfig-olc/
http://www.slashroot.in/how-are-passwords-stored-linux-understanding-hashing-shadow-utils