четверг, 29 мая 2008 г.

Инфраструктура Kerberos, сервисы

Теперь, когда у нас есть KDC, можно настраивать доступ к службам с использованием Kerberos.
Первым делом пример доступа к LDAP, так как этот сервис уже настроен. OpenLDAP поддерживает SASL(Simple Authentication and Security Layer). Почитать: http://bog.pp.ru/work/SASL.html или в Википедии.

Одним из механизмов SASL выступает GSSAPI - как раз наш случай, Kerberos (на самом деле не только). Остается настроить этот механизм (фрагмент /etc/ldap/slapd.conf):

sasl-realm REALM.TLD
sasl-host kdc.realm.tld

Далее надо чтобы был настроен /etc/krb5.conf, установлен пакет libsasl2-modules-gssapi-heimdal (или mit, но если вы читали статью про heimdal-kcm, то выбрать вам будет проще).

Если это все готово, то переходим к следующему этапу - надо сделать keytab файл для slapd. Тут стоит остановиться подробнее - keytab файл хранит ключи для сервисов (вы же не будете настукивать пароль при каждом обращении пользователя :) ). Чтение этого файла, всем кроме root, противопоказано, а slapd у нас работает от пользователя openldap. Значит делаем финт ушами:
в /etc/default/slapd добавим строчку - export KRB5_KTNAME="FILE:/etc/ldap/slapd.keytab". Это укажет slapd где искать ключи. Далее надо собствено выгрузить ключи:

kadmin -l ext --keytab=/etc/ldap/slapd.keytab ldap/kdc.realm.tld
chown openldap.openldap /etc/ldap/slapd.keytab


Надеюсь вы помните, что мы пока находимся все на том же сервере, а поэтому мы используем kadmin с ключом "-l", что позволяет пользователю root делать что угодно с базой kerberos. Для доступа с другой машины надо будет настроить kadmind

Ключи выгружены, slapd настроен, рестартуем демон: /etc/init.d/slapd restart
Проверка:

root@kdc:~# kinit deepwalker <<< надо будет ввести пароль
root@kdc:~# ldapsearch -H ldap://127.0.0.1 -Y GSSAPI
>>> тут должно быть выведено все дерево каталога <<<


Примерно таким же образом настраивается все остальное. Например для apache надо будет подгрузить модуль с поддержкой kerberos, выгрузить ключи вида HTTP/wwwhost.realm.tld. Для NFSv4 ключи надо выгружать в стандартный /etc/krb5.keytab для каждой машины. Впрочем по отдельным сервисам статьи можно написать будет отдельно. Главную мысль вы, я надеюсь, уловили.

Проблемы:
главная проблема это DNS (расхождения времени мы пока получить не можем, так как машина одна). В /etc/hosts вбейте нечто вроде:

127.0.0.1 localhost
192.168.0.1 kdc.realm.tld kdc


Следующая серия должна быть про настройку рабочей станции. А гневные комментарии все так же привествуются, давайте сделаем из этой серии заметок нечто качественное.

понедельник, 26 мая 2008 г.

mount.cifs & kerberos

Недавно прошла конференция в Томске, на которой я был докладчиком. В качестве одной из проблем взаимодействия linux и windows сред, я указал проблему с поддержкой kerberos в cifs подсистеме ядра - долгое время ее там просто не было.

Так вот http://abbra.livejournal.com/ подсказал, что ребята из Samba team озаботились этой проблемой - kerberos в mount.cifs быть!

После этого замечания, я стал искать - а как же я могу уже это использовать, и пришел к выводу, что пока никак - для работы, как я выяснил, требуется cifs.spnego - хелпер для mount.cifs. Также, как я понял, этот хелпер будет в Samba 3.2. Вообще толковой информации накопать так и не удалось, ясно, что smbfs из ядра уберут (или уже), а cifs доделали. Неясно в какой версии ядра это все есть, неясно как использовать.

Если есть кто то, кто в курсе, как это будет, напишите пожалуйста.

