Использование пространств имен команды mount

Полезные применения "продвинутых" функций Linux-команды mount

Как позволить пользователям самостоятельно монтировать файловые системы, не ограничиваясь структурой, навязанной системным администратором? Пользователи могли бы экспортировать часть своей файловой системы или импортировать экспортированные части систем других пользователей. Эта статья, предназначенная для системных администраторов Linux®, в пошаговом режиме показывает, как можно реализовать это расширением команды mount.

Серж Е. Халлин, штатный программист, IBM

Серж Халлин (Serge Hallyn) — сотрудник Linux Technology Center корпорации IBM, специализирующийся на разработке ядра и системы безопасности Linux. Он получил степень Ph.D. в области компьютерных наук в College of William and Mary. Он является автором и соавтором ряда модулей безопасности. Сейчас он занимается реализацией поддержки функциональности виртуальных серверов, контрольных точек/рестарта приложений и разрешений POSIX для файлов.



Рам Пэй, инженер-программист, IBM

Рам Пэй (Ram Pai) – сотрудник всемирной сети Linux Technology Center корпорации IBM. Он имеет степень магистра в области компьютерных наук. В настоящее время Рам руководит группой Linux System x Device Driver. Рам участвует в движении открытого программного обеспечения с 2001 года; наиболее заметен его вклад в Consensus Cluster Membership Layer проекта Linux-HA, алгоритме Readahead и функциональности совместно используемых поддеревьев ядра Linux.



29.01.2008

Когда-то файловая система Linux® была устроена довольно просто. С помощью вызова chroot() процесс мог смонтировать корневой каталог своего файлового дерева на любой каталог главного файлового дерева системы. На любой узел файловой системы можно установить файловую систему нового устройства.

В 2000 году Ал Виро (Al Viro) реализовал в Linux монтирование связей (bind mount) и пространства имен файловой системы (filesystem namespaces):

  • Монтирование связей открывает доступ к любому файлу или директории из любого другого места.
  • Пространства имен файловой системы - это полностью независимые деревья файловых систем, ассоциированные с различными процессами.

clone(2)

Из страниц man Linux: Системный вызов clone(2) создает дочерний (новый) процесс, который использует совместно с родительским процессом часть ресурсов, таких как выделенная память, таблица дескрипторов файлов и таблица обработки сигналов. Подробнее: см. раздел Ресурсы.

Процесс запрашивает копию текущего дерева файловой системы при вызове clone(2) (см. раздел Ресурсы), после чего новый процесс получает идентичную копию оригинального дерева файловой системы. После создания копии любое действие по монтированию, совершенное на одном из деревьев, не отражается на другом.

Хотя в теории было бы заманчиво ограничить пространство имен пределами процесса, на практике такое ограничение оказывается чересчур жестким. Если процесс клонируется и получает собственную копию файловой системы в пространстве имен главной файловой системы, то запущенный ранее системный демон не сможет смонтировать CD-ROM для пользователя, так как монтирование, осуществляемое в пространстве имен оригинальной файловой системы, не отражается на ее пользовательской копии.

Исправить ситуацию была призвана введенная в 2006 году концепция распространения монтирования - mount propagation - определяющая взаимосвязи между объектами монтирования. Эти взаимосвязи устанавливают, каким образом монтирование какого-либо объекта отражается на других объектах системы:

  • Монтирование одного из объектов отражается на другом объекте (и наоборот), если между ними установлена равноправная взаимосвязь.
  • Монтирование одного из объектов отражается на другом объекте (но не наоборот), если между ними установлена связь на условиях подчинения, когда подчиненный объект (slave) может быть лишь "получателем" события.

Объект монтирования, отправляющий события, принято называть совместно используемым объектом монтирования (shared mount); объект монтирования, принимающий событие, принято называть подчиненным объектом монтирования (slave mount). Объект монтирования, который не отправляет и не получает события, называется частным объектом монтирования (private mount). Существуют также несвязываемые объекты монтирования (unbindable mount): они похожи на частные объекты монтирования, но дополнительно не допускают монтирования себя командой mount bind. Такие виды объектов удобны для сдерживания опасного роста числа объектов монтирования (подробнее остановимся на этом позже).

