Estoy tratando de montar un recurso compartido NFS en mi teléfono Android.
Ya he compilado e instalado todos los módulos del núcleo necesarios. El recurso compartido NFS se monta sin problemas, pero solo puedo montarlo como system
usuario. Esto no sería un problema si pudiera establecer la propiedad correcta para el sistema de archivos montado. El problema es que el recurso compartido siempre se monta como user= system
y group= system
, lo que lo hace inaccesible para las aplicaciones normales.
Me gustaría poder especificar la propiedad del sistema de archivos montado en el momento del montaje.
Así es como monto el recurso compartido 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 /etc/exports
archivo en el servidor 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:
PD: El UID y GID en el servidor son 1000 y 1000 respectivamente. En Android, están reservados para el usuario y el grupo del sistema. Todas mis otras computadoras tienen UID y GID 1000 y el mismo nombre de usuario neo
. Sería mucho más fácil simplemente modificar cómo Android monta el recurso compartido. Me gustaría mapear uid=neo(1000)->root(0)
y guid=neo(1000)->sdcard_r(1028)
dónde neo:neo
está el usuario en el servidor y dónde root:sdcard_r
está el usuario en el teléfono.
Me gustaría mapear 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 .
La propiedad de archivos root:sdcard_r (0:1028)
en Android no funcionará para las aplicaciones. Debe ser root:everybody (0:9997)
con modo 0770
para directorios y 0660
para archivos.
El *NIX DAC tradicional (UID/GID) se diseñó para aislar a los usuarios humanos, no a los programas. En los dispositivos Android, destinados generalmente a un usuario humano, las aplicaciones deben aislarse y protegerse para que no puedan acceder a los datos de los demás. Android trata cada aplicación como un usuario humano, con un UID/GID único.
El almacenamiento externo ( /sdcard
), ya sea físicamente externo o interno, debe ser compartido por todas las aplicaciones, es decir, varios UID. Si se trata de un sistema de archivos *NIX tradicional (como ext4
), cada aplicación crearía archivos con su propio UID/GID, lo que hace que sea 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 UID/GID y modos también controlan el acceso de las aplicaciones a los archivos en formato /sdcard
. Entonces, si queremos montar NFS para que todas las aplicaciones puedan acceder a ellos, debemos mitigar estos permisos de acuerdo con el diseño de almacenamiento de Android.
sec=sys
El modo impone los permisos de UNIX sin ninguna traducción. Como se explicó en la sección anterior, diferentes aplicaciones crearían archivos con propiedad aleatoria. Por lo tanto, deben ser legibles/escribibles por todo el mundo ( o+rw
), de lo contrario, las aplicaciones no podrán leer/escribir. Pero no hay forma de hacer cumplir el modo de creación de archivos globalmente, ni es una buena idea compartir archivos con permisos demasiado abiertos. Una posible solución es usar el mapeo de ID y/o el modo anónimo.
Con NFSv4 es posible mapear diferentes UID/GID entre el cliente y el servidor. Si existe un usuario con el mismo nombre pero diferentes UID/GID en ambos lados, los archivos en el cliente parecen propiedad del mismo nombre de usuario, no del mismo UID. La función de administración de claves de Linux * se utiliza para almacenar asignaciones de ID de usuarios remotos a ID de usuarios locales. Una buena explicación se puede encontrar aquí .
Entonces, la idea es crear un usuario: grupo 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 que consulta la base de datos del usuario ( /etc/{passwd,group}
en el caso más simple) a través de NSS . En kernels más recientes, el cliente NFSv4 usa nfsidmap y solo recurre a él rpc.idmapd
si hay un problema al ejecutarnfsidmap
. Kernel llama a /sbin/request-key
binario en el espacio de usuario que se ejecuta nfsidmap
para consultar la base de datos de los usuarios. Entonces, el núcleo asignaría el servidor neo
( 1000
:) 1000
al cliente neo
( 0
:) 9997
.
En un caso simple, si el rango de UID/GID de 10000 a 19999 no está asignado a ningún usuario/grupo en el servidor NFS y / Nobody-User = root
está Nobody-Group = everybody
definido en /etc/idmapd.conf
el cliente, se devolverán todos los archivos (creados por aplicaciones de Android) propiedad de usuarios inexistentes en el servidor. como propiedad de 0
: 9997
en el cliente. Pero no resuelve el problema de la propiedad aleatoria de archivos en el servidor.
Ejecutar rpc.idmapd
o proporcionar 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 una propiedad fija 0:9997
para todas las asignaciones, independientemente de cuál sea la propiedad real:
En el servidor NFS:
Ejecutar 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 init
los servicios necesarios. También desbloquee puertos a través del firewall en el cliente y el servidor.
En Android:
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
Coloque archivos, establezca permisos y habilite 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
Dado que NFS no es compatible oficialmente con Android, la política de SELinux no tiene reglas requeridas. Es posible que deba configurar SELinux como permisivo o permitir que el núcleo lea/escriba claves, ejecute archivos en el espacio de usuario y realice 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 }'
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
Desafortunadamente, lo que obtenemos de toda esta configuración es que ahora las aplicaciones aparentemente ven archivos /sdcard/NFS
propiedad de 0:9997
. Pero con sec=sys
la seguridad, el acceso real a los archivos no se rige por la asignación de UID de NFSv4 ** . Los permisos se aplican mediante un mecanismo RPC que aún no está listo para trabajar con la asignación de ID. Por lo tanto, la asignación de UID sin Kerberos
seguridad solo funciona si el nombre de usuario/grupo y los espacios numéricos son consistentes entre el cliente y el servidor. Significa que neo
el usuario en el servidor debe tener UID/GID: 0
/ 9997
(lo que anula todo el propósito de la asignación de ID). Por otro lado, la seguridad de Kerberos ( sec=krb5
) es demasiado frenética para probarla en Android.
De manera similar, el bloqueo de archivos en NFSv2/3 requiere portmapper
( rpcbind
) y rpc.statd
se ejecuta tanto en el servidor como en el cliente, lo que no es el caso con el cliente de Android. Entonces, necesitamos usar nolock
la opción de montaje. En NFSv4, sin embargo, el bloqueo está integrado en el protocolo NFS, no se necesita NLM. Así que mejor es no optar por UID Mapping
(en NFSv4 y File Locking
en NFSv2/3). Si necesita que todas las características de NFS (incluido Kerberos) funcionen en un dispositivo Android, pruebe una pequeña distribución de Linux en chroot
.
* En kernel anterior a v4.6, /proc/keys
está expuesto si el kernel está construido con KEYS_DEBUG_PROC_KEYS
.
** Referencias: 1 , 2 , 3 , 4 , 5 , 6
Uno de los sabores de seguridad de NFS es el modo anónimo . Cada solicitud de cualquier UID/GID en el cliente se trata 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án la misma propiedad:
Exportar compartir con sec=none
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 también se ejecuta rpcbind
(en el puerto predeterminado 111)
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
* Use -t
y vers=
de acuerdo con la configuración de compilación de su núcleo CONFIG_NFS_V[2|3|4|4_1|4_2]
.
* Usar -t nfs -o nolock
con vers=3
o vers=2
.
* Asegúrese de que está montando desde el espacio de nombres de montaje raíz.
* El montaje con sec=none
no funciona con NFSv3.
Todas las aplicaciones en Android ahora pueden 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 administrar por un usuario. Sin embargo, la propiedad aparente (si la asignación de ID no está configurada) y el modo de permiso no están de acuerdo con el sabor de Android, por lo que...
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 sdcardfs
, use bindfs
y reemplace 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 obtener más detalles, consulte ¿Cómo vincular el montaje de una carpeta dentro de /sdcard con los permisos correctos?
NFS, que es el sistema de archivos nativo de Linux (como ext4
), está destinado a hacer cumplir los permisos *NIX. Los sistemas de archivos no nativos como FAT y CIFS permiten establecer permisos y propiedad fijos en todo el sistema de archivos. Entonces, lo que está buscando es relativamente fácil de lograr 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 kernel debe construirse con CONFIG_CIFS
y, preferiblemente CONFIG_CIFS_SMB2
, usarse vers=
en consecuencia.
* Para obtener detalles sobre las opciones de montaje, consulte mount.cifs(8)
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.
Parece que todas las distribuciones de Busybox que están en Google Play no son compatibles con NFSv4 sino solo con NFSv3. Así que renuncié a la idea de usar idmapd en Android.
system
ya no se correspondan con el usuario.adduser
y ).addgroup
/mnt/nfs
directorio para montar el recurso compartido.No tengo tiempo para investigar cuál de los puntos anteriores es necesario y cuál es solo irrelevante u opcional. Si tienes alguna idea, escríbela en un comentario.
Irfan Latif