FreeIPA - Linux SSO

Home Network Accounts and SSO

These are my notes on setting up network accounts with single-sign-on (SSO) for a network running linux machines.

Background

Why? Why would anyone do this?

The reason I am doing this is because we have family members who use multiple, shared computers. Some members have a specific primary machine, but all may need to log onto any computer for the purposes online schooling resources, playing games, or engaging in computer related art and hobbies. The alternatives to a network SSO system are:

  1. Shared family computer accounts: Downsides are lack of privacy; possibility of in progress work getting modified or removed. Similar problems exist with having no password. This is a non-starter for every member of my family.
  2. Individual accounts on multiple devices: Possibility of password getting out of sync as they get changed for various reasons; requiring an administrator user to add an account the first time a user wants to use a new computer. This would be hugely inconvenient for the parents in the family, and would lead to password sharing during temporary alliances, which would lead to strife after the inevitable collapse of those alliances.

Additionally, setting up this kind of thing can be a fun learning experience.

Environment and history

My network consists entirely of computers running in service Ubuntu (18.04 and 20.04).

This is not my first foray into this territory. I began with a manually created kerberos + LDAP + sssd + nslcd etc. Needless to say, this is a lot to manage, and hard to setup in a repeatable and restorable way. I basically only used this setup for myself, and only for testing purposes.

FreeIPA seems like it solves all of these issues, and so a few years ago I setup a CentOS7 VM running the server and added all our machines. This worked great, until an unfortunate mouse incident happened which took out my basement server which was running the VM. Fortunately, every machine still has cached credentials for any account that had logged in at least once on it, so fixing it has been a low priority. I recently discovered that upgrading ubuntu from 18.04 to 20.04 seems to lose that cache, so I'd like to setup a new, more permanent system.

The two drawbacks with my last IPA install were:

  1. Up-time: My basement server is primarily meant for testing and large processing jobs, and isn't on a UPS and isn't a high priority to restart usually. That means that there is a chance when someone logs on to a new system, the VM isn't running. This is a fixable problem, obviously, and I intend to fix it and have a local replica running eventually.
  2. Outside the home: Even with 100% uptime, with a basement solution we currently won't be able to access the server remotely. Our ISP's setup is such that it's more trouble than it's worth to get around this.

The solution is a public internet server running ipa. Fortunately, the system resource requirements are quite small1.

Setup

OS

I would have loved to use debian stable as the server, but this is unfortunately not currently possible. As a second choice, I'd prefer to use Ubuntu, but sadly the freeipa-server package isn't available in 20.04 anymore. For now I'm going to risk using CentOS 8, despite the recent unfortunate news.

Any modern Linux distribution probably supports being an IPA client. This article describes setting it up on CentOS 8, Debian, and Ubuntu. I imagine any Debian derived distribution will work nearly identically to the Debian and Ubuntu instructions below.

Testing Environment

Rather than start experimenting on the open internet, I'll start by setting up a local network of VMs to work out exactly what I want to do. For testing it's hard to beat Vagrant. Here's the cut-down Vagrant file I used:

Vagrant.configure("2") do |config|
  config.vm.define "master1", autostart: true do |v|
    v.vm.box = "centos/8"
    v.vm.network  "private_network", ip: "10.0.100.2"
    v.vm.provider "libvirt" do |v|
      v.cpus = 1
      v.memory = 1024
    end
  end
  config.vm.define "master2", autostart: true do |v|
    v.vm.box = "centos/8"
    v.vm.network  "private_network", ip: "10.0.100.3"
    v.vm.provider "libvirt" do |v|
      v.cpus = 1
      v.memory = 1024
    end
  end
  config.vm.define "client1", autostart: true do |v|
    v.vm.box = "generic/ubuntu2004"
    v.vm.network  "private_network", ip: "10.0.100.11"
    v.vm.provider "libvirt" do |v|
      v.cpus = 1
      v.memory = 1024
    end
  end
  config.vm.define "client2", autostart: true do |v|
    v.vm.box = "debian/buster64"
    v.vm.network  "private_network", ip: "10.0.100.12"
    v.vm.provider "libvirt" do |v|
      v.cpus = 1
      v.memory = 1024
    end
  end
  config.vm.define "client3", autostart: true do |v|
    v.vm.box = "centos/8"
    v.vm.network  "private_network", ip: "10.0.100.13"
    v.vm.provider "libvirt" do |v|
      v.cpus = 1
      v.memory = 1024
    end
  end
