Samba / SMB File Sharing

Set up an SMB file share on Linux — from install to a working, secured, production-ready share.

What Samba is

Samba is the Linux implementation of the SMB (Server Message Block) protocol — the same protocol Windows uses for file and printer sharing. It lets Linux hosts share directories that Windows, macOS, and other Linux machines can mount as network drives.

You will encounter Samba when:

The key packages are samba (the server) and samba-client (the smbclient tool for testing). The main daemon is smbd (handles file and print services) supported by nmbd (handles NetBIOS name resolution) and winbindd (if joining an AD domain).

Installation

# RHEL / Rocky / AlmaLinux
dnf install -y samba samba-client samba-common

# Debian / Ubuntu
apt install -y samba samba-common-bin

The main config file is /etc/samba/smb.conf. Back it up before editing: cp /etc/samba/smb.conf /etc/samba/smb.conf.orig

smb.conf structure

smb.conf uses an INI-style format. Each section header [name] defines either a global setting or a share. The [global] section is special — all other sections define shares.

[global]
   workgroup = WORKGROUP
   server string = File Server
   ...

[sharename]
   path = /srv/share
   ...

[homes]          # special: per-user home directory shares
   ...

[printers]       # special: printer sharing
   ...

Lines starting with # or ; are comments. Parameters are name = value. Whitespace around = is ignored. Parameter names are case-insensitive.

The [global] section

[global]
   # The Windows workgroup or domain name
   workgroup = WORKGROUP

   # Displayed in Windows network browser
   server string = Linux File Server %v

   # Security mode:
   # user = require authentication (standard for file servers)
   # ads  = join Active Directory domain
   security = user

   # Map unknown users to guest (none = reject them)
   map to guest = bad user

   # Logging
   log file = /var/log/samba/log.%m     # %m = client machine name
   max log size = 50                     # KB before rotating

   # Performance
   socket options = TCP_NODELAY

   # Disable printing if you are not sharing printers
   load printers = no
   printing = bsd
   printcap name = /dev/null
   disable spoolss = yes

Defining a share

Each share section defines one directory exported over SMB:

[docs]
   comment = Team Documentation
   path = /srv/samba/docs          # filesystem path to share
   browseable = yes                # visible in network browser
   read only = no                  # allow writes
   writable = yes                  # same as read only = no
   create mask = 0664              # permissions for new files
   directory mask = 0775           # permissions for new dirs
   valid users = @teamdocs         # only this group can connect
   write list = @teamdocs          # only this group can write

Key parameters explained:

Share types: public, authenticated, admin-only

Public read-only share (no login required)

[public]
   comment = Public Files
   path = /srv/samba/public
   browseable = yes
   read only = yes
   guest ok = yes              # allow unauthenticated access

Authenticated share (login required, everyone in group can write)

[teamshare]
   comment = Team Share
   path = /srv/samba/team
   browseable = yes
   writable = yes
   create mask = 0664
   directory mask = 0775
   force group = smbteam           # all created files owned by smbteam group
   valid users = @smbteam

Admin-only share (hidden, full access)

[admin$]
   comment = Admin Share
   path = /srv/samba/admin
   browseable = no                 # hidden from browser
   writable = yes
   valid users = smbadmin

Linux filesystem permissions

Samba enforces both Samba-level access (smb.conf) and Linux filesystem permissions. A user who has Samba permission to write but not Linux write permission on the directory will fail.

# Create the share directory
mkdir -p /srv/samba/team

# Create the Linux group that will own the share
groupadd smbteam

# Set ownership — owned by root, group-owned by smbteam
chown root:smbteam /srv/samba/team

# Permissions: rwxrwsr-x
# setgid bit (s) means new files inherit the group
chmod 2775 /srv/samba/team

# Verify
ls -ld /srv/samba/team
# drwxrwsr-x. 2 root smbteam 6 Apr 10 10:00 /srv/samba/team

The setgid bit (chmod 2775 or chmod g+s) on a directory means new files and subdirectories inherit the directory's group automatically. This is essential for shared team directories — without it, files created by different users end up with different groups and permissions become inconsistent.

Samba users and passwords

Samba maintains its own password database separate from /etc/shadow. A Samba user must:

  1. Exist as a Linux system user (in /etc/passwd)
  2. Have a Samba password set with smbpasswd
# Create a Linux user for Samba (no login shell, no home dir needed if just for file sharing)
useradd -M -s /sbin/nologin alice

# Add alice to the smbteam group
usermod -aG smbteam alice

# Set alice's Samba password
smbpasswd -a alice         # adds and sets password
# Enter password: ...

# Enable the account (enabled by default after -a)
smbpasswd -e alice

# Disable an account
smbpasswd -d alice

# Delete a Samba user
smbpasswd -x alice

# List all Samba users
pdbedit -L
pdbedit -Lv              # verbose — shows SID, flags, expiry
Use groups, not individual users in smb.conf. Add users to Linux groups (usermod -aG groupname user), then use valid users = @groupname in smb.conf. This way you manage access by adding/removing group members rather than editing smb.conf.

SELinux

On RHEL/Rocky, SELinux will block Samba from accessing directories unless they are labelled correctly and the right booleans are set.

Label the share directory

# Label with samba_share_t (Samba can read/write these)
semanage fcontext -a -t samba_share_t "/srv/samba(/.*)?"
restorecon -Rv /srv/samba/

# Verify
ls -dZ /srv/samba/team/
# system_u:object_r:samba_share_t:s0  /srv/samba/team/

SELinux booleans for Samba

# List all Samba booleans
getsebool -a | grep samba

# Allow Samba to share any directory under /home
setsebool -P samba_enable_home_dirs on

# Allow Samba to export NFS-mounted directories
setsebool -P samba_share_nfs on

