5 votos

¿Cómo montar NFS en Android con los permisos correctos?

Estoy tratando de montar un recurso compartido de NFS en mi teléfono Android.

Ya he compilado e instalado todos los módulos del kernel necesarios. El recurso de NFS se monta sin problemas, pero solo puedo montarlo como usuario system. Esto no sería un problema si pudiera establecer la propiedad correcta para el sistema de archivos montado. El problema es que el recurso siempre se monta como usuario=system y grupo=system, lo que lo hace inaccesible para aplicaciones regulares.

Me gustaría poder especificar la propiedad del sistema de archivos montado en el momento de montarlo.

Así es como monto el recurso de NFS

su -c "busybox mount -o nosuid,nolock,nodev,rw,nofail,noatime,intr,tcp,actimeo=1800,context=u:object_r:rootfs:s0,user -t nfs $REMOTE_URI $LOCAL_DIRECTORY"

donde REMOTE_URI es la ubicación remota y LOCAL_DIRECTORY es el directorio local. El comando anterior está dentro de un script.

Esta es la línea relevante del archivo /etc/exports en el servidor de NFS (una Raspberry Pi 3)

/media/neo/BLACKBOX XXX.XXX.XXX.XXX/0(rw,insecure,sync,no_root_squash,no_subtree_check,anonuid=1000,anongid=1000)

Especificaciones del sistema:

  • LG V20 H990DS
  • Sistema - V10i-TWN (6 de noviembre de 2017) 7.0 Stock, Rooteado & Xposed
  • Kernel - D.O.T.S. v1.4
  • Versión de NFS 4

P.D.: El UID y GID en el servidor son 1000 y 1000 respectivamente. En Android, esos están reservados para el usuario y grupo de sistema. Todos mis otros equipos tienen UID y GID 1000 y el mismo nombre de usuario neo. Sería mucho más fácil modificar la forma en que Android monta el recurso. Me gustaría mapear uid=neo(1000)->root(0) y gid=neo(1000)->sdcard_r(1028) donde neo:neo es el usuario en el servidor y root:sdcard_r es el usuario en el teléfono.

0 votos

10voto

Jack Wade Puntos 231

Me gustaría asignar uid=neo(1000)->Root(0) y guid=neo(1000)->sdcard_r(1028) donde neo:neo es el usuario en el servidor y Root:sdcard_r es el usuario en el teléfono.

Propiedad de los archivos root:sdcard_r (0:1028) en Android no funcionará para las aplicaciones. Debería ser root:everybody (0:9997) con el modo 0770 para los directorios y 0660 para los archivos.

¿CÓMO HACER QUE NFS FUNCIONE CON LAS APPS DE ANDROID?

DAC tradicional de *NIX (UIDs/GIDs) fue diseñado para aislar a los usuarios humanos, no a los programas. En los dispositivos Android, pensados para un solo usuario humano, las aplicaciones deben estar aisladas y protegidas para que no puedan acceder a los datos de las demás. Así que Android trata a cada aplicación como a un usuario humano, con un UID/GID único.

Almacenamiento externo ( /sdcard ) -ya sea físicamente externa o interna- está pensada para ser compartida por todas las aplicaciones, es decir, con múltiples UID. Si se trata de un sistema de archivos tradicional *NIX (como ext4 ), cada aplicación crearía archivos con su propio UID/GID lo que hace casi imposible compartir archivos entre todas las aplicaciones con acceso de lectura/escritura. Para encontrar una solución, Android optó por un sistema de archivos emulado, basado en FUSE o sdcardfs - con propiedad y modo fijos. Ver detalles en ¿Qué es el UID "u#_everybody"?

Estos UIDs/GIDs y modos también controlan el acceso de las aplicaciones a los archivos en /sdcard . Así que si queremos montar NFS para que sea accesible por todas las aplicaciones, tenemos que mitigar estos permisos de acuerdo con el diseño de almacenamiento de Android.

SEGURIDAD DE UNIX EN NFS:

sec=sys impone los permisos UNIX sin ninguna traducción. Como se ha explicado en la sección anterior, los archivos serán creados con propiedad aleatoria por diferentes aplicaciones. Así que estos tienen que ser legibles/escribibles por el mundo ( o+rw ), de lo contrario las aplicaciones no podrán leer/escribir. Pero no hay manera de hacer cumplir modo de creación de archivos globalmente, tampoco es buena idea compartir archivos con permisos demasiado abiertos. Una posible solución es utilizar la asignación de ID y/o el modo anónimo.

