FreeBSD: Home NAS, part 7 – NFSv4 та підключення до Linux
0 (0)

Автор |  31/12/2025
Click to rate this post!
[Total: 0 Average: 0]

Наступний крок в процесі налаштування домашнього NAS на FreeBSD – додати NFS share.

Samba зробили в попередній частині – тепер до неї додамо шари з NFS: моя ідея в тому, щоб Samba share використовувалась для всяких медіа-ресурсів, до яких потрібен доступ з телефонів та TV, а NFS буде виключно для Linux-хостів – двох ноутбуків в різних мережах (дім і офіс), які на цей розділ будуть робити свої бекапи з rsync, rclone або restic.

Попередні пости цієї серії:

NFSv3 vs NFSv4

Наразі є дві основні версії NFS – v3 та v4.

FreeBSD підтримує робота з обома (я під час тестів налаштовував і v3 теж), але, очевидно, що v4 більш актуальна, і має низку переваг:

  • NFSv3 – stateless, а NFSv4 – stateful: v4 зберігає стан клієнтів і сесій, що спрощує роботу з блокуваннями та доступом до файлів
  • NFSv3 вважається простішою: але, як на мене, то NFSv4 налаштовується не складніше, і навіть простіше через меншу кількість компонентів
  • аутентифікація:
    • NFSv3 аутентифікація – IP клієнта + UID/GID (хто підключився і від імені якого користувача)
    • NFSv4 – розширена модель доступу, підтримка ACL (і база для Kerberos, якщо потрібно)

Див. NFSv3 and NFSv4: What’s the difference?

Створення ZFS datasets

Аби мати можливість окремих налаштувань снапшотів і ZFS quotes – датасети для NFS зробимо ієрархічно:

  • nas/: корневий датасет ZFS-пулу
    • nas/nfs/: корневий датасет для всього NFS
      • nas/nfs/backups/: датасет для бекапів з інших машин

А в nas/nfs/backups/ вже будуть окремі каталоги з іменами хостів для їхніх бекапів – “setevoy-home“, “setevoy-work“, “setevoy-rtfm” і т.д.

Пізніше, при потребі, в nas/nfs/ можна буде додати нову NFS-шару.

Додаємо новий dataset:

root@setevoy-nas:/home/setevoy # zfs create nas/nfs

В ньому створюємо другий, для бекапів:

root@setevoy-nas:/home/setevoy # zfs create nas/nfs/backups

Сам NFS daemon в системі є з коробки, нам треба тільки додати його запуск:

root@setevoy-nas:/home/setevoy # sysrc nfs_server_enable="YES"
root@setevoy-nas:/home/setevoy # sysrc nfsv4_server_enable="YES"
root@setevoy-nas:/home/setevoy # sysrc nfsv4_server_only="YES"
root@setevoy-nas:/home/setevoy # sysrc nfsuserd_enable="YES"

NFSv4 працює з іменами, а не “сирими” UID/GID, тому, аби ID коректно мапились в імена – додаємо в /etc/sysctl.conf, і активуємо зміни зараз:

root@setevoy-nas:/home/setevoy # sysctl vfs.nfs.enable_uidtostring=1
vfs.nfs.enable_uidtostring: 0 -> 1
root@setevoy-nas:/home/setevoy # sysctl vfs.nfsd.enable_stringtouid=1
vfs.nfsd.enable_stringtouid: 0 -> 1

NFS, ZFS та sharenfs 

ZFS підтримує налаштування NFS для датасетів через dataset property sharenfs (а для Samba – через sharesmb), хоча є має обмеження.

Для NFSv4 обов’язково треба вказати корневу директорію, в якій будуть самі шари, див. NFS Version 4 Protocol.

Додаємо її в файл /etc/exports:

V4: /nas/nfs

Але доступу до цього корневого каталогу не буде, поки ми не додамо його явно, наприклад як:

# zfs set sharenfs="-network 192.168.0.0/24 -ro" nas/nfs

Тепер можемо через sharenfs розшарити nas/nfs/backups датасет, для якого з -network вказуємо з яких адрес буде дозволений доступ:

root@setevoy-nas:/home/setevoy # zfs set sharenfs="-network 192.168.0.0/24" nas/nfs/backups

Перевіряємо:

root@setevoy-nas:/home/setevoy # zfs get sharenfs nas/nfs/backups
NAME             PROPERTY  VALUE                    SOURCE
nas/nfs/backups  sharenfs  -network 192.168.0.0/24  local

По факту, zfs set sharenfs просто редагує файл /etc/zfs/exports, який потім читається mountd:

root@setevoy-nas:~ # cat /etc/zfs/exports
# !!! DO NOT EDIT THIS FILE MANUALLY !!!

/nas/nfs/backups        -network 192.168.0.0/24

Запускаємо nfsd:

root@setevoy-nas:/home/setevoy # service nfsd start

nfsd автоматом запустить mountd, який власне відповідає за NFS sharing і читає конфігурацію з /etc/exports та /etc/zfs/exports:

root@setevoy-nas:~ # ps aux | grep mount
root      9475   0.0  0.0     13888   1076  -  Is   05:46       0:00.00 /usr/sbin/mountd -r -S -R /etc/exports /etc/zfs/exports

Додаємо правило на фаєрволі:

...
pass in on em0 proto { tcp udp } from { 192.168.0.0/24, 192.168.100.0/24, 10.8.0.0/24 } to (em0) port 2049
...

Перевіряємо синтаксис, застосовуємо зміни pf:

root@setevoy-nas:~ # pfctl -nvf /etc/pf.conf && service pf reload

Монтуємо на клієнті – вказуємо явно -t nfs4:

[setevoy@setevoy-work ~] $ sudo mkdir /mnt/test/
[setevoy@setevoy-work ~] $ sudo mount -t nfs4 192.168.0.2:/backups /mnt/test/

В 192.168.0.2:/backups каталог /backups вказуємо від корня, який задали в /etc/exports: тобто корінь у нас “/nas/nfs” – значить на клієнтах він буде “/“, і, відповідно, внутрішні датасети монтуємо від цього корня як /backups.

Перевіряємо маунти на клієнті:

[setevoy@setevoy-work ~]  $ findmnt /mnt/test/
TARGET SOURCE            FSTYPE OPTIONS
/mnt/test
       192.168.0.2:/backups
                         nfs4   rw,relatime,vers=4.2,rsize=131072,wsize=131072,namlen=255,hard,fatal_neterrors=none,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.0.4,local_lock=none,addr

З цікавого для нас тут:

  • nfs4: протокол
  • vers=4.2: версія
  • sec=sys: аутентифікація клієнтів (буде важливо для /etc/fstab на клієнтах, див. в кінці)
  • clientaddr=192.168.0.4: і адреса самого клієнта

Аналогічно на клієнті можна перевірити активне підключення з nfsstat -m:

[setevoy@setevoy-work ~]  $ nfsstat -m
/mnt/test from 192.168.0.2:/backups
 Flags: rw,relatime,vers=4.2,rsize=131072,wsize=131072,namlen=255,hard,fatal_neterrors=none,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.0.4,local_lock=none,addr=192.168.0.2

А на сервері – з nfsdumpstate:

root@setevoy-nas:/home/setevoy # nfsdumpstate
Flags         OpenOwner      Open LockOwner      Lock     Deleg  OldDeleg Clientaddr
                      0         0         0         0         0         0 192.168.0.4

А з опцією -l можна подивитись активні Opens and Locks – але зараз тут пусто.

Перевіряємо доступи – створимо файл на сервері:

root@setevoy-nas:/home/setevoy # touch /nas/nfs/backups/test-server

І дивимось на клієнті:

[setevoy@setevoy-work ~]  $ ll /mnt/test/
total 1
-rw-r--r-- 1 root root 0 Dec 31 13:12 test-server

(Dec 31 – коли б ще сетапити домашній NAS на FreeBSD, правда? 😀 )

NFS, users та “Permission denied”

Але з клієнта ми зараз не можемо нічого писати в каталог:

[setevoy@setevoy-work ~]  $ touch /mnt/test/test-client
touch: cannot touch '/mnt/test/test-client': Permission denied

Бо на сервері він створювався від root:

root@setevoy-nas:~ # stat /nas/nfs/backups/test-server
4446369902026857636 4 -rw-r--r-- 1 root wheel 0 0 "Dec 31 13:12:36 2025" "Dec 31 13:12:36 2025" "Dec 31 13:12:36 2025" "Dec 31 13:12:36 2025" 131072 1 0x800 /nas/nfs/backups/test-server

