Ich versuche, eine NFS-Freigabe auf meinem Android-Telefon bereitzustellen.
Ich habe bereits alle benötigten Kernel-Module kompiliert und installiert. Die NFS-Freigabe wird fehlerfrei bereitgestellt, aber ich kann sie nur als system
Benutzer bereitstellen. Dies wäre kein Problem, wenn ich den richtigen Besitz für das gemountete Dateisystem festlegen könnte. system
Das Problem ist, dass die Freigabe immer als user= und group= gemountet wird system
, wodurch sie für normale Apps nicht zugänglich ist.
Ich möchte in der Lage sein, den Besitz des gemounteten Dateisystems zum Zeitpunkt des Mountens anzugeben.
So mounte ich die NFS-Freigabe
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"
wobei REMOTE_URI der entfernte Speicherort und LOCAL_DIRECTORY das lokale Verzeichnis ist. Der obige Befehl befindet sich in einem Skript.
Dies ist die relevante Zeile der /etc/exports
Datei auf dem NFS-Server (ein Himbeer-Pi 3)
/media/neo/BLACKBOX XXX.XXX.XXX.XXX/0(rw,insecure,sync,no_root_squash,no_subtree_check,anonuid=1000,anongid=1000)
Systemspezifikationen:
PS: Die UID und GID auf dem Server sind 1000 bzw. 1000. In Android sind diese für den Systembenutzer und die Gruppe reserviert. Alle meine anderen Computer haben UID und GID 1000 und denselben Benutzernamen neo
. Es wäre viel einfacher, einfach zu ändern, wie Android die Freigabe bereitstellt. Ich möchte abbilden uid=neo(1000)->root(0)
und guid=neo(1000)->sdcard_r(1028)
wo neo:neo
ist der User auf dem Server und wo root:sdcard_r
ist der User am Telefon.
Ich möchte uid=neo(1000)->root(0) und guid=neo(1000)->sdcard_r(1028) zuordnen, wobei neo:neo der Benutzer auf dem Server und root:sdcard_r der Benutzer auf dem Telefon ist .
Der Besitz von Dateien root:sdcard_r (0:1028)
auf Android funktioniert nicht für Apps. Es sollte root:everybody (0:9997)
mit Modus 0770
für Verzeichnisse und 0660
für Dateien sein.
Herkömmliche *NIX-DAC (UIDs/GIDs) wurden entwickelt, um menschliche Benutzer zu isolieren, nicht Programme. Auf Android-Geräten – die normalerweise für einen menschlichen Benutzer gedacht sind – müssen Apps isoliert und geschützt werden, damit sie nicht auf die Daten des anderen zugreifen können. Android behandelt also jede App wie einen menschlichen Benutzer – mit einer eindeutigen UID/GID.
Externer Speicher ( /sdcard
) – ob physisch extern oder intern – soll von allen Apps geteilt werden, dh mehrere UIDs. Wenn es sich um ein traditionelles *NIX-Dateisystem (wie ext4
) handelt, würde jede App Dateien mit ihrer eigenen UID/GID erstellen, was es fast unmöglich macht, Dateien zwischen allen Apps mit Lese-/Schreibzugriff zu teilen. Um eine Lösung zu finden, entschied sich Android für ein emuliertes Dateisystem – basierend auf FUSE oder sdcardfs
– mit festem Besitz und Modus. Siehe Details unter Was ist die UID „u#_everybody“?
Diese UIDs/GIDs und Modi steuern auch den Zugriff von Apps auf Dateien in /sdcard
. Wenn wir NFS so bereitstellen möchten, dass es für alle Apps zugänglich ist, müssen wir diese Berechtigungen gemäß dem Speicherdesign von Android verringern.
sec=sys
Modus erzwingt UNIX-Berechtigungen ohne Übersetzung. Wie im vorherigen Abschnitt erläutert, werden Dateien mit zufälligem Besitz von verschiedenen Apps erstellt. Diese müssen also von world ( o+rw
) lesbar/schreibbar sein, sonst können Apps nicht lesen/schreiben. Es gibt jedoch keine Möglichkeit, den Dateierstellungsmodus global zu erzwingen , und es ist auch keine gute Idee, Dateien mit zu offenen Berechtigungen freizugeben. Eine mögliche Lösung ist die Verwendung von ID-Mapping und/oder anonymem Modus.
Mit NFSv4 ist es möglich, unterschiedliche UIDs/GIDs zwischen Client und Server abzubilden. Wenn ein Benutzer mit demselben Namen, aber unterschiedlichen UIDs/GIDs auf beiden Seiten vorhanden ist, sehen Dateien auf dem Client aus, als gehörten sie demselben Benutzernamen, nicht derselben UID. Die Linux-Schlüsselverwaltungsfunktion * wird verwendet, um Zuordnungen von entfernten Benutzer-IDs zu lokalen Benutzer-IDs zu speichern. Eine gute Erklärung finden Sie hier .
Die Idee ist also, user:group neo:neo
auf dem NFS-Server ( 1000
: 1000
) und auf Android ( 0
: 9997
) zu erstellen. Dazu rpc.idmapd
muss sowohl auf dem Server als auch auf dem Client ausgeführt werden, der die Benutzerdatenbank ( /etc/{passwd,group}
im einfachsten Fall) über NSS abfragt . Auf neueren Kerneln verwendet der NFSv4-Client stattdessen nfsidmap und greift nur dann darauf zurück, rpc.idmapd
wenn es ein Problem beim Ausführen dernfsidmap
. Kernel ruft /sbin/request-key
Binärdateien im Userspace auf, die ausgeführt werden nfsidmap
, um die Benutzerdatenbank abzufragen. Der Kernel würde also Server neo
( 1000
: 1000
) auf Client neo
( 0
: 9997
) abbilden.
In einem einfachen Fall, wenn der UID/GID-Bereich 10000 bis 19999 keinem Benutzer/einer Gruppe auf dem NFS-Server zugewiesen ist und Nobody-User = root
/ auf dem Client Nobody-Group = everybody
definiert ist /etc/idmapd.conf
, werden alle Dateien (erstellt von Android-Apps), die nicht existierenden Benutzern auf dem Server gehören, zurückgegeben wie im Besitz von 0
: 9997
auf Client. Aber es löst nicht das Problem des zufälligen Besitzes von Dateien auf dem Server.
Das Ausführen rpc.idmapd
oder Bereitstellen nfsidmap
auf Android ist eine komplizierte Aufgabe (statische Verknüpfung und all das). Wir können jedoch - unter Verwendung von keyutils
( request-key
und ) - den Kernel dazu bringen, feste Eigentumsrechte für alle Zuordnungen keyctl
anzuzeigen, unabhängig davon, was die tatsächliche Eigentümerschaft ist:0:9997
Auf dem NFS-Server:
Dienste ausführen:
~# 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
Oder konfigurieren und führen Sie erforderliche init
Dienste aus. Entsperren Sie auch Ports durch die Firewall auf Client und Server.
Auf Android:
Erstellen /sbin/nfsidmap_pseudo
und /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
Dateien platzieren, Berechtigungen festlegen und ID-Mapping aktivieren:
~# 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
Da NFS auf Android nicht offiziell unterstützt wird, hat die SELinux-Richtlinie keine erforderlichen Regeln. Möglicherweise müssen Sie SELinux permissiv einstellen oder dem Kernel erlauben, Schlüssel zu lesen/schreiben, Dateien im Userspace auszuführen und Verbindungen herzustellen:
~# 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 }'
Montieren:
~# 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
Leider bekommen wir von all diesem Setup, dass Apps jetzt anscheinend Dateien im /sdcard/NFS
Besitz von sehen 0:9997
. Aber mit sec=sys
Sicherheit wird der tatsächliche Dateizugriff nicht durch die NFSv4-UID-Zuordnung ** geregelt . Berechtigungen werden durch den RPC-Mechanismus erzwungen, der noch nicht bereit ist, mit der ID-Zuordnung zu arbeiten. Die UID-Zuordnung ohne Kerberos
Sicherheit funktioniert also nur, wenn der Benutzer-/Gruppenname und die Nummernräume zwischen Client und Server konsistent sind. Dies bedeutet, dass neo
der Benutzer auf dem Server UID/GID: 0
/ haben sollte 9997
(was den gesamten Zweck der ID-Zuordnung zunichte macht). Andererseits sec=krb5
ist die Kerberos-Sicherheit ( ) zu hektisch , um sie auf Android auszuprobieren.
In ähnlicher Weise erfordert das Sperren von Dateien auf NFSv2/3 portmapper
( rpcbind
) und rpc.statd
die Ausführung sowohl auf dem Server als auch auf dem Client, was beim Android-Client nicht der Fall ist. Also müssen wir nolock
die Mount-Option verwenden. Bei NFSv4 ist das Sperren jedoch im NFS-Protokoll integriert, NLM wird nicht benötigt. Also ist es besser, nicht zu gehen UID Mapping
(auf NFSv4 und File Locking
auf NFSv2/3). Wenn Sie alle NFS-Funktionen (einschließlich Kerberos) auf einem Android-Gerät benötigen, versuchen Sie es lieber mit einer winzigen Linux-Distribution in chroot
.
* Auf einem Kernel älter als v4.6 /proc/keys
wird angezeigt, wenn der Kernel mit KEYS_DEBUG_PROC_KEYS
.
** Referenzen: 1 , 2 , 3 , 4 , 5 , 6
Eine der NFS-Sicherheitsvarianten ist der anonyme Modus . Jede Anfrage von einer beliebigen UID/GID auf dem Client wird als anonyme UID/GID auf dem Server behandelt. Anfänglich müssen alle Dateien im gemeinsam genutzten Verzeichnis dieser UID/GID gehören, und alle nachfolgend erstellten Dateien von der Client-Seite würden auch den gleichen Besitz haben:
Freigabe exportieren mit sec=none
auf dem Server:
~# 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
* Für NFSv2/3 auch ausführen rpcbind
(auf Standardport 111)
Mounten mit sec=none
On 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
* Verwenden Sie -t
und vers=
gemäß Ihrer Kernel-Build-Konfiguration CONFIG_NFS_V[2|3|4|4_1|4_2]
.
* Verwenden Sie -t nfs -o nolock
mit vers=3
oder vers=2
.
* Stellen Sie sicher, dass Sie aus dem Root-Mount-Namespace mounten.
* Mounten mit sec=none
funktioniert nicht mit NFSv3.
Alle Apps auf Android können jetzt im NFS-Verzeichnis lesen und schreiben und alle Dateien werden mit einer einzigen anonymen UID/GID auf dem Server erstellt, die von einem Benutzer einfach zu verwalten ist. Der offensichtliche Besitz (wenn die ID-Zuordnung nicht eingerichtet ist) und der Berechtigungsmodus entsprechen jedoch nicht dem Geschmack von Android, also ...
Lassen Sie uns FUSE oder sdcardfs
wie Android verwenden:
~# 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
Wenn Ihr Gerät nicht unterstützt sdcardfs
, verwenden bindfs
und ersetzen Sie den SELinux-Kontext u:object_r:sdcardfs:s0
durch 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
Es lässt uns ohne verbleibende Probleme. Weitere Einzelheiten finden Sie unter How to bind mount a folder within /sdcard with correct permissions?
NFS ist das native Dateisystem von Linux (wie ext4
) und soll *NIX-Berechtigungen erzwingen. Bei nicht nativen Dateisystemen wie FAT und CIFS können feste Eigentumsrechte und Berechtigungen für das gesamte Dateisystem festgelegt werden. Was Sie also suchen, ist mit CIFS relativ einfach zu erreichen :
~# 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
* Der Kernel muss mit erstellt werden CONFIG_CIFS
und sollte vorzugsweise entsprechend CONFIG_CIFS_SMB2
verwendet werden vers=
.
* Einzelheiten zu Mount-Optionen finden Sie unter mount.cifs(8)
Andere FUSE-basierte einhängbare Dateisysteme sind sshfs
und rclone
. Letzteres bietet eine große Auswahl an Protokollen und Konfigurationen, erfordert aber eine sehr einfache Einrichtung.
Es scheint, dass jede Distribution von Busybox, die auf Google Play ist, nicht mit NFSv4 kompatibel ist, sondern nur mit NFSv3. Also habe ich die Idee aufgegeben, idmapd auf Android zu verwenden.
system
mehr dem Benutzer entsprechen.adduser
und addgroup
enthalten)./mnt/nfs
Verzeichnis zum Mounten der Freigabe ausgewählt.Ich habe keine Zeit zu untersuchen, welcher der vorherigen Punkte notwendig ist und welcher nur irrelevant oder optional ist. Wenn Sie eine Idee haben, schreiben Sie sie bitte in einen Kommentar.
Irfan Latif