NFSv4 ID MAPPING:

Con NFSv4 es posible asignar diferentes UIDs/GIDs entre el cliente y el servidor. Si un usuario con el mismo nombre pero diferentes UIDs/GIDs existe en ambos lados, los archivos en el cliente parecen ser propiedad del mismo nombre de usuario, no del mismo UID. Instalación de gestión de claves de Linux * se utiliza para almacenar mapeos de IDs de usuarios remotos a IDs de usuarios locales. Se puede encontrar una buena explicación aquí .

Así que la idea es crear user:group neo:neo en el servidor NFS ( 1000 : 1000 ) y en Android ( 0 : 9997 ). Para esto, rpc.idmapd debe ejecutarse tanto en el servidor como en el cliente para consultar la base de datos del usuario ( /etc/{passwd,group} en el caso más sencillo) a través de NSS . En los núcleos más recientes, el cliente NFSv4 utiliza en su lugar nfsidmap y sólo vuelve a caer en rpc.idmapd si hubiera un problema al ejecutar el nfsidmap . Llamadas al núcleo /sbin/request-key binario en el espacio de usuario que ejecuta nfsidmap para consultar la base de datos de los usuarios. Así que el kernel mapearía la base de datos del servidor neo ( 1000 : 1000 ) al cliente neo ( 0 : 9997 ).

En un caso sencillo, si el rango UID/GID 10000 a 19999 no está asignado a ningún usuario/grupo en el servidor NFS y Nobody-User = root / Nobody-Group = everybody se define en /etc/idmapd.conf en el cliente, todos los archivos (creados por las aplicaciones de Android) que pertenezcan a usuarios inexistentes en el servidor serán devueltos como propiedad de 0 : 9997 en el cliente. Pero no resuelve el problema de la propiedad aleatoria de los archivos en el servidor.

Corriendo rpc.idmapd o proporcionando nfsidmap en Android es una tarea complicada (enlace estático y todo eso). Sin embargo - haciendo uso de keyutils ( request-key y keyctl ) - podemos engañar al kernel para que muestre la propiedad fija 0:9997 para todas las asignaciones, independientemente de cuál sea la propiedad real:

En el servidor NFS:

  1. Ejecutar los servicios:

    ~# mount -t nfsd nfsd /proc/fs/nfsd
    ~# rpc.mountd -g -N2 -N3 -N4 -N4.1 -N4.2 -p 4000
    ~# rpc.nfsd -N2 -N3 -N4 -N4.1 -N4.2 -U --p 2049 4
    ~# mkdir -p /run/rpc_pipefs
    ~# mount -t rpc_pipefs sunrpc /run/rpc_pipefs
    ~# rpc.idmapd -S -p /run/rpc_pipefs
    ~# echo -n N >/sys/module/nfsd/parameters/nfs4_disable_idmapping
    ~# exportfs -o rw,insecure,hide,no_subtree_check,sec=sys <CLIENT_IP>:/SHARED_DIR

    O configure y ejecute lo requerido init servicios. También desbloquear los puertos a través del firewall en el cliente y el servidor.

