Ansible playbook: Join CentOS server to Active Directory
As your homelab or enterprise expands with new servers it tends to get more and more frustrating to keep track of all local user accounts and passwords. One simple way to minimize the frustration is to utilize something that, I dare say, every organization already uses.
Microsoft Active Directory.
This playbook will join your CentOS server to the Active Directory and limit logon access and sudo access using security groups.
Keep in mind that the playbook in this post has been used to bulk join multiple servers to the AD. Feel free to customize it to fit your needs.
Go ahead and skim through the playbook. I will go in to detail further down in the post.
---
## This playbook installs and configures AD authentication
- name: Install and configure AD authentication
hosts: centos
become: yes
become_method: sudo
vars_prompt:
- name: "bind_password"
prompt: "Password for safdal\\administrator"
private: yes
tasks:
- name: Install ad_auth required tools
yum:
name: libselinux-python,realmd,sssd,oddjob,oddjob-mkhomedir,adcli,samba-common,samba-common-tools,ntpdate,ntp,python-pip,sudo
state: present
- name: Install pexpect using pip
pip:
name: pexpect
- name: Check if machine is bound
shell: /bin/bash -c "realm list | grep sssd"
register: realmd_bound
changed_when: false
ignore_errors: true
- name: Join system to AD and put the computer object in the Linux OU
expect:
command: /bin/bash -c "/usr/sbin/realm join --user=ADMINISTRATOR@safdal.se --computer-ou=OU=Linux,OU=Servers,OU=Safdal,DC=safdal,DC=se safdal.se"
responses:
Password for *: "{{ bind_password }}"
when: realmd_bound|failed
- name: Add default_domain_suffix to sssd.conf
lineinfile:
dest: /etc/sssd/sssd.conf
line: 'default_domain_suffix = safdal.se'
insertafter: '^\[sssd\]'
notify:
- restart sssd
when: realmd_bound|failed
- name: Restrict access based on specific ad group
command: /bin/bash -c "/usr/sbin/realm permit -g g_Server.{{ item }}-logon@safdal.se"
with_items: "{{ inventory_hostname_short }}"
when: realmd_bound|failed
- name: Add ad group to sudoers
lineinfile:
dest: /etc/sudoers
line: '%g_Server.{{ item }}-sudo@safdal.se ALL=(ALL) ALL'
insertafter: '^%wheel'
with_items: "{{ inventory_hostname_short }}"
when: realmd_bound|failed
handlers:
- name: restart sssd
service:
name: sssd
state: restarted
First of we have the vars_prompt
to keep the password for the domain joining account safe. When you execute the playbook you will get prompted for the password to be typed in. By using the option private: yes
the characters will not be echoed on the screen. I have hardcoded the domain join user in the playbook. Just add an additional variable to the vars_prompt
if you want.
The next section will install all of the required applications if not already installed. When the command for the actual domain join is executed it will prompt for the password of the specified user account. To get ansible to echo the password back to the prompt we will use pexpect
. Which explains why it being installed by pip.
To make the playbook idempotent we first check to see if the machine already is "bound" to a domain. If it is bound to a domain nothing further will happen. If the machine is not bound we will continue with the process of joining it to the domain. You might be wondering why shell:
is used instead of command:
, the reason for that is that shell:
allows us to use functions like pipe. Read more about it here.
Next up we have the actual joining to the domain. Please edit this line to match your needs and environment:
command: /bin/bash -c "/usr/sbin/realm join --user=ADMINISTRATOR@safdal.se --computer-ou=OU=Linux,OU=Servers,OU=Safdal,DC=safdal,DC=se safdal.se"
- --user= Specify the user account that will be doing the join to the domain. Note that all caps is required on the user. And of course rights to join computers to the specified OU.
- --computer-ou= Specify the path to the OU you want to join the computer to. This is optional. If no OU is specified it will be joined to the standard computer OU.
- Lastly specify the name of the domain. In my case safdal.se.
The playbook will utilize lineinfile
to make changes to the sssd.conf
. Edit this line to match your domain name:
line: 'default_domain_suffix = safdal.se'
Lastly we have the, in my mind, most useful section. This is where logon access and sudo ability is restricted by using security groups from the AD.
command: /bin/bash -c "/usr/sbin/realm permit -g g_Server.{{ item }}-logon@safdal.se"
As we permit the specified group none other than the members in this group will be able to for example ssh to the machine. As I stated earlier I used this playbook to bulk join servers so go ahead and remove the with_items
if not applicable in your use case.
The same goes for the adding of security group(s) to sudoers.
line: '%g_Server.{{ item }}-sudo@safdal.se ALL=(ALL) ALL'
I have verified that both %groupname@domain.tld and %DOMAIN.TLD\\groupname works. It's like tabs vs. spaces, up to you.
When your CentOS server is properly joined to the domain you can use AD credentials to logon to it. And if you're lucky you even got sudo rights.
ssh username@domain.tld@ip-or-fqdn-to-server
This is a working, albeit not flawless, playbook. If you have comments or suggestions feel free to contribute. (Dodgydude #2 must implement a comment section first, tho.)