назад вверх вперёд содержание НПО Криста

Подразделы


О запуске нескольких разных InterBase в Linux

Общие принципы

Во-первых, о применимости данного опыта. Я говорю о Linux, потому что я именно на нём это дело испытывал. Конкретно -- на Slackware 7.1 с доустановленными обновлениями из Slackware-current. Однако на самом деле подобные сооружения должны работать на любой версии любой юниксообразной системы. Главное, чтобы в этой системе работал InterBase и chroot.

Во-вторых, я пробовал виртуализовать только Classic. Есть мысли о том, как это можно сделать в Super, изложены ниже.

Итак, создав в файловой системе ветку, по структуре напоминающую систему в целом, и содержащую файлы, необходимые для запуска нужного приложения, можно создать для него виртуальную среду, достаточно сильно изолированную от остальной части системы. В этой среде с помощью системного вызова chroot или одноимённой команды можно запускать нужные процессы, видящие в качестве корня только свою ветку. Этот изменённый корень наследуется процессами, так что выйти за его пределы они обычно не могут.

Тут же замечу, что хотя chroot и способствует усложнению взлома системы со стороны процессов, работающих в ``защищённой'' среде, тем не менее полных гарантий сам по себе не даёт. Особенно если в рамках среды chroot существуют процессы с правами root, есть программы suid, и т. п. Список требований достаточно длинный, и к сожалению никто не может поручиться, что полный. Так что безопасность -- вообще отдельный вопрос, который мы сейчас не рассматриваем.

В системе одновременно может функционировать сколько угодно виртуальных сред на основе chroot. Точнее -- столько, на сколько хватит ресурсов. При этом в разных средах может быть установлен разный софт разных версий. Главное, чтобы он нормально взаимодействовал с ядром системы, так как оно у всех общее. К счастью, в Linux системные вызовы в последние годы существенно не менялись, особенно в той части, которая нужна для InterBase. И версии libc разных поколений с текущими ядрами вполне нормально уживаются. В моём случае было ядро 2.2.18, libc 5.4.46 в ветке для InterBase 4.0, glibc 2.2.1 во всех.

И последний из теоретических вопросов -- как поделить между серверами сеть. Ведь клиенты ожидают, что в сервере будет работать только один InterBase, и он должен ``слушать'' знаменитый порт 3050. Проблема решается навешиванием на сетевой интерфейс машины нескольких адресов IP, которым через hosts/DNS присваиваются разные имена. Далее пишется небольшая программка (у меня был скрипт на Perl), которая в зависимости от адреса, на который пришло соединение, делает chroot в нужную среду, и запускает там местный gds_inet_server.

Как это было

Вообще надо сказать, что для таких операций, как создание веток файловой системы под chroot, запуска различных команд из inetd в зависимости от адреса соединения, и т. п., существуют готовые инструменты. Однако в те дни, когда я это делал, на солнце были вспышки, спутниковая тарелка, через которую у нас Инет, глючила всеми возможными способами, да и просто было лениво ``вылезать наружу''. Тем более что самопальные решения оказались не такими уж и сложными.

Итак, понадобилось тестировать разрабатываемый софт с разными версиями IB. А так же средство для конвертации баз между разными ODS через backup-restore. Для установки были выбраны дистрибутивы:

Первым делом нужно было сформировать сами виртуальные среды. Я начал с того, что сформировал ``пустышку'', в которой были набросаны файлы, минимально необходимые для запуска инсталятора всего остального. В Slackware -- installpkg из пакета hdsetup.tgz. Понадобились каталоги /bin, /sbin, /lib (часть крупных, но ненужных файлов выкинул), /tmp (пустой), кое-что из /var. Выяснял методом тыка, периодически запуская что-нибудь наподобие chroot виртуальный-корень /sbin/installpkg /bash.tgz, и выясняя, чего ему ещё не хватает. Сам bash.tgz, естественно, тоже пришлось положить в ту самую ветку, чтобы он был виден изнутри.