По умолчанию все объекты монтирования являются частными. Объект монтирования может стать совместно используемым только после специальной команды:

mount --make-shared <mount-object>

Например, чтобы сделать / совместно используемым объектом монтирования, нужно набрать следующую команду:

mount --make-shared /

Объект монтирования, клонированный от совместно используемого, также является совместно используемым объектом монтирования; оба этих объекта равноправны.

Если нужно сделать объект монтирования подчиненным, то команда должна быть следующей:

mount --make-slave <shared-mount-object>

Объект монтирования, клонированный от подчиненного, также является подчиненным и подчиняется тому же хозяину (master), от которого был клонирован оригинальный объект.

Объект монтирования можно сделать частным с помощью такой команды:

mount --make-private <mount-object>

Объект монтирования станет несвязываемым после следующей команды:

mount --make-unbindable <mount-object>

Наконец, все эти параметры можно применять рекурсивно, распространяя их на все объекты монтирования, находящиеся ниже указанного объекта монтирования в файловой системе.

Например:

mount --make-rshared /

преобразует все объекты монтирования, находящиеся в файловой системе ниже /, в совместно используемые объекты монтирования.

Пользовательские пространства имен

В листинге 1 приведена часть кода модуля PAM (pluggable authentication module, подключаемого модуля авторизации), который предоставляет каждому пользователю, кроме root, обособленное пространство имен. Если директория /tmp/priv/USER существует, то она будет смонтирована с помощью bind на директорию /tmp в пользовательском пространстве имен.

Листинг 1. Часть кода PAM с настройкой пространства имен для сеанса пользователя
#define DIRNAMSZ 200
int handle_login(const char *user)
{
        int ret = 0;
        struct stat statbuf;
        char dirnam[DIRNAMSZ];

        if (strcmp(user, "root") == 0)
                return PAM_SUCCESS;

        ret = unshare(CLONE_NEWNS);
        if (ret) {
                mysyslog(LOG_ERR, "failed to unshare mounts for %s\n", user);
                return PAM_SESSION_ERR;
        }

        snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
        ret = stat(dirnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
                if (ret) {
                        mysyslog(LOG_ERR, "failed to mount tmp for %s\n", user);
                        return PAM_SESSION_ERR;
                }
        } else
                mysyslog(LOG_INFO, "No private /tmp for user %s\n", user);
        return PAM_SUCCESS;
}

int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
                const char **argv)
{
        const char *PAM_user = NULL;
        char *fnam;
        int ret;

        ret = pam_get_user(pamh, &PAM_user, NULL);
        if (ret != PAM_SUCCESS) {
                mysyslog(LOG_ERR, "PAM-NS: couldn't get user\n");
                return PAM_SESSION_ERR;
        }

        return handle_login(PAM_user);
}

Чтобы воспользоваться этим PAM-модулем, скачайте файлы pam_ns.c и makefile по ссылке из раздела Загрузка внизу. Скомпилируйте и скопируйте полученный файл pam_ns.so в /lib/security/. Затем добавьте следующую запись:

session   required   pam_ns.so

в /etc/pam.d/login и /etc/pam.d/sshd. После этого для какого-нибудь пользователя USER создайте частный каталог tmp.

mkdir /tmp/priv
chmod 000 /tmp/priv
mkdir /tmp/priv/USER
chown -R USER /tmp/priv/USER

Теперь войдите на одном терминале как пользователь root, а на другом - как USER. Из сеанса работы пользователя USER попробуйте выполнить следующие команды:

touch /tmp/ab
ls /tmp

Вы можете заметить, что директория /tmp пользователя USER содержит единственный - только что созданный - файл.

Затем выведите список содержимого /tmp в терминале root; вы увидите, что в каталоге есть какие-то другие файлы, но нет /tmp/ab. Фактически существует два разных каталога /tmp. Чтобы найти каталог /tmp пользователя USER из терминала root, введите

ls /tmp/priv/USER

Вы увидите искомый файл ab. Теперь в терминале root, смонтируйте что-нибудь в /mnt:

