Igor is the household NAS, running Ubuntu with a ZFS storage pool. The storage is divided into a number of top-level directories covering media, infrastructure data, personal files, and game servers. The goal was to expose all of these as SMB shares, authenticated against the Samba AD domain controller (vetinari), with a tiered group access model that allows delegating access to individual share categories or to all of Igor at once.

Access model

Rather than creating a flat group per share (which would mean a large number of groups for a large number of shares), I settled on a two-level model:

  • A whole-igor pair: ag_igor (read) and adm_igor (read-write), granting access across all shares
  • Per-tier pairs for each of four categories: media, infra, personal, and gameservers

The tiers break down as follows:

  • media: audio, video, ebooks, games, images, and upload staging
  • infra: container data, VM images, backups, OS images, and drivers
  • personal: home files, projects, and applications
  • gameservers: game server data

My standard account is a member of ag_igor, giving read-only access across all shares. My admin account is a member of adm_igor, giving full read-write access. Per-tier groups can be populated later to delegate access to specific categories without granting whole-NAS access.

Creating the AD groups

Groups live in CN=Users on vetinari. The samba-tool group add command takes a --groupou parameter that is relative to the domain base DN, not an absolute DN – passing the full DN causes it to be appended to the domain base, resulting in a doubled suffix and a failure. The correct form is just CN=Users.

for group in ag_igor adm_igor ag_igor_media adm_igor_media ag_igor_infra adm_igor_infra ag_igor_personal adm_igor_personal ag_igor_gameservers adm_igor_gameservers; do
    samba-tool group add $group --groupou="CN=Users"
done

samba-tool group addmembers ag_igor [user]
samba-tool group addmembers ag_igor [adm_user]
samba-tool group addmembers adm_igor [adm_user]

Samba configuration

The configuration is split into a main smb.conf containing global settings and include directives, with per-tier drop files in /etc/samba/smb.d/. This makes it straightforward to add or modify a tier without touching the global configuration.

/etc/samba/smb.conf:

[global]
    workgroup = DISCWORLD
    realm = DISCWORLD.NETWORK
    security = ADS
    idmap config * : backend = tdb
    idmap config * : range = 10000-19999
    idmap config DISCWORLD : backend = nss
    idmap config DISCWORLD : range = 600000000-799999999
    winbind use default domain = yes
    winbind enum users = no
    winbind enum groups = no
    kerberos method = secrets and keytab
    socket options = TCP_NODELAY IPTOS_LOWDELAY
    read raw = yes
    write raw = yes
    use sendfile = yes
    vfs objects = fruit streams_xattr
    fruit:metadata = stream
    fruit:model = RackMac
    fruit:posix_rename = yes
    fruit:veto_appledouble = no
    fruit:wipe_intentionally_left_blank_rfork = yes
    fruit:delete_empty_adfiles = yes
    ea support = yes
    min protocol = SMB2
    log file = /var/log/samba/log.%m
    max log size = 1000
    logging = file
    panic action = /usr/share/samba/panic-action %d
    include = /etc/samba/smb.d/homes.conf
    include = /etc/samba/smb.d/media.conf
    include = /etc/samba/smb.d/infra.conf
    include = /etc/samba/smb.d/personal.conf
    include = /etc/samba/smb.d/gameservers.conf

Each tier file follows the same pattern. Here is an infra share as an example:

[backups]
    path = /storage/backups
    comment = Backups
    browseable = yes
    read only = no
    create mask = 0660
    directory mask = 0770
    valid users = @"DISCWORLD\ag_igor" @"DISCWORLD\ag_igor_infra" @"DISCWORLD\adm_igor" @"DISCWORLD\adm_igor_infra"
    write list = @"DISCWORLD\adm_igor" @"DISCWORLD\adm_igor_infra"

POSIX ACLs

ZFS on Igor already had acltype=posixacl set on the storage pool. POSIX ACLs are applied to each share root with default ACL entries so that new files and directories inherit the correct permissions.

apt install acl

setfacl -m group:"ag_igor":r-x /storage/backups
setfacl -m default:group:"ag_igor":r-x /storage/backups
setfacl -m group:"adm_igor":rwx /storage/backups
setfacl -m default:group:"adm_igor":rwx /storage/backups
setfacl -m group:"ag_igor_infra":r-x /storage/backups
setfacl -m default:group:"ag_igor_infra":r-x /storage/backups
setfacl -m group:"adm_igor_infra":rwx /storage/backups
setfacl -m default:group:"adm_igor_infra":rwx /storage/backups
setfacl -m other::--- /storage/backups
setfacl -m default:other::--- /storage/backups