На самом деле Samba выглядит гораздо более готовой для использования, чем NFSv4. Также Abbra говорил, что большую часть работы в ветке Samba4 доделают к концу года, а мне казалось, что проект стоит ожидать в следующем веке. В этом свете, мне кажется, что можно будет строить домены на Samba, не изобретая свой велосипед на Kerberos + LDAP, ведь у этой связки есть достаточно серьезные проблемы о которых будет следующая запись.

Инфраструктура Kerberos, начало

Как и обещал, попробую написать, как это делается. Приветствуются замечания, предложения, улучшения. Если есть что добавить к документу - я всеми руками за. Указывайте на ошибки, ругайте и тп : )

Вообще автор ждет Samba4, а пока спасается как может.

Итак, мы строим следующую структуру:
1. Heimdal KDC - центр распределения ключей;
2. OpenLDAP 2.4 - каталог, здесь будет храниться вся сетевая конфигурация. Такая как ключи kerberos, DNS, конфигурация (в предыдущих постах я описывал SCFL, только вот пока не выложил).

Я использую ubuntu 8.04, поэтому, как обычно, правит каждый сам под себя. Да и вообще - не надо всему, что вы прочли в интернете, слепо верить - стоит почитать документацию.

Ставим минимальный набор:

aptitude install heimdal-kdc slapd heimdal-clients pdns-server pdns-backend-ldap


Правим /etc/heimdal-kdc/kdc.conf:

[kdc]
logging = FILE:/var/log/heimdal-kdc.log
database = {
realm = REALM.TLD
dbname = ldap:dc=realm,dc=tld
mkey_file = /var/lib/heimdal-kdc/m-key
acl_file = /etc/heimdal-kdc/kadmind.acl
}


Правим /etc/ldap/slapd.conf:

include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/hdb.schema
include /etc/ldap/schema/dnsdomain2.schema #PowerDNS схема расширяющая стандартную

pidfile /var/run/slapd/slapd.pid
argsfile /var/run/slapd/slapd.args
loglevel 0

modulepath /usr/lib/ldap
moduleload back_bdb
moduleload syncprov #Для синхронизации

sizelimit 500
tool-threads 1
backend bdb
sasl-realm REALM.TLD
sasl-host kdc.realm.tld

# Следующая строчка преобразует пользователя, подключающегося через sasl механизм
# GSSAPI(kerberos) к его реальному DN в дереве.
# В данном случае поиском, так как у меня они не лежат в одной папке.
authz-regexp uid=([^,]*),cn=realm.tld,cn=gssapi,cn=auth
ldap:///dc=realm,dc=tld??sub?(uid=$1)


database bdb #Мне тут сказали, что лучше использовать hdb. Попробую, почитаю.
suffix "dc=realm,dc=tld"
checkpoint 512 30
directory "/var/lib/ldap"

# Далее настройки хранилища bdb какие были по умолчанию. Надо бы разобраться и докрутить.
dbconfig set_cachesize 0 2097152 0
dbconfig set_lk_max_objects 1500
dbconfig set_lk_max_locks 1500
dbconfig set_lk_max_lockers 1500

# Настройки индекса. В общем то надо расширить их значительно.
index objectClass,uid eq
lastmod on

access to attrs=userPassword,shadowLastChange
# Везде рекомендуют вначале привести к нормальному имени.
# Пометим как TODO
by dn="gidnumber=0+uidnumber=0,cn=peercred,cn=external,cn=auth" write
by * none

access to attrs=krb5Key
by dn="gidnumber=0+uidnumber=0,cn=peercred,cn=external,cn=auth" write
by * none

access to dn.base="" by * read
access to dn.subtree="ou=config,dc=realm,dc=tld"
# Мне можно править : )
by dn.base="uid=deepwalker,ou=users,dc=realm,dc=tld" manage
# Остальным читать
by users read
by anonymous auth
by * none

access to *
by dn="gidnumber=0+uidnumber=0,cn=peercred,cn=external,cn=auth" write
by users read
by anonymous auth
by * none
# Последняя строчка для синхронизации
overlay syncprov

Теперь надо инициализировать LDAP, но для начала в /etc/default/slapd вставляем строчку:

SLAPD_SERVICES="ldap:/// ldapi:///"

