Join a forest with the autorid backend

Joining a more complex Active Directory forest with the autorid backend is very similar to the rid backend. The only difference is in the idmap configuration in /etc/samba/smb.conf:

[global]
    security = ads
    realm = EXAMPLE.INTERNAL
    workgroup = EXAMPLE

    idmap config * : backend = autorid
    # 1,000,000 - 19,999,999
    idmap config * : range   = 1000000 - 19999999
    # 1,000,000
    idmap config * : rangesize = 1000000

    # allow logins when the DC is unreachable
    winbind offline logon = yes
    # this *can* be yes if there is absolute certainty that there is only a
    # single domain involved
    winbind use default domain = no
    # setting these enumeration options to yes has a high performance impact
    # and can cause instabilities
    winbind enum groups = no
    winbind enum users = no
    winbind refresh tickets = yes
    # if domain users should be allowed to login, they will need a login shell
    template shell = /bin/bash
    # the home directory template for domain users
    template homedir = /home/%D/%U
    kerberos method = secrets and keytab

Note that there is no specific domain mentioned in the idmap configuration. That’s because the autorid backend does the allocations on demand, according to the defined slots. The configuration above defines the following:

  • 1 million IDs per slot
  • 19 slots (or domains)
  • full ID range, covering all slots, is from 1,000,000 to 19,999,999

That being said, the machine still needs to be joined to a specific domain of that forest, and in this example that will be EXAMPLE.INTERNAL.

Running the recommended testparm command gives us confidence that the configuration is at least free from syntax and other logical errors:

$ testparm
Load smb config files from /etc/samba/smb.conf
Loaded services file OK.
Weak crypto is allowed by GnuTLS (e.g. NTLM as a compatibility fallback)

Server role: ROLE_DOMAIN_MEMBER

Press enter to see a dump of your service definitions

Like with the rid idmap backend, if this system is not yet in the AD DNS server, it’s best to change its hostname (including the short hostname) do be the fully qualified domain name (FQDN), as that will allow the joining procedure to also update the DNS records, if so allowed by the AD server (normally it is).

For this example, the system’s hostname is n2 in the example.internal domain, so the FQDN is n2.example.internal:

sudo hostnamectl hostname n2.example.internal

Now the domain join can be performed:

$ sudo net ads join -U Administrator
Password for [EXAMPLE\Administrator]:
Using short domain name -- EXAMPLE
Joined 'N2' to dns domain 'example.internal'

And we can revert the hostname change:

sudo hostnamectl hostname n2

If the DNS server was updated correctly (and there were no errors about that in the join output above), then the hostname should now be correctly set, even though we have just the short name in /etc/hostname:

$ hostname
n2

$ hostname -f
n2.example.internal

The last step is to restart the winbind service:

sudo systemctl restart winbind.service

Verifying the join

The quickest way to test the integrity of the domain join is via the wbinfo command:

$ sudo wbinfo -t
checking the trust secret for domain EXAMPLE via RPC calls succeeded

The next verification step should be to actually try to resolve an existing username from the domain. In the EXAMPLE.INTERNAL domain, for example, we have some test users we can check:

$ id jammy@example.internal
uid=2001103(EXAMPLE\jammy) gid=2000513(EXAMPLE\domain users) groups=2000513(EXAMPLE\domain users),2001103(EXAMPLE\jammy)

If you compare this with the rid domain join, note how the ID that the jammy user got is different. That’s why it’s important to correctly chose an idmap backend, and correctly assess if deterministic IDs are important for your use case or not.

Another valid syntax for domain users is prefixing the name with the domain, like this:

$ id EXAMPLE\\jammy
uid=2001103(EXAMPLE\jammy) gid=2000513(EXAMPLE\domain users) groups=2000513(EXAMPLE\domain users),2001103(EXAMPLE\jammy)

And here we try a console login:

n2 login: jammy@example.internal
Password:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.5.0-26-generic x86_64)
(...)
Creating directory '/home/EXAMPLE/jammy'.
EXAMPLE\jammy@n1:~$

The output above also shows the automatic on-demand home directory creation, according to the template defined in /etc/samba/smb.conf.

Since we joined a forest, we should also be able to verify users from other domains in that forest. For example, in this example, the domain MYDOMAIN.INTERNAL is also part of the forest, and we can verify its users:

$ id noble@mydomain.internal
uid=3001104(MYDOMAIN\noble) gid=3000513(MYDOMAIN\domain users) groups=3000513(MYDOMAIN\domain users),3001104(MYDOMAIN\noble)

$ id MYDOMAIN\\noble
uid=3001104(MYDOMAIN\noble) gid=3000513(MYDOMAIN\domain users) groups=3000513(MYDOMAIN\domain users),3001104(MYDOMAIN\noble)

A console login also works:

n2 login: noble@mydomain.internal
Password:
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64)
(...)
Creating directory '/home/MYDOMAIN/noble'.
MYDOMAIN\noble@n2:~$

Notice how the domain name being part of the home directory path is useful: it separates the users from different domains, avoiding collisions for the same username.

NOTE

The actual login name used can have multiple formats: DOMAIN\user at the terminal login prompt, DOMAIN\\user when refered to in shell scripts (note the escaping of the ‘\’ character), and user@domain is also accepted.