En Android:

  1. Crear /sbin/nfsidmap_pseudo y /etc/request-key.conf :

    #!/system/bin/sh
    
    uid=0
    gid=9997
    
    case $2 in
        uid:*)
            printf '%s\0' "$uid" | /sbin/keyctl pinstantiate $1 0 ;;
        gid:*)
            printf '%s\0' "$gid" | /sbin/keyctl pinstantiate $1 0 ;;
        *)
            # won't do anything to "user" or "group" types
            exit 1 ;;
    esac
    
    # /etc/request-key.conf
    create  id_resolver  *  *  /sbin/nfsidmap_pseudo  %k  %d
  2. Colocar los archivos, establecer los permisos y habilitar la asignación de ID::

    ~# chmod 0755 /sbin/request-key /sbin/nfsidmap_pseudo /sbin/keyctl
    ~# chmod 0644 /etc/request-key.conf
    ~# chown 0.0 /sbin/request-key /sbin/nfsidmap_pseudo /sbin/keyctl /etc/request-key.conf
    ~# chcon u:object_r:rootfs:s0 /sbin/request-key /sbin/nfsidmap_pseudo /sbin/keyctl
    ~# chcon u:object_r:system_file:s0 /etc/request-key.conf
    ~# echo -n N >/sys/module/nfs/parameters/nfs4_disable_idmapping
  3. Como NFS no está soportado oficialmente en Android, la política de SELinux no tiene reglas requeridas. Es posible que tenga que establecer SELinux permisivo o permitir que el kernel lea/escriba claves, ejecute archivos en el espacio de usuario y establezca conexiones:

    ~# supolicy --live 'allow kernel kernel key { search write view read }'
    ~# supolicy --live 'allow kernel kernel capability { net_raw net_bind_service sys_admin }'
    ~# supolicy --live 'allow kernel { rootfs shell_exec system_file } file { execute_no_trans execute open getattr }'
  4. Montar:

    ~# mkdir -p /sdcard/NFS
    ~# busybox mount -v -t nfs4 -o vers=4.2,sec=sys,rw,tcp,port=2049,context=u:object_r:sdcardfs:s0 <SERVER_IP>:/SHARED_DIR /mnt/runtime/write/emulated/0/NFS

Lamentablemente lo que obtenemos de toda esta configuración es que ahora las aplicaciones aparentemente ven los archivos en /sdcard/NFS propiedad de 0:9997 . Pero con sec=sys seguridad, el acceso real a los archivos no se rige por la asignación de UID de NFSv4 ** . Los permisos son aplicados por el mecanismo RPC que aún no está preparado para trabajar con el mapeo de ID. Así que el mapeo UID sin Kerberos sólo funciona si los espacios de nombre y número de usuario/grupo son coherentes entre el cliente y el servidor. Esto significa que neo El usuario en el servidor debe tener UID/GID: 0 / 9997 (lo que anula todo el propósito del mapeo de ID). Por otro lado, la seguridad de Kerberos ( sec=krb5 ) es demasiado agitado para ser probado en Android.

Del mismo modo, el bloqueo de archivos en NFSv2/3 requiere portmapper ( rpcbind ) y rpc.statd que se ejecuta tanto en el servidor como en el cliente, lo que no ocurre con el cliente de Android. Por lo tanto, tenemos que utilizar nolock opción de montaje. En NFSv4, sin embargo, el bloqueo está incorporado en el protocolo NFS, NLM no es necesario. Así que es mejor no ir por UID Mapping (en NFSv4, y File Locking en NFSv2/3). Si necesitas que todas las características de NFS (incluyendo Kerberos) funcionen en un dispositivo Android, mejor prueba una pequeña distro de Linux en chroot .

* En el kernel anterior a la v4.6, /proc/keys se expone si el núcleo se construye con KEYS_DEBUG_PROC_KEYS .
** Referencias: 1 , 2 , 3 , 4 , 5 , 6

ACCESO ANÓNIMO NFS:

Uno de los tipos de seguridad de NFS es modo anónimo . Cada solicitud de cualquier UID/GID en el cliente es tratada como el UID/GID anónimo en el servidor. Inicialmente, todos los archivos en el directorio compartido deben ser propiedad de este UID/GID y todos los archivos creados posteriormente desde el lado del cliente también tendrían la misma propiedad:

Compartir la exportación con sec=none en el servidor:

~# exportfs -o rw,insecure,no_subtree_check,anonuid=1000,anongid=1000,sec=none,root_squash,all_squash <CLIENT_IP>:/SHARED_DIR
~# chown -R 1000.1000 /SHARED_DIR; chmod 0700 /SHARED_SIR

* Para NFSv2/3 ejecute también rpcbind (en el puerto 111 por defecto)

Montar con sec=none En Android:

~# busybox mount -v -t nfs4 -o vers=4.2,sec=none,rw,tcp,port=2049,context=u:object_r:sdcardfs:s0 <SERVER_IP>:/SHARED_DIR /mnt/runtime/write/emulated/0/NFS