mount --bind /dev /mnt

mount(8) и unshare(2)

Из страниц man Linux: Команда mount(8) служит для присоединения файловой системы какого-либо устройства к основному дереву файловой системы. Системный вызов unshare(2) позволяет вызывающему процессу заменять пространства имен для выбранных ресурсов копиями оригинального пространства имен. Подробнее: см. Ресурсы.

Заметим, что содержимое директории /dev появляется под /mnt в терминале root, но не в терминале USER . Деревья монтирования для этих двух терминалов полностью изолированы друг от друга. С помощью команды mount(8) вы можете управлять распространением монтирования. По умолчанию все объекты монтирования являются частными. Поэтому до входа в систему пользователя USER попробуйте следующую команду:

mount --make-rshared /

После этого объекты монтирования будут распространяться между пространствами имен, которые иначе были бы обособленными. Однако после входа в систему пользователя USER монтирование /tmp/priv/USER на /tmp не будет распространяться на родительское пространство имен. Чтобы изменить это, в pam_ns.so можно сделать файловую систему пользователя подчиненной, как показано в листинге 2.

Листинг 2. Модуль PAM с подчиненным пользовательским пространством имен
#define DIRNAMSZ 200
#ifndef MS_SLAVE
#define MS_SLAVE 1<<19
#endif
#ifndef MS_REC
#define MS_REC 0x4000
#endif
int handle_login(const char *user)
{
        int ret = 0;
        struct stat statbuf;
        char dirnam[DIRNAMSZ];

        if (strcmp(user, "root") == 0)
                return PAM_SUCCESS;

        ret = unshare(CLONE_NEWNS);
        if (ret) {
                mysyslog(LOG_ERR, "failed to unshare mounts for %s\n", user);
                return PAM_SESSION_ERR;
        }

        ret = mount("", "/", "dontcare", MS_REC|MS_SLAVE, ""));
        if (ret) {
                mysyslog(LOG_ERR, "failed to mark / rslave for %s\n", user);
                return PAM_SESSION_ERR;
        }

        snprintf(dirnam, DIRNAMSZ, "/tmp/priv/%s", user);
        ret = stat(dirnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = mount(dirnam, "/tmp", "none", MS_BIND, NULL);
                if (ret) {
                        mysyslog(LOG_ERR, "failed to mount tmp for %s\n", user);
                        return PAM_SESSION_ERR;
                }
        } else
                mysyslog(LOG_INFO, "No private /tmp for user %s\n", user);
        return PAM_SUCCESS;
}

Каждому пользователю - свой root

LSPP

Спецификация Common Criteria Labeled Security Protection Profile определяет пакет функциональных и обеспечительных требований к ИТ-продуктам. В спецификации поддерживаются два вида механизмов контроля доступа—один из них позволяет пользователям самостоятельно определять совместное использование своих ресурсов (осуществляется с помощью отметок безопасности - меток (labels)), а другой вводит ограничения на совместное использование ресурсов пользователями. Защита, обеспечиваемая LSPP, считается достаточной для "невраждебного с хорошо управляемого сообщества пользователей, которому требуется защита от угроз безопасности системы, возникающих в результате небрежности или несистематических попыток нарушения безопасности". Подробнее см. Ресурсы.

В разделе Пользовательские пространства имен вы видели пример простого применения пространств имен команды mount в целях предоставления пользователям собственных пространств имен. Распространение монтирования позволяет использовать такое решение для обеспечения пользователей собственными директориями /tmp, а дополнения к файлу настройки по образцу pam_ns.c позволяют перенаправить в распоряжение пользователя другие директории. Именно так LSPP-системы распоряжаются несколькими экземплярами домашних директорий, монтируя на /home/USER ту директорию, которая соответствует параметрам процесса авторизации.

Однако при каждом входе пользователь получает другую частную подчиненную файловую систему. Поэтому операции монтирования, выполненные пользователем в ходе одного сеанса, не отражаются в другом сеансе.