end

This will create 5 machines:

master1
The primary server
master2
Not used in this article; intended to test replication capability
client1
Ubuntu 20.04 client
client2
Debian 10 (Buster) client
client3
CentOS 8 client

For this test setup I'm using the mysite.test TLD per RFC2606. To make it all work I'll add the IP addresses and host names to the /etc/hosts file on each VM and my host computer. The entries will look like:

10.0.100.2  master1.mysite.test
10.0.100.3  master2.mysite.test
10.0.100.11 client1.mysite.test
10.0.100.12 client2.mysite.test
10.0.100.13 client3.mysite.test

Using host files allows each machine (and the host) to reach the VMs, but it does not provide the various SRV and TXT DNS records needed to make clients fully autoconfigurable on startup.

Then it's simply a vagrant up and we're ready to start experimenting.

See the Vagrant documentation for more info. To just get started quickly, VMs can be accessed by using vagrant ssh master1 (for example) from the command line.

Installation

CentOS comes with DNF now, apparently. I found this handy guide on setting up freeipa on CentOS 8, but it doesn't really explain what each step does.

sudo dnf module install idm:DL1/{server,client}

Note 1: the {server,client} uses shell expansion to turn the line into sudo dnf module install idm:DL1/server idm:DL1/client. More info here.

Note 2: dns can also be included with server and clientto use the integrated IPA DNS. I've opted not to use integrated DNS as discussed below. If using dns, add it to the shell expansion braces (idm:DL1/{server,client,dns}).

CentOS 8 now includes the concept of modules, which are package groups representing "an application, language runtime, or set of tools" (ref). Using dnf install <package> works only when installing a single traditional package, or a modular package in the default module or any module that's been specifically enabled (ref). Specifying dnf module install instead allows installing packages from a module that isn't enabled. The format is dnf module install <NAME>:<STREAM>/<PROFILE>.

You can list the available modules and their available streams with dnf module list (or dnf module list <module> to limit the list to a specific module).

All this is to explain that using the line sudo dnf module install idm:DL1/{server,client} is a shortcut to install a whole mess of packages required for running free ipa server and client.

Server Setup

Before configuring IPA it helps to set the fully-qualified domain name for the host.

The preferred way appears to be using the hostnamectl command:

sudo hostnamectl set-hostname master1.mysite.test

You can verify that it worked with hostnamectl

Now is also a good time to ensure that there is an entry for at least this machine in the /etc/hosts file.

To configure the server, use2:

sudo ipa-server-install --idstart=1234500000 --mkhomedir

The --idstart switch configures the starting user ID. This is important since I'm trying to keep existing home directory permissions and such. Users will need to reset their password, but aside from that will be blissfully unaware of the change. This argument is not necessary for a new install; IPA will choose a nice high number starting UID.

The --mkhomedir option is important because it ensures that the first time a user logs into the system their home directory will be created. This option will be used by the ipa-client-install commands later as well.

ipa-server-install launches a configuration wizard which will automatically try to detect domain, kerberos realm, and other things based on hostname. I chose to not setup DNS, and accepted the defaults for everything. I used "password" for all passwords during testing.

The very last question asks you to confirm the settings that will be used, and defaults to "no". You will need to type yes and hit enter.

Using the values here, the domain will be set to mysite.test, and the kerberos realm to MYSITE.TEST.

Your first server should now be setup, and can be accessed by pointing a web browser at https://master1.mysite.test. You will get a certificate error for the self-signed certificate. This is expected. Login with user admin and password password.

You may want to update the Policy->Password Policies->Max lifetime value, depending on the network risk threshold and any extant spousal acceptance factor.

Securing the Master Server:

The IPA server should be secured as much as possible, especially if it will be on the internet.

Firewall

At the end of the server install, the command will output a list of firewall ports that will need to be open. These can actually be configured ahead of time for additional security. All other ports should be closed.

Next steps:
        1. You must make sure these network ports are open:
                TCP Ports:
                  * 80, 443: HTTP/HTTPS
                  * 389, 636: LDAP/LDAPS
                  * 88, 464: kerberos
                UDP Ports:
                  * 88, 464: kerberos
                  * 123: ntp

        2. You can now obtain a kerberos ticket using the command: 'kinit admin'
           This ticket will allow you to use the IPA tools (e.g., ipa user-add)
           and the web user interface.

