Por que os usuários sem privilégios não podem aninhar as montagens FUSE, mas eles podem montar o FUSE dentro do NFS com root_squash?

0
$ mkdir mnt

$ bindfs /tmp mnt
fusermount: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf

$ bindfs --no-allow-other /tmp mnt

$ mkdir /tmp/mnt2
$ bindfs --no-allow-other /tmp mnt/mnt2
fusermount: bad mount point /home/alan/mnt/mnt2: Permission denied

fusermount falha porque é executado como um usuário diferente.

$ sudo ls mnt/
ls: cannot open directory 'mnt/': Permission denied

fusermount é set-uid root . Isso é necessário porque usuários não privilegiados não podem usar a chamada do sistema mount() .

$ ls -l $(which fusermount)
-rwsr-xr-x. 1 root root 32848 Feb  7  2018 /usr/bin/fusermount

   ^ set-uid bit

e ainda. É relatado que o FUSE pode ser usado dentro de um diretório inicial do NFS. Mesmo que o diretório inicial tenha o modo 700 - acessível somente pelo usuário proprietário. E o servidor NFS assume como padrão root_squash , o que significa que "o usuário root terá o mesmo acesso que o usuário nobody".

Por que esses dois casos diferem?

Estou testando no Fedora 28. Os relatórios sobre o NFS são do Ubuntu 18.04. Essas distribuições são bastante semelhantes em termos de idade, mas pode haver algumas diferenças.

    
por sourcejedi 16.08.2018 / 23:29

1 resposta

0

Primeiro, considere a implementação do FUSE no_allow_others .

Requer que os UIDs (IDs de usuário) efetivos, reais e salvos sejam correspondentes. (E o mesmo para o GID). Isso é deliberadamente impedir que um programa set-UID acesse a montagem.

link

Calling into a user-controlled filesystem gives the filesystem
daemon ptrace-like capabilities over the current process. This
means, that the filesystem daemon is able to record the exact
filesystem operations performed, and can also control the behavior
of the requester process in otherwise impossible ways. For example
it can delay the operation for arbitrary length of time allowing
DoS against the requester.

Agora vamos rastrear o que o fusermount faz. Nós podemos tentar olhar para

strace -f bindfs ...

e

sudo perf trace -o trace.txt -a sleep 2; sleep 1; bindfs ...

O primeiro atinge um erro fatal "Permissão negada", porque a raiz do set-UID não funciona quando executado em strace . O segundo é bem-sucedido, mas não pode mostrar parâmetros de string, como caminhos. Eu acho que os dois traços mostram o mesmo caminho de código geral até o erro fatal. Isso significa que podemos usar os resultados strace para preencher os parâmetros de string ausentes.

Essa última chamada nos resultados strace é:

[pid 30609] mount("/home/alan-sysop/mnt", ".", "fuse", MS_NOSUID|MS_NODEV, "default_permissions,fd=5,rootmod"...) = -1 EPERM (Operation not permitted)

Interessante! "." significa o diretório atual. Então fusermount já deve estar rodando no ponto de montagem ... de alguma forma. Às vezes, esse truque pode ser usado para acessar um diretório que você atualmente não pode acessar usando seu caminho absoluto.

Se rolarmos para cima, podemos ver que fusermount realmente foi alterado para esse diretório. E também dançava com algumas chamadas de sistema relacionadas a UID (e relacionadas a GIDs).

[pid 30609] getuid()                    = 1000
[pid 30609] setfsuid(1000)              = 1000
[pid 30609] getgid()                    = 1000
[pid 30609] setfsgid(1000)              = 1000
[pid 30609] openat(AT_FDCWD, "/etc/fuse.conf", O_RDONLY) = 6
...
[pid 30609] lstat("/home/alan-sysop/mnt", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid 30609] getuid()                    = 1000
[pid 30609] chdir("/home/alan-sysop/mnt") = 0
[pid 30609] lstat(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid 30609] access(".", W_OK)           = 0
[pid 30609] getuid()                    = 1000
[pid 30609] setfsuid(1000)              = 1000
[pid 30609] setfsgid(1000)              = 1000

Os resultados do UID estão "errados" na sessão strace . Podemos ver melhor a parte de dança do UID na sessão perf trace . (Eu removi as colunas mais à esquerda para facilitar a leitura).

getuid(                                                               ) = 1000
setfsuid(uid: 1000                                                    ) = 0
getgid(                                                               ) = 1000
setfsgid(gid: 1000                                                    ) = 1000
openat(dfd: CWD, filename: 0xa428e2bc                                 ) = 6
    ...
close(fd: 6                                                           ) = 0
lstat(filename: 0xa63882a0, statbuf: 0x7ffe7bd4f6d0                   ) = 0
getuid(                                                               ) = 1000
chdir(filename: 0xa63882a0                                            ) = 0
lstat(filename: 0xa428eca5, statbuf: 0x7ffe7bd4f6d0                   ) = 0
access(filename: 0xa428eca5, mode: W                                  ) = 0
getuid(                                                               ) = 1000
setfsuid(                                                             ) = 1000
setfsgid(gid: 1000                                                    ) = 1000
getuid(                                                               ) = 1000

As chamadas setfsuid() estão nas funções drop_privs() e restore_privs() em fusermount.c .

A chamada chdir() está sorrateiramente oculta na função chamada check_perm() .

Conclusão

Por que isso funciona no NFS? Resposta: porque o NFS examina os fsuid (e fsgid ), que foram definidos para o UID não-raiz.

Por que isso não funciona no FUSE, a menos que você tenha allow_others ? Resposta: porque o FUSE verifica o UID "real", e não o fsuid .

    
por 16.08.2018 / 23:29

Tags