Consider the following setup:
An NFS server exports the directory /srv/nfsv4 to one client. It is exported with the option “fsid=0” for use with NFSv4.
/srv/nfsv4 192.168.0.10/24(ro,sync,secure,root_squash,subtree_check,fsid=0)
The bind-mounted directory within it, foo, is exported as well. Client 192.168.0.10 can successfully mount and write to it.
/srv/nfsv4/foo 192.168.0.10/24(rw,sync,secure,root_squash,subtree_check)
“foo” has multiple subdirectories. While client 192.168.0.10 should have full read-write access to all of them, another client, 192.168.0.20, should only see a specific subset of these directories. Everything else should not only be read-only, but not mountable at all.
At first, I did it like this:
/srv/nfsv4 192.168.0.10/24(ro,sync,secure,root_squash,subtree_check,fsid=0) \
192.168.0.20/24(ro,sync,secure,root_squash,subtree_check,fsid=0)
/srv/nfsv4/foo 192.168.0.10/24(rw,sync,secure,root_squash,subtree_check) \
192.168.0.20/24(ro,sync,secure,root_squash,subtree_check)
/srv/nfsv4/foo/dir1 192.168.0.20/24(rw,sync,secure,root_squash,subtree_check)
/srv/nfsv4/foo/dir2 192.168.0.20/24(rw,sync,secure,root_squash,subtree_check)
With the effect that client 192.168.0.20 could still mount all other subdirectories of foo (even though read-only).
So, in an attempt to achieve the desired behavior, I created a second parent directory /srv/nfsv4/bar/ that has only the intended set of subdirectories bind-mounted to it:
srv/
└── nfsv4/
├── foo/
│ ├── dir1
│ ├── dir2
│ ├── dir3
│ └── dir4
└── bar/
├── dir1
└── dir2
And changed /etc/exports to look like this:
/srv/nfsv4 192.168.0.10/24(ro,sync,secure,root_squash,subtree_check,fsid=0) \
192.168.0.20/24(ro,sync,secure,root_squash,subtree_check,fsid=0)
/srv/nfsv4/foo 192.168.0.10/24(rw,sync,secure,root_squash,subtree_check)
/srv/nfsv4/bar 192.168.0.20/24(rw,sync,secure,root_squash,subtree_check)
Now, when I mount nfs-server:/bar on client 192.168.0.20, everything seems as expected. Except that I could still mount nfs-server:/ (the exported root) and have read access to foo. My understanding was that, unless I explicitly exported foo to 192.168.0.20, it should not be visible to it.
What did I do wrong?


The problem looks to be your subnet specification. /24 is 256 addresses. So for your 192.168.0.10/24 export, you’re exposing it to 0.10 and the following 255 IPs.
The closest thing to what you want to achieve would be a /29, which would expose it to the given IP and the following 7 IPs.
That can’t be right. For exposing it to a whole subnet, it would have to be 192.168.0.0/24, wouldn’t it?
Edit: Completely misread things, I thought you had 192.168.10.0 and 192.168.20.0. Yes it would be the whole subnet for a /24, if you wanted less you’d go higher as mentioned.
192.168.0.0/29 would include 192.168.0.1-192.168.0.7.
/28 would get you up to 192.168.0.15.
No, /24 is just 256 IPs.
192.168.0.0/24 = 192.168.0.0 mask 255.255.255.0, or 192.168.0.1-192.168.0.254.
If you want 192.168.0.0 through, say, 192.168.7.254, you need to cover 2k ip’s, which is a /21. The mask would be 255.255.248.0.
A /24 would only be the last octet.
No, if you mean 192.168.0.0 - 192.168.255.255, that is a /16 192.168.0.0 - 192.168.0.255 is a /24 192.168.0.0 - 192.168.0.7 is a /29
I was a little glib in my initial response. You really don’t want to start your definition in the middle of a subnet.
I would set it up so your first group of /29 would be 192.168.0.0 - 192.168.0.7
Second would be 192.168.0.8 - 192.168.0.15 (192.168.0.8/29)
Third would be 192.168.0.16 - 192.168.0.23 (192.168.0.16/29)
And so on.