A centralized authentication/authorization mechanism is a key part of a security stance as well as a convenience to end users. There are multiple packages and systems to achieve this, and in this article I will focus on integrating back into Windows Active Directory using SSSD for login and group membership.
When complete, you will have an Ubuntu xenial/bionic/focal host that allows login using an Active Directory id, with group memberships preserved. Optionally, this Ubuntu host can be joined to the domain, which will delegate authentication to Kerberos.
This is a multiple step procedure requiring package installation, a root CA certificate for secure LDAP communication back to the domain, and using realm to optionally join the domain; so we will use Ansible to automate the process.
Prerequisites
Windows Domain Controller
The first prerequisite is that you have a Windows server running Active Directory Domain services. I am running Windows 2016 for this article, and you can download an ISO with a 180 day evaluation from Microsoft.
Although SSSD will work over insecure LDAP (port 389), it does not make sense to test in this mode when any production AD server is going to require LDAPS (port 636). My experience in production systems is that the cert for this communication is not typically chained back to a public CA, and instead uses a privately generated CA. See this article on how to enable secure LDAP on a Domain Controller. You can also create and load the cert manually, here is a powershell script I wrote to generate the CA and certificate.
It is always good to validate this secure LDAP communication from another Windows host as a sanity test that you have correctly identified the root CA cert.
If you do not control the AD server, you will need to request that your administrator provide the CA cert, because it is not sent in the initial TLS exchange (you can’t use openssl against port 636 fetch it).
You will need a service account created on this AD server for user searches and group queries by SSSD. Ensure the service account user is capable of searching the directory for users and groups (“Delegate Control” menu from Active Directory Users and Computers).
For the optional domain join, you can either use your own personal account or a different service account that is capable of joining computers to the domain.
Ansible
Because of the number of steps required, and so we have a automated process, this procedure is wrapped into an Ansible role.
For Ubuntu, use the ppa to install the latest version of Ansible.
Running the Ansible playbook
Fetch tests
# make sure git client is installed sudo apt-get install git -y # get my Ansible role that includes tests git clone https://github.com/fabianlee/ansible-sssd.git cd ansible-sssd/tests
Copy root CA cert locally
The playbooks currently have their ldap_tls_cacert’ set to ‘/etc/ssl/certs/myCA.pem’. This is the location it will be copied unto remote hosts, and the logic in the role is to look in the current directory for a file with the same name in the current directory.
So be sure to copy the root CA cert into the current directory with the exact base name specificed in ldap_tls_cacert (currently “myCA.pem”).
Modify host inventory
Define hosts in the ansible inventory that you want to apply a playbook unto: specify hostname, username, and credentials. Modify according to your environment.
cat ansible_inventory
Modify the ‘ansible_ssh_private_key_file’ to point to a private ssh key if that is required to access the hosts.
There is no need to add these hosts to a particular group for this test.
Optionally, I’ve provided a Terraform script for creating a set of 3 Ubuntu hosts running xenial, bionic, and focal. If you are running qemu/kvm locally and install the libvirt provider which I describe here then use the commands below.
# go into test directory cd tests/tf-create # run terraform init,apply make apply
Modify playbook for LDAP integration
You will need to modify “ansible-sssd-playbook-ad-ldap-only.yml” per your environment. The values are currently populated for a Windows domain controller named “flee-dc1.fabian.lee” in the FABIAN.LEE realm.
Critical keys to consider replacing per your environment:
- sssd_sudoers – list of any id/groups you want to add as sudoers
- sssd_testuser – final validation test will be run to see if this user can be fetched
- sssd_domains – capitalized REALM name
- ldap_uri – ldap secure url to port 636
- ldap_default_bind_dn – service account capable of searching users/groups
- ldap_default_authtok – password to service account, define here or override with ‘ldap_default_authtok’ passed at command line
- ldap_search_base
- ldap_user_search_base
- ldap_group_search_base
Modify playbook for LDAP plus Kerberos integration
You will need to modify “ansible-sssd-playbook-ad-ldap-plus-krb5.yml” per your environment. The values are currently populated for a Windows domain controller named “flee-dc1.fabian.lee” in the FABIAN.LEE realm.
In addition to all the keys listed in the section above on LDAP customization, you also need to modify these keys to match your environment:
- systemd_dns_primary – IP address of domain controller, realm requires primary DNS be DC
- systemd_dns_domains – domain dns suffix
- realmd_domain – capitalized realm name
- kerberos_user – user that will invoke domain join for computer
- kerberos_user_password – password to kerberos user above, define here or override with ‘kerberos_user_password’ passed at command line
- realm_ad_ou – OU where computers joining domain are created
Invoke playbook
After modification of the LDAP playbook with values set for your environment, the playbook can be invoked.
# test connection ansible -l <host> -m ping # run playbook ansible-playbook ansible-sssd-playbook-ad-ldap-only.yml -l <host> --extra-vars "ldap_default_authtok=MyP4ss!"
This is an example of overriding the password to the ldap service account, so the password does not have to be kept in the playbook.
Invoking the playbook for the LDAP plus Kerberos integration is similar.
# test connection ansible -l <host> -m ping # run playbook ansible-playbook ansible-sssd-playbook-ad-ldap-plus-krb5.yml -l <host> --extra-vars "ldap_default_authtok=MyP4ss! kerberos_user_password=MyP4ss!"
We have overriden the ldap service account for SSSD once again, as well as the ‘kerberos_user_password’ for the account used to join the domain.
Testing the integration
Although the Ansible roles themselves have validation checks, you can test by attempting an ssh login into your host using an id from the Windows AD side.
# ssh into host using windows id (domain suffix not required) ssh <host> -l <windowsID> # once logged in, 'id' should provide list of windows groups id # works if you have set 'sssd_sudoers' to a member group sudo su
If you have issues, then login to the Ubuntu host with the root/ubuntu admin user and view the logs in the “/var/log/sssd” directory.
# check logs cd /var/log/sssd tail -f * # 'id' should provide info on candidate windows id id <windowsId> # restart sssd systemctl restart sssd
While logged in as root, the ‘id’ command should return back information on windows users. If not, that is an issue you need to troubleshoot using the local logs.
REFERENCES
ubuntu, sssd ref joining domain and using kerberos
ubuntu ref, sssd authentication
kifarunix.com, sssd for Ubuntu 18.04
kifarunix.com, sssd for Ubuntu 20.04
blog.ndk.name, sssh against AD without joining domain, using ssh key in altSecurityIdentities
rakeshjain-devops, joining Ubuntu 18 vm to AD with sssd
rootusers.com, deleting sssd cache and database
redhat, sssd id and auth providers
redhat, ports necessary for sssd (ldap,krb5,ldap global catalog,ntp)
gist alces, ansible local connection
blog.cloudera, using krb5_validate to false speeds up login by preventing TGT verification
mankier.com, sssd ad provider
redhat.com, different provider types explanation
NOTES
Testing ldap connection from host (/etc/ldap/ldap.conf validates cert)
# using full DN for binding, prompt for password ldapsearch -LLL -H ldaps://flee-dc1.fabian.lee:636 -D 'CN=Administrator,CN=Users,DC=fabian,DC=lee' -W -b DC=fabian,DC=lee -s sub "(SAMAccountName=flee)" -W # using just id for binding, prompt for password ldapsearch -LLL -H ldaps://flee-dc1.fabian.lee:636 -D 'Administrator@fabian.lee' -W -b DC=fabian,DC=lee -s sub "(SAMAccountName=flee)" -W # instead of ldap.conf, specify CA in env var TLS_CACERT=/etc/ssl/certs/myCA.pem ldapsearch -LLL -H ldaps://flee-dc1.fabian.lee:636 -D 'Administrator@fabian.lee' -W -b DC=fabian,DC=lee -s sub "(SAMAccountName=flee)" -W
kerberos connected
# entry that will be used in passwd file for AD user getent passwd <id>@<domain> # group membership for AD user id <id>@<domain>
searching for domain controller records
dig -t SRV _ldap._tcp.<full>.<domain> dig -t SRV _ldap._tcp.dc._msdcs.<full>.<domain>
test chrony ntp
chrony -d 'server pool.ntp.org iburst'
clear sssd cache
# clear all sudo sss_cache -E # clear usr sudo sss-cache -u <user> # if you need to remove all files sudo systemctl stop sssd sudo rm /var/lib/sss/db/* sudo systemctl start sssd
Manual domain join with realm
# discover domain sudo realm discover -v fabian.lee # join domain, create /etc/krb5.keytab sudo realm join -v --user=svc1 fabian.lee --install=/ # verify that domain was joined sudo adcli info fabian.lee # enable home dir creation sudo pam-auth-update --enable mkhomedir # restart service systemctl restart sssd # retrieve user information getent passwd <user>@fabian.lee