There are many options for configuring the firewall on CentOS; these notes describe two.

Use only ONE of the following

firewalld

firewalld is a nice front-end available on CentOS 8. It may already be installed; if not, it can be installed with :

sudo dnf install firewalld       # install
sudo systemctl start firewalld   # start
sudo systemctl enable firewalld  # start on boot
sudo firewall-cmd --permanent \ 
  --add-service={http,https,ldap,ldaps,kerberos}

After updating the firewall rules, they need to be reloaded with:

sudo firewall-cmd --reload
nftables

The CentOS 8 firewall can instead be configured with nftables.

If firewalld running, be sure to stop and disable it (you can check with sudo systemctl status firewalld):

sudo systemctl stop firewalld
sudo systemctl disable firewalld

Install and ensure nftables is enabled and running:

sudo dnf install nftables       # install nftables
sudo systemctl start nftables   # start the service
sudo systemctl enable nftables  # configure to start on boot

Add the required rules to nftables:

sudo nft add rule ip filter INPUT iif lo accept
sudo nft add rule ip filter INPUT ct state established,related accept
sudo nft add rule ip filter INPUT tcp dport 22 accept
sudo nft add rule ip filter INPUT tcp dport 80 accept
sudo nft add rule ip filter INPUT tcp dport 443 accept
sudo nft add rule ip filter INPUT tcp dport 389 accept
sudo nft add rule ip filter INPUT tcp dport 636 accept
sudo nft add rule ip filter INPUT tcp dport 88 accept 
sudo nft add rule ip filter INPUT tcp dport 464 accept
sudo nft add rule ip filter INPUT udp dport 88 accept
sudo nft add rule ip filter INPUT udp dport 464 accept
sudo nft add rule ip filter INPUT udp dport 123 accept
sudo nft add rule ip filter INPUT counter drop

Check the output of sudo nft list table ip filter. It should look something like:

table ip filter {
        chain INPUT {
                type filter hook input priority filter; policy accept;
                iif "lo" accept
                ct state established,related accept
                tcp dport 22 accept
                tcp dport 80 accept
                tcp dport 443 accept
                tcp dport 389 accept
                tcp dport 636 accept
                tcp dport 88 accept
                tcp dport 464 accept
                udp dport 88 accept
                udp dport 464 accept
                udp dport 123 accept
                counter packets 38 bytes 1880 drop
        }

        chain FORWARD {
                type filter hook forward priority filter; policy accept;
        }

        chain OUTPUT {
                type filter hook output priority filter; policy accept;
        }
}

Disable Anonymous directory access

By default IPA allows anonymous ldap directory enumeration. This is crazy to me. It means anyone can run an ldapsearch against your ldap port and get a list of users and their information. Test this anonymous directory capability with: ldapsearch -x -h master1.mysite.test -b dc=mysite,dc=test.

To disable anonymous access, run: ldapmodify -x -D "cn=Directory Manager" -W

You will be prompted for the "LDAP Password" (which is the directory password you entered during server install). After entering the password and hitting enter, the program will be waiting for your input. Enter the following:

dn: cn=config
changetype: modify
replace: nsslapd-allow-anonymous-access
nsslapd-allow-anonymous-access: rootdse

You may need to hit <shift>+D to exit. Verify with the ldapsearch command above to verify that you no longer enumerate your directory.

Restart the service with sudo systemctl restart dirsrv.target.

(source: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/linux_domain_identity_authentication_and_policy_guide/disabling-anon-binds)

Adding Users

Log into master1 and in the users tab you can add users. Note that a first and last name are required. If you click Add and Edit you will have an opportunity to modify settings. You should make a regular user who is also in the admin group.

Before this user will be able to do anything, they will need to log into either the ipa web gui or an existing IPA server.

Setup Client(s)

IPA Client installation

Follow instructions below as needed for your site to setup the client and install the IPA client software and install from the new client. There is another method that does not require entering an admin password which is described below.

Add Ubuntu Client

Set the hostname as above (sudo hostnamectl set-hostname client2.mysite.test).

add master1 and client2 to /etc/hosts

10.0.100.2       master1.mysite.test
10.0.100.11      client1.mysite.test

Install the freeipa-client package:

sudo apt install freeipa-client

