Wie mounte ich NFS auf Android mit den richtigen Berechtigungen?

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 systemBenutzer bereitstellen. Dies wäre kein Problem, wenn ich den richtigen Besitz für das gemountete Dateisystem festlegen könnte. systemDas 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/exportsDatei 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:

  • LG V20 H990DS
  • System - V10i-TWN (6. Nov. 17) 7.0 Stock, Rooted & Xposed
  • Kernel - DOTS v1.4
  • NFS-Version 4

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:neoist der User auf dem Server und wo root:sdcard_rist der User am Telefon.

Antworten (2)

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 0770für Verzeichnisse und 0660für Dateien sein.

WIE KANN NFS MIT ANDROID-APPS FUNKTIONIEREN?

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.

EINFACHE UNIX-SICHERHEIT AUF NFS:

sec=sysModus 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.

NFSv4-ID-ZUORDNUNG:

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:neoauf dem NFS-Server ( 1000: 1000) und auf Android ( 0: 9997) zu erstellen. Dazu rpc.idmapdmuss 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.idmapdwenn es ein Problem beim Ausführen dernfsidmap . Kernel ruft /sbin/request-keyBinä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 = everybodydefiniert 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: 9997auf Client. Aber es löst nicht das Problem des zufälligen Besitzes von Dateien auf dem Server.

Das Ausführen rpc.idmapdoder Bereitstellen nfsidmapauf Android ist eine komplizierte Aufgabe (statische Verknüpfung und all das). Wir können jedoch - unter Verwendung von keyutils( request-keyund ) - den Kernel dazu bringen, feste Eigentumsrechte für alle Zuordnungen keyctlanzuzeigen, unabhängig davon, was die tatsächliche Eigentümerschaft ist:0:9997

Auf dem NFS-Server:

  1. 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 initDienste aus. Entsperren Sie auch Ports durch die Firewall auf Client und Server.

Auf Android:

  1. Erstellen /sbin/nfsidmap_pseudound /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. 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
    
  3. 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 }'
    
  4. 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/NFSBesitz von sehen 0:9997. Aber mit sec=sysSicherheit 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 KerberosSicherheit funktioniert also nur, wenn der Benutzer-/Gruppenname und die Nummernräume zwischen Client und Server konsistent sind. Dies bedeutet, dass neoder Benutzer auf dem Server UID/GID: 0/ haben sollte 9997(was den gesamten Zweck der ID-Zuordnung zunichte macht). Andererseits sec=krb5ist 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.statddie Ausführung sowohl auf dem Server als auch auf dem Client, was beim Android-Client nicht der Fall ist. Also müssen wir nolockdie 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 Lockingauf 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/keyswird angezeigt, wenn der Kernel mit KEYS_DEBUG_PROC_KEYS.
** Referenzen: 1 , 2 , 3 , 4 , 5 , 6

ANONYMER NFS-ZUGRIFF:

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=noneauf 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=noneOn 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 -tund vers=gemäß Ihrer Kernel-Build-Konfiguration CONFIG_NFS_V[2|3|4|4_1|4_2].
* Verwenden Sie -t nfs -o nolockmit vers=3oder 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 ...

VERSUCHEN SIE ES AUF ANDROID-ART...

Lassen Sie uns FUSE oder sdcardfswie 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 bindfsund ersetzen Sie den SELinux-Kontext u:object_r:sdcardfs:s0durch 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?

GEBEN SIE CIFS EINE CHANCE...

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_CIFSund sollte vorzugsweise entsprechend CONFIG_CIFS_SMB2verwendet werden vers=.
* Einzelheiten zu Mount-Optionen finden Sie unter mount.cifs(8)

ANDERE OPTIONEN?

Andere FUSE-basierte einhängbare Dateisysteme sind sshfsund rclone. Letzteres bietet eine große Auswahl an Protokollen und Konfigurationen, erfordert aber eine sehr einfache Einrichtung.

+1 Schätzen Sie das Detail
Ich liebe die detaillierte Erklärung der Grundlagen, die sich darauf auswirken. Vielen Dank!
@polynomial Ich bin froh, dass es geholfen hat.

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.

  • Stattdessen habe ich die UID und GID auf dem NFS-Server auf 4444 geändert, damit sie nicht systemmehr dem Benutzer entsprechen.
  • Dann habe ich einen neuen Benutzer auf Busybox mit demselben Namen, UID und GID wie der Server erstellt (in meiner Busybox-Distribution sind die Dienstprogramme adduserund addgroupenthalten).
  • Ich habe Tasker gekauft und diese Aufgaben aus dem XDA-Forum heruntergeladen.
  • Ich habe das /mnt/nfsVerzeichnis 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.