Документ распространяется в соответствии
с условиями лицензии GNU
Free Documentation License. Ни автор, ни распространители, ни любой
другой контрибьютор данного документа, не несет никакой ответственности
за физический, финансовый, моральный или любой другой ущерб, понесенный
при выполнении рекомендаций данного документа.
Содержание:
Для небольшой локальной сети необходимо обеспечить:
Централизованное управление сетевыми настройками рабочих станций
(IP-адрес, DNS-имя, маска, адреса шлюза, DNS-сервера, WINS-сервера) на
основании MAC-адреса
Централизованное управление правами доступа в интернет и способом
подключения (NAT, прокси, прозрачный прокси)
Централизованный учет потребляемого трафика по всем возможным
параметрам
Простой пользователький интерфейс ко всему вышеперечисленному:
сеть должен уметь обслуживать человек, имеющий минимальные познания в
области системного администрирования.
1.2. Способы решения задач
Традиционно такого рода задачи решаются стандартными средствами
открытых UNIX-систем. Для решения каждой подзадачи конфигурируется один
из стандарных сервисов:
dhcpd - для автоматической выдачи сетевых настроек клиентам на
основании их MAC-адресов либо произвольно
bind - для прямого и обратного преобразования имен сетевых
клиентов и их IP-адресов
squid - для организации кэширующего прокси-сервера
ipchains или iptables для Linux и ipfw для FreeBSD - для
организации брандмауэра с дополнительными функциями вроде поддержки
прозрачного прокси и NAT
сервер баз данных (как правило, MySQL) для хранения данных о
трафике + какая-нибудь система сбора этих данных
Основной недостаток такого решения - сложность администрирования.
Например, чтобы включить в сеть еще один компьютер, необходимо
проделать следующее:
прописать этот компьютер в конфигурационном файле dhcpd
прописать его в файлах прямой и обратной зоны bind
прописать его в acl squid
прописать его в стартовом скрипте брандмауэра
Т.е. человеку, обслуживающего такую систему, приходится выполнять
слишком много рутинной работы. Гораздо правильнее будет переложить
большую часть работы на плечи компьютера.
Простейшей способ упростить администрирование сводится к выделению
наиболее часто изменяемых параметров и вынесению их в отдельное
хранилище - текстовый или xml-файл, базу данных и т.д. Затем для
каждого сервиса пишется скрипт, вынимающий необходимые данные из
главного конфигурационного файла, и генерирующий конфиг для самого
сервиса.
Написание скриптов для генерации конфигов может быть довольно
трудоемкой задачей. Ситуацию упрощает тот факт, что многие сервисы
имеют возможность хранить свои конфигурационные данные не в текстовых
файлах, а в некотором внешнем хранилище, в качестве которого чаще всего
выступает LDAP. Некоторые особо продвинутые сервисы даже имеют развитые
средства для написания собственных backend-ов (примеры - bind и
courier-imap), однако LDAP все равно остается самым распространенным
внешним хранилищем конфигурационных даных.
Цель данной статьи - показать, как использовать LDAP для хранения
конфигурационных данных как тех сервисов, которые непосредственно умеют
работать с LDAP, так и тех, которые этого не умеют. Кроме того, в
статье будут рассмотрены смежные вопросы, имеющие непосредственное
отношение к построению централизованной системы управления сетью: учет
трафика и написание кроссплатформенного графического интерфейса к
системе управления сетью.
2. Реализация
Для построения сервера был использован
Linux, а точнее ALT Linux Master 2.2. В целом система выглядит так:
Реализация каждого компонента системы далее рассмотрена подробно.
2.1. Подсистема управления настройками
2.1.1. Первоначальная настройка OpenLDAP
После установки OpenLDAP (в ALT Linux это можно сделать выполнив
команду apt-get install openldap openldap-servers openldap-clients
openldap-guide) необходимо отредактировать его
главный конфигурационный файл /etc/openldap/slapd.conf
следующим
образом:
# Включение необходимых схем. В каждой схеме описан набор классов
и их атрибутов.
# В дереве LDAP могут присутствовать только такие записи, которые
являются экземлярами
# классов, описанных в схемах, и удовлетворяют ограничениям этих классов
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/misc.schema
include /etc/openldap/schema/nis.schema
include /etc/openldap/schema/openldap.schema
# pid-файл и файл с аргументами запуска
pidfile
/var/run/slapd.pid
argsfile /var/run/slapd.args
# Права доступа к дереву LDAP
access to attr=userPassword
by users write
by anonymous auth
by * none
access to *
by users write
by anonymous read
by * none
# Используемый backend для хранения дерева
database ldbm
# Суффикс дерева
suffix
"dc=myserver,dc=myprovider,dc=ru"
# Администратор дерева
rootdn
"cn=manager,dc=myserver,dc=myprovider,dc=ru"
# Пароль администратора. Желательно хранить его не в открытом виде,
# а генерировать с помощью slappasswd
rootpw secret
# Каталог для хранения дерева
directory /var/lib/ldap/bases
# Уровень отладки
loglevel 0
# Индексы для ускорения поиска в дереве
index objectClass eq
В конфигурационном файле определены только самые необходимые настройки.
Об остальных можно прочесть в комментариях к оригинальному
конфигурационному файлу и в документации к OpenLDAP.
Теперь можно запустить OpenLDAP (в ALT Linux это можно
сделать выполнив команду service ldap start).
2.1.2. Настройка связки DHCP+LDAP
Для хранения конфигурации ISC DHCP-сервера в LDAP его необходимо
пересобрать с патчем dhcp-3.0.1rc13-ldap-patch
(его последнюю версию можно
найти на http://home.ntelos.net/~masneyb/).
Пользователи ALT Linux
вместо ручной пересборки могут подключить
репозиторий nm и выполнить команду apt-get install dhcp. После
установки DHCP-сервера нужно
скопировать файл dhcp.schema
(который также можно найти в составе
пакета dhcp) в каталог /etc/openldap/schema и модифицировать файл
/etc/openldap/slapd.conf (добавленные строки выделены):
# Включение необходимых схем. В каждой схеме описан набор классов
и их атрибутов.
# В дереве LDAP могут присутствовать только такие записи, которые
являются экземлярами
# классов, описанных в схемах, и удовлетворяют ограничениям этих классов
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/misc.schema
include /etc/openldap/schema/nis.schema
include /etc/openldap/schema/openldap.schema
# Включение схемы для хранения
настроек DHCP в LDAP include
/etc/openldap/schema/dhcp.schema
# pid-файл и файл с аргументами запуска
pidfile
/var/run/slapd.pid
argsfile /var/run/slapd.args
...
# Индексы для ускорения поиска в дереве
index objectClass eq
#Индексы для DHCP
index dhcpHWAddress eq index dhcpClassData
eq
После перезапуска OpenLDAP
(service ldap restart в ALT Linux) необходимо создать файл dhcpd.ldif со следующим содержимым
(или взять его из пакета dhcp):
После запуска DHCP-сервера (service dhcpd start в ALT Linux) в качестве
источника настроек он будет использовать дерево LDAP.
2.1.3. Настройка DDNS
Первоначально для хранения конфигурации DNS в LDAP планировалось
использовать соответствующий backend bind. Но затем нашлось более
простое решение: DDNS - динамический DNS, автоматически редактирующий
записи о хостах в файлах прямой и обратной зон по запросу DHCP-сервера.
Для его настройки
необходимо отредактировать настройки DHCP-сервера непосредственно в
дереве LDAP, используя файл dhcp-ddns-add.ldif:
Затем необходимо установить bind (apt-get install bind в ALT Linux) и
включить в нем поддержку DDNS. Содержимое
конфигурационных файлов и права доступа к ним приведены ниже
(предполагаем, что bind выполняется в chroot - в ALT Linux так оно и
есть):
# ls -l /var/lib/bind
total 2
drwx--x--- 2 root
named 48 Feb
12 2003 dev
drwx--x--- 2 root
named 328 Jul 11 14:11
etc
drwx--x--- 2 root
named 48 Feb
12 2003 var
drwx--x--- 2 root
named 344 Jul 11 14:23
zone
# ls -l /var/lib/bind/etc/
total 36
-rw-r----- 1 root
named 100 Apr 29 14:31
dhcp.key
-rw-r----- 1 root
named 182 Jul 11 14:00
local.conf
-rw-r----- 1 root
named 298 Jul 11 14:07
myserver.myprovider.conf
-rw-r----- 1 root
named 293 Apr 29 14:33
named.conf
-rw-r----- 1 root
named 447 Feb 10
2003 options.conf
-rw-r----- 1 root
named 564 Feb
9 2003 rfc1912.conf
-rw-r----- 1 root
named 1515 Feb 9
2003 rfc1918.conf
-rw-r----- 1 root
named 108 Feb
9 2003 rndc.conf
-rw-r----- 1 root
named 97 Jul 11
14:04 rndc.key
# cat /var/lib/bind/etc/named.conf
// This is the primary configuration file for the BIND DNS server named.
//
// If you are just adding zones, please do that in
/var/lib/bind/etc/local.conf
include "/etc/options.conf";
include "/etc/rndc.conf";
include "/etc/dhcp.key";
include "/etc/rfc1912.conf";
include "/etc/local.conf";
// Consider adding the 1918 zones here, if they are not used in your
organization.
// include "/etc/rfc1918.conf";
// Add other zones here
include "/etc/myserver.myprovider.conf";
# cat /var/lib/bind/etc/myserver.myprovider.conf
zone "myserver.myprovider.ru" {
type master;
file "myserver.myprovider.ru";
forwarders {};
allow-update { key DHCP_UPDATE; };
};
zone "1.168.192.in-addr.arpa" {
type master;
file "1.168.192.in-addr.arpa";
forwarders {};
allow-update { key DHCP_UPDATE; };
};
# ls -l /var/lib/bind/zone/
total 32
-rw-r----- 1 root
named 433 Jul 11 14:49
1.168.192.in-addr.arpa
-rw-r----- 1 root
named 212 Feb
9 2003 127.in-addr.arpa
-rw-r----- 1 root
named 309 Feb
9 2003 empty
-rw-r----- 1 root
named 208 Feb 12
2003 localdomain
-rw-r----- 1 root
named 178 Jan 13
2003 localhost
-rw-r----- 1 root
named 561 Jul 11 14:49
myserver.myprovider.ru
# cat /var/lib/bind/zone/1.168.192.in-addr.arpa
$TTL
1D
@
IN SOA
myserver.myprovider.ru. root.myserver.myprovider.ru. (
2003050603 ; serial
12H
; refresh
1H
; retry
1W
; expire
1H
; ncache
)
IN NS
myserver.myprovider.ru.
1
IN PTR
myserver.myprovider.ru.
# cat /var/lib/bind/zone/myserver.myprovider.ru
$TTL
1D
@
IN SOA
ns.myserver.myprovider.ru. root.myserver.myprovider.ru. (
2003102202 ; serial
12H
; refresh
1H
; retry
1W
; expire
1H
; ncache
)
IN
NS ns.myserver.myprovider.ru.
IN
MX 10 mail.myserver.myprovider.ru.
@
IN
A 192.168.1.1
ns
IN
A 192.168.1.1
mail
IN
A 192.168.1.1
WOfB3kj8IhJK4OZ5s3zHeQ== - это ключ,
сгенерированный следующим образом:
dnssec-keygen -a HMAC-MD5 -b 128 -n USER DHCP_UPDATE
После выполнения этой команды создаются файлы
Kdhcp_update.+157+56116.key и Kdhcp_update.+157+56116.private, из
любого можно взять строку ключа.
Перед первым запуском bind необходимо выставить на каталог
/var/lib/bind/zone права rwxrwx---, чтобы bind мог стать владельцем
файлов зон и создать файлы журналов. После того, как первый хост будет
зарегистирирован в DNS, права можно вернуть обратно.
Если все сконфигурировано
верно, при регистрации первого хоста в логах можно увидеть следующее:
Jul 11 14:23:53 myserver named[3515]: client 192.168.1.1#32770:
updating zone 'myserver.myprovider.ru/IN': adding an RR
Jul 11 14:23:53 myserver named[3515]: client 192.168.1.1#32770:
updating zone 'myserver.myprovider.ru/IN': adding an RR
Jul 11 14:23:53 myserver named[3515]: journal file
myserver.myprovider.ru.jnl does not exist, creating it
Jul 11 14:23:53 myserver dhcpd: Added new forward map from
host1.myserver.myprovider.ru to 192.168.1.11
Jul 11 14:23:53 myserver named[3515]: client 192.168.1.1#32770:
updating zone '1.168.192.in-addr.arpa/IN': deleting an rrset
Jul 11 14:23:53 myserver named[3515]: client 192.168.1.1#32770:
updating zone '1.168.192.in-addr.arpa/IN': adding an RR
Jul 11 14:23:53 myserver named[3515]: journal file
1.168.192.in-addr.arpa.jnl does not exist, creating it
Jul 11 14:23:53 myserver dhcpd: added reverse map from
11.1.168.192.in-addr.arpa to host1.myserver.myprovider.ru
Jul 11 14:23:53 myserver dhcpd: DHCPREQUEST for 192.168.1.11 from
00:c0:26:31:6a:13 via eth0
Jul 11 14:23:53 myserver dhcpd: DHCPACK on 192.168.1.11 to
00:c0:26:31:6a:13 via eth0
2.1.4. Настройка Squid и IPTables
Ни Squid, ни IPTables не поддерживают хранение собственных
настроек в LDAP. Для Squid существует модуль авторизации пользователей
из LDAP, но нам требуется разграничение доступа не по пользователям, а
по рабочим станциям. Настройки IPTables - это стартовый скрипт и/или
файл дампа, что тоже не совсем подходит.
Самый простой выход в данной ситуации - создание собственной схемы
internet-access.schema
со следующим содержимым:
attributetype ( 1.1.2.1.1.1 NAME 'allowNat'
DESC 'allow use NAT'
EQUALITY booleanMatch
SYNTAX
1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
attributetype ( 1.1.2.1.1.2 NAME 'allowProxy'
DESC 'allow use proxy'
EQUALITY booleanMatch
SYNTAX
1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
attributetype ( 1.1.2.1.1.3 NAME 'forceProxy'
DESC 'force use proxy'
EQUALITY booleanMatch
SYNTAX
1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
objectclass ( 1.1.2.2.1.1 NAME 'internetAccess' SUP top STRUCTURAL
DESC 'internet access rules'
MUST ( allowNat $ allowProxy
$ forceProxy ))
В схеме определяется класс internetAccess и его атрибуты: allowNat
(разрешить использование NAT),
allowProxy (разрешить использование прокси-сервера) или forceProxy
(использовать прокси-сервер в прозрачном режиме). Cозданную схему
необходимо скопировать в каталог
/etc/openldap/schema и модифицировать файл
/etc/openldap/slapd.conf (добавленные строки выделены):
...
# Включение схемы для хранения
настроек DHCP в LDAP
include
/etc/openldap/schema/dhcp.schema
# Включение схемы для хранения
настроек Squid и IPTables
include
/etc/openldap/schema/internet-access.schema
# pid-файл и файл с аргументами запуска
pidfile
/var/run/slapd.pid
argsfile /var/run/slapd.args
...
После перезапуска OpenLDAP
(service ldap restart в ALT Linux) необходимо внести данные,
соответствующие схеме, из файла
internet-access.ldif:
dn: cn=host1, cn=192.168.1.0, cn=Network, dc=myserver, dc=myprovider,
dc=ru
changetype: modify
add: objectClass
objectClass: internetAccess
-
replace: allowNat
allowNat: TRUE
-
replace: allowProxy
allowProxy: TRUE
-
replace: forceProxy
forceProxy: TRUE
Для извлечения списка хостов, у которых одно из свойств allowNat,
allowProxy или forceProxy установлено в TRUE,
можно использовать такой простой скрипт (/opt/scripts/make_ldap_filter.sh):
# Устанавливаем правила по умолчанию для тех пакетов,
# про которые мы случайно забудем при написании правил ;)
$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -P FORWARD DROP
# Создаем конечные цепочки для регистрации пакетов
# подробнее об этом в разделе 2.2.3
$IPTABLES -N allowed
$IPTABLES -A allowed -j ULOG --ulog-nlgroup 1 --ulog-qthreshold 50
--ulog-prefix allow
$IPTABLES -A allowed -j ACCEPT
$IPTABLES -N rejected
$IPTABLES -A rejected -j ULOG --ulog-nlgroup 1 --ulog-qthreshold 50
--ulog-prefix drop
$IPTABLES -A rejected -j DROP
# Выпускаем все пакеты наружу без ограничений
$IPTABLES -A OUTPUT -j allowed
# Впускаем все пакеты, которые пришли не с внешнего интерфейса
$IPTABLES -A INPUT ! -i $EXTERNAL_DEVICE -j allowed
# Не впускаем TCP-пакеты, пришедшие с внешнего интерфейса,
# для которых статус установлен в NEW, но которые на самом деле
таковыми не являются
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p TCP ! --syn -m state --state
NEW -j rejected
# Впускаем TCP-пакеты, пришедшие с внешнего интерфейса на порт 22
# и пытающиеся установить новое соединение (для ssh)
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p TCP --syn --dport 22 -j
allowed
# Впускаем все TCP-пакеты, пришедшие с внешнего интерфейса, для которых
соединение уже установлено
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p TCP -m state --state
ESTABLISHED,RELATED -j allowed
# Впускаем все UDP-пакеты, пришедшие с внешнего интерфейса, необходимые
для DNS
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p UDP --dport 53 -j allowed
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p UDP --sport 53 -j allowed
# Впускаем все UDP-пакеты, пришедшие с внешнего интерфейса, необходимые
для NTP
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p UDP --dport 123 -j allowed
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p UDP --sport 123 -j allowed
# Впускаем все ICMP-пакеты, пришедшие с внешнего интерфейса
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -p ICMP -j allowed
# Не впускаем все прочие пакеты, пришедшие с внешнего интерфейса
$IPTABLES -A INPUT -i $EXTERNAL_DEVICE -j rejected
# Добавляем правила для всех хостов, которым разрешено использовать NAT
for i in `/opt/scripts/make_ldap_filter.sh allowNat`
do
# Заменяем адреса пакетов
$IPTABLES -t nat -A POSTROUTING -o $EXTERNAL_DEVICE
-s $i -j MASQUERADE
# Выпускаем все пакеты наружу без ограничений
$IPTABLES -A FORWARD -i $INTERNAL_DEVICE -o
$EXTERNAL_DEVICE -s $i -j allowed
# Не впускаем TCP-пакеты, пришедшие с внешнего
интерфейса, пытающиеся установить новое соединение
$IPTABLES -A FORWARD -o $INTERNAL_DEVICE -i
$EXTERNAL_DEVICE -p TCP --syn -d $i -j rejected
# Не впускаем ICMP-пакеты с типом 8 (request),
пришедшие с внешнего интерфейса
$IPTABLES -A FORWARD -o $INTERNAL_DEVICE -i
$EXTERNAL_DEVICE -p ICMP --icmp-type 8 -d $i -j rejected
# Впускаем все прочие пакеты
$IPTABLES -A FORWARD -o $INTERNAL_DEVICE -i
$EXTERNAL_DEVICE -d $i -j allowed
done
# Добавляем правила для всех хостов, которым разрешено использовать
прокси-сервер в прозрачном режиме
for i in `/opt/scripts/make_ldap_filter.sh forceProxy`
do
# Заворачиваем HTTP-запросы на прокси-сервер
$IPTABLES -t nat -A PREROUTING -s $i -p TCP --dport
80 -j REDIRECT --to-port 3128
done
При этом в конфигурационном файл Squid (/etc/squid/squid.conf)
должно быть написано примерно следующее:
hierarchy_stoplist cgi-bin ?
acl QUERY urlpath_regex cgi-bin \?
no_cache deny QUERY
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours
refresh_pattern
^ftp:
1440 20% 10080
refresh_pattern ^gopher:
1440 0% 1440
refresh_pattern
.
0 20% 4320
acl all src 0.0.0.0/0.0.0.0
acl manager proto cache_object
acl localhost src 127.0.0.1/255.255.255.255
acl to_localhost dst 127.0.0.0/8
acl SSL_ports port 443 563
acl Jabber_ports port 5222
acl Safe_ports port
80 # http
acl Safe_ports port
21 # ftp
acl Safe_ports port 443 563 # https, snews
acl Safe_ports port
70 # gopher
acl Safe_ports port 210
# wais
acl Safe_ports port 1025-65535 # unregistered ports
acl Safe_ports port 280
# http-mgmt
acl Safe_ports port 488
# gss-http
acl Safe_ports port 591
# filemaker
acl Safe_ports port 777
# multiling http
acl CONNECT method CONNECT
http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports !Jabber_ports
acl allowed_hosts src "/etc/squid/allowed_hosts"
http_access allow allowed_hosts
http_access allow localhost
http_access deny all
http_reply_access allow all
icp_access allow all
httpd_accel_host virtual
httpd_accel_with_proxy on
httpd_accel_uses_host_header on
error_directory /usr/share/squid/errors/Russian-koi8-r
coredump_dir /var/spool/squid
visible_hostname myserver.myprovider.ru
2.1.5. Настройка авторизации в OpenLDAP
Если все основные настройки системы хранятся в LDAP, то вполне
естественно хранить там же и учетные записи пользователей. Для этого
желательно модифицировать файл
/etc/openldap/slapd.conf (добавленные строки выделены):
...
# Индексы для ускорения поиска в дереве
index objectClass eq
#Индексы для DHCP
index dhcpHWAddress eq
index dhcpClassData
eq
# Индексы для авторизации index cn, uid, uidNumber, gidNumber eq
Затем необходимо создать файл users.ldif
со следующим содержимым:
dn: cn=Users, dc=myserver, dc=myprovider, dc=ru
objectClass: top
Теперь созданные пользователи LDAP являются также системными
пользователями:
# id ldapuser1
uid=1001(ldapuser1) gid=10(wheel) groups=10(wheel)
# passwd ldapuser1
passwd: updating all authentication tokens for user ldapuser1.
New password:
Re-enter new password:
LDAP password information changed for ldapuser1
passwd: all authentication tokens updated successfully.
$ ssh ldapuser1@localhost ldapuser1@localhost's password: Last login: Tue Apr 20 10:38:21 2004 from localhost.localdomain $
2.1.6. Хранение в OpenLDAP
произвольной
справочной информации
Все что в настоящее хранится в LDAP (как информация о хостах, так и о
пользователях), тем или иным способом используется различными
сервисами. Кроме этого, иногда бывает полезно хранить дополнительную
информацию о хостах, которая будет представлять интерес только для
системного администратора. Хранение такой дополнительной информации
поддерживает схема info.schema:
attributetype ( 1.1.2.1.2.1 NAME 'comment'
DESC 'Host comment'
EQUALITY caseIgnoreMatch
SYNTAX
1.3.6.1.4.1.1466.115.121.1.15{256} )
objectclass ( 1.1.2.2.1.2 NAME 'hostsInfo' SUP top STRUCTURAL
DESC 'Hosts additional info'
MAY ( comment ))
Необходимо скопровать схему в каталог /etc/openldap/schema и
модифицировать файл
/etc/openldap/slapd.conf (добавленные строки выделены):
# В дереве LDAP могут присутствовать только такие записи, которые
являются экземлярами
# классов, описанных в схемах, и удовлетворяют ограничениям этих классов
include /etc/openldap/schema/core.schema
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/misc.schema
include /etc/openldap/schema/nis.schema
include /etc/openldap/schema/openldap.schema
# Включение схемы для хранения настроек DHCP в LDAP
include /etc/openldap/schema/dhcp.schema
# Включение схемы для хранения настроек Squid и IPTables
include /etc/openldap/schema/internet-access.schema
# Включение схемы для хранения
справочной информации о хостах include
/etc/openldap/schema/info.schema
# pid-файл и файл с аргументами запуска
pidfile
/var/run/slapd.pid
argsfile /var/run/slapd.args
...
После перезапуска OpenLDAP
(service ldap restart в ALT Linux) необходимо создать файл info.ldif со следующим содержимым:
dn: cn=host1, cn=192.168.1.0, cn=Network, dc=myserver, dc=myprovider,
dc=ru
changetype: modify
add: objectClass
objectClass: hostsInfo
Главный скрипт, который заставляет все необходимые сервисы перечитать
конфигурацию (/opt/scripts/make_all.sh),
выглядит следующим образом:
#!/bin/bash
echo "Apply DHCP settings ..."
service dhcpd restart
echo "Apply Firewall settings ..."
/opt/scripts/make_iptables.sh
service iptables save
echo "Apply Proxy settings ..."
/opt/scripts/make_squid.sh
service squid restart
Таким образом, первые две задачи - централизованное управление
настройками рабочих станций и централизованное управление доступом в
интернет - решены. Для внесения изменений в конфигурацию рабочей
станции достаточно изменить запись о ней в дереве LDAP и выполнить
скрипт /opt/scripts/make_all.sh
2.2. Подсистема учета трафика
В качестве СУБД для подсистемы учета трафика был выбран Firebird
Classic Server 1.5. Основная причина такого выбора заключалась в том,
что сервер, на котором конфигурировалась подсистема управления
настройками, имел очень скромную аппаратную конфигурацию. В то же время
рядом стоял уже настроенный сервер БД, поэтому и было принято решение
использовать его.
Скрипты для обработки данных о трафике написаны на Perl, поэтому для их
работы необходимо наличие в системе самого Perl'а и DBI-драйвера для
Firebird, последняя версия которого доступна с http://dbi-interbase.sourceforge.net.
Пользователи ALT Linux
вместо ручной установки драйвера могут подключить
репозиторий nm и выполнить команду apt-get install
perl-DBD-InterBase. Кроме того, необходимо наличие как минимум
клиентской части Firebird. Существует 2 неправильных способа установить
клиентскую часть Firebird: установить Firebird целиком (его последняя
версия доступна на http://firebird.sourceforge.net)
или скопировать с машины, на которую он установлен, файл
libfbclient.so.1.5.0 и создать ссылку libfbclient.so.1 ->
libfbclient.so.1.5.0. Правильным способом была бы пересборка Firebird c
учетом специфики ALT Linux (включая разбиение его на пакеты
firebird-server-cs, firebird-server-ss, firebird-client,
firebird-devel, firebird-doc), однако эту задачу я отложил до лучших
времен.
2.2.1. Создание базы данных
Для создания базы данных на сервере Firebird необходимо создать
следующий файл metadata.sql:
SET SQL DIALECT 3;
CREATE DATABASE 'localhost:billing' USER 'sysdba' PASSWORD 'masterkey';
/* Таблица для хранения данных о трафике, прошедшем через прокси-сервер */ CREATE TABLE PROXY_LOG (LOG_DATE DATE, /* дата */ CLIENT_ADDRESS VARCHAR(256), /* адрес рабочей станции */ HOST VARCHAR(255), /* адрес сервера, на который отправлен HTTP-запрос */ REQUEST_METHOD VARCHAR(10), /* тип HTTP-запроса */ TOTAL_COUNT BIGINT, /* общее количество запросов */ TOTAL_BYTES BIGINT, /* общее количество запрошенных байт */ CACHE_COUNT BIGINT, /* количество запросов, для ответа на которые был использован кэш прокси-сервера */ CACHE_BYTES BIGINT); /* количество байт, взятых из кэша */
/* Таблица для хранения данных о трафике, прошедшем через NAT */ CREATE TABLE TRANSFER_LOG (LOG_DATE DATE, /* дата */ PROTOCOL SMALLINT, /* номер протокола (из /etc/protocols) */ SOURCE VARCHAR(15), /* отправитель пакета */ SOURCE_PORT INTEGER, /* порт отправителя */ DESTINATION VARCHAR(15), /* получатель пакета */ DESTINATION_PORT INTEGER, /* порт получателя */ INCOMING VARCHAR(4), /* входящий интерфейс */ OUTGOING VARCHAR(4), /* исходящий интерфейс */ PREFIX VARCHAR(25), /* префикс из ULOG */ PACKETS BIGINT, /* количество пакетов */ BYTES BIGINT); /* количество байт */
COMMIT WORK;
CREATE INDEX PROXY_LOG_DATE ON PROXY_LOG(LOG_DATE); CREATE INDEX TRANSFER_LOG_DATE ON TRANSFER_LOG(LOG_DATE); CREATE INDEX TRANSFER_LOG_DESTINATION ON TRANSFER_LOG(DESTINATION); CREATE INDEX TRANSFER_LOG_DESTINATION_PORT ON TRANSFER_LOG(DESTINATION_PORT); CREATE INDEX TRANSFER_LOG_INCOMING ON TRANSFER_LOG(INCOMING); CREATE INDEX TRANSFER_LOG_OUTGOING ON TRANSFER_LOG(OUTGOING); CREATE INDEX TRANSFER_LOG_PREFIX ON TRANSFER_LOG(PREFIX); CREATE INDEX TRANSFER_LOG_SOURCE ON TRANSFER_LOG(SOURCE); CREATE INDEX TRANSFER_LOG_SOURCE_PORT ON TRANSFER_LOG(SOURCE_PORT); COMMIT WORK;
GRANT SELECT ON PROXY_LOG TO USER OPERATOR; GRANT SELECT ON TRANSFER_LOG TO USER OPERATOR; COMMIT WORK;
В файл /opt/firebird/aliases.conf на сервере Firebird необходимо
добавить строку:
billing = /var/db/firebird/billing.fdb
Затем на сервере Firebird нужно выполнить команду:
/opt/firebird/bin/isql -i metadata.sql
2.2.2. Учет прокси-трафика
Источником данных о трафике, проходящем через прокси-сервер
Squid, является файл /var/log/squid/access.log. Формат файла описан в
документации Squid. Существует множество готовых программ и скриптов
для анализа содержимого access.log, доступных по какой-либо открытой
лицензии. На основе одного из них (скрипта из пакета loganalyzer из ALT
Linux) с минимальными изменениями был написан скрипт /opt/scripts/translate_squid.pl:
#!/usr/bin/perl # # Based on loganalyzer (Sergey N. Yatskevich <syatskevich@altlinux.ru>) # # Обработка лога Squid'а и запись результатов в таблицу proxy_log # # При обработке лога извлекается следующая информация : # 1) время (с детализацией по дням) обращения # 2) машина, с которой произошло обращение # 3) хост, к которому произошло обращение (только доменное имя) # 4) метод обращения (GET или POST) # 5) Взят-ли объект из кэша или произошла его загрузка из сети # 6) объем загруженной информации # # Интересует "объем информации"/"количество обращений" в разрезе # 1, 2, 3, 4 #
# Пропускаем сообщения об ошибках выдаваемых Squid пользователям # и запросы к самому Squid'у if ($result_codes =~ /^NONE/ || $url =~ /^cache_object:/) { return ("SKIP_LINE", "", "", "", "", 0, 0); }
# Squid не проставляет при типе соединения CONNECT (по крайней # мере для ICQ) используемый протокол, что вызывает ошибку # в пакете URI. Поэтому добавляем протокол сами. if ($request_method eq "CONNECT" && $url !~ /^https:/i) { $url = "https://$url"; }
die "Ошибка вставки записи, откат в начальное состояние\n"; } } }
$dbh->commit; $dbh->disconnect;
Для регулярной очистки файла access.log и загрузки его содежимого в
базу данных можно использовать мехнизм logrotate. В этом случае файл /etc/logrotate.d/squid
необходимо отредактировать следующим образом:
/var/log/squid/store.log {
daily
rotate 7
copytruncate
compress
notifempty
missingok
# This script asks squid to rotate its logs on its own.
# Restarting squid is a long process and it is not worth
# doing it just to rotate logs
postrotate
/usr/sbin/squid -k rotate
endscript
}
2.2.3. Учет NAT-трафика
Для учета NAT-трафика используется механизм ULOG. Работает он
следующим образом: средствами IPTables для части пакетов может быть
указана цель ULOG. Это означает, что копии пакетов (или только
заголовки) из ядра отправляются в userspace, где их ждет специальный
демон, умеющий их обрабатывать. В настоящее время существует 2 таких
демона: ulogd и ulog-acctd. Ulog-acctd проще, компактнее и умеет
группировать пакеты, поэтому он и был выбран как средство учета
NAT-трафика. Последнюю версию ulog-acctd можно загрузить с http://alioth.debian.org/projects/pkg-ulog-acctd.
Пользователи ALT Linux
вместо ручной сборки могут подключить
репозиторий nm и выполнить команду apt-get install ulog-acctd.
После установки ulog-acctd необходимо отредактировать его
конфигурационный файл /etc/ulog-acctd.conf:
Затем нужно запустить ulog-acctd (service ulog-acctd start в ALT Linux).
Источником данных о трафике, проходящем через ulog-acctd,
является файл /var/log/ulog-acctd/account.log. Для обработки данных о
трафике на основе скрипта из пакета loganalyzer из ALT
Linux был написан скрипт /opt/scripts/translate_ulog.pl:
#!/usr/bin/perl # # Based on loganalyzer (Sergey N. Yatskevich <syatskevich@altlinux.ru>) # # Обработка лога ulog-acctd и запись результатов в таблицу transfer_log # # При обработке лога извлекается следующая информация : # 1) время (с детализацией по дням) обращения # 2) протокол (1 - ICMP, 6 - TCP, 17 - UDP) # 3) отправитель пакета # 4) порт отправителя # 5) адресат пакета # 6) порт адресата # 7) входящий интерфейс # 8) исходящий интерфейс # 9) префикс ulog-acctd # 10) количество и размер пакетов
die "Ошибка вставки записи, откат в начальное состояние\n"; } } }
$dbh->commit; $dbh->disconnect;
Для регулярной очистки файла account.log и загрузки его содежимого в
базу данных можно использовать мехнизм logrotate. В этом случае файл /etc/logrotate.d/ulog-acctd
необходимо отредактировать следующим образом:
/var/log/ulog-acctd/account.log /var/log/ulog-acctd/debug.log { weekly rotate 5 copytruncate compress notifempty missingok sharedscripts prerotate if [ -e /var/run/ulog-acctd.pid ]; then kill -STOP `cat /var/run/ulog-acctd.pid`; fi endscript postrotate if [ -e /var/run/ulog-acctd.pid ]; then kill -CONT `cat /var/run/ulog-acctd.pid`; fi endscript }
2.3. Графический интерфейс системы администрирования
Для адинистрирования системы можно использовать стандартные
средства LDAP (от утилит, работающих в режиме командной строки, до
графических клиентов: GQ - http://biot.com/gq/,
JXplorer - http://pegacat.com/jxplorer/)
и Firebird (от стандартного isql до IBExpert - http://ibexpert.com/). Однако все они
обладают слишком широкой функциональностью, поэтому для управления
созданной структурой была создана Network Console. Бинарный дистрибутив
(для Linux и Windows) лежит здесь,
а исходные коды - здесь.
Network Console написана на Java, поэтому для исполнения она требует
JRE, а для компиляции - JDK. И JDK, и JRE (как отдельно, так и в
составе JDK) доступны на http://java.sun.com/j2se/1.4.2/download.html.
2.3.1. Описание графического интерфейса с точки
зрения пользователя
В составе бинарного дистрибутива есть 3 стартовых скрипта:
Скрипт
Назначение
networkconsole-linux-gtk2.sh
Скрипт для запуска Network
Console в Linux с использованием GTK2
networkconsole-linux-motif.sh
Скрипт для запуска Network
Console в Linux с использованием OpenMotif
networkconsole-win32.bat
Скрипт для запуска Network
Console в Windows
При запуске Network Console необходимо указать параметры подключения к
OpenLDAP и Firebird:
Кроме того, существуют еще 2 параметра подключения к OpenLDAP, которые
можно изменить только в конфигурационном файле
networkconsole.properties:
Главное окно запущенной Network Console состоит из 3-х вкладок:
1. Управление пользователями OpenLDAP (теми, кто может модифицировать
дерево LDAP, в том числе и с помощью Network Console) - версия для GTK2:
2. Управление настройками хостов - версия для OpenMotif:
3. Выполнение запросов к базе данных, в которой хранится информация о
трафике - версия для Win32:
Для первых двух вкладок возможно добавление, удаление и редактирование
записей:
Для всех вкладок возможен экспорт списка в XML с XSLT-трансформацией:
Для вкладки "Трафик" возможно использование готовых запросов,
реализованных в виде хранимых процедур. Чтобы использовать эту
возможность, необходимо на сервере Firebird создать файл procedures.sql со следующим
содержимым:
SET ECHO ON; SET SQL DIALECT 3; SET NAMES WIN1251;
CONNECT 'localhost:billing' USER 'sysdba' PASSWORD 'masterkey';
begin select sum(bytes) from transfer_log where outgoing<>'ppp0' and incoming='ppp0' and prefix='allow' and log_date>=:period_begin and log_date<=:period_end into :incoming_allowed; select sum(bytes) from transfer_log where outgoing<>'ppp0' and incoming='ppp0' and prefix='drop' and log_date>=:period_begin and log_date<=:period_end into :incoming_rejected; select sum(bytes) from transfer_log where outgoing='ppp0' and incoming<>'ppp0' and log_date>=:period_begin and log_date<=:period_end into :outgoing; suspend; end ^
CREATE PROCEDURE NAT_TRAFFIC_BY_HOSTS (PERIOD_BEGIN DATE, PERIOD_END DATE) RETURNS (HOST VARCHAR(15) CHARACTER SET NONE, INCOMING_ALLOWED BIGINT, INCOMING_REJECTED BIGINT, OUTGOING BIGINT) AS
begin for select destination, sum(bytes) from transfer_log where outgoing='eth0' and incoming='ppp0' and prefix='allow' and log_date>=:period_begin and log_date<=:period_end group by destination into :host, :incoming_allowed do begin select sum(bytes) from transfer_log where outgoing='eth0' and incoming='ppp0' and prefix='drop' and log_date>=:period_begin and log_date<=:period_end and destination=:host into :incoming_rejected; select sum(bytes) from transfer_log where outgoing='ppp0' and incoming='eth0' and log_date>=:period_begin and log_date<=:period_end and source=:host into :outgoing; suspend; end end ^
CREATE PROCEDURE PROXY_TRAFFIC_BY_HOSTS (PERIOD_BEGIN DATE, PERIOD_END DATE) RETURNS (HOST VARCHAR(15) CHARACTER SET NONE, TOTAL BIGINT, CACHED BIGINT) AS
begin for select client_address, sum(total_bytes), sum(cache_bytes) from proxy_log where log_date>=:period_begin and log_date<=:period_end group by client_address into :host, :total, :cached do begin suspend; end end ^
CREATE PROCEDURE REJECTED_INNER_TRAFFIC (PERIOD_BEGIN DATE, PERIOD_END DATE) RETURNS (PROTOCOL SMALLINT, SOURCE VARCHAR(15) CHARACTER SET NONE, SOURCE_PORT INTEGER, DESTINATION VARCHAR(15) CHARACTER SET NONE, DESTINATION_PORT INTEGER, BYTES BIGINT) AS
begin for select protocol, source, source_port, destination, destination_port, sum(bytes) from transfer_log where outgoing='eth0' and incoming='ppp0' and prefix='drop' group by protocol, source, source_port, destination, destination_port order by 6 desc into :protocol, :source, :source_port, :destination, :destination_port, :bytes do begin suspend; end end ^
CREATE PROCEDURE REJECTED_OUTER_TRAFFIC (PERIOD_BEGIN DATE, PERIOD_END DATE) RETURNS (PROTOCOL SMALLINT, SOURCE VARCHAR(15) CHARACTER SET NONE, SOURCE_PORT INTEGER, DESTINATION VARCHAR(15) CHARACTER SET NONE, DESTINATION_PORT INTEGER, BYTES BIGINT) AS
begin for select first 25 protocol, source, source_port, destination, destination_port, sum(bytes) from transfer_log where outgoing<>'ppp0' and incoming='ppp0' and prefix='drop' group by protocol, source, source_port, destination, destination_port order by 6 desc into :protocol, :source, :source_port, :destination, :destination_port, :bytes do begin suspend; end end ^
SET TERM ; ^
COMMIT WORK ;
GRANT SELECT ON PROXY_LOG TO USER OPERATOR; GRANT SELECT ON TRANSFER_LOG TO USER OPERATOR; GRANT EXECUTE ON PROCEDURE FULL_TRAFFIC TO USER OPERATOR; GRANT EXECUTE ON PROCEDURE NAT_TRAFFIC_BY_HOSTS TO USER OPERATOR; GRANT EXECUTE ON PROCEDURE PROXY_TRAFFIC_BY_HOSTS TO USER OPERATOR; GRANT EXECUTE ON PROCEDURE REJECTED_INNER_TRAFFIC TO USER OPERATOR; GRANT EXECUTE ON PROCEDURE REJECTED_OUTER_TRAFFIC TO USER OPERATOR;
COMMIT WORK ;
CREATE TABLE TRANSLATION (LANGUAGE VARCHAR(10), OBJECT_TYPE VARCHAR(25), SYSTEM_NAME VARCHAR(256), TRANSLATED_NAME VARCHAR(256) CHARACTER SET WIN1251);
Затем на сервере Firebird нужно выполнить команду:
/opt/firebird/bin/isql -i procedures.sql
После этого в файле networkconsole.properties исправить
ui.view.traffic.translate=false на ui.view.traffic.translate=true и
перезапустить Network Console.
На вкладке "Трафик" на панели инструментов нажать кнопку "Выбрать" и
указать требуемый отчет:
Теперь в поле редактирования sql-запроса появится:
-- Потребление proxy-трафика по хостам
select * from proxy_traffic_by_hosts('11.04.2004','11.07.2004')
Этот запрос можно выполнить обычным образом, нажав кнопку "Обновить".
Аналогичным образом можно создавать в базе данных любые отчеты в виде
хранимых процедур, принимающих в качестве параметров даты начала и
окончания перода, и их описаний в таблице TRANSLATION. Вносить
изменения в Network Console для поддержки этих отчетов не требуется.
2.3.2. Реализация графического интерфейса
В состав архива с исходными кодами Network Console включены все
необходимы библиотеки, поэтому ничего, кроме стандартного JDK, для
сборки не потребуется. Для сборки необходимо отредактировать файл
build.sh (или build.bat) и выполнить его с параметром
distrib-crossplatform.
Для отрисовки графического интерфейса Network Console использует
SWT/JFace вместо стандартной для Java библиотеки Swing. Основное
отличие SWT/JFace от Swing - использование стандартных графических
библиотек той операционной системы, под управлением которой исполняется
приложение. Например, под Windows Network Console использует Win23 API,
а под Linux - GTK2 или OpenMotif. Помимо увеличения производительности
такой подход позволяет Network Console выглядеть стандартным образом в
различных средах и использовать настройки внешнего вида той среды, в
которой она работает. Отрицательная стророна такого подхода -
использование механизма JNI для подключения графических библиотек и,
как следствие, худшая переносимость по сравнению со Swing, который не
использует никаких посторонних библиотек, а рисует виджеты собственными
средствами.
Приводить здесь листинг всех исходных кодов Network Console не имеет
смысла, лучше ограничиться общим описанием пакетов и классов.
Основным пакетом является networkconsole.datamodel. Диаграмма, на
которой изображены классы и интерфейсы пакета и связи между ними,
выглядит так:
В пакете существуют 2 иерархии: потомки абстрактных классов Entry
(предствляет запись, которая может храниться как в LDAP, так и в JDBC)
и EntryList (представляет набор таких записей). Потомки LdapEntry имеет
фиксированное число полей (для каждого поля определены методы
set/get/describe), а количество полей JdbcEntry заранее не известно.
Значения полей потомков LdapEntry можно модифицировать. Потомки
EntryList умеют оповещать о своих изменениях с помощью интерфейса
IEntryListAdapter.
Примеры использования классов пакета networkconsole.datamodel можно
найти в пакете networkconsole.tests.
Следующие пакеты используют классы networkconsole.datamodel для решения
своих задач:
Пакет
Назначение
networkconsole.datamodel.persistance
Сохранение/извлечение потомков
LdapEntry в/из LDAP посредством фабрик JNDI
networkconsole.datamodel.sax
Представление потомков
LdapEntryList и JdbcEntryList в виде потока событий SAX для
использования в JAXP
networkconsole.datamodel.swt.dialogs
Предоставление диалоговых окон
для экспорта и редактирования потомков LdapEntryList и JdbcEntryList
networkconsole.datamodel.swt.providers
Предоставление провайдеров JFace
отображения потомков LdapEntryList и JdbcEntryList в TableViewer
Два последних пакета ориентированы на использование
networkconsole.datamodel в десктопном приложении на основе SWT/JFace,
но ничто не мешает создать аналогичные пакеты для использования классов
networkconsole.datamodel в приложении с web-интерфейсом или интерфейсом
на основе Swing.
Пакет networkconsole.swt.extensions расширяет возможности SWT/JFace,
добавляя поддержку еще одного провайдера - ITableColumnProvider - для
управления колонками TableViewer.
Наконец, в пакете networkconsole.ui находится основной код Network
Console - диалоговое окно для ввода параметров подключения, главное
окно приложения, вкладки, окно выбора готовых отчетов об использовании
трафика.
Такое разделение на пакеты/классы позволяет локализовать внесение
изменений, не затрагивая те модули, для которых эти изменения не
принципиальны. Например, чтобы добавить поддержку еще одного параметра
для хоста, нужно исправить только класс LdapHostEntry и 2 фабрики из
networkconsole.datamodel.persistance: LdapHostObjectFactory и
LdapHostStateFactory, при этом основной код Network Console править не
нужно.
3. Итоги
Главное - не конкретные решения, а технология. Предложенные в статье
решения применимы без изменений для довольно узкого круга задач. Но
описанная технология в любом случае позволяет упростить
администрирование небольших локальных сетей. Та же технология с
некоторыми изменениями (фактически они сведутся к увеличению настроек,
хранимых в LDAP, поддержке новых сервисов и, как следствие, к
модификации Network Console) может быть использована для управления
более крупными сетями.
Приложение А. Подключение
репозитория nm для пользователей ALT Linux
Для подключения репозитория nm необходимо загрузить файл nm-repositiry.tar.bz2 и извлечь его
содержимое в любой каталог (например,
/home/public/distrib/nm-repository), а затем вписать следующие строки в
файл /etc/apt/sources.list:
rpm file:/home/public/distrib/nm-repository i586 nm
rpm-src file:/home/public/distrib/nm-repository
i586 nm