LINUX.ORG.RU

Как сделать файл, содержимое которого зависит от читающего юзера?

 


1

2

На одном шаред-хостинге у каждого юзера путь к файлам своего сайтика начинается с /home/www/yourdomain/

readlink говорит, что:
/home - это ссылка на папку /srv/home/home/,
/srv/home/home - это ссылка на домашнюю папку текущего юзера (путь зависит от имени юзера).

Получается, что разные юзеры читают один и тот же линк /srv/home/home, но попадают по этому линку в разные папки.
Я проверил через [-с /srv/home/home] - это обычный линк на папку, а не девайс.
Неужели в линуксе есть стандартный способ нарушить консистентность файловой системы?
Они как-то монтируют в путь /srv/home/ каждому юзеру свою папку, в которой лежит его персональный симлинк?
Как такое можно сделать?


Ответ на: комментарий от crutch_master

С чего ты взял, что у тебя нормальный корень фс?

если бы был «ненормальный» корень фс, тогда были бы не нужны выкрутасы со ссылкой на ссылку на папку

Egor_ ()

Папки - у вантузоидов, запомни это!

У нормальных людей каталоги, директории или справочники!!!

Тоже к fuse склоняюсь.

anonymous ()

А что показывают mount и mountpoint /srv/home? И ещё, в какой именно путь раскрывается /srv/home/home (анонимизируй в нём свой юзернейм, если хочешь)?

Могу ещё предложить код, который проверит, нет ли каких-то манипуляций в юзерспейсе. Компилировать на любом дистрибутиве, вызывается только ядро:

// Только для amd64. Компилировать как gcc -ffreestanding -nodefaultlibs -nostartfiles -o test-link test-link.c
#if __STDC_HOSTED__ == 1
#error "Я же сказал: компилировать с -ffreestanding -nodefaultlibs -nostartfiles !!!1"
#endif

#define PATHBUF_SIZE 8192

#include <sys/types.h>
#include <sys/stat.h>

extern long int syscall (long int __sysno, ...);

const char* weird_link = "/srv/home/home";

const char* msg_stat_failed =	"stat failed, code ";
const char* msg_st_mode = 	"st_mode:  ";
const char* msg_st_uid = 	"st_uid:   ";
const char* msg_st_gid =	"st_gid:   ";
const char* msg_st_rdev =	"st_rdev:  ";
const char* msg_st_size =	"st_size:  ";
const char* msg_readlink =	"readlink: ";
const char* msg_readlink_failed = "readlink failed, code ";

long syscall_write(unsigned int fd, const void* buf, size_t count) {
	asm("mov $1, %rax");
	asm("syscall");
}

long syscall_stat(const char* path, struct stat* statbuf) {
	asm("mov $4, %rax");
	asm("syscall");
}

long syscall_readlink(const char* path, char* buf, int bufsz) {
	asm("mov $89, %rax");
	asm("syscall");
}

size_t strlen(const char* s) {
	size_t result = 0;
	while(*s++ != '\0') {
		++result;
	}
	return result;
}

void writeout(const void* buf, size_t count) {
	syscall_write(1, buf, count);
}

void kolkhoz_writell(long long n) {
	if(n < 0) {
		writeout("-", 1);
		kolkhoz_writell(-n);
	} else {
		if(n > 9) {
			kolkhoz_writell(n / 10);
			n %= 10;
		}
		char c = (char) n + '0';
		writeout(&c, 1);
	}
}

void writeline(const void* header, long long value) {
	writeout(header, strlen(header));
	kolkhoz_writell(value);
	writeout("\n", 1);
}

void _start(void) {
	struct stat statbuf;
	char pathbuf[PATHBUF_SIZE];
	long result = syscall_stat(weird_link, &statbuf);
	if(result) {
		writeline(msg_stat_failed, result);
		goto exit;
	}
	writeline(msg_st_mode, statbuf.st_mode);
	writeline(msg_st_uid, statbuf.st_uid);
	writeline(msg_st_gid, statbuf.st_gid);
	writeline(msg_st_rdev, statbuf.st_rdev);
	writeline(msg_st_size, statbuf.st_size);
	result = syscall_readlink(weird_link, pathbuf, PATHBUF_SIZE);
	if(result <= 0) {
		writeline(msg_readlink_failed, result);
		goto exit;
	} else {
		writeout(msg_readlink, strlen(msg_readlink));
		writeout(pathbuf, (size_t) result);
		writeout("\n", 1);
	}
exit:
	asm("mov $0, %rdi\nmov $60, %rax\nsyscall");
}


Так на вскидку не знаю, как такого добиться. В некоторых случаях (например, в GoboLinux) для подобных фокусов делают свой патч на ядро. Для динамически скомпилированных программ на C/C++ возможен перехват libc.