Это заставит демон слушать на локальном сокете. Перезапускаем, и создаем файлик base.ldif с содержимым:

# realm.tld
dn: dc=realm,dc=tld
objectClass: dcObject
objectClass: organization
o: realm
dc: realm

# users, realm.tld
dn: ou=users,dc=realm,dc=tld
ou: users
objectClass: top
objectClass: organizationalUnit

# hosts, realm.tld
dn: ou=hosts,dc=realm,dc=tld
ou: hosts
objectClass: top
objectClass: organizationalUnit

# groups, realm.tld
dn: ou=groups,dc=realm,dc=tld
ou: groups
objectClass: top
objectClass: organizationalUnit

Теперь загрузим его в базу:

cat base.ldif | ldapadd -H ldapi:/// -Y EXTERNAL

Здесь это делается от root, мы подключаемся к локальному сокету и аутентифицируемся по специальному sasl механизму external - просто локальные пользователи.

Ну и инициализация KDC:

# /etc/init.d/heimdal-kdc restart
# kadmin -l
> init REALM.TLD

Далее надо по идее создать пользователя. Если вы в kadmin сделаете "add deepwalker", то создаст он вам пользователя немного не так, как хотелось бы, а по сему делаем tmpuser.ldif:

dn: uid=TMPUSER,ou=users,dc=realm,dc=tld
uid: TMPUSER
krb5PrincipalName: TMPUSER@REALM.TLD
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: userSecurityInformation
objectClass: krb5KDCEntry
objectClass: krb5Principal
loginShell: /bin/bash
uidNumber: TMPUIDNUM
krb5KDCFlags: 126
gidNumber: 2000
sn: TMPUSER
homeDirectory: /home/TMPUSER
krb5KeyVersionNumber: 1

И используем:

root@kdc:~# a=`ldapsearch -H ldapi:/// -Y external uidNumber | grep ^uidNumber | cut -c 12- | sort |tail -n 1`; let last_uid=$a+1
root@kdc:~# cat tmpuser.ldif | sed s/TMPUSER/deepwalker/g | sed s/TMPUIDNUM/$last_uid/g | ldapadd -H ldapi:/// -Y external
root@kdc:~# kadmin -l cpw deepwalker

Можно посмотреть содержимое каталога LDAP:

slapcat
# или
ldapsearch -Y external -H ldapi:///

Теперь надо настроить /etc/krb5.conf:

[libdefaults]
default_realm = REALM.TLD
default_tgs_enctypes = des-cbc-crc
default_tkt_enctypes = des-cbc-crc
default_etypes = des-cbc-crc
default_etypes_des = des-cbc-crc

fcc-mit-ticketflags = true


[realms]
REALM.TLD = {
kdc = kdc.realm.tld
admin_server = kdc.realm.tld
}

[domain_realm]
realm.tld = REALM.TLD
.realm.tld = REALM.TLD

Пробуем получить билет:

kinit deepwalker

Далее пишем гневные комментарии и ждем следующей части.

среда, 7 мая 2008 г.

py-configurator, scfl - добавки к Cfengine

Для нужд рабочих я написал несколько скриптов:
1. py-configurator - уже описывал и выкладывал;
2. scfl - Sync Config From LDAP - скоро выложу;
3. apt-control - ставит или удаляет пакеты руководствуясь списком из файла.

Это все добавки к Cfengine, хотя если будет время создать py-facter (для выуживания данных из окружения) и py-controller (контроль поцессов, запуск команд шелла и тп), то это будет полностью самостоятельной группой программ. По идее py-configurator и scfl уже связаны в тандем - один выгружает, второй рендерит шаблоны и кладет на место, - но они достаточно легко могут быть использованы по отдельности (достаточно конфигурацию поменять, так как py-configurator по умолчанию лезет в папку, в которую scfl по умолчанию выгружает).

Собственно сайт проекта на Google Code - py-configurator. Со временем туда выложу SCFL и apt-control (надо очистить их от рабочих данных).

Ключевая фишка всего набора - ориентирован он по умолчанию на Kerberos+LDAP окружение, чего, к сожалению, нет ни в одной альтернативе. Иначе я бы не стал изобретать велосипед.