Ба більше – доступу нема навіть від локального root на клієнті:

[setevoy@setevoy-work ~]  $ sudo touch /mnt/test/test-client
touch: cannot touch '/mnt/test/test-client': Permission denied

А по-дефолту NFS виконує root_squash, і всі операції від клієнтів на сервері виконує від локального юзера nobody.

Є кілька варіантів вирішення:

  • на сервері створити групу типу nfsusers, дати їй права на запис в каталог (775), і додати локального юзера setevoy в цю групу
    • самий кошерний і безпечний варіант
  • або можна задати опцію -maproot=root – тоді root на клієнті буде == root на сервері (UID в обох 0)
    • але це тільки про доступ до файлів, і тільки в межах NFS root – /nas/nfs
    • ОК варіант для домашнього NAS
  • трохи безпечніший варіант – вказати -maproot=setevoy і на сервері змінити власника /nas/nfs/backups/ – тоді операції від root на клієнті на сервері будуть виконуватись від UID/GID юзера setevoy на сервері
  • або взагалі зробити -mapall=root – і тоді всі юзери на клієнті будуть виконувати операції як локальний root
    • аналогічно до -maproot=root, але і самий небезпечний варіант

Так як ця шара для бекапів, які на клієнтах будуть виконуватись від root – то можна використати maproot=root:

root@setevoy-nas:/home/setevoy # zfs set sharenfs="-network 192.168.0.0/24 -maproot=root" nas/nfs/backups
root@setevoy-nas:/home/setevoy # service nfsd restart

Тепер на клієнті від звичайного юзера у нас доступу все ще нема:

[setevoy@setevoy-work ~]  $ touch /mnt/test/test-client
touch: cannot touch '/mnt/test/test-client': Permission denied

Але є від локального root, бо він отримує права root на сервері:

[setevoy@setevoy-work ~]  $ sudo touch /mnt/test/test-client

ZFS sharenfs та muliple -network

Дуже довго проковирявся з цим 🙁

Допомогли на форумі FreeBSD, див. NFSv4 and share for multiply networks (взагалі, FreeBSD community дуже відкрите, і набагато менш токсичне, аніж ті ж форуми Arch Linux).

В чому проблема: в мене доступ до хоста FreeBSD відбувається з кількох мереж (див. FreeBSD: Home NAS, part 3 – WireGuard VPN, Linux peer та routing):

  • 192.168.0.0/24: мережа офісу
  • 192.168.100.0/24:  домашня мережа
  • 10.8.0.0/24: VPN

І, звісно, хочеться доступ до NFS мати захищеним на рівні мереж (хоча в моєму випадку цілком можна було обійтись без цього, але краще відразу робити правильно).

А проблема полягає в том, що у ZFS ver 2.2.7, яка використовується у FreeBSD 14.3 зараз, нема можливості вказати кілька мереж в sharenfs property.

Тобто, не можна використати щось типу:

# zfs set sharenfs="-network 192.168.0.0/24 -network 192.168.100.0/24 -network 10.8.0.0/24" nas/nfs/backups

Але у FreeBSD 15.0 і ZFS 2.4.0 начебто розширили синтаксис, і там вже можна передати список, розділений “;“:

# zfs  sharenfs="-network 192.168.0.0/24 -maproot=root;-network 192.168.100.0/24" nas/nfs/backups

Ну а на версії 2.2.7 – просто робимо шару не через zfs set sharenfs, а напряму у файлі /etc/exports.

Відмонтовуємо шару на клієнті:

[setevoy@setevoy-work ~]  $ sudo umount /mnt/test

На сервері прибираємо sharenfs:

root@setevoy-nas:~ # zfs set sharenfs=off nas/nfs/backups

Перевіряємо:

root@setevoy-nas:~ # zfs get sharenfs nas/nfs/backups
NAME             PROPERTY  VALUE     SOURCE
nas/nfs/backups  sharenfs  off       local

Перевіряємо, що /etc/zfs/exports тепер пустий:

root@setevoy-nas:~ # cat /etc/zfs/exports
# !!! DO NOT EDIT THIS FILE MANUALLY !!!