Есть несколько способов монтирования файловых систем рядовыми пользователями. Например, с помощью FUSE пользователь может смонтировать файловую систему sshfs (secure shell) или файловую систему loopback (см. раздел Ресурсы). Вопрос общего доступа к таким объектам монтирования будет рассмотрен ниже, однако интуитивно непонятно, почему такие объекты монтирования исчезают в других сессиях работы того же пользователя. Между тем в варианте, описанном в разделе о пространстве имен, происходит именно это.

Листинг 3 демонстрирует часть PAM-модуля pam_chroot.so. В то время как pam_ns.so клонирует пространство имен монтирования при входе пользователя в систему, модуль pam_chroot.so, предполагая, что пользовательская файловая система монтируется в /share/USER/root, просто с помощью chroot() "запирает" пользователя в своей частной файловой системе.

Листинг 3. Модуль PAM, использующий chroot()
int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
                const char **argv)
{
        const char *PAM_user = NULL;
        char fnam[400];
        int ret, err, count, i;
        struct mount_entries *entries;
        struct stat statbuf;

        ret = pam_get_user(pamh, &PAM_user, NULL);
        if (ret != PAM_SUCCESS) {
                mysyslog(LOG_ERR, "PAM-MOUNT: couldn't get user\n");
                return PAM_SESSION_ERR;
        }

        /* check whether /share/$pam_user/root exists.  If so, chroot to it */
        sprintf(fnam, "/share/%s/root", PAM_user);
        ret = stat(fnam, &statbuf);
        if (ret == 0 && S_ISDIR(statbuf.st_mode)) {
                ret = chroot(fnam);
                if (ret) {
                        mysyslog(LOG_ERR, "PAM-MOUNT: unable to chroot to %s\n", fnam);
                        return PAM_SESSION_ERR;
                }
        }

        return PAM_SUCCESS;
}

В этом случае все операции монтирования происходят заранее, при запуске системы. Например, после загрузки:

mkdir -p /share/USER/root
mount --make-rshared /
mount --rbind / /share/USER/root
mount --make-rslave /share/USER/root
mount --bind /share/USER/root/tmp/priv/USER /share/USER/root/tmp

Частные пространства имен здесь не используются. Вместо этого при каждом входе в систему пользователя USER, его файловая система по команде chroot() монтируется в один и тот же каталог /share/USER/root. Таким образом, результат любой операции монтирования, осуществленной в любом сеансе работы пользователя USER, будет видимым и в других его сеансах. Напротив, файловая система пользователя OTHERUSER по команде chroot() будет смонтирована в каталог /share/OTHERUSER/root, и от него будут скрыты любые операции монтирования, выполненные пользователем USER.

Один из недостатков такого подхода заключается в том, что обычный вызов chroot() можно обойти, хотя для этого понадобятся некоторые привилегии. Например, при исполнении с некоторыми дополнительными привилегиями, включая CAP_SYS_CHROOT, способность программы избежать подчинения chroot() (см. раздел Ресурсы) может привести к выходу в корневую директорию реальной файловой системы. В зависимости от цели и способа использования пользовательских файловых систем, это может представлять собой серьезную проблему.

pivot_root(2) и chroot(2)

Из man-страниц Linux: Команда pivot_root(2) перемещает корневую директорию текущего процесса под директорию put_old (резервную) и выделяет новую директорию, new_root, для новой файловой системы текущего процесса. Команда chroot(2) меняет корневую директорию на ту, которая указана в пути команды; корневая директория наследуется всеми дочерними процессами текущего процесса. Этот вызов доступен только привилегированным процессам. Подробнее: см. Ресурсы.

Устранить эту проблему можно использованием pivot_root(2) вместо chroot(2) в пользовательском пространстве имен для монтирования пользовательской корневой директории в /share/USER/root. В то время как chroot() просто указывает файловой системе процесса точку монтирования в новой директории, pivot_root() отделяет указанную директорию new_root (монтируемый объект) от прежней точки монтирования и монтирует new_root к корневой директории процесса. Так как у монтируемой файловой системы нет родительского процесса, то обмануть систему, как в случае с chroot(), уже не получится. В дальнейшем мы будем пользоваться функцией pivot_root().

Настройка системы для организации отдельных каталогов root для каждого пользователя

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

