Sharing The Love…

So, the ZFS datasets have been created, and we now have to start creating the shares…

<rant>No thanks to Micro$oft, NFS support on is now only available on Enterprise versions of Windows 10, and therefore I will cannot use (the more efficient) NFS in my largely Windows environment network.</rant>

Goals

My goal was to:

  1. set up several different shares, mapping directly to the ZFS datasets
    1. this meant setting up SAMBA in a “WORKGROUP” environment
  2. allow different users to map this share on Windows, and be able to view only, or modify any objects within (add/delete/edit) according to their permissions per share

What follows is the steps required to:

  • create the SAMBA share(s) and securing it (or at least setting the correct permissions)
  • set-up the SAMBA user(s)

Note: There is a difference between Linux uid/user and gid/group and SAMBA SID/user and GID/group; within this context, I attempt to refer to the latter as “SMB User” and “SMB group” for disambiguation.

Partially referencing these instructions I found:

 

Setting Up SAMBA

As I had already chosen to install the SAMBA file server “task” as part of running tasksel, there was no need to run apt-get; if you did not, I suggest you install it through tasksel (instead).

Create the Share

I use the same share name as the dataset name, so:

zfs set sharesmb=on <dataset>

20161025 Update: Since my Ubuntu 16.04.1 LTS does not have the same share and unshare commands as Solaris, the zfs sharesmb=name=<share name> command does not work; hence, I had to make copies of the SMB shares definitions (found in /var/lib/samba/usershares, which is the location as defined in /etc/samba/smb.conf)

20161026 Update: Due to other issues encountered (read below), in an attempt to limit the possible causes, I decided to not use /var/lib/samba/usershares but hard-code everything in /etc/samba/smb.conf instead; either way, the sections/settings should still be the same, but YMMV.

# [Global] additions:
map acl inherit = yes
store dos attributes = yes

# ZFS Share (group writeable)
[<share name>]
   comment = <comment>
   path = /<pool>/<dataset>
   browseable = yes
   guest ok = no
   writable = yes
   valid users = @<SMB groupname>
   create mask = 0660
   directory mask = 0770
   force group = <group>
# ZFS Share (specific user/s writeable)
[<share name>]
   comment = <comment>
   path = /<pool>/<dataset>
   browseable = yes
   guest ok = no
   writable = no
   write users = <SMB user>, <SMB User>...
   valid users = @<SMB groupname>
   create mask = 0660
   directory mask = 0770
   force group = <group>

20200505 Update: Due to other issues, detailed in the same-dated update further below, the sample configuration sections should follow those below instead.

Securing the Share

We attempt to secure the share at the FS level first:

Setting Permissions

Since zfs created the dataset directories, these had owner and group as root. Since I have to allow users from a particular group to read and write (and modify each others’) files and directories, I changed the group for the datasets to the group in question (i.e. the existing-by-default sambashare group):

chgrp -R <group name> /<pool>/<dataset>

Optional: Since I do not any account other than root to be able to change the /<pool> directory, I removed group and others’ write permissions:

chown root /<pool>
chgrp root /<pool>
chmod -R go-w /<pool>
chmod -R go+rx /<pool>

Optional: Since I do not expect local system accounts (that are not already part of the sambashare group) to use the datasets, I removed “other”‘s permissions:

chmod -R o-rwx /<pool>/<dataset>

20161026 Update: Wasted a lot of time (~4 hours, to be exact), trying to be “too smart” – I accidentally did chmod -R o-rwx /<pool> instead of using /<pool>/<dataset>, thus the top-level /<pool> directory (although definitely recommended to not be “world writeable”), ended up being not accessible by anyone (other than root, because /<pool>‘s owner and group was root). Way to be forced to learn more about troubleshooting SAMBA! My loss, your gain: read the end of this article for some troubleshooting tips.

20170107 Update: Since I stored ISO images and the VM disks in one of the RAIDZ2 pool datasets, I had to actually backtrack on tightening the permissions on the parent directories and files on the various paths used by KVM. It appears KVM likes to grab user and group ownership on the various files it uses.

20200505 Update: Since libvirt-qemu looks like they will never fix this silly bug, the only available solution, due to sharing of files via SAMBA, is to set any ISO files to be world readable and writable i.e.:

find <absolute ISO path> -type f -iname '*.iso' -exec chmod -R 0777 '{}' \;

Since I had existing data (having already copied all the back-ups back into the RAIDZ2 pool), I then setgid on the directories within the dataset (to ensure all files and directories within will always have the same group):

find <dataset's absolute path> -type d -exec chmod g+s '{}' \

20161125 Update: Whereas all the OS permissions set up so far allow access to group members, SAMBA still somehow understands the difference between opening a file across the network (for copying, etc.) vs. executing the file across the network (the o+x bit in this case)… Therefore, despite my wanting to make things unnecessarily complicated, I need to now rely on setfacl to force the “x” bit for groups for all files.

We need to first enable ACLs on the dataset and set ACL inheritance:

zfs set acltype=posixacl <dataset's absolute path>
zfs set aclinherit=passthrough <dataset's absolute path>

