FreeIPA is a solution that allows, among other things, centralized authentication, authorization and account information. A mimimun FreeIPA environment would consist of a main server (responsible for CA revewal), a replica server (as you want to have some resilience on your auth environment), and one client (actually a bunch of clients).
Although planning a FreeIPA environment is the hard part, deploying it may be slightly easier, and can be automated with Ansible, using ansible-freeipa.
The goal of this document is to show how FreeIPA deployment can be automated with ansible-freeipa. It assumes you have a controller node and more than one target nodes. All nodes should have network configured, static hostnames, and have hostnames resolvable, either though DNS or other means (e.g.: /etc/hosts
). As packages will be installed, the target nodes should be able to connect to a dnf
repository.
Deploying a FreeIPA cluster with Ansible using ansible-freeipa requires either the use of the ansible-freeipa repository, or the installation of the ansible-freeipa RPM package, or using a collection.
RPM packages are available for Fedora, CentOS and Red Hat Enterprise Linux, and they differ a little from the collections as the roles and modules are not provided in a collection, so, instead of
freeipa.ansible_freeipa.ipaserver
, one would simply useipaserver
.
For Ansible Automation Platform subscribers,
ansible-freeipa
is available through `Automation Hub’.
Of course you must have Ansible installed on your controller machine. You may use a Python virtual environment:
python -m venv /tmp/freeipa
. /tmp/freeipa/bin/activate
pip install ansible-core
Ansible Galaxy ansible-freeipa collection will be used, and it has to be installed before use:
ansible-galaxy collection install freeipa.ansible_freeipa
In the examples, some variables available only on version 1.9.0 of ansible-freeipa collection will be used. Make sure you have at least version 1.9.0 installed.
With the collection installed, the first FreeIPA server can be deployed using the playbook install-server.yaml
:
- name: Deploy an IPA server
hosts: ipaserver
become: true
roles:
- freeipa.ansible_freeipa.ipaserver:
state: present
Altough some features can be added to an IPA server after it is installed (e.g.: KRA and DNS), adding new components is not suppported by ansible-freeipa, and you have to plan before installing the server.
In this example, the server deployed should have all the required components to be used as a testing host for ansible-freeipa, which means we need to have DNS, KRA and AD Trust components.
The configuration is done through role variables, which can be set in an inventory file:
all:
children:
ipacluster:
hosts:
ipaserver:
ipareplicas:
ipaclients:
vars:
ipadm_password: "{{ ipa_dm_password }}"
ipaadmin_password: "{{ ipa_admin_password }}"
ipaserver_domain: ipa.test
ipaserver_realm: IPA.TEST
ipaserver:
hosts:
server.ipa.test:
ansible_user: root
vars:
# KRA
ipaserver_setup_kra: true
# DNS
ipaserver_setup_dns: true
ipaserver_forwarders: 1.1.1.1
ipaserver_auto_reverse: true
ipaserver_allow_zone_overlap: true
# this is required for AD trust
ipaserver_no_dnssec_validation: true
# trust vars
ipaserver_setup_adtrust: false
# disable 'allow all' HBAC rule
ipaserver_no_hbac_allow: true
The AD Trust and KRA setup are pretty simple and straightforward for ansible-freeipa tests. DNS is also somewhat simple. We need to set no_dnssec_validation
, as it is required to setup a trust with Microsoft Ad, and adding a DNS forwarder allows us to keep external name resolution in an easy way. The ipaserver_no_hbac_allow
is used as it is a recommended setting.
For more information on the ipaserver
role options, see the offical documentation.
Note that the domain and realm are set as variables for the cluster, as the same values will be used by replicas and clients. The Directory Manager password (ipadm_password
) and the Administrator (user admin
, ipaadmin_password
), are also set for the whole cluster.
Note: As it is not in the scope of this document to describe the ways you have to provide a password for an Ansible playbook, a placeholder will be used in the place of the passwords. ansbile-freeipa uses
SomeDMpassword
andSomeADMINpassword
for testing purposes, and they are useally stored directly on the playbooks, but this behavior should be avoided, specially if you plan to share playbooks.
After deploying the server, some backup files are created with the private and public keys for the CA and KRA on the ansible_user
directory. These files should be securely stored and backed up, what can be accomplished with a playbook:
- name: Backup IPA server certificates
hosts: ipaserver
become: false
gather_facts: false
vars:
target_dir: "-certs"
tasks:
- name: Ensure backup directory exists locally
ansible.builtin.file:
path: ""
state: directory
delegate_to: localhost
- name: Retrieve IPA server master certificates
ansible.builtin.fetch:
src: ""
dest: "/"
flat: true
with_items:
- cacert.p12
- ca-agent.p12
- kracert.p12
become: true
Once the server is deployed, we can start deploying replicas and clients.
Adding a replica to an IPA realm adds redundancy to your network, and allow clients to authenticate, resolve DNS, or access KRA data (vaults), even in the face o a complete meltdown of the initial server. If your server is also high on load, this might give it some relief.
A replica is also required in the case of upgrading the server. Even if upgrading a running server may be possible, it is not recommended, and upgrading servers involve decommissioning it, installing the new version, and waiting for replication.
The playbook to deploy a replica with ansible-freeipa is similar to the server deployment:
- name: Deploy the IPA replicas
hosts: ipareplicas
become: true
roles:
- freeipa.ansible_freeipa.ipareplica:
state: present
The changes occur in the variables configured in the inventory file:
all:
children:
ipacluster:
hosts:
ipaserver:
ipareplicas:
ipaclients:
vars:
ipadm_password: "{{ ipa_dm_password }}"
ipaadmin_password: "{{ ipa_admin_password }}"
ipaserver_domain: ipa.test
ipaserver_realm: IPA.TEST
ipareplicas:
hosts:
rep-01.fed.ipa.test:
ansible_user: root
# CA backup
ipareplica_setup_ca: true
# KRA backup
ipareplica_setup_kra: true
# DNS backup
ipareplica_setup_dns: true
ipareplica_no_dnssec_validation: true
ipareplica_no_forwarders: true
# Trust backup
ipareplica_setup_trust: true
vars:
# Update DNS address
ipasssd_enable_dns_updates: true
# Automatically handle DNS nameservers (v1.9.0+)
ipaclient_configure_dns_resolver: true
ipaclient_dns_servers:
# server.fed.ipa.test IPv4 address
- 192.168.122.30
# rep-01.fed.ipa.test IPv4 address
- 192.168.122.31
After the initial IPA server is deployed, any number of replicas can be added to the realm (in an already existing environment, it is recommended that replicas are added one by one, and enough time for replication to finish is given), and each replica can have a different configuration.
Since each replica can have its own set of services, the configuration is done per host, and in this case, a similar configuration to the initial server is used. The main difference is that no forwarder is configured for DNS.
Although similar, a replica installation is different than the initial server. A replica is installed first as a client, then it is promoted to a replica. This means that the same configuration available for the ipaclient
role is also availabel for the replica.
On ansible-freeipa prior to 1.9.0 one would have to configure the DNS resolution of the target node, specially if the only DNS server was the IPA server. This would involve a few tasks, depending on the network configuration.
Starting with version 1.9.0, the ansible-freeipa’s ipaclient
role allows one to configure the host DNS resolver to use a specific set of nameservers by setting the variables ipaclient_configure_dns_resolver
and ipaclient_dns_servers
. This will work with systemd-resolved
, NetworkManager
or plain /etc/resolv.conf
.
Acconding to the oficial documentation, ipaclient_all_ip_addresses
“defines if DNS A/AAAA records for each IP address on the client will be created”, and, if the target node has a single IP this has the effect of adding the host IP to the embedded DNS database, and this is needed to finish ipareplica
deployment, as it will allow replication to take place, finishing replica setup.
For more information on the ipareplica
role options, see the offical documentation.
Now that our network has some redundancy, we can add some clients to it.
As it should be expected, by now, the playbook to deploy an IPA client is not much different from the playbooks used to deploy servers and replicas.
- name: Deploy the IPA clients
hosts: ipaclients
become: true
roles:
- freeipa.ansible_freeipa.ipaclient:
state: present
The options, on this case, will configure the environment users will see when they log into the client hosts. And although there might have some different configurations for each client, it is not uncommon that the configuration is shared between all client hosts:
all:
children:
ipacluster:
hosts:
ipaserver:
ipareplicas:
ipaclients:
vars:
ipadm_password: "{{ ipa_dm_password }}"
ipaadmin_password: "{{ ipa_admin_password }}"
ipaserver_domain: ipa.test
ipaserver_realm: IPA.TEST
ipaclients:
hosts:
cli-01.fed.ipa.test:
ansible_user: root
vars:
# Client options
ipaclient_mkhomedir: true
# Update DNS address
ipasssd_enable_dns_updates: true
# Automatically handle DNS nameservers (v1.9.0+)
ipaclient_configure_dns_resolver: true
ipaclient_dns_servers:
# server.fed.ipa.test IPv4 address
- 192.168.122.30
# rep-01.fed.ipa.test IPv4 address
- 192.168.122.31
In this case the only specific setting for the client hosts is to allow the creation of the user home directory upon first login (as NFS shares are not used for user home directories).
With the clients deployed, we have the whole IPA cluster up and running, and now we can manage its configuration.
As the first server was installed with ipaserver_no_hbac_allow: true
, access to all clients by IPA users is disabled by default. Using ansible-freipa plugins allow us to add users and HBAC rules for these users to allow them to log into the client host.
---
- name: Configure users and host access
hosts: ipaserver
become: false
gather_facts: false
tasks:
- name: Add a testing user
ipauser:
ipaadmin_password: ""
name: jdoe
first: John
last: Doe
password: SomeJOHN
- name: Ensure HBAC login services exist
ipahbacsvc:
ipaadmin_password: ""
name: ""
state: present
loop:
- sshd
- login
- name: Allow user login on client hosts
ipahbacrule:
ipaadmin_password: ""
name: "User Login"
usercategory: "all"
hbacsvc:
- "sshd"
- "login"
host: ""
Note that importing a large number of users or rules will have an impact on the environment due to load in the server where the playbook runs (in this case ipaserver
, the first server) and due to replication. One alternative is to use a hidden replica for bigger changes.
To test the final configuration, issue ssh jdoe@cli-01.fed.ipa.test
and use the password SomeJOHN
to log into the computing node.
Installing an IPA cluster with ansible-freeipa is somewhat easy, but this should not encourage one to not think thoroughly on the environment being created. It only allows you to achieve what you pretend in an easier, automated and reproducible way.
Some important topics were not covered here for the provided playbooks to be used in production, like securing passwords, the domain names, or IPA addresses. These should all be well defined, specially since the domain cannot be changed once the realm is up.
Another interesting point not covered here is the deployment of hidden replicas, which are useful for backing up the configuration, or performing mass or lengty operations without disrupting the environment for clients.
What has been shown here is that using ansible-freeipa, specially in the later versions, can ease the setup and allow for fast experiences with an environment. For example, with a set of three virtual machines with network and domain names already setup, it is possible to bring up a cluster in about 20 minutes (most of the time is spent on downloading packages, so a local package repository or pre-downloaded packages will speed up). This is fast enough to make some experiments with the desired environment.
To deploy the whole cluster with a single execution you can merge the playbooks and the inventory files, allowing the cluster deployment in a single execution, for example:
ansible-playbook -i inventory.yaml install-freeipa-cluster.yml
Find below the code and the download option for these files.
---
- name: Install IPA server
hosts: ipaserver
become: true
gather_facts: true
roles:
- role: freeipa.ansible_freeipa.ipaserver
state: present
- name: Backup IPA server certificates
hosts: ipaserver
become: false
gather_facts: false
vars:
target_dir: "-certs"
tasks:
- name: Ensure backup directory exists locally
ansible.builtin.file:
path: ""
state: directory
delegate_to: localhost
- name: Retrieve IPA server master certificates
ansible.builtin.fetch:
src: ""
dest: "/"
flat: true
with_items:
- cacert.p12
- ca-agent.p12
- kracert.p12
become: true
- name: Install IPA replicas
hosts: ipareplicas
become: true
gather_facts: true
roles:
- role: freeipa.ansible_freeipa.ipareplica
state: present
- name: Install IPA clients
hosts: ipaclients
become: true
gather_facts: true
roles:
- role: freeipa.ansible_freeipa.ipaclient
state: present
---
all:
children:
# define cluster
ipacluster:
children:
ipaserver:
ipareplicas:
ipaclients:
vars:
ipaserver_domain: fed.ipa.test
ipaserver_realm: FED.IPA.TEST
ipadm_password: "{{ ipa_dm_password }}"
ipaadmin_password: "{{ ipa_admin_password }}"
# IPA First Server (CA Renewal)
ipaserver:
hosts:
server.fed.ipa.test:
ansible_user: root
vars:
# KRA
ipaserver_setup_kra: true
# DNS
ipaserver_setup_dns: true
ipaserver_forwarders: 1.1.1.1
ipaserver_auto_reverse: true
ipaserver_allow_zone_overlap: true
# this is required for AD trust
ipaserver_no_dnssec_validation: true
# trust vars
ipaserver_setup_adtrust: true
# disable 'allow all' HBAC rule
ipaserver_no_hbac_allow: true
# other vars
# IPA Replica Servers
ipareplicas:
hosts:
rep-01.fed.ipa.test:
ansible_user: root
# CA backup
ipareplica_setup_ca: true
# KRA backup
ipareplica_setup_kra: true
# DNS backup
ipareplica_setup_dns: true
ipareplica_no_dnssec_validation: true
ipareplica_no_forwarders: true
# Trust backup
ipareplica_setup_trust: true
vars:
# Update DNS address
ipasssd_enable_dns_updates: true
# Automatically handle DNS nameservers (v1.9.0+)
ipaclient_configure_dns_resolver: true
ipaclient_dns_servers:
# server.fed.ipa.test IPv4 address
- 192.168.122.30
# rep-01.fed.ipa.test IPv4 address
- 192.168.122.31
# IPA Client hosts
ipaclients:
hosts:
cli-01.fed.ipa.test:
ansible_user: root
vars:
# Client options
ipaclient_mkhomedir: true
# Update DNS address
ipasssd_enable_dns_updates: true
# Automatically handle DNS nameservers (v1.9.0+)
ipaclient_configure_dns_resolver: true
ipaclient_dns_servers:
# server.fed.ipa.test IPv4 address
- 192.168.122.30
# rep-01.fed.ipa.test IPv4 address
- 192.168.122.31