# Allow Samba to act as a domain controller (rarely needed)
setsebool -P samba_domain_controller on
Without correct SELinux labels, Samba will get "Permission denied" even if Linux filesystem permissions are wide open. Always check ls -Z on the share directory and look for AVC denials in /var/log/audit/audit.log.

firewalld

Samba uses TCP 445 (SMB direct) and UDP 137/138 + TCP 139 (NetBIOS). The samba firewalld service covers all of these.

# Open the firewall for Samba
firewall-cmd --permanent --add-service=samba
firewall-cmd --reload

# Verify
firewall-cmd --list-services

If you only need file sharing (no NetBIOS name resolution), port 445/tcp alone is sufficient for modern clients. The samba service opens all required ports.

Starting and enabling the service

# Validate config before starting
testparm                           # syntax check smb.conf
testparm -s                        # show effective config (no prompts)

# Start and enable
systemctl enable --now smb         # RHEL: smb and nmb
systemctl enable --now nmb         # NetBIOS name service

# On Debian/Ubuntu the service names differ:
systemctl enable --now smbd
systemctl enable --now nmbd

# Check status
systemctl status smb nmb

Testing the share

# List shares on the local server (as a specific user)
smbclient -L localhost -U alice

# Connect to a specific share interactively
smbclient //localhost/teamshare -U alice
# smb: \> ls          (list files)
# smb: \> put file    (upload)
# smb: \> get file    (download)
# smb: \> quit

# Test as the guest/anonymous user
smbclient //localhost/public -N    # -N = no password

The smbclient -L output shows all shares the user can see:

Sharename       Type      Comment
---------       ----      -------
teamshare       Disk      Team Share
IPC$            IPC       IPC Service

Mounting from a client

Linux client — one-time mount

# Install the client package
dnf install -y cifs-utils          # RHEL
apt install -y cifs-utils          # Debian

# Mount a share
mount -t cifs //fileserver/teamshare /mnt/team \
  -o username=alice,password=secret,uid=1000,gid=1000

# Mount with a credentials file (safer — keeps password out of shell history)
echo "username=alice" > /etc/samba/alice.creds
echo "password=secret" >> /etc/samba/alice.creds
chmod 600 /etc/samba/alice.creds

mount -t cifs //fileserver/teamshare /mnt/team \
  -o credentials=/etc/samba/alice.creds,uid=1000,gid=1000

Linux client — persistent mount via /etc/fstab

# Add to /etc/fstab for automatic mounting at boot
//fileserver/teamshare  /mnt/team  cifs  credentials=/etc/samba/alice.creds,uid=1000,gid=1000,_netdev  0  0

_netdev tells the system this filesystem requires the network and should be mounted after the network is up. Without it, a boot-time mount failure can cause the system to hang at boot.

Windows client

# In Windows Run dialog or Explorer address bar:
\\fileserver\teamshare

# Map as a drive letter via command line
net use Z: \\fileserver\teamshare /user:WORKGROUP\alice password

macOS client

Finder → Go → Connect to Server (⌘K): smb://fileserver/teamshare

Troubleshooting

Cannot connect at all

# Is smbd running?
systemctl status smb

# Is port 445 open and listening?
ss -tlnp | grep 445

# Is the firewall allowing it?
firewall-cmd --list-services | grep samba

# Test from another host
smbclient -L fileserver -U alice

"Permission denied" on a file/directory

# Check Linux permissions on the share directory
ls -la /srv/samba/teamshare/

# Check SELinux label
ls -dZ /srv/samba/teamshare/

# Check for SELinux denials
ausearch -m avc -ts recent | grep samba | audit2why

"NT_STATUS_LOGON_FAILURE" — bad credentials

# Is the user in the Samba database?
pdbedit -L | grep alice

# Reset the Samba password
smbpasswd alice

# Is the Linux user enabled?
id alice

"NT_STATUS_ACCESS_DENIED" — connected but cannot read/write

# Check valid users in smb.conf
testparm -s | grep -A 10 "\[teamshare\]"

# Is the user in the required group?
id alice    # shows groups

# Add user to group if missing
usermod -aG smbteam alice

Samba log files

# Logs per-client (based on log file = /var/log/samba/log.%m)
ls /var/log/samba/

# Watch the main log
tail -f /var/log/samba/log.smbd

# Increase logging verbosity temporarily (in [global])
# log level = 3
# Then restart: systemctl restart smb

Ansible role pattern

---
# roles/samba/tasks/main.yml

- name: Install Samba
  ansible.builtin.package:
    name:
      - samba
      - samba-client
      - samba-common
    state: present

- name: Create share directories
  ansible.builtin.file:
    path: "{{ item.path }}"
    state: directory
    owner: root
    group: "{{ item.group }}"
    mode: "{{ item.mode | default('2775') }}"
  loop: "{{ samba_shares }}"

- name: Deploy smb.conf
  ansible.builtin.template:
    src: smb.conf.j2
    dest: /etc/samba/smb.conf
    owner: root
    group: root
    mode: '0644'
    validate: testparm -s %s
  notify: Restart Samba

- name: Configure SELinux labels on share paths
  community.general.sefcontext:
    target: "{{ item.path }}(/.*)?"
    setype: samba_share_t
    state: present
  loop: "{{ samba_shares }}"
  notify: Restore SELinux contexts

- name: Open firewall for Samba
  ansible.posix.firewalld:
    service: samba
    permanent: true
    state: enabled
  notify: Reload firewalld

- name: Enable and start Samba
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: true
  loop:
    - smb
    - nmb
# Example group_vars variable definition
samba_shares:
  - path: /srv/samba/team
    group: smbteam
    comment: Team Share
    valid_users: "@smbteam"
    writable: "yes"