root@setevoy-nas:~ #

Далі, редагуємо файл /etc/exports, і задаємо шари тут, кожен запис для окремої мережі:

V4: /nas/nfs
/nas/nfs/backups -network 192.168.0.0/24 -maproot=root
/nas/nfs/backups -network 192.168.100.0/24 -maproot=root
/nas/nfs/backups -network 10.8.0.0/24 -maproot=root

Перезапускаємо nfsd та mountd (mountd рестартимо явно вручну, бо були проблеми з доступом і помилки “Input/output error“):

Перевіряємо доступ з клієнта в офісі:

[setevoy@setevoy-work ~]  $ sudo mount -t nfs4 192.168.0.2:/backups /mnt/test
[setevoy@setevoy-work ~]  $ file /mnt/test/test-client
/mnt/test/test-client: empty

І з клієнта, який вдома підключений через VPN:

[setevoy@setevoy-home ~]$ sudo wg show
interface: wg0
...
peer: xLWA/FgF3LBswHD5Z1uZZMOiCbtSvDaUOOFjH4IF6W8=
  endpoint: 178.***.***.184:51830
  allowed ips: 10.8.0.1/32, 192.168.0.0/24

З адресою 10.8.0.3:

[setevoy@setevoy-home ~]$ ip a s wg0
44: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none 
    inet 10.8.0.3/24 
    ...

Встановлюємо nfs-utils, інакше буде помилка “NFS: mount program didn’t pass remote address“:

[setevoy@setevoy-home ~]$ sudo pacman -S nfs-utils

Підключаємо шару на цьому клієнті:

[setevoy@setevoy-home ~]$ sudo mount -t nfs4 192.168.0.2:/backups /mnt/test/

Перевіряємо доступ:

[setevoy@setevoy-home ~]$ sudo touch /mnt/test/test-vpn-client

Перевіряємо:

[setevoy@setevoy-home ~]$ ls -l /mnt/test/test-vpn-client
-rw-r--r-- 1 root root 0 Dec 31 15:16 /mnt/test/test-vpn-client

І на сервері тепер бачимо два активних підключення: один Clientaddr з адреси мережі VPN – 10.8.0.3, і один з офісної – робочий ноут з 192.168.0.4:

root@setevoy-nas:~ # nfsdumpstate
Flags         OpenOwner      Open LockOwner      Lock     Deleg  OldDeleg Clientaddr
CB                    1         0         0         0         0         0 10.8.0.3   
                      0         0         0         0         0         0 192.168.0.4

Linux, /etc/fstab та systemd-automount

І наостанок додамо автомаунт, як це зроблено для Samba share.

Відключаємо шару зараз:

[setevoy@setevoy-work ~] $ sudo umount /mnt/test

Створюємо вже постійний каталог:

[setevoy@setevoy-work ~] $ sudo mkdir /nas/nfs/backups/

На клієнтах редагуємо /etc/fstab:

...

192.168.0.2:/backups  /nas/nfs/backups  nfs  sec=sys,_netdev,noauto,x-systemd.automount,nofail,noatime  0  0

Тут явно вказуємо sec=sys, тобто аутентифікацію за AUTH_SYS (по UID/GID, див. The AUTH_SYS authentication method).

Виконуємо sudo systemctl daemon-reload, перевіряємо нові unit-файли:

[setevoy@setevoy-work ~]  $ ll /run/systemd/generator/ | grep nfs
-rw-r--r-- 1 root root 177 Dec 31 15:29 nas-nfs-backups.automount
-rw-r--r-- 1 root root 274 Dec 31 15:29 nas-nfs-backups.mount

Активуємо їх:

[setevoy@setevoy-work ~]  $ sudo systemctl restart remote-fs.target

І перевіряємо доступ – шара має підключитись автоматично:

[setevoy@setevoy-work ~]  $ ll /nas/nfs/backups
total 2
-rw-r--r-- 1 setevoy nfsusers 0 Dec 31 14:18 test-client
-rw-r--r-- 1 root    root     0 Dec 31 13:12 test-server
-rw-r--r-- 1 root    nfsusers 0 Dec 31 15:16 test-vpn-client

Готово.

Корисні посилання

Loading