После того, как инсталятор в виртуальной среде ожил, тем же путём (то есть закидыванием пакета в каталог, и запуском инсталятора ``внутри'' были поставлены aaa_base, bin, elflibs, etc, fileutils, find, glibcso, grep, gzip, hdsetup (для чистоты), ldso, perl, tar, txtutils, util, zoneinfo.

Полученная ветка файловой системы была заархивирована и использовалась впоследствии в качестве заготовки. С ней было снято три копии, под три варианта InterBase. В каждую копию был положен соответствующий дистрибутив, после чего делалось chroot виртуальный-корень /bin/bash и в получившейся комстроке делалась обычная установка. Инсталятор обновлял /etc/services и /etc/inetd.conf, которые видел, и которые к реальному функционированию системы отношения не имели.

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

В заключение установок каждой ветки создавался каталог /database (в соответствующей ветке) и ему настраивались права для логина interbase. Сам логин был руками внесён в /etc/passwd и /etc/shadow каждой ветки, но исходно был создан в основной системе. При таких копированиях важно проследить, чтобы один и тот же пользователь везде получил один и тот же номер uid. Кроме этого были сняты атрибуты suid и sgid со всех файлов в ветках chroot. На всякий случай.

После того, как локальные подключения в пределах каждого chrootбыли проверены на работоспособность, я перешёл к сетевой части. Во-первых, были изготовлены дополнительные адреса, и навешаны через ifconfig eth0:0 192.168.1..., ifconfig eth0:1 192.168.1.... И соответственно прописаны в местном DNS.

Далее был написан скрипт. Привожу то, что возникло в результате отладки. #!/usr/bin/perl # (C) Dmitri Popov, 2001 # Freeware use Socket; my $sockaddr = getsockname(STDIN); exit if ! $sockaddr; open(LOGSTREAM, '>>/var/log/gdsconnect'); my ($port, $addr) = sockaddr_in($sockaddr); $addr = inet_ntoa($addr); dolog("addr=$addr"); runserver('/roots/ib5', '/usr/interbase') if( $addr eq '192.168.1.251' ); runserver('/roots/fb', '/opt/interbase') if( $addr eq '192.168.1.252' ); runserver('/roots/ib4', '/usr/interbase') if( $addr eq '192.168.1.253' ); exit 133; # паранойя sub runserver { my $root = shift; my $base = shift; dolog("root=$root, base=$base"); chdir($root); chroot($root); exec( "/bin/su interbase -c" . "$base/bin/gds_inet_server"); } sub dolog { my $str = shift; printf LOGSTREAM "%s %s\n", scalar localtime(), $str; }

Замечания

Ну и последняя операция состояла в том, чтобы запускать этот скрипт из inetd. Перед этим необходимо обеспечить наличие gds_db в глобальном /etc/services, если его там ещё нет. Содрать можно с любого результата установки в chroot. Непосредственно в (глобальный!) inetd.conf пошла строчка: gds_db stream tcp nowait.100 root /usr/sbin/tcpd /usr/local/sbin/gdsconnect

Обратите внимание -- запуск идёт от рута. Это необходимо для того, чтобы сработал chroot. Переключение на interbase производится уже после.

Конечно, заработало не сразу. Приходилось вставлять в скрипт вызовы dolog() с подробностями. Большинство из них из окончательной версии удалены.


Мысли про супер

Как известно, супер функционирует сам, без помощи inetd. На постоянной основе. В связи с чем фокус с chrootна каждое подключение не проходит. Сервера должны изначально сидеть каждый в своей ветке, и при этом слушать каждый свой адрес. В самом InterBase средств для этой цели нет. Но прикрутить на мой взгляд тоже можно, но несколько хитрее.

Во-первых, в ветках файловых систем для каждого сервера в его etc/services можно прописать разные порты для gds_db. Таким образом, они смогут запуститься, не переконфликтовав друг с другом, так как будут слушать сеть на всех локальных адресах, но на разных портах.

Далее можно взять документацию по системе, и почитать на тему NAT, то есть трансляции адресов в пакетах. С помощью подобного механизма можно заставить пакеты прозрачным для клиента и сервера образом менять свою адресную информацию. В Linux-2.4.x и iptables задача представляется решаемой.

Или можно взять программу-редиректор (их мне попадалось множество, сам иногда использую redir), и вызывать её из вышеприведённого скрипта с нужными ключами заместо gds_inet_server. Таким образом, получается прокси-классик, запускающий индивидуальное перенаправление для каждого клиента.


назад вверх вперёд содержание НПО Криста