This will launch a couple of ncurses wizards, which should automatically figure out and suggest that MYSITE.TEST is the kerberos realm. The other things you'll have to enter; use master1.mysite.test as the kerberos server, and enter the password configured during installation of the server (password, in my case). Enter ldaps://master1.mysite.test/ for the LDAP server URI, select try, and enable everything for libnss-ldapd.

When installation is complete, run ipa-client-install as root: sudo ipa-client-install --mkhomedir.

There will be a warning about DNS setup failing, which will cause failover to not work. That's okay, we'll fix it in the real installation where we have real dns.

For the "User authorized to enroll computers" use either "admin" or the user in the admin group.

If your admin user password fails at this stage, it may be because you haven't logged in and changed that user's password yet.

Add Debian Client

Set the hostname as above (sudo hostnamectl set-hostname client2.mysite.test).

add master1 and client2 to /etc/hosts

10.0.100.2       master1.mysite.test
10.0.100.12      client2.mysite.test

Install the freeipa-client package:

sudo apt install free-ipa-client

This will launch a couple of ncurses wizards, which should automatically figure out and suggest that MYSITE.TEST is the kerberos realm. The other things you'll have to enter; use master1.mysite.test as the kerberos server, and enter the password configured during installation of the server (password, in my case). Enter ldaps://master1.mysite.test/ for the LDAP server URI, select try, and enable everything for libnss-ldapd.

When installation is complete, run ipa-client-install as root: sudo ipa-client-install --mkhomedir.

There is a warning about how failover won't work. That's okay, we'll fix it in the real installation where we have real dns.

For the "User authorized to enroll computers" use either "admin" or the user in the admin group.

Add Centos Client

Set the hostname as with the server (sudo hostnamectl set-hostname client3.mysite.test).

add master1 and client3 to /etc/hosts:

10.0.100.2  master1.mysite.test
10.0.100.13 client3.mysite.test

Install the ipa client packages:

sudo dnf module install idm:DL1/client

When installation is complete, run ipa-client-install as root: sudo ipa-client-install --mkhomedir.

There is a warning about how failover won't work. That's okay, we'll fix it in the real installation where we have real dns.

For the "User authorized to enroll computers" use either "admin" or the user in the admin group.

IPA Client configuration with one-time password

As an alternative to using a username / password of an authorized user when running ipa-client-install, you can use a one-time-password if your server and client are on the same network.

THIS ONLY WORKS if you have a DNS record for the host, or if you know the IP and can enter it in the add box.

In the IPA Web UI navigate to UI->Hosts, and click +Add. Set the host name the computer will have, and check the Generate OTP box. (Enter the IP address if you know what it will be).

When running the ipa-client-install program add the -w <OTP> switch. For example:

sudo ipa-client-install --mkhomedir -w Ab12cCDd34Ee56Ff78Gg90

There is a warning about how the cert will be downloaded over http. This is probably not a good idea if it's over the internet, but on a LAN the risk may be low.

Real Install

When you are happy with how the tests worked out, you can move onto the real install. The process will be just about identical. Below are some additional considerations for a real deployment.

DNS

For my installation I'm opting to not use the IPA integrated DNS, despite the strong recommendations of the freeipa project. It's worth noting that the freeipa dns goals specifically do not include being a general purpose DNS. I need my DNS (which I already have working) to function in a more general purpose way.

To ease installation of clients and enable failover, and other functionallity, you will need to configure the external DNS. When the server command finished, it created a file containing the DNS records that need to be added. If you paid attention, you may have seen a message similar to:

Please add records in this file to your DNS system: /tmp/ipa.system.records.<random>.db

