This post is not too AWS-specific, in fact the steps below should work not only on Amazon Linux but also on RedHat Linux, CentOS and Oracle Linux and posibly on Debian and Ubuntu based distros as well.

There is a number of prerequisities for a successful completion of this task.

At least the following records should be resolvable:

_gc._tcp.example.com.                   3600    IN      SRV     1 100 3268 adc1.example.com.
_gc._tcp.example.com.                   3600    IN      SRV     1 100 3268 adc2.example.com.

_kerberos-master._tcp.example.com.      3600    IN      SRV     1 100 88 adc1.example.com.
_kerberos-master._tcp.example.com.      3600    IN      SRV     1 100 88 adc2.example.com.

_kerberos-master._udp.example.com.      3600    IN      SRV     1 100 88 adc1.example.com.
_kerberos-master._udp.example.com.      3600    IN      SRV     1 100 88 adc2.example.com.

_kerberos._tcp.dc._msdcs.example.com.   3600    IN      SRV     1 100 88 adc1.example.com.
_kerberos._tcp.dc._msdcs.example.com.   3600    IN      SRV     1 100 88 adc2.example.com.

_kerberos._tcp.example.com.             3600    IN      SRV     1 100 88 adc1.example.com.
_kerberos._tcp.example.com.             3600    IN      SRV     1 100 88 adc2.example.com.

_kerberos._udp.example.com.             3600    IN      SRV     1 100 88 adc1.example.com.
_kerberos._udp.example.com.             3600    IN      SRV     1 100 88 adc2.example.com.

_ldap._tcp.dc._msdcs.example.com.       3600    IN      SRV     1 100 389 adc1.example.com.
_ldap._tcp.dc._msdcs.example.com.       3600    IN      SRV     1 100 389 adc2.example.com.

_ldap._tcp.example.com.                 3600    IN      SRV     1 100 389 adc1.example.com.
_ldap._tcp.example.com.                 3600    IN      SRV     1 100 389 adc2.example.com.

Verify that it works with dig command from bind-utils package:

~ # dig srv _kerberos-master._tcp.example.com.

;; QUESTION SECTION:
;_kerberos-master._tcp.example.com. IN SRV

;; ANSWER SECTION:
_kerberos-master._tcp.example.com. 3600 IN SRV 1 100 88 adc1.example.com.
_kerberos-master._tcp.example.com. 3600 IN SRV 1 100 88 adc2.example.com.

;; ADDITIONAL SECTION:
adc1.example.com. 3600 IN A	192.168.148.5
adc2.example.com. 3600 IN A	192.168.148.4
telnet adc1.example.com. 88
Trying 192.168.148.4...
Connected to adc1.example.com.
Escape character is '^]'.
^]
telnet> Connection closed.

If instead you get a connection timeout then there is something wrong with the connectivity, routing, VPN, firewall, security groups, etc.

Typically if you are member of Domain Administrators group you should be fine. If not ask the AD admins to give you the privilege to join machines to the domain.
I’m pretty sure you have this part sorted if you are reading this blog post 🙂

Now that you ticked all the boxes we are ready for the real work!

Joining a Linux server to AD domain

First of all install the necessary packages.

[root@ec2-instance ~]# yum install sssd realmd krb5-workstation pam_krb5 \
                                        oddjob samba-winbind samba-winbind-clients

Discover the AD domain – obviously replace EXAMPLE.COM with your real domain written in UPPERCASE. This is really important with all the commands below that show uppercase domain names!!!

[root@ec2-instance ~]# realm discover EXAMPLE.COM
example.com
 type: kerberos
 realm-name: EXAMPLE.COM
 domain-name: example.com
 configured: kerberos-member
 server-software: active-directory
 client-software: winbind
 required-package: oddjob-mkhomedir
 required-package: oddjob
 required-package: samba-winbind-clients
 required-package: samba-winbind
 required-package: samba-common-tools
 login-formats: EXAMPLE\%U
 login-policy: allow-any-login

Test login to the domain.

[root@ec2-instance ~]# kinit michael.ludvig@EXAMPLE.COM
Password for michael.ludvig@EXAMPLE.COM:
[root@ec2-instance ~]# 

This step will fail if the DNS is not correctly configured or resolvable or if the network connectivity to the ADC is broken. The error will be something along these lines:

kinit: Cannot find KDC for realm "ENTERPRISEIT.CO.NZ" while getting initial credentials

In that case double check the DNS configuration – expand the list above for an example and minimum requirements.

If kinit worked it’s finally time to join the system to the AD realm. This step will fail if your AD account doesn’t have enough privileges to join new computers to the realm.

[root@ec2-instance ~]# realm join -U michael.ludvig@EXAMPLE.COM EXAMPLE.COM
Password for michael.ludvig@EXAMPLE.COM:
[root@ec2-instance ~]# 

Succeeded? Great! Got an error? Not so great, see below for common errors and solutions…

One last step

One last step, entirely optional, is to change /etc/sssd/sssd.conf to make the newly joined domain a default for Linux logins. Without it the users will have to login as EXAMPLE.COM\\michael.ludvig. With the settings below the username michael.ludvig will be looked up in EXAMPLE.COM by default.
Also the home directories will be created with just the user name without the domain.

# /etc/sssd/sssd.conf
[sssd]
default_domain_suffix = EXAMPLE.COM
full_name_format = %1$s

[domain/example.com]
fallback_homedir = /home/%u@%d
fallback_homedir = /home/%u

DNS issues

Recently I started getting this error:

[...]
  * Authenticated as user: michael.ludvig@EXAMPLE.COM
  ! Couldn't authenticate to active directory: SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Server not found in Kerberos database)
 adcli: couldn't connect to example.com domain: Couldn't authenticate to active directory: SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Server not found in Kerberos database)
  ! Insufficient permissions to join the domain

Although it looks like it’s a permission problem it’s in fact a DNS issue. Make sure the reverse resolution of the AD servers IPs works. If for any reason you can’t work around it:

# /etc/krb5.conf
[libdefaults]
	rdns = false

Testing and maintenance

Make sure you test the AD username resolution by running id command:

[root@ec2-instance ~]# id some.user
uid=946201234(some.user) gid=946200513(domain users) groups=946200513(domain users),...

In case the recently logged in user’s AD domain membership changes it may take some time for the Linux box to catch up with the changes. Speed it up by flushing the SSSD users and groups cache.

sss_cache -U -G

That’s it. Leave me a comment if I should add or clarify something in the steps above.