Samba File Server With Windows ACLs in a Docker Container
This article explains how to set up a Samba file server with Windows ACLs as a domain member in a Docker container. This post is part of my series on home automation, networking & self-hosting that shows how to install, configure, and run a home server with dockerized or virtualized services.
This article is part of a mini-series about running Samba Active Directory and a file server service in a Docker container on a home server:
- Samba Active Directory in a Docker Container: Installation Guide
- Samba Active Directory as Authelia’s Authentication Backend
- Samba File Server With Windows ACLs in a Docker Container (this article)
- Web Access Through Filebrowser With SSO & HTTPS
- GitHub repository with Docker files and helper scripts
Please read the previous articles of this mini-series before proceeding.
Dockerized Samba File Server Installation
If you followed my Samba Active Directory domain controller installation guide, you already have a Samba file server in a Docker container. The only thing left to do is configure it.
Supervisord Config File supervisord.conf
Create config-fs1/supervisor/supervisord.conf
with the following content:
[supervisord]
nodaemon=true
user=root
logfile=/dev/null
logfile_maxbytes=0
[program:smbd]
command=smbd --foreground --no-process-group --debug-stdout
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
startretries=1
autorestart=false
[program:winbindd]
command=winbindd --foreground --no-process-group --debug-stdout
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
startretries=1
autorestart=false
Start the Fileserver Container
Verify that when you start the Samba containers you see the following when you inspect the logs with docker compose logs fs1
:
No Samba config file found at: /etc/samba/config/smb.conf
Please exec into the container and join a domain by running the following commands:
docker exec -it samba bash
/usr/helpers/samba-join.sh
Domain Join
Exec into the FS container and run the domain join script:
docker exec -it fs1 bash
/usr/helpers/samba-join.sh
After the file server has joined the domain, restart the FS container to give my init script the chance to do its magic at container start:
docker compose stop fs1
docker compose start fs1
Inspect the container logs for errors with the command docker compose logs fs1
. You should see the following:
INFO Set uid to user 0 succeeded
INFO supervisord started with pid 7
INFO spawned: 'smbd' with pid 11
INFO spawned: 'winbindd' with pid 12
smbd version 4.19.5-Ubuntu started.
Copyright Andrew Tridgell and the Samba Team 1992-2023
winbindd version 4.19.5-Ubuntu started.
Copyright Andrew Tridgell and the Samba Team 1992-2023
INFO success: smbd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
INFO success: winbindd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
Verification
Test Samba Configuration
Test the Samba config file by running testparm
.
DNS Record
Make sure that the file server’s name can be resolved on other machines on the network:
host fs1.ad.internal
Winbindd
Run the following commands to verify that Winbindd is able to connect to the AD DC:
wbinfo --ping-dc
# Expected output: checking the NETLOGON for domain[AD] dc connection to "dc1.ad.internal" succeeded
wbinfo -u
# Expected output: [list of users in the domain]
wbinfo -g
# Expected output: [list of groups in the domain]
wbinfo -i administrator
# Expected output: [passwd entry for administrator]
getent passwd administrator
# Expected output: [passwd entry for administrator]
getent group "AD\Domain Users"
# Expected output: domain users:x:10513:
Re-Join
Before you can re-join a machine to AD you need to delete its DNS record or dynamic DNS updates will fail because only the previous machine SID has permissions to update the existing record.
Run the following on the DC:
# Usage
samba-tool dns delete <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>
# Example
samba-tool dns delete localhost ad.internal fs1 A 192.168.0.6 -U administrator
Files Shares
Create a File Share
The Docker compose file has a bind mount for file shares that is visible in the container as /srv/samba
. To create a file share data
, perform the following steps (via exec
in the fs1
container):
- Create a directory in the file system:
mkdir /srv/samba/data
- Set Linux permissions on the shared folder. This is necessary because effective permissions for Windows users are determined by combining Linux permissions with Windows ACLs: both must allow the requested access. Since we want effective permissions to be governed by Windows ACLs, only, we set full control (
777
) on Linux.chmod 777 /srv/samba/data
- Set the Linux owner and group to Domain Admins and Domain Users, respectively:
chown "domain admins":"domain users" /srv/samba/data
- Add the share definition as a new stanza to
config/smb.conf
:[Data] path = /srv/samba/data read only = no # VFS modules vfs objects = acl_xattr # Ignore the standard Unix permissions; don't show them in Windows ACL Editor. acl_xattr:ignore system acls = yes
- Reload the Samba configuration:
smbcontrol all reload-config
Map (Mount) a File Share From Windows
To map a Samba file share on a Windows machine it’s not necessary for the Windows machine to be joined to the Samba domain. Run the following command to map the Data
share on any Windows PC connected to the same network:
# When prompted, enter:
# 1) The user name in the format: AD\USERNAME
# 2) The user's password
# Optionally replace S: with a different drive letter
net use s: \\fs1.ad.internal\data /savecred /p:yes
Note: The password is stored securely in Windows Credential Manager (open GUI with control keymgr.dll
). The stored credentials persist reboots; they are automatically re-used when the network drive is being re-connected.
SeDiskOperatorPrivilege: Allow a User/Group to Manage Share Permissions
By default, only the members of domain member’s local group Administrators
can manage file share permissions. That is generally a sensible configuration that shouldn’t be changed. I’m only mentioning this here because the SeDiskOperatorPrivilege
privilege creeps up in many Samba guides on the internet.
Set Windows File System Permissions (ACLs) on Linux
You should be able to manage shares and permissions from a domain-joined Windows machine (docs). On machines that are not domain-joined, user/group name lookup seems to be difficult. If you don’t join your Windows PCs to the Samba domain (I don’t), you can manage permissions from Linux, too.
Note that I only cover file system permissions here because as a rule of thumb you shouldn’t use share-level permissions.
Directory Structure
Before diving into the fun topic of ACLs, we’ll need to decide on a directory structure.
I want to map one network drive only. The base directory of that shared folder is to contain directories that would otherwise be shared individually. File system permissions govern access to those directories on level 1 in the shared folder. Example:
/srv/samba/data/ <<-- base dir (shared folder)
├── Family-Data/ <<-- level 1 in shared folder
├── Pictures/ <<-- level 1 in shared folder
└── Videos/ <<-- level 1 in shared folder
Tools for Managing Windows Permissions on Linux
Samba comes with two different tools aimed at managing Windows file system ACLs on Linux.
Samba-Tool NTAcl
The Swiss army knife samba-tool
with the option ntacl
allows you to get and set permissions as SDDL strings locally on a Samba file server. Its main downside is not that you’re forced to deal with SDDL but that it seems to be incompatible with the Samba option acl_xattr:ignore system acls
. In my testing I found that it adds Linux UGO permissions to the Windows ACL. The result can only be described as impractical.
Smbcacls
smbcacls
is Samba’s equivalent to Windows’ icacls.exe
. It provides a much friendlier access to ACLs and – most importantly – doesn’t throw Linux UGO permissions into the mix. That makes it the tool of choice for anyone who needs to manage Windows ACLs on a Samba file server.
List Windows ACLs on Linux
The following command lists the default permissions on our newly-shared directory from a Windows point of view:
smbcacls //localhost/data / -U USERNAME
Note: do not use the Administrator
user in the smbcacls
command or you’ll get the error code NT_STATUS_INVALID_TOKEN
.
Create Groups to Assign Permissions
As a best practice, always assign permissions to groups specifically created for that purpose. Create one group that grants full permissions and another that grants read permissions only. To do so for our shared data directory run the following in the DC container:
# Read & write
samba-tool group add FS_Data_RW --groupou="OU=Groups,OU=My"
# Read only
samba-tool group add FS_Data_R --groupou="OU=Groups,OU=My"
Set Windows ACLs on Linux
With all the pieces in place, we can now set permissions:
smbcacls //localhost/data / -U USERNAME --set "REVISION:1,OWNER:AD\Domain Admins,GROUP:AD\Domain Users,ACL:AD\Domain Admins:ALLOWED/OI|CI/FULL,ACL:AD\Domain Users:ALLOWED/0x0/R,ACL:AD\FS_Data_RW:ALLOWED/OI|CI/FULL,ACL:AD\FS_Data_R:ALLOWED/OI|CI/READ"
Notes:
- The
set
command replaces (deletes) existing ACLs. Set
requiresREVISION
,OWNER
, andGROUP
.Set
fails withNT_STATUS_ACCESS_DENIED
if you’re not the directory owner (either directly or via group membership).
What About Linux Owner/Group/Other Permissions?
We don’t have to worry about the Linux permissions. Samba sets those to the user creating a file/directory and the group domain users
, respectively. Linux permissions are set to 770 (full control for user and group).
Permissions on Level 1 in the Shared Folder
Repeat the following process to create each directory on level 1 within the shared folder with the appropriate permissions.
Create the Directory
On a Windows machine (!), create the L1 directory, e.g., Pictures
. If you create the directory on Linux, Windows permissions are not inherited from the parent directory.
Create the Groups
Back on Linux, create two domain groups for setting permissions:
samba-tool group add FS_Data_Pictures_RW --groupou="OU=Groups,OU=My"
samba-tool group add FS_Data_Pictures_R --groupou="OU=Groups,OU=My"
Add Permissions
Add an access control entry (ACE) to the ACL that already contains ACEs inherited from the parent directory:
smbcacls //localhost/data /Pictures -U USERNAME --add "ACL:AD\FS_Data_PICTURES_RW:ALLOWED/OI|CI/FULL,ACL:AD\FS_Data_Pictures_R:ALLOWED/OI|CI/READ" [--propagate-inheritance]
If Pictures
is not empty (ie., it contains files or subfolders), add the --propagate-inheritance
parameter.
Automatic Propagation of Permissions to Subfolders and Files
Windows ACL Editor automatically propagates ACEs to subfolders and files recursively (according to each ACE’s inheritance options, of course). Smbcacls doesn’t. That’s why it’s necessary to explicitly add the --propagate-inheritance
parameter.
Additional Features & Optimizations
Recycle Bin
Samba comes with a recycle bin feature that moves files to a .recycle
directory instead of deleting them. This feature is independent of the Windows File Explorer recycle bin.
Notes
- With Samba’s recycle bin enabled, there is no way to “really” delete files (e.g., with
Shift+Del
) without moving them to the.recycle
directory. - Just like the Windows original, Samba’s recycle bin is not emptied automatically.
- The
.recycle
directory is created with thehidden
attribute so that’s it’s not visible in Windows File Explorer by default.
Enable Recycle Bin
To enable Samba’s recycle bin, add the following lines to a share configuration in smb.conf
:
# VFS modules (make sure to include all modules - this is not cumulative to a global definition but replaces it instead)
vfs objects = recycle acl_xattr
# Recycle bin configuration
recycle:directory_mode = 775
recycle:keeptree = yes
recycle:versions = yes
Set Permissions
Files moved to the .recycle
directory don’t keep their original Windows permissions. Instead, they inherit the Windows permissions from the .recycle
directory. You may want to grant additional read permissions as follows:
smbcacls //localhost/data .recycle -U USERNAME --add "ACL:AD\Domain Users:ALLOWED/OI|CI/READ" --propagate-inheritance
Delete Files After 60 Days
To prevent the recycle bin from growing indefinitely we’ll set up a script on the Docker host that runs daily and deletes files and directories in the bin after 60 days.
Create the file /usr/local/bin/samba-recycle-cleanup.sh
with the following content:
#!/bin/bash
RecyclePath=/rpool/encrypted/docker/samba/shares-fs1/data/.recycle
MaxDays=60
# Delete all files created earlier than MaxDays
find $RecyclePath -name "*" -ctime +$MaxDays -type f -delete
# Delete empty directories
find $RecyclePath -name "*" -empty -type d -delete
Make the script executable:
chmod +x /usr/local/bin/samba-recycle-cleanup.sh
Create a cron job to run the script daily one minute after midnight:
crontab -e
Paste the following into the file that opens:
1 0 * * * /usr/local/bin/samba-recycle-cleanup.sh
Access-Based Enumeration (ABE)
What is Access-Based Enumeration (ABE)?
Access-based enumeration is a popular Microsoft technology that has existed in Windows Server since 2003. ABE displays only those files and folders a user has permission to access. It does this by filtering directory listings when a network share is accessed via SMB, removing files or folders the user doesn’t have read permissions for.
Enabling Access-Based Enumeration (ABE) in Samba
Samba has an equivalent to ABE that can be enabled by adding the following to the share definition stanza in smb.conf
:
# Enable access-based enumeration
hide unreadable = yes
Note: this works well because we’ve enabled acl_xattr:ignore system acls
. Otherwise, it would also consider Linux permissions, which would make this feature impractical.
Shadow Copies as Previous Versions
Samba’s vfs_shadow_copy2
module exposes Linux file system snapshots as Windows shadow copies that can be accessed as “previous versions” in File Explorer.
Warning: the configuration described below works, but it causes errors: ZFS snapshots cannot be deleted anymore as soon as they’ve been access through Windows’ Previous Versions.
Install and Configure zfs-auto-snapshot
Install the auto-snapshotting script zfs-auto-snapshot
:
apt install zfs-auto-snapshot
Note that this also installs cron jobs in /etc/cron.*
. You can list them with la /etc/cron.*/zfs-auto*
.
I don’t want snapshots for the various rpool/ROOT/pve-1/*
datasets, so I changed the script’s default behavior to only snapshot datasets where the property com.sun:auto-snapshot
has been explicitly set to true
. To do so, edit each cron job file, appending --default-exclude
. Example:
*/15 * * * * root which zfs-auto-snapshot > /dev/null || exit 0 ; zfs-auto-snapshot --quiet --syslog --label=frequent --keep=4 --default-exclude //
Enable snapshots for the dataset where Samba data is stored:
zfs set com.sun:auto-snapshot=true rpool/encrypted
Enable and Configure Samba’s shadow_copy2 Module
To enable Samba’s recycle bin, add the following lines to a share configuration in smb.conf
:
# Enable recycle bin and shadow copies
vfs objects = recycle shadow_copy2
# Shadow copy configuration
shadow:snapdir = /.zfs/snapshot
shadow:snapsharepath = docker/samba/shares-fs1/data
shadow:snapprefix = zfs
shadow:delimiter = -20
shadow:format = -%Y-%m-%d-%H%M
Mount the Snapshot Directory in the Docker Container
Add the following volume definition to docker-compose.yml
:
volumes:
- /rpool/encrypted/.zfs/snapshot:/.zfs/snapshot:ro
Error: Cannot Destroy Snapshot: Dataset is Busy
It seems that whenever a Windows client accesses a “previous version” (aka, a ZFS snapshot), that snapshot cannot be deleted anymore. If that happens for a “frequent” snapshot, you may get one email from Cron every 15 minutes with a message like the following:
cannot destroy snapshot rpool/encrypted@zfs-auto-snap_frequent-2024-06-23-2145: dataset is busy
I am not aware of a solution to this issue.
Disable the Print Service
To disable print server functionality, add the following to the [global]
stanza of the machine’s config/smb.conf
file:
load printers = no
printing = bsd
printcap name = /dev/null
disable spoolss = yes
Note: this is also a useful configuration for domain controllers.
Backup
My backup solution based on restic works with this Samba file server setup very well, indeed.
Running Samba in an Unprivileged Container
Historically, Samba had be run in a privileged Docker container because it stores Windows permissions in the security.NTACL
extended attribute, and the security.*
namespace is only accessible as root
.
As of Samba 4.18, an alternative location can be specified via the acl_xattr:security_acl_name
configuration option (docs).
Troubleshooting
Enable Samba Logging
To enable Samba logging, add the following to the [global]
stanza of smb.conf
:
# Increase the level up to 10 for more detail
log level = 3
Reload Samba’s configuration with smbcontrol all reload-config
.
The log output is redirected to Docker. Inspect it on the host:
docker compose logs CONTAINER_NAME --timestamps
Resources
- Samba Wiki: File System Support
- Samba Wiki: Troubleshooting Samba Domain Members
- Samba Wiki: Samba Member Server Troubleshooting
- Samba Wiki: Updating Samba
- Samba Wiki: FAQ
Changelog
2024-09-09
- Improved the script
samba-recycle-cleanup.sh
.
2024-07-22
Missing Supervisord Config File
- Added the section Supervisord Config File supervisord.conf.
2024-07-10
Map (Mount) a File Share From Windows
- Changed the
net use
command so that credentials are stored in Windows Credential Manager, persisting a reboot. - Added command to access Credential Manager’s UI.