The file listed contains the records. If you missed that, no need to worry. You can output the rules to the console with the following command (from the FreeIPA Documentation:

ipa dns-update-system-records --dry-run

Note: before running any ipa commands, you'll need to have a kerberos ticket for an IPA admin. If you log into a system as an IPA admin you will automatically be issued a ticket for that user. If you're logged in as a normal (non-IPA) user, or an IPA user who isn't in the admin group, you'll need to first get a ticket for a qualified user using the kinit command: kinit admin. You will need to enter password for that "user", but then you can run the ipa commands.

The records should be a number of TXT and SRV records, and an A record (and probably an AAAA record as well). Add all these records to your external DNS system and clients will auto-detect the information they need when configuring them.

It may be easier to just use FreeIPA integrated DNS, but note that doing so requires a minimum of one additional IPA master on the public internet, and means you are responsible for maintaining those servers. I prefer to let my service provider let me use their 5 servers, and it saves me a couple of dollars per month.

Replication server

The basic procedure is:

  1. install the server and client software (just like setting up the first server)
  2. run ipa-client-install as normal
  3. in the IPA UI add the new client to the ipaservers group
  4. log into the host to be a replica, and run ipa-replica-install.

See official documentation.

Prevent non-admins from logging into servers

This is desirable prevent possible security incidents. By default, any user can log into any machine. Host groups are the easiest way to do this.

TBD: process

Allow admins to sudo

In the web UI you can grant sudo. This can be done on a per-host basis (i.e.- user is admin on one specific workstation):

Now ssh into client3 as test2, and that user will be able to run sudo.

Alternatively, you can allow specific users or groups to sudo on any host. FreeIPA also allows creating host groups which can be effected. There are a large number of capabilities in the policy section of IPA.

There may be a delay before new sudo rules take effect. Restarting the sssd service may also be necessary. The quickest reliable way to restart sssd (sudo systemctl restart sssd) then logout and re-login as the desired user.

Set default shell

IPA sets users' default shell to /bin/sh. To set it to something more friendly you can modify the Login shell attribute of a particular user, or you can set the default shell from the command line using ipa config-mod --defaultshell=/bin/bash.

To instead use the GUI, navigate to IPA Server->Configuration and modify the Default shell value.

I recommend not setting the default shell to something you aren't sure will be present on all your systems.

List (Enumerate) IPA users on clients

When you first login to an IPA client, you may find that running getent passwd does not list your IPA users.

To fix, edit the /etc/sssd/sssd.conf file as root. At the top of the file should be a section [domain/mysite.test]. under this heading add the line enumerate = true.

The file should now look like:

[domain/mysite.test]
enumerate = true

Restart sssd (sudo systemctl restart sssd) and getent will return network users as well.

Greeter user list

Showing network users in getent may not automatically make them show in your greeter (the system login scren). Or, you may want to hide your local users in the greeter on workstations. Accomplishing this will depend on the greeter you use. There should be an option to add a miminum and maximum user ID in the greeter configuration, but IPA uses really large user ID values. For SDDM at least, this means the user IDs you care about are actually larger than the values allowed in the GUI for configuring SDDM3. SDDM itself actually will support those users, and can be added by modifying the configuration file /etc/sddm.conf. Add MaximumUid and MinimumUid entries to the [Users] section. I set the values to 12345000014 and 1300000000 respectively.

This will hide local system users, which may or may not be what you want. If you set the MinimumUid low enough to show system users, you'll want to investigate the HideShells and HideUsers configuration variables.

#... possibly other settings

[Users]
MinimumUid=1234500001
MaximumUid=1300000000

Location dependent servers and clients

It is possible to have a public server, and a local replica. Ultimately this is my goal, but I haven't done it yet. The FreeIPA documentation on the topic is here: https://www.freeipa.org/page/Howto/IPA_locations.

Troubleshooting SSH SSO Problems

This error manifests as getting a password prompt when using SSH to connect from one IPA client to another.

For any SSH problems, make sure you have the following in /etc/ssh/sshd_config:

PubkeyAuthentication yes
KerberosAuthentication no
GSSAPIAuthentication yes
UsePAM yes
ChallengeResponseAuthentication yes
AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
AuthorizedKeysCommandUser nobody

If you enable SSH server debugging, you may see an error such as:

Unspecified GSS failure.  Minor code may provide more information\nNo key table entry found matching host/client1

In this case, make sure the hostname command outputs the FQDN. If not, set it with sudo hostnamectl set-hostname client1.mysite.test. Ensure that domainname outputs the domain portion (mysite.test), and hostname -s outputs only the host name.

Web Interface improvements

There is a user interface on the FreeIPA server which users can log into to change passwords, and where administrators can do all kinds of management tasks. By default it uses a self-signed certificate and users log in with their username and password.

See the Certificate Fun page for enabling trusted certificates and enabling kerberos login.


  1. This is old, but I'm not getting anywhere near the limits they list: https://docs.fedoraproject.org/en-US/Fedora/18/html/FreeIPA_Guide/installing-ipa.html#Preparing_for_an_IPA_Installation-Hardware_Requirements 

  2. This differs from the guide referenced above, because I don't like to include passwords on the command line ever. 

  3. As of KDE install in Kubuntu 20.04 with plasma-desktop 5.18.5 

  4. Not my real user range.