We then (recursively) force the ACLs for group to be “rwx” as well as change the default mask to “rwx”…

setfacl -dR -m g::rwx <dataset's absolute path>

Verification:

getfacl <dataset's absolute path>

20200505 Update: After having fluffed around with ACLs and not wanting to write script/s to constantly update files recursively nor finding a quick, easy “real-time” solution to this, I finally got fed up and just forced the setuid and setgid bits along with the force create mode and force directory mode options…

So first up is to reverse the ACL settings (as set up above; skip if you have not done the above bits):

setfacl -bkR <dataset's absolute path>
zfs set acltype=noacl <dataset's absolute path>

This is followed by just setting the owner and group to shared values, then setting permissions accordingly.

chown -R <shared owner>:<shared group> <dataset's absolute path>
find <dataset's absolute path> -type d -exec chmod ug=rwxs,o=rx '{}' \;
find <dataset's absolute path> -type f -exec chmod ug=rwx,o=rx '{}' \;

Then edit the various smb.conf shares accordingly, remembering to set obey pam permissions to “no”:

# ZFS Share (group writeable)
[<share name>]
   comment = <comment>
   path = /<pool>/<dataset>
   browseable = yes
   guest ok = no
   writable = yes
   valid users = @<SMB groupname>
   create mask = 0775
   force create mode = 0775
   directory mask = 6775
   force directory mode = 6775
   force user = <shared user>
   force group = <shared group>
# ZFS Share (specific user/s writeable)
[<share name>]
   comment = <comment>
   path = /<pool>/<dataset>
   browseable = yes
   guest ok = no
   writable = no
   write users = <SMB user>, <SMB User>...
   valid users = @<SMB groupname>
   create mask = 0775
   force create mode = 0775
   directory mask = 6775
   force directory mode = 6775
   force user = <shared user>
   force group = <shared group>

WARNING: Just remember that these users and groups specified in smb.conf are SMB users and SMB groups. To set these up, check the following sections.

Setting Up Linux Users and Groups

Next, we start to create the users and/or groups within Linux. Given that SAMBA is already installed, there should already be a sambashare group. Take note of the gid (i.e. “116” in this case) as it will be handy during troubleshooting.

fgrep "sambashare" /etc/group

2016-10-26_10-43-38

Per default, Linux creates a new group per user (with the user name as the group name). We now need to add sambashare as the secondary group(s):

usermod -a -G <group> <user>

20200519 Update: I decided to allow sharing some files via SMB over the Internet but needed to restrict certain shares to these users. For purposes of local access permissions (since, as above, all directories and files are o+rx), I created a “dummy user” without log in access for this purpose, and a “dummy” group:

adduser --home /dev/null --shell /usr/sbin/nologin --no-create-home --disabled-password --disabled-login <guest user>;
addgroup --system sambaguestshare;
usermod -a -G sambaguestshare <guest user>;

To restrict this SMB user or group from accessing certain shares, you could do it in either one of two ways:

  • assuming the affected SMB user/s are not part of existing groups already given access rights, simply not give access to this new SMB group
  • alternatively, if you need to exclude this specific user, simply add the following “invalid users” line before the “valid users” line:
invalid users = <SMB user>, <SMB user>

Setting Up SMB Users and Groups

SAMBA, by default, uses a “PDB” database file to store the users and passwords, which are different from the OS users (and passwords and groups).

We manually add each SAMBA user:

pdbedit -a <user>

This is followed by adding a map between a (new) SAMBA group to the existing Linux sambashare group, before we start adding SAMBA users to the (newly created/mapped) SAMBA group:

net groupmap add unixgroup=<Linux group name> type=local ntgroup=<SMB group> comment=<comment>
net sam addmem <SMB group> <SMB user>

After that, we can list the SAMBA users, groups and all the group members:

pdbedit -L
net groupmap list
net sam listmem <SMB group>

20200519 Update: As per the addendum above, I decided to allow sharing some files via SMB over the Internet but needed to restrict certain shares to these users. I created a “dummy SMB user” and a “dummy SMB group” to match (and map):

pdbedit -a <SMB guest user>;
net groupmap add unixgroup=sambaguestshare type=local ntgroup=sambaguestshare comment=<comment>;
net sam addmem sambaguestshare <SMB guest user>

Restarting SAMBA

We then restart SAMBA for changes to take effect:

/etc/init.d/samba restart

20200505 Update: It is now nearly four years on from whence this article was first written, so we should use systemctl instead of init.d scripts:

systemctl restart smbd

With that, we should now be able to see the shares from another Windows computer (assuming the appropriate requirements are met – but that is whole different series of posts).

 

Troubleshooting Tips

Some troubleshooting tips:

Run testparm to always confirm that your smb.conf is valid:

testparm smb.conf

Within the smb.conf, you could add/ensure the following is present to assist in troubleshooting:

log level = 3
log file = /var/log/samba/log.%m

With the above, you can then check /var/log/samba/log.<ip> and/or /var/log/samba/log.<remote machine name>. (which was how I spotted the permissions error).

Leave a Reply