В листинге 4 показан пример скрипта, используемого при создании любой учетной записи пользователя.

Листинг 4. Скрипт создания учетной записи
create_user_tree {
        user = $1
        mkdir /user/$user
        mount --rbind / /user/$user
        mount --make-rslave /user/$user
        mount --make-rshared /user/$user

	#создание точки монтирования private. Нужно, чтобы pivot_root
	#хранил здесь старый корень дерева файловой системы
	#до полного отделения старого дерева файловой системы. 
	#ЗАМЕТЬТЕ: pivot_root не даст поместить
	#старый корень файловой системы в совместно используемый
	#объект монтирования.
	pushd /user/$user/
	mkdir -p __my_private_mnt__
	mount --bind __my_private_mnt__ __my_private_mnt__
	mount --make-private __my_private_mnt__
	popd
}

Этот скрипт предполагает, что уже был выполнен скрипт init_per_user_namespace, который мы обсудим далее. Для учетной записи создается директория в каталоге /user/. Затем корневая директория рекурсивно монтируется с использованием bind в /user/$user/. Эта рекурсивная копия дерева корневой файловой системы становится постоянным (с точки зрения сеансов, но не перезагрузок) местом действий пользователя по монтированию.

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

Наконец, создается частный объект монтирования под названием __my_private_mnt__ . Это делается для того, чтобы pivot_root() (см. Листинг 6) временно хранил здесь корневую точку монтирования до удаления дерева. Не ломайте голову над этим шагом. Необходимость его станет понятной тогда, когда прояснится семантика pivot_root(). Просто помните, что функция pivot_root() не сможет завершиться успешно, если промежуточная точка монтирования является совместно используемой.

Листинг 5 - пример скрипта, запускающегося при загрузке системы.

Листинг 5. Скрипт инициализации при загрузке
init_per_user_namespace {

	#начнем с чистого листа, пометив
	#все точки монтирования как private.
	mount --make-rprivate /

	#создаем неразделяемую точку монтирования 'user'
	#и предписываем всем пользователям помещать
	#корневые директории своих файловых систем под нее.

        mkdir /user
        mount --bind /user /user
        mount --make-rshared /
        mount --make-unbindable /user
        foreach user in existing_user {
                create_user_tree $user
        }
}

Скрипт создает каталог /user, в который будут монтироваться все пользовательские файловые системы. Затем эта директория монтируется с помощью bind сама на себя. Так как директивы распространения монтирования, такие как --rshared, можно указывать лишь для конкретной точки монтирования, то этот шаг гарантирует наличие такой точки в каталоге /user.

Затем корневой каталог файловой системы отмечается как --rshared, поэтому все последующие копии (созданные монтированием с bind или клонированием пространства имен при монтировании) будут равноправны с этим объектом монтирования, и все действия по монтированию в одной из копий будут отражены во всех других.

После этого точка монтирования в /user отмечается как несвязываемая. Для каждого пользователя один раз рекурсивно копируется полное монтируемое дерево, поэтому обычно после создания копии для первого пользователя в /user/$user_1, копия, созданная в /user/$user_2 будет содержать рекурсивную копию /user/$user_1 в /user/$user_2/user/$user_1. Легко понять, что в этой ситуации вся свободная память очень быстро будет израсходована. Сделав точку монтирования /user несвязываемой, мы предотвращаем копирование /user при рекурсивном монтировании корневой директории с bind.

Наконец, для каждой учетной записи один раз исполняется скрипт, показанный в листинге 4, который создает каталог /user/$user (если он не существует) и устанавливает нужные параметры распространения монтирования, рассмотренные выше.

В листинге 6 показана часть кода модуля PAM, исполняемого при каждом входе пользователя в систему.

Листинг 6. Часть кода модуля PAM, исполняемого при входе пользователя в систему
#ifndef MNT_DETACH
#define MNT_DETACH		0x0000002
#endif
#ifndef MS_REC
#define MS_REC			0x4000
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE              1<<18   /* Private */
#endif