* Utilizar -t y vers= de acuerdo con la configuración de construcción del kernel CONFIG_NFS_V[2|3|4|4_1|4_2] .
* Utilizar -t nfs -o nolock con vers=3 o vers=2 .
* Asegúrese de que está montando desde el espacio de nombres de montaje Root.
* Montaje con sec=none no funciona con NFSv3.

Todas las aplicaciones en Android pueden ahora leer y escribir en el directorio NFS y todos los archivos se crean con un único UID/GID anónimo en el servidor, fácil de gestionar por un usuario. Sin embargo, la propiedad aparente (si el mapeo de ID no está configurado) y el modo de permiso no está de acuerdo con el sabor de Android, por lo que...

Pruébalo a la manera de Android...

Hagamos uso de FUSE o sdcardfs como lo hace Android:

~# mkdir -p /mnt/NFS /sdcard/NFS
~# busybox mount -v -t nfs4 -o vers=4.2,sec=none,rw,tcp,port=2049,context=u:object_r:sdcardfs:s0 <SERVER_IP>:/SHARED_DIR /mnt/NFS
~# mount -t sdcardfs -o nosuid,nodev,noexec,noatime,mask=7,gid=9997 /mnt/NFS /mnt/runtime/write/emulated/0/NFS

Si su dispositivo no es compatible con sdcardfs Utilizar bindfs y reemplazar el contexto de SELinux u:object_r:sdcardfs:s0 con u:object_r:fuse:s0 :

~# bindfs -o nosuid,nodev,noexec,noatime,context=u:object_r:fuse:s0 -u 0 -g 9997 -p a-rwx,ug+rw,ugo+X --xattr-none --chown-ignore --chgrp-ignore --chmod-ignore /mnt/NFS /mnt/runtime/write/emulated/0/NFS

Nos deja sin problemas restantes. Para más detalles, consulte ¿Cómo se puede montar una carpeta dentro de /sdcard con los permisos correctos?

DALE UNA OPORTUNIDAD A CIFS...

NFS es el sistema de archivos nativo de Linux (como ext4 ) está destinado a reforzar los permisos *NIX. Los sistemas de archivos no nativos, como FAT y CIFS, permiten establecer la propiedad y los permisos fijos en todo el sistema de archivos. Así que lo que buscas es relativamente fácil de conseguir con CIFS :

~# mkdir -p /sdcard/CIFS
~# busybox mount -v -t cifs -o vers=3.0,nosuid,nodev,noexec,noatime,rw,hard,context=u:object_r:sdcardfs:s0,nounix,uid=0,gid=9997,file_mode=0660,dir_mode=0770,nouser_xattr,port=445,username=USERNAME,password=PASSWORD //<SERVER_IP>/SHARED_DIR /mnt/runtime/write/emulated/0/CIFS

* El núcleo debe ser construido con CONFIG_CIFS y preferiblemente CONFIG_CIFS_SMB2 Utilizar vers= en consecuencia.
* Para el detalle de las opciones de montaje ver mount.cifs(8)

¿OTRAS OPCIONES?

Otros sistemas de archivos montables basados en FUSE son sshfs y rclone . Este último ofrece una amplia gama de protocolos y configuraciones y requiere una configuración muy sencilla.

1 votos

+1 Aprecio el detalle

1 votos

Me encanta la explicación detallada de los fundamentos que afectan esto. ¡Muchas gracias!

0 votos

@polynomial Me alegra que haya ayudado.

2voto

Gopal Puntos 109

Parece que cada distribución de Busybox que está en Google Play no es compatible con NFSv4 sino solo con NFSv3. Así que abandoné la idea de usar idmapd en Android.

  • En su lugar, cambié el UID y el GID en el servidor NFS a 4444 para que ya no correspondan al usuario system.
  • Luego creé un nuevo usuario en Busybox con el mismo nombre, UID y GID que el servidor (en mi distribución de Busybox se incluyen las utilidades adduser y addgroup).
  • Compré Tasker y descargué este conjunto de tareas del foro de XDA.
  • Elegí el directorio /mnt/nfs para montar el recurso compartido.

No tengo tiempo para investigar cuál de los puntos anteriores es necesario y cuál es solo irrelevante o opcional. Si tienes alguna idea, por favor escríbela en un comentario.

PreguntAndroid.com

PreguntAndroid es una comunidad de usuarios de Android en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X