Наступний крок в процесі налаштування домашнього NAS на FreeBSD – додати NFS share.
Samba зробили в попередній частині – тепер до неї додамо шари з NFS: моя ідея в тому, щоб Samba share використовувалась для всяких медіа-ресурсів, до яких потрібен доступ з телефонів та TV, а NFS буде виключно для Linux-хостів – двох ноутбуків в різних мережах (дім і офіс), які на цей розділ будуть робити свої бекапи з rsync, rclone або restic.
Попередні пости цієї серії:
- FreeBSD: Home NAS, part 1 – налаштування ZFS mirror (RAID1) (тест на віртуальній машині)
- FreeBSD: Home NAS, part 2 – знайомство з Packet Filter (PF) firewall
- FreeBSD: Home NAS, part 3 – WireGuard VPN, Linux peer та routing
- FreeBSD: Home NAS, part 4 – локальний DNS з Unbound
- FreeBSD: Home NAS, part 5 – ZFS pool, datasets, snapshots та моніторинг
- FreeBSD: Home NAS, part 6 – Samba server та підключення клієнтів
- (current) FreeBSD: Home NAS, part 7 – NFSv4 та підключення до Linux
- (далі буде)
Зміст
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
- але це тільки про доступ до файлів, і тільки в межах NFS root –
- трохи безпечніший варіант – вказати
-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
Готово.
Корисні посилання
- Network File System (NFS): документація Redhat; синтаксис може трохи відрізнятись, але загальної інформації багато
- Network File System (NFS): FreeBSD Handbook, канонічна документація FreeBSD
- mount.nfs4 access denied by server: тут знайшов вирішення помилки, якщо не вказати sec=sys в /etc/fstab клієнтів
- How to Share ZFS Filesystems with NFS: трохи по оптимізації
![]()