#define DIRNAMSZ 200
int handle_login(const char *user)
{
	int ret = 0;
	struct stat statbuf;
	char dirnam[DIRNAMSZ], oldroot[DIRNAMSZ];

	snprintf(dirnam, DIRNAMSZ, "/user/%s", user);
	ret = stat(dirnam, &statbuf);
	if (ret != 0 || !S_ISDIR(statbuf.st_mode))
		return PAM_SUCCESS;

	ret = unshare(CLONE_NEWNS);
	if (ret) {
		mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d\n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	ret = chdir(dirnam);
	if (ret) {
		mysyslog(LOG_ERR, "failed to unshare mounts for %s, error %d\n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	snprintf(oldroot, DIRNAMSZ, "%s/__my_private_mnt__", dirnam);
	ret = pivot_root(dirnam, oldroot);
	if (ret) {
		mysyslog(LOG_ERR, "failed to pivot_root for %s, error %d\n",
			user, errno);
		mysyslog(LOG_ERR, "pivot_root was (%s,%s)\n", dirnam, oldroot);
		return PAM_SESSION_ERR;
	}
	
	ret = mount("", "/__my_private_mnt__", "dontcare", MS_REC|MS_PRIVATE, "");
	if (ret) {
		mysyslog(LOG_ERR, "failed to mark /tmp private for %s, error %d\n",
			user, errno);
		return PAM_SESSION_ERR;
	}

	ret = umount2("/__my_private_mnt__", MNT_DETACH);
	if (ret) {
		mysyslog(LOG_ERR, "failed to umount old_root %s, error %d\n",
			user, ret);
		return PAM_SESSION_ERR;
	}

	return PAM_SUCCESS;
}

Сначала в модуле проверяется существование дерева /user/USER для входящего пользователя. Если его нет, то вход пользователя продолжается без всяких дополнительных действий.

Если дерево /user/USER существует, то первым делом выполняется клонирование частного пространства имен для задач, которые будут выполняться в данном сеансе. Таким образом, эти процессы получают собственные копии системного дерева монтирования. Но эти деревья не разделяются; каждый узел монтирования дерева-копии сообщается с соответствующим узлом оригинального дерева.

Затем процесс, управляющий входом пользователя в систему, с помощью pivot_root() переносит корневую директорию файловой системы процессов в каталог /user/$user. Оригинальная корневая директория остается смонтированной в новой __my_private_mnt__.

На следующем шаге __my_private_mnt__ отмечается как частный объект монтирования, чтобы последующее размонтирование не повлияло на другие копии корневой файловой системы, включая оригинальную.

Наконец, исходный корневой каталог размонтируется из __my_private_mnt__.

В скрипте создания учетной записи (см. листинг 4 ) мы сделали каталог __my_private_mnt__ частным объектом монтирования и упомянули, что это необходимо для работы pivot_root(). Причина этого - плохо документированное ограничение pivot_root(), относящееся к статусу старого и нового корневых каталогов при распространении монтирования. Для успешного выполнения pivot_root() следующие объекты монтирования не должны быть совместно используемыми:

  1. Целевой каталог старого корня файловой системы
  2. Текущий родительский каталог нового корня файловой системы (на момент вызова pivot_root())
  3. Родительский каталог нового корня файловой системы

Первое условие обеспечивается отметкой каталога __my_private_mount как частного ближе к окончанию листинга 4. Второе условие уже соблюдено, так как текущим родителем new_root является /user, а монтирование в /user помечено как несвязываемое. Третье условие также соблюдено, так как родительским каталогом нового корня файловой системы является родительский каталог текущего корневого каталога. Этот объект монтирования является скрытым rootfs-монтированием и изначально является частным.

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


Пользовательские деревья монтирования с избирательным совместным использованием

Мы говорили о предоставлении пользователям собственных деревьев монтирования. Теперь давайте обсудим, как можно обеспечить избирательный взаимный доступ к частям деревьев монтирования разных пользователей. Вот скрипт, исполняемый при загрузке системы.

Листинг 7. Скрипт загрузки системы
init_per_user_namespace {
	mkdir -p /user/slave_tree
	mkdir -p /user/share_tree

	#начнем с чистого листа. Все объекты 
	#монтирования отмечаются как private. 
	mount --make-rprivate /

	mount --bind /user /user
	mount --bind /user/share_tree /user/share_tree
	mount --bind /user/slave_tree /user/slave_tree

	mount --make-rshared  /

	mount --make-unbindable /user

	for user in `cat /etc/user_list`; do
		sh /bin/create_user_tree $user
	done
}

Мы создали точку монтирования /user, к которой монтируются корневые каталоги каждого пользователя. В каталоге /user мы создали каталог /user/share_tree для тех объектов монтирования, к которым пользователь хотел бы открыть общий доступ. Также мы создали каталог /user/slave_tree для тех объектов монтирования, которыми пользователь хотел бы поделиться, но без возможности их модификации другими пользователями. Разумеется, чтобы предотвратить неконтролируемое создание объектов монтирования, мы пометили точку монтирования /user как несвязываемую. В заключение мы вызываем create_user_tree, создавая деревья монтирования для каждого пользователя.

В листинге 8 показаны шаги, необходимые для создания дерева монтирования, а также для организации совместного доступа пользователей к объектам монтирования.

Листинг 8. Скрипт создания учетной записи
create_user_tree {

        user = $1
	mkdir -p /user/$user

	#копируем полное дерево монтирования в /user/$user
	mount --rbind / /user/$user
	make --make-rslave /user/$user
	make --make-rshared /user/$user

	cd /user/$user/home/$user

	#экспорт совместно используемых объектов монтирования
	mkdir -p my_shared_exports
	chown $user my_shared_exports
	mount --bind my_shared_exports my_shared_exports
	mount --make-private my_shared_exports
	mount --make-shared my_shared_exports
	mkdir -p /user/share_tree/$user
	mount --bind my_shared_exports /user/share_tree/$user

	#экспорт подчиненных объектов монтирования
	mkdir -p my_slave_exports
	chown $user my_slave_exports
	mount --bind my_slave_exports my_slave_exports
	mount --make-private my_slave_exports
	mount --make-shared my_slave_exports
	mkdir -p /user/slave_tree/$user
	mount --bind my_slave_exports /user/slave_tree/$user

	cd /user/$user

	#импорт чужих совместно используемых объектов монтирования
	mkdir -p others_shared_exports
	mount --rbind /user/share_tree others_shared_exports

	#импорт чужих подчиненных объектов монтирования
	mkdir -p others_slave_exports
	mount --rbind /user/slave_tree others_slave_exports
	mount --make-rslave others_slave_exports

	#установка частного объекта монтирования в пользовательское
	#дерево файловой системы. Это делается для того, чтобы 
	#выполнилась функция pivot_mount, вызываемая позже 
	#при входе пользователя в систему.
	mkdir -p __my_private_mnt__
	mount --bind __my_private_mnt__ __my_private_mnt__
	mount --make-private __my_private_mnt__
}

Сначала мы клонируем полное дерево монтирования в /user/$user. В пользовательском дереве файловой системы создается совместно используемый объект монтирования под названием my_shared_exports, который экспортируется для всех пользователей клонированием на /user/share_tree/$user. Подобным же образом создается точка монтирования my_slave_exports в пользовательском дереве — она тоже экспортируется для всех пользователей клонированием в /user/slave_tree/$user. Основная идея состоит в том, что пользователь, монтируя что-нибудь в my_shared_tree, автоматически предоставляет общий доступ к этой точке монтирования.

После этого импортируются совместно используемые точки монтирования всех остальных пользователей клонированием их деревьев монтирования в /user/share_tree и монтированием их в others_shared_exports других вошедших в систему пользователей. Аналогично импортируются подчиненные объекты монтирования: клонированием деревьев монтирования в /user/slave_tree и монтированием их в others_slave_exports. Естественно, так как экспортирующий их пользователь имеет в виду сделать их подчиненными, мы их отмечаем соответствующим образом.

После этой начальной настройки нужного нам совместного использования ресурсов алгоритм входа пользователя может быть таким же, как описан в листинге 6. После входа в систему пользователь получает то же дерево монтирования, что и в любом другом своем сеансе; в то же время он может видеть все совместно используемые и подчиненные объекты монтирования остальных пользователей в каталогах /others_shared_export и /others_slave_exports соответственно.

Если пользователь захочет открыть общий доступ к какой-то части своих ресурсов, то ему достаточно смонтировать этот ресурс в my_shared_exports, и он "волшебным образом" станет видимым для остальных пользователей.


Заключение

Монтирование с использованием bind позволяет монтировать любой файл или каталог в любой другой. Поддержка пространств имен позволяет процессам получать собственные изолированные копии родительских деревьев монтирования. Распространение монтирования позволяет прежде изолированным копиям файловых систем обмениваться объектами монтирования в одном или обоих направлениях. Эти функции дают пользователям «квазичастные» копии деревьев монтирования, открывая при этом доступ к системным событиям вроде монтирования CD-ROM и допуская избирательное совместное использование с другими пользователями.

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


Загрузка

ОписаниеИмяРазмер
Sample mount propagation code for this articledw.mountscode.tgz3KB

Ресурсы

Научиться

  • "Оригинал статьи (EN)"
  • В статье "Create uniform namespace using autofs with NFS Version 3 clients and servers" (EN) (developerWorks, январь 2007 г.) рассказывается о том, как можно использовать открытые реализации autofs и LDAP для получения доступа к данным, экспортированным из нескольких файловых серверов, в одной глобальной точке монтирования.
  • В статье "System Administration Toolkit: Migrating and moving UNIX filesystems" (EN) (developerWorks, июль 2006 г.) объясняется, как полностью перенести файловую систему на действующую систему, включая вопросы создания, копирования и восстановления.
  • Статья "Differentiating UNIX and Linux" (EN) (developerWorks, март 2006 г.) содержит краткий урок о различиях в поддержке файловых систем Linux и UNIX.
  • Из man-страниц Linux узнайте больше о(EN) clone(2), unshare(2), mount(8), pivot_root(2), и chroot(2).
  • Common Criteria Labeled Security Protection Profile (LSPP) определяет пакет функциональных и обеспечительных требований - два класса механизмов контроля доступа - к ИТ-продуктам.(EN)
  • FUSE (Filesystem in Userspace, пользовательская файловая система) позволяет построить в пользовательском пространстве программы полноценную файловую систему, имеющую ряд достоинств: простой API, отсутствие необходимости модифицировать или перекомпилировать ядро, безопасная реализация, стабильность, возможность использования непривилегированными пользователями, работа на ядрах 2.4.x и 2.6.x . (EN)
  • The sshfs - это клиент файловой системы, основанный на SSH File Transfer Protocol (безопасном протоколе передачи файлов); большинство SSH-серверов уже поддерживают этот протокол, поэтому для входа на сервер через ssh ничего не нужно настраивать ни на серверной, ни на клиентской стороне.(EN)
  • Файловая система loopback позволяет создавать новые виртуальные файловые системы, которые предоставляют доступ к существующим файлам с помощью альтернативных параметров; будучи однажды созданной, эта файловая система позволяет монтировать внутри себя другие файловые системы, не оказывая воздействия на исходную.(EN)
  • Сыграй в игру "обмани chroot()".(EN)
  • Linux PAM - это гибкий механизм авторизации пользователей, который позволяет создавать программы, независимые от схемы авторизации (так, "новое устройство" не обязано иметь эквивалентных записей во всех программах, поддерживающих авторизацию).(EN)
  • На сайте Linux Documentation Project имеется множество различных полезных документов, особенно руководств HOWTO.(EN)
  • В разделе Linux сайта developerWorks, находится множество ресурсов для разработчиков Linux, а также самые популярные статьи и учебные пособия.(EN)
  • Советы и учебные пособия по Linux на developerWorks.
  • Раздел технических мероприятий и Web-трансляций developerWorksпоможет вам оставаться в курсе последних технологий. (EN) .

Получить продукты и технологии

  • Используйте в своем следующем проекте для Linux ознакомительное ПО IBM, которое можно скачать напрямую с developerWorks.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux
ArticleID=284765
ArticleTitle=Использование пространств имен команды mount
publish-date=01292008