Verification:

# file: storage/backups
# owner: root
# group: adm_igor
user::rwx
group::r-x
group:ag_igor:r-x
group:adm_igor:rwx
group:ag_igor_infra:r-x
group:adm_igor_infra:rwx
mask::rwx
other::---
default:user::rwx
default:group::r-x
default:group:ag_igor:r-x
default:group:adm_igor:rwx
default:group:ag_igor_infra:r-x
default:group:adm_igor_infra:rwx
default:mask::rwx
default:other::---

The authentication rabbit hole

This is where things got interesting. Igor was already joined to the domain via realmd and adcli, with sssd handling identity. Samba, however, requires winbind to authenticate domain users – sssd alone is not sufficient, even with a working domain join and a valid keytab.

sssd was crashed

sssd had been down for two days due to a keytab issue:

sdap_select_principal_from_keytab_sync: Failed to get principal from keytab
ad_set_sdap_options: Cannot set the SASL-related options

The keytab was fine – klist -k /etc/krb5.keytab showed all the expected principals. The problem was sssd was looking for a bare igor.ankh-morpork.discworld.network@DISCWORLD.NETWORK principal rather than the host/ prefixed form that adcli creates. A manual systemctl restart sssd brought it back up cleanly (the crash loop had stopped retrying).

use_fully_qualified_names

With sssd running, getent passwd [user] still returned nothing. sssd was configured with use_fully_qualified_names = True, meaning the correct lookup was getent passwd [user]@discworld.network. This would have caused Samba group matching to fail since the smb.conf valid users entries use unqualified names. Setting use_fully_qualified_names = False in /etc/sssd/sssd.conf fixed both issues.

winbind had no machine credentials

Installing winbind and starting it failed immediately:

Could not fetch our SID - did we join?

The realmd join stores the machine account credentials in a way winbind cannot read. net ads testjoin confirmed the problem:

net_ads_join_ok: Failed to get machine credentials
Join to domain is not valid: Access Denied

The fix was to use an existing Kerberos ticket to register the machine credentials into Samba’s secrets.tdb without re-joining the domain:

kinit [user]@DISCWORLD.NETWORK
net ads join -k

After that, net ads testjoin passed and winbind started successfully.

ID mapping conflict

Even with winbind running, authentication still failed with NT_STATUS_LOGON_FAILURE. The smbd log showed:

add_local_groups: SID S-1-5-21-...-[RID] -> getpwuid([UID]) failed, is nsswitch configured?

The problem was an ID mapping conflict. sssd (using ldap_id_mapping = True) computes UIDs algorithmically from the AD objectSid, producing UIDs in a high numeric range. Winbind’s rid backend independently computes UIDs from the RID against a configured range, producing completely different UIDs for the same user. The two are irreconcilable.

The idmap_sss backend (which would delegate winbind ID lookups to sssd) is not shipped in Ubuntu’s Samba packages. The correct alternative is the nss backend, which tells winbind to resolve UIDs via nsswitch, and therefore via sssd:

idmap config DISCWORLD : backend = nss
idmap config DISCWORLD : range = 600000000-799999999

The range must cover the UIDs sssd is generating. After flushing the winbind cache and restarting both services, ID resolution worked correctly and smbclient produced a full share listing across all tiers.

Key lessons

samba-tool --groupou takes a relative DN, not an absolute one. Passing CN=Users,DC=domain,DC=tld causes the domain suffix to be appended again, producing an invalid path. Use CN=Users only.

On a realmd/sssd host, Samba still requires winbind. sssd handles PAM and NSS but Samba’s authentication path goes through winbind regardless. Both need to be running.

After a realmd join, winbind has no machine credentials. Run net ads join -k with an existing Kerberos ticket to populate secrets.tdb without disturbing the domain join.

When sssd and winbind coexist, they must agree on UIDs. The rid idmap backend will produce different UIDs to sssd’s algorithmic mapping. Use idmap config DOMAIN : backend = nss to make winbind defer to nsswitch (and therefore sssd) for all ID resolution.

The idmap_sss backend does not exist in Ubuntu’s Samba packages despite being documented. Use nss instead.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.