вторник, 6 мая 2008 г.

LDAP vs Kerberos

Часто, когда люди узнают, что я использую Kerberos они говорят, что у них чистый LDAP (pam_ldap) и все прекрасно работает, папки монтируются, сервер проверяется на истинность сертификатами. Но главное, что у них не работает это SSO - Single Sign On. То есть пароль придется вводить на доступ к каждой службе отдельно, отдельно на прокси, отдельно на почту. И это мой главный аргумент за использование Kerberos. О том, что в случае использования Kerberos, пароли по сети не передаются в любом виде, я и вовсе молчу. К хорошей сети сложно подключить машину, которой там быть не должно - EAP существует достаточно давно, а потому украсть передаваемый трафик это очень сложная задача.

Тут можно привести еще один аргумент - в среде Kerberos сервер также не получает ваш пароль, и подстановка фальшивого сервера практически невозможна. Ну а если некто и взломает сервер, то пароль он все равно получить не сможет и все что ему достанется это информация на этом самом сервере.

Для меня выбор Kerberos очевиден еще и потому, что установив дружеские отношения с доменом Active Directory, я фактически решил проблему взаимодействия Linux и Windows сред.

пятница, 2 мая 2008 г.

Скрипт для конфигурации по шаблонам

Кратенький код для конфигурирования машины по шаблонам файлов. Действует так - натравливаете его на каталог с шаблонами, он строит в указаной вами папке то самое дерево, только с отрендеренными шаблонами. По первому же импорту знающие люди поймут какой должен быть синтаксис : ) Ну а для людей не от мира Django будут примеры. Код:


#! /usr/bin/python

from jinja import Environment, FileSystemLoader
from ConfigParser import SafeConfigParser
import os


# pc_cfg = py-configurator
pc_cfg = SafeConfigParser()
pc_cfg.read('/etc/py-configurator/py-configurator.cfg')
pc_config = dict(pc_cfg.items('main'))

base_path = pc_cfg.get('main','templates').rstrip('/')
target_path = pc_cfg.get('main','target').rstrip('/')
if not target_path:
target_path='/'
machine_cfg = pc_cfg.get('main','machine_cfg')
print 'Configuration:',pc_config.items()

ws_cfg = SafeConfigParser()
ws_cfg.read(machine_cfg)
ws_config = dict(ws_cfg.items('main'))

base_len = len(base_path)+1
env = Environment(loader=FileSystemLoader(base_path))

for root, dirs, files in os.walk(base_path, topdown=False):
for name in files:
na_root = root[base_len:]+'/' if root!=base_path else ''
print "Work on file:",name,"Root:",root,"Short root",na_root
tmpl_file=na_root+name
try:
os.lstat(os.path.join(target_path,na_root))
except:
os.mkdir(os.path.join(target_path,na_root))
tmpl = env.get_template(tmpl_file)

tmpl_vars={'name':tmpl_file}
tmpl_vars.update(os.environ)
tmpl_vars.update(ws_config)

open(os.path.join(target_path,tmpl_file),'w').write(tmpl.render(**tmpl_vars))



Пример конфига:

[main]
templates = /etc/py-configurator/templates/
target = /etc
machine_cfg = /etc/machine.cfg

Переменные берет из /etc/machine.cfg, например из такого:

[main]
ws_name = ws3
ws_num = 3
domain = msk5
region_addr = 24
dep_addr = 55

По сути сварганить бы что то вроде facter из puppet, но еще один велосипед, даже не знаю стоит ли.
Обещаный пример (/etc/hosts):

127.0.0.1 localhost
10.{{ region_addr }}.{{ dep_addr }}.{{ ws_num }} {{ ws_name }}.{{ domain }}.realm.tld {{ ws_name }}
10.{{ region_addr }}.{{ dep_addr }}.1 kdc.{{ domain }}.realm.tld

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

Подробнее о синтаксисе шаблонов (а он богат возможностями) тут.
Собственно вся затея с etc-шаблонятором родилась от любви к шаблонам Django, ну и слегка требовалась утилита шаблонной настройки.