Categories
Directory Service Kerberos LDAP Linux

Joining Ubuntu client to Active Directory

…or to be exact: the Samba version of it 🙂

So as an Open Source guy I obviously don’t run a Microsoft Active Directory. However since Samba version 4 this software does not only support file and print services, it also can act as an Active Directory. And guess what: That’s what I do.

Unfortunately in the initial release of Ubuntu 24.04 the Active Directory join during installation is buggy, so you can’t just do this with the option offered during installing (s. screenshot below).

When you try to do so, the installation will just hang indefinitely after you entered the data required to join (screenshot #2).

I’m not sure about whether this is a bug that only happens when using Samba or if this also happens with a “real” Active Directory, however this bugreport suggests both implementations are affected.

Update: Just retried with Ubuntu 24.04.1 and 24.10 – but the problem still exists with the updated release.

So we’ll have to skip this step during installation and do the join afterwards.

So after a fresh installation of Ubuntu 24.04 Desktop we need to install some additional software packages:

linux > sudo apt install realmd krb5-user

After that we’ll try to auto-detect available Active Directories (or their samba equivalent):

linux > realm discover -v
 * Resolving: _ldap._tcp.mydomain.de
 * Performing LDAP DSE lookup on: 192.168.1.33
 * Successfully discovered: mydomain.de
mydomain.de
  type: kerberos
  realm-name: MYDOMAIN.DE
  domain-name: mydomain.de
  configured: no
  server-software: active-directory
  client-software: sssd
  required-package: sssd-tools
  required-package: sssd
  required-package: libnss-sss
  required-package: libpam-sss
  required-package: adcli
  required-package: samba-common-bin

Ok, looks like we got a winner. Now let’s try to join our linux machine (called “rainbow”). I’m adding some extra options here, to make sure the directory object is created in the right path, (“–computer-ou”) and we’ll use the pre-defined unix user and group ids from AD (instead of generating pseudo-ids, “–automatic-id-mapping=no”). We also require an account with AD admin privileges in order to join (in my case “Administrator”):

linux > sudo realm join -v -U Administrator mydomain.de --computer-name rainbow --computer-ou=CN=Computers,DC=mydomain,DC=de --automatic-id-mapping=no
 * Resolving: _ldap._tcp.mydomain.de
 * Performing LDAP DSE lookup on: 192.168.1.33
 * Successfully discovered: mydomain.de
Password for Administrator: 
 * Unconditionally checking packages
 * Resolving required packages
 * Joining using a manual netbios name: rainbow
 * LANG=C /usr/sbin/adcli join --verbose --domain mydomain.de --domain-realm MYDOMAIN.DE --domain-controller 192.168.1.33 --computer-name rainbow --computer-ou CN=Computers,DC=mydomain,DC=de --login-type user --login-user Administrator --stdin-password
 * Using domain name: mydomain.de
 * Using computer account name: rainbow
 * Using domain realm: mydomain.de
 * Sending NetLogon ping to domain controller: 192.168.1.33
 * Received NetLogon info from: dc1.mydomain.de
 * Wrote out krb5.conf snippet to /var/cache/realmd/adcli-krb5-ldBXZk/krb5.d/adcli-krb5-conf-G2hbOE
 * Authenticated as user: Administrator@MYDOMAIN.DE
 * Using GSS-SPNEGO for SASL bind
 * Looked up short domain name: MYDOMAIN
 * Looked up domain SID: S-1-5-21-1235318456-3360809864-399383818
 * Using fully qualified name: rainbow.mydomain.de
 * Using domain name: mydomain.de
 * Using computer account name: rainbow
 * Using domain realm: mydomain.de
 * Enrolling computer name: rainbow
 * Generated 120 character computer password
 * Using keytab: FILE:/etc/krb5.keytab
 * A computer account for rainbow$ does not exist
 * Well known computer container not found, but found suitable one at: CN=Computers,DC=mydomain,DC=de
 * Calculated computer account: CN=rainbow,CN=Computers,DC=mydomain,DC=de
 * Encryption type [3] not permitted.
 * Encryption type [1] not permitted.
 * Created computer account: CN=rainbow,CN=Computers,DC=mydomain,DC=de
 * Sending NetLogon ping to domain controller: 192.168.1.33
 * Received NetLogon info from: dc1.mydomain.de
 * Set computer password
 * Retrieved kvno '2' for computer account in directory: CN=rainbow,CN=Computers,DC=mydomain,DC=de
 * Checking host/rainbow
 *    Added host/rainbow
 * Checking host/rainbow.mydomain.de
 *    Added host/rainbow.mydomain.de
 * Checking RestrictedKrbHost/rainbow
 *    Added RestrictedKrbHost/rainbow
 * Checking RestrictedKrbHost/rainbow.mydomain.de
 *    Added RestrictedKrbHost/rainbow.mydomain.de
 * Discovered which keytab salt to use
 * Added the entries to the keytab: rainbow$@MYDOMAIN.DE: FILE:/etc/krb5.keytab
 * Added the entries to the keytab: host/rainbow@MYDOMAIN.DE: FILE:/etc/krb5.keytab
 * Added the entries to the keytab: host/rainbow.mydomain.de@MYDOMAIN.DE: FILE:/etc/krb5.keytab
 * Added the entries to the keytab: RestrictedKrbHost/rainbow@MYDOMAIN.DE: FILE:/etc/krb5.keytab
 * Added the entries to the keytab: RestrictedKrbHost/rainbow.mydomain.de@MYDOMAIN.DE: FILE:/etc/krb5.keytab
 ! Failed to update Kerberos configuration, not fatal, please check manually: Setting attribute standard::type not supported
 * /usr/sbin/update-rc.d sssd enable
 * /usr/sbin/service sssd restart
 * Successfully enrolled machine in realm

So what did those commands do?

Basically it created/modified a bunch of configuration files, including configurations for pam, nss, sssd and krb5.

linux > sudo cat /etc/sssd/sssd.conf
[sssd]
domains = mydomain.de
config_file_version = 2
services = nss, pam

[domain/mydomain.de]
default_shell = /bin/bash
krb5_store_password_if_offline = True
cache_credentials = True
krb5_realm = MYDOMAIN.DE
realmd_tags = manages-system joined-with-adcli 
id_provider = ad
ldap_sasl_authid = rainbow$
fallback_homedir = /home/%u@%d
ad_domain = mydomain.de
use_fully_qualified_names = True
ldap_id_mapping = False
access_provider = ad
linux > sudo ktutil
ktutil:  rkt /etc/krb5.keytab 
ktutil:  list -e
slot KVNO Principal
---- ---- ---------------------------------------------------------------------
   1    2                     rainbow$@MYDOMAIN.DE (arcfour-hmac) 
   2    2                     rainbow$@MYDOMAIN.DE (aes128-cts-hmac-sha1-96) 
   3    2                     rainbow$@MYDOMAIN.DE (aes256-cts-hmac-sha1-96) 
   4    2                 host/rainbow@MYDOMAIN.DE (arcfour-hmac) 
   5    2                 host/rainbow@MYDOMAIN.DE (aes128-cts-hmac-sha1-96) 
   6    2                 host/rainbow@MYDOMAIN.DE (aes256-cts-hmac-sha1-96) 
   7    2     host/rainbow.mydomain.de@MYDOMAIN.DE (arcfour-hmac) 
   8    2     host/rainbow.mydomain.de@MYDOMAIN.DE (aes128-cts-hmac-sha1-96) 
   9    2     host/rainbow.mydomain.de@MYDOMAIN.DE (aes256-cts-hmac-sha1-96) 
  10    2    RestrictedKrbHost/rainbow@MYDOMAIN.DE (arcfour-hmac) 
  11    2    RestrictedKrbHost/rainbow@MYDOMAIN.DE (aes128-cts-hmac-sha1-96) 
  12    2    RestrictedKrbHost/rainbow@MYDOMAIN.DE (aes256-cts-hmac-sha1-96) 
  13    2 RestrictedKrbHost/rainbow.mydomain.de@MYDOMAIN.DE (arcfour-hmac) 
  14    2 RestrictedKrbHost/rainbow.mydomain.de@MYDOMAIN.DE (aes128-cts-hmac-sha1-96) 
  15    2 RestrictedKrbHost/rainbow.mydomain.de@MYDOMAIN.DE (aes256-cts-hmac-sha1-96) 
ktutil:  quit

Some more things to checkout: Test the configuration of the NSS libraries (for user and group lookups):

linux > getent passwd marcel@mydomain.de
marcel@mydomain.de:*:1000:1001:Marcel:/home/marcel:/bin/bash

This however is only the lookup, we also want to login as this user:

linux > ssh -l marcel@mydomain.de localhost
marcel@mydomain.de@localhost.rrze.uni-erlangen.de’s password:
Connection closed by 127.0.0.1 port 22

Ok, that was not what I expected: The login failed – but I’m sure the password is correct:

linux > sudo journalctl
<...>
Aug 25 19:46:38 rainbow.mydomain.de sshd[12921]: pam_sss(sshd:account): Access denied for user marcel@mydomain.de: 4 (System error)
Aug 25 19:46:38 rainbow.mydomain.de sshd[12921]: Failed password for marcel@mydomain.de from 127.0.0.1 port 50462 ssh2
Aug 25 19:46:38 rainbow.mydomain.de sshd[12921]: fatal: Access denied for user marcel@mydomain.de by PAM account configuration [preauth]
<...>

After doing some research, the sssd.conf is missing an important line in the “mydomain.de” section:

ad_gpo_access_control = permissive

After that just restart sssd and things will start working:

linux > sudo systemctl restart sssd

If your home directory is mounted as NFSv4 volume and ls only shows nobody:nogroup ownerships (but you’re able to write files to that directory) there’s something wrong with your NFS idmapper (request-key on newer or rpc.idmapd on older systems).

Further debugging of Ubuntu bug

Looks like the new installer subiquity is playing some kind of role in this mess. You can open a separate shell during installation using Ctrl-Alt-T. Using this shell I found some hints in the log files (located at /var/log/installer/). They reported some URLs that seem to be involved like:

http://localhost/active_directory

However the default HTTP port 80 is not open and wget/curl commands fail to download this URL. So this log entries seem to be a little misleading. However the next best thing to an open port is a socket (something that can be discovered with tools like netstat or ss):

ubuntu@ubuntu > sudo ss  | grep subiqu
u_str ESTAB 0      0                                       /run/subiquity/socket 16871             * 16870 

So let’s try to access this socket (I’m using curl that’s not part of the installer image, so we’ll need to install it):

ubuntu@ubuntu > sudo apt install curl
ubuntu@ubuntu > curl --unix-socket /run/subiquity/socket --silent http://localhost/active_directory | jq
{
  "admin_name": "",
  "domain_name": "mydomain.de",
  "password": ""
}

The above command was issued before entering and submitting the information required to join an AD. Once submitted the output will resemble the data entered.

By the way: The jq command only adds a little formatting to the javascript output but is not necessary.

The last URL seen in /var/log/installer/ubuntu_bootstrap.log is:

http://localhost/active_directory/join_result?wait=true

An yes, if you try to query this URL like we did above the command will hang (like the installer does). Given the option wait=true this is not surprising. But what about setting this option to false?

ubuntu@ubuntu > curl --unix-socket /run/subiquity/socket --silent http://localhost/active_directory/join_result?wait=false | jq
"UNKNOWN"

While trying to figure what the Ubuntu installer tries to do I also had a look at the subiquity code especially the part concerning the AD join. And basically this code seems to run the program realm (I did a little re-formatting to save some space):

<...>
self.realm, "join",
"--install", root_dir,
"--user",
info.admin_name,
"--computer-name", hostname,
"--unattended", info.domain_name,
<...>

What’s a little special here is the usage of –install root_dir which would apply the data to the to-be-installed-on disk.

Unfortunately at the point when this command is called the disk is not (yet) partitioned and mounted (at least in my test setup) and therefore the join will fail miserably.

A related problem was reported as fixed with subiquity version 23.04.2 (in earlier versions the join had worked (well, kind of), however the live install system was joined, not the intended target system, fixed by PR#1618).

I wouldn’t be surprised it this change was applied, but never tested afterwards.

One reply on “Joining Ubuntu client to Active Directory”

So glad I found this page! This is a major failure on the part of quality assurance department at Canonical. Something that worked flawlessly on 22.04 is now broken on 24.04. Don’t they do any testing before releasing a new version?

Leave a Reply

Your email address will not be published. Required fields are marked *