proud_anon ★★★★★ ()
Ответ на: комментарий от proud_anon

Прикольно.

Так на вскидку не знаю, как такого добиться.

proot (ptrace) подойдет?

Проверил бы сам, да

/nix/store/ajrrkivdfvp8dp4vdg5hp1h5hblmanc9-binutils-2.31.1/bin/ld: /run/user/1000/ccEIhAmP.o: in function `kolkhoz_writell.localalias.1':
check.c:(.text+0x11f): undefined reference to `__stack_chk_fail'
/nix/store/ajrrkivdfvp8dp4vdg5hp1h5hblmanc9-binutils-2.31.1/bin/ld: /run/user/1000/ccEIhAmP.o: in function `_start':
check.c:(.text+0x2c5): undefined reference to `__stack_chk_fail'
collect2: error: ld returned 1 exit status
t184256 ★★★★★ ()
Ответ на: комментарий от t184256

proot (ptrace) подойдет?

Как раз один из вариантов, как это сделать чисто в юзерспейсе путём обмана всех программ.

Проверил бы сам, да (...) undefined reference to `__stack_chk_fail'

Может быть, gcc крайне строгий по умолчанию. Попробуй добавить флаги -fno-stack-protector -O0. Тут ведь не то чтобы кошерный кроссплатформенный Си был.

proud_anon ★★★★★ ()

mount –bind

В Линуксе уже очень давно используется создание для каждого пользователя своей независимой файловой системы. Элемент секурности чтобы пользователи друг-другу не мешали.

cvv ★★★★★ ()
Ответ на: комментарий от proud_anon

А что показывают mount и mountpoint /srv/home?

mount не показывает ничего (специально скрыто от любопытных глаз?)
mountpoint говорит /srv/home is a mountpoint

в какой именно путь раскрывается /srv/home/home?

/home                ->  /srv/home/home/
/srv/home/home       ->  /srv/.links/*******
/srv/.links/*******  ->  ../disk**/*******

итого после раскрытия трёх симлинков
/home == /srv/disk**/*******

(анонимизируй в нём свой юзернейм, если хочешь)

да, я онанизировал - заменил все цифры на звёздочки ))

Только для amd64

64-битные бинарники там не работают
а вот ELF32 - да, можно скомпилировать у себя и скопировать туда - всё пашет.
нопеши мне 32-битную версию своей проги

Egor_ ()
Последнее исправление: Egor_ (всего исправлений: 3)
Ответ на: комментарий от t184256

Так на вскидку не знаю, как такого добиться.

proot (ptrace) подойдет?

интересная хрень proot
и какой процент процессорного времени сжирается на эту эмуляцию?
будет ли разумно запускать через proot все юзер-процессы на хостинге?

Egor_ ()
Ответ на: комментарий от Egor_

нопеши мне 32-битную версию своей проги

не, не надо писать 32-битную версию
оно работает!
вот щука, а мои 64-битные проги там не запускались

вот результат (я опять заонанимимировал свой номер пользователя)

st_mode:  16872
st_uid:   *******
st_gid:   1111
st_rdev:  0
st_size:  61
readlink: /srv/.links/*******

st_mode: 16872 = восьмеричное 40750, что значит что это обычная папка
ок, в чём был смысл этого тестирования?
как я понимаю, программа показала не стат ссылки, а стат папки, куда эта ссылка ведёт

Egor_ ()
Последнее исправление: Egor_ (всего исправлений: 3)
Ответ на: комментарий от Egor_

0-50+% в зависимости от частоты сисколлов, абсолютно категорично нет. Я вон его для запуска Nix на андроиде юзаю, и то бы с радостью не юзал, а то на IO без слез не глянешь.

t184256 ★★★★★ ()
Ответ на: комментарий от Egor_

Хорошо, существуют дополнительные ключи которые делают это монтирование актуальным исключительно конкретному пользователю. Мне лень гуглить. Глянь как в твоем линуксе /tmp смонтирован. Последние лет 5 большинство дистров дают личный, приватный /tmp для каждого пользователя.

cvv ★★★★★ ()
Ответ на: комментарий от cvv

мне всё больше кажется, что ты под веществами: тебя накрыло, и из тебя попёр креатив )))
сначала несуществующие опции у mount, теперь вот ещё персональный /tmp для каждого юзера

Глянь как в твоем линуксе /tmp смонтирован.

на домашнем debian 9 - никак не смонтирован, обычная папка в корне фс
на шаред-хостинге (о котором речь в этой теме) /tmp как-то смонтирован, но mount молчит как партизан и не выдаёт своих
но учитывая, сколько разного интересного мусора валяется в /tmp, он несомненно общий для всех пользователей

Последние лет 5 большинство дистров дают личный, приватный /tmp для каждого пользователя.

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

Egor_ ()
Последнее исправление: Egor_ (всего исправлений: 1)
Ответ на: комментарий от Egor_

вот щука, а мои 64-битные проги там не запускались

Наверное, там просто не установлен 64-битный юзерспейс. Если принести свой libc или компилировать статически, должно будет работать.

ок, в чём был смысл этого тестирования?

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

Вариант, что все процессы пользователя запускаются под ptrace или через proot я даже не рассматривал. Это же большие потери на каждом сисколле, и ради чего? Да и потом, что если пользователь сам захочет дебаггер запустить?

Но мне пришло в голову вот ещё что. Если, например, при входе пользователя в систему войти в отдельный mount namespace, а в нём уже запускать шелл пользователя, то можно сделать этому пользователю какую угодно файловую систему. Я даже почти уверен, что это делается какой-нибудь простой настройкой в systemd, там для юнитов это стандартная функция.

Но как определить без рута, так это сделано или нет, не знаю.

Можно почитать /proc/self/mountinfo, вывод lsns или посмотреть конфигурационные файлы systemd, если они доступны всем для чтения.

proud_anon ★★★★★ ()
Ответ на: комментарий от cvv

Последние лет 5 большинство дистров дают личный, приватный /tmp для каждого пользователя.

Это где такие дистры? Так, конечно, можно сделать, но, во-первых, возникают вопросы типа «как быть с /tmp/.X11-unix», а во-вторых, Debian и все производные, по-моему, даже не удосуживаются примонтировать туда tmpfs.

proud_anon ★★★★★ ()
Последнее исправление: proud_anon (всего исправлений: 1)
Ответ на: комментарий от proud_anon

Можно почитать /proc/self/mountinfo, вывод lsns

$ mount 
mount: failed to read mtab: No such file or directory
$ cat /proc/self/mountinfo
cat: /proc/self/mountinfo: No such file or directory
$ lsns 
$

или посмотреть конфигурационные файлы systemd, если они доступны всем для чтения.

$ find -L /etc/systemd/ -type f -exec sh -c 'echo "\n============\n{}\n============"; cat "{}"' \;

из вывода я убрал все закомментированные # строки, чтобы влезь в ограничение на длину поста на лоре

============
/etc/systemd/network/50-virtio-kernel-names.link
============
[Match]
Driver=virtio_net

[Link]
NamePolicy=onboard kernel

============
/etc/systemd/system/syslog.service
============
[Unit]
Description=System Logging Service
Requires=syslog.socket
Documentation=man:rsyslogd(8)
Documentation=http://www.rsyslog.com/doc/

[Service]
Type=notify
ExecStart=/usr/sbin/rsyslogd -n
StandardOutput=null
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=syslog.service

============
/etc/systemd/system/getty.target.wants/getty@tty1.service
============

[Unit]
Description=Getty on %I
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service
After=rc-local.service

Before=getty.target
IgnoreOnIsolate=yes

Conflicts=rescue.service
Before=rescue.service

ConditionPathExists=/dev/tty0

[Service]
ExecStart=-/sbin/agetty --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes

Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=

[Install]
WantedBy=getty.target
DefaultInstance=tty1

============
/etc/systemd/system/multi-user.target.wants/cron.service
============
[Unit]
Description=Regular background program processing daemon
Documentation=man:cron(8)

[Service]
EnvironmentFile=-/etc/default/cron
ExecStart=/usr/sbin/cron -f $EXTRA_OPTS
IgnoreSIGPIPE=false
KillMode=process

[Install]
WantedBy=multi-user.target

============
/etc/systemd/system/multi-user.target.wants/dns-clean.service
============
[Unit]
Description=Clean up any mess left by 0dns-up

DefaultDependencies=false
Before=network-manager.service systemd-networkd.service networking.service resolvconf.service
After=local-fs.target
Requires=local-fs.target

ConditionPathExists=/etc/ppp/ip-down.d/0dns-down

[Service]
Type=oneshot
ExecStartPre=/bin/mkdir -p /var/run/pppconfig
ExecStart=/etc/ppp/ip-down.d/0dns-down "0dns-clean"

[Install]
WantedBy=multi-user.target

============
/etc/systemd/system/multi-user.target.wants/networking.service
============
[Unit]
Description=Raise network interfaces
Documentation=man:interfaces(5)
DefaultDependencies=no
Wants=network.target
After=local-fs.target network-pre.target apparmor.service systemd-sysctl.service systemd-modules-load.service
Before=network.target shutdown.target network-online.target
Conflicts=shutdown.target

[Install]
WantedBy=multi-user.target
WantedBy=network-online.target

[Service]
Type=oneshot
EnvironmentFile=-/etc/default/networking
ExecStartPre=-/bin/sh -c '[ "$CONFIGURE_INTERFACES" != "no" ] && [ -n "$(ifquery --read-environment --list --exclude=lo)" ] && udevadm settle'
ExecStart=/sbin/ifup -a --read-environment
ExecStop=/sbin/ifdown -a --read-environment --exclude=lo
RemainAfterExit=true
TimeoutStartSec=5min

============
/etc/systemd/system/multi-user.target.wants/pppd-dns.service
============
[Unit]
Description=Restore /etc/resolv.conf if the system crashed before the ppp link was shut down

[Service]
Type=oneshot
ExecStart=/etc/ppp/ip-down.d/0000usepeerdns

[Install]
WantedBy=multi-user.target


============
/etc/systemd/system/multi-user.target.wants/remote-fs.target
============

[Unit]
Description=Remote File Systems
Documentation=man:systemd.special(7)
After=remote-fs-pre.target
DefaultDependencies=no
Conflicts=shutdown.target

[Install]
WantedBy=multi-user.target

============
/etc/systemd/system/multi-user.target.wants/rsync.service
============
[Unit]
Description=fast remote file copy program daemon
ConditionPathExists=/etc/rsyncd.conf

[Service]
ExecStart=/usr/bin/rsync --daemon --no-detach

[Install]
WantedBy=multi-user.target

============
/etc/systemd/system/multi-user.target.wants/rsyslog.service
============
[Unit]
Description=System Logging Service
Requires=syslog.socket
Documentation=man:rsyslogd(8)
Documentation=http://www.rsyslog.com/doc/

[Service]
Type=notify
ExecStart=/usr/sbin/rsyslogd -n
StandardOutput=null
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=syslog.service

============
/etc/systemd/system/network-online.target.wants/networking.service
============
[Unit]
Description=Raise network interfaces
Documentation=man:interfaces(5)
DefaultDependencies=no
Wants=network.target
After=local-fs.target network-pre.target apparmor.service systemd-sysctl.service systemd-modules-load.service
Before=network.target shutdown.target network-online.target
Conflicts=shutdown.target

[Install]
WantedBy=multi-user.target
WantedBy=network-online.target

[Service]
Type=oneshot
EnvironmentFile=-/etc/default/networking
ExecStartPre=-/bin/sh -c '[ "$CONFIGURE_INTERFACES" != "no" ] && [ -n "$(ifquery --read-environment --list --exclude=lo)" ] && udevadm settle'
ExecStart=/sbin/ifup -a --read-environment
ExecStop=/sbin/ifdown -a --read-environment --exclude=lo
RemainAfterExit=true
TimeoutStartSec=5min

============
/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service
============

[Unit]
Description=Network Time Synchronization
Documentation=man:systemd-timesyncd.service(8)
ConditionCapability=CAP_SYS_TIME
ConditionVirtualization=!container
DefaultDependencies=no
RequiresMountsFor=/var/lib/systemd/clock
After=systemd-remount-fs.service systemd-tmpfiles-setup.service systemd-sysusers.service
Before=time-sync.target sysinit.target shutdown.target
Conflicts=shutdown.target
Wants=time-sync.target

[Service]
Type=notify
Restart=always
RestartSec=0
ExecStart=/lib/systemd/systemd-timesyncd
WatchdogSec=3min
CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=full
ProtectHome=yes
ProtectControlGroups=yes
ProtectKernelTunables=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io

[Install]
WantedBy=sysinit.target

============
/etc/systemd/system/timers.target.wants/apt-daily-upgrade.timer
============
[Unit]
Description=Daily apt upgrade and clean activities
After=apt-daily.timer

[Timer]
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
Persistent=true

[Install]
WantedBy=timers.target

============
/etc/systemd/system/timers.target.wants/apt-daily.timer
============
[Unit]
Description=Daily apt download activities

[Timer]
OnCalendar=*-*-* 6,18:00
RandomizedDelaySec=12h
Persistent=true

[Install]
WantedBy=timers.target

============
/etc/systemd/system/timers.target.wants/phpsessionclean.timer
============
[Unit]
Description=Clean PHP session files every 30 mins

[Timer]
OnCalendar=*-*-* *:09,39:00
Persistent=true

[Install]
WantedBy=timers.target

============
/etc/systemd/journald.conf
============

[Journal]

============
/etc/systemd/logind.conf
============

[Login]

============
/etc/systemd/resolved.conf
============

[Resolve]

============
/etc/systemd/system.conf
============

[Manager]

============
/etc/systemd/timesyncd.conf
============

[Time]

============
/etc/systemd/user.conf
============

[Manager]

Egor_ ()