Centrally manage SELinux user mapping with (Free)IPA

SELinux LogoSELinux allows to confine users with SELinux user mappings. This article covers some basics about the confinement of users and shows how to manage them in central way with the help of (Free)IPA. It will greatly enhance your systems security.

SELinux is available and enabled on all Red Hat based distributions such as RHEL, CentOS and Fedora. for the basics please have a look at article. Before proceeding with the examples in this article:

  • ensure your system is running in enforcing mode otherwise you will experience strange results with the sysadm_r role when logging in.
  • temporary enable root login via SSH or have access to the systems console, used for debugging

Helpful packages

It is recommended to install a few packages to manage SELinux.

# yum -y install libselinux-python policycoreutils-python policycoreutils-devel policycoreutils-newrole setools-console

The basics

By default, every user is mapped to the SELinux user unconfined_u as you can proof with semanage login -l

rhel7:~# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

Overview about standard SELinux users and roles

The following SELinux users are predefined with the standard policy:

User Role Domain su/sudo Exec * X11 Networking
sysadm_u sysadm_r sysadm_t sudo, su yes yes yes
staff_u staff_r staff_t sudo yes yes yes
user_u user_r user_t yes yes yes
guest_u guest_r guest_t no no no
xguest_u xguest_r xguest_t no yes yes, http(s) only

* Execution of scripts and binaries in /home and /tmp

Role transition

This is important to understand. Which users or which roles are allowed to switch the role?

seinfo is your friend…

seinfo -xu
[..some output ommited...]
   staff_u
      default level: s0
      range: s0 - s0:c0.c1023
      roles:
         object_r
         staff_r
         sysadm_r
         system_r
         unconfined_r
[..some output ommited...]

This means that a Linux user mapped to staff_u can switch the role to sysadm_r, but not to guest_r.

Allowed target types

How do I figure out what is allowed to do for a certain role? Again, seinfo is your friend:

root@server ~]# seinfo -rsysadm_r -x      
   sysadm_r
      Dominated Roles:
         sysadm_r
      Types:
         aide_t
         alsa_home_t
         amanda_recover_t
         antivirus_home_t
         httpd_helper_t
         auth_home_t
         chkpwd_t
         pam_timestamp_t
         updpwd_t
         utempter_t
         bacula_admin_t
         ndc_t
         bootloader_t
[.. lots of output ommited ..]

Note: There must be no space between r and sysadm_r, the parameter is really rsysadm_r.

There you can see every target type the role sysadm_r is allowed to access, it is a lot. Of course this looks different for each role, have a look at them.

Implementation with IPA

Change the default SELinux usermap order

In this example we will map the sysadmins group to the sysadm_r role. The SELinux user for this role, sysadm_u is not defined in the IPA configuration. Lets change that.

ipa config-mod --ipaselinuxusermaporder='guest_u:s0$xguest_u:s0$user_u:s0$staff_u:s0-s0:c0.c1023$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023'

To ensure non-mapped users are confined, create a default mapping:

ipa config-mod --ipaselinuxusermapdefault='user_u:s0'

Create users, groups and add members

echo Welcome123|ipa user-add --first=Luc --last="de Louw" --password luc
echo Welcome123|ipa user-add --first=Luc --last="de Louw" --password ldelouw
echo Welcome123|ipa user-add --first=Joe --last=Doe --password jdoe
echo Welcome123|ipa user-add --first=Guest --last=User --password guest

ipa group-add sysadmins
ipa group-add-member sysadmins --users=luc

ipa group-add staff
ipa group-add-member staff --users=ldelouw

ipa group-add users
ipa group-add-member users --users=jdoe

ipa group-add guests
ipa group-add-member guests --users=guest

The password of the users will immediately expire and need to be changed on the first login. Please log in with every user and change the password before proceeding.

Adding some HBAC rules

In this example I’ll make user of a host group testservers. Please create that and add one or more of your hosts to that group first.

ipa hbacrule-add sysadmins-allhosts --hostcat=all --servicecat=all
ipa hbacrule-add-user --groups=sysadmins sysadmins-allhosts

ipa hbacrule-add staff-testservers --servicecat=all
ipa hbacrule-add-user --groups=staff staff-testservers
ipa hbacrule-add-host --hostgroup=testservers staff-testservers

ipa hbacrule-add users-testservers --servicecat=all
ipa hbacrule-add-user --groups=users users-testservers
ipa hbacrule-add-host --hostgroup=testservers users-testservers

ipa hbacrule-add guests-testservers --servicecat=all
ipa hbacrule-add-user --groups=guests guests-testservers
ipa hbacrule-add-host --hostgroup=testservers guests-testservers

This creates four HBAC rules. The first allows everyone in the sysadmins group to log in to all hosts, the others restrict its users to the hostgroup testservers.

Create SELinux maps

ipa selinuxusermap-add --selinuxuser='sysadm_u:s0-s0:c0.c1023' --hbacrule=sysadmins-allhosts sysadmins
ipa selinuxusermap-add --selinuxuser='staff_u:s0-s0:c0.c1023' --hbacrule=staff-testservers staff
ipa selinuxusermap-add --selinuxuser='user_u:s0' --hbacrule=users-testservers users
ipa selinuxusermap-add --selinuxuser='guest_u:s0' --hbacrule=guests-testservers guests

In this example, I use HBAC rules for the mapping. This is recommended practice because the HBAC rules are the central point to manage user access rules.

However, you also can assign users (and groups of them), hosts (and groups of them) to a SELinux map, i.e. with ipa selinuxusermap-add-host –hostgroups=testservers semapname and ipa selinuxusermap-add-user –groups=users semapname. For single user or hosts it is the parameter –hosts or –users.

Sudo

Note that roles like user_r and lower can not do sudo to other users (incl. root). guest_r does even not allow to use the network. Please have a look at the table above.

Lets create the sudoers rules for the groups staff and sysadmins.

ipa sudocmd-add "/bin/bash"
ipa sudorule-add --hostcat=all --runasusercat=all --runasgroupcat=all sysadmins-can-sudo-i
ipa sudorule-add-user --group=sysadmins sysadmins-can-sudo-i
ipa sudorule-add-allow-command --sudocmds=/bin/bash sysadmins-can-sudo-i
ipa sudorule-add-option --sudooption='!authenticate' sysadmins-can-sudo-i
ipa sudorule-add-option --sudooption='type=sysadm_t' sysadmins-can-sudo-i
ipa sudorule-add-option --sudooption='role=sysadm_r' sysadmins-can-sudo-i

ipa sudorule-add --runasusercat=all --runasgroupcat=all staff-can-sudo-i
ipa sudorule-add-user --group=staff staff-can-sudo-i
ipa sudorule-add-host --hostgroups=testservers staff-can-sudo-i
ipa sudorule-add-allow-command --sudocmds=/bin/bash staff-can-sudo-i
ipa sudorule-add-option --sudooption='!authenticate' staff-can-sudo-i
ipa sudorule-add-option --sudooption='type=sysadm_t' staff-can-sudo-i
ipa sudorule-add-option --sudooption='role=sysadm_r' staff-can-sudo-i

Switching roles

Switching roles can be done in three different ways:

  • Using the newrole command when already logged in with the default role
  • Provide the role when logging in with ssh
  • Using sudo, please see above

If you are already logged in, you can switch the role with the newrole command.

[ldelouw@server ~]$ newrole -r sysadm_r

The output if id -Z shows that you are now mapped to the same SELinux user staff_u but in the role of sysadm_r

staff_u:sysadm_r:sysadm_t:s0-s0:c0.c1023

Most of the time you will know what you gonna do on a target system. You can provide the role as a parameter to ssh:

[luc@client ~]$ ssh ldelouw/unconfined_r@server.example.com
ldelouw@server.example.com's password:
Last login: Sun Jun 24 11:29:54 2018 from client.example.com
Kickstarted on 2018-01-16
[ldelouw@server ~]$ id -Z
staff_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[ldelouw@server ~]$

This example allows you to use the unconfined_r role instead of the default defined in the SELinux user map (staff_r).

Be aware that this is by default not possible with the sysadm_r role, as logging in with that role is turned off.

Troubleshooting

sysadm_u can not log in

By default a user with the role of sysadm_r is not allowed to log in via ssh, only console logins are allowed.

To change this for testing, set the following boolean:

# setsebool ssh_sysadm_login on

Use the -P parameter to make the change persistent:

# setsebool -P ssh_sysadm_login on

User mapped to sysadm_u can login but is not supposed to do so

If you can log in into a host as a user mapped to sysadm_u, and id shows the following obviously wrong SELinux user role and domain:

[luc@server ~]$ id
uid=594600001(luc) gid=594600001(luc) groups=594600001(luc),594600005(sysadmins)  context=system_u:system_r:unconfined_t:s0-s0:c0.c1023

Then probably the ssh_sysadm_login boolean is set to false and your system runs in permissive mode.

Sudo to root fails with permission denied to .bash_profile

[ldelouw@server ~]$ sudo -i
-bash: /root/.bash_profile: Permission denied

You dont have the role sysadm_r, this is why access to files owned by root are denied. Please have a look at the sudo configuration as described in this article.

Sudo trows errors

[ldelouw@server ~]$ sudo -i
sudo: sudoRole sudo_root_admins: unknown defaults entry "TYPE"
sudo: sudoRole sudo_root_admins: unknown defaults entry "ROLE"
-bash: /root/.bash_profile: Permission denied

There is a difference between using traditional /etc/sudoers files and IPA. The man sudoers reads SELinux_Spec ::= (‘ROLE=role’ | ‘TYPE=type’) which is correct for file based configuration but wrong for IPA.

All sudoers options defined in IPA must be lowercase to get recognized in the correct way.

Changes in IPA are not working on the clients

This is can be a caching problem.

sss_cache -E

This wipes everything in the sssd cache. Sometimes sudoers will still not be working, it is safe to remove the whole SSSD database and restart sssd.

rm -rf /var/lib/sss/db/*
systemctl restart sssd

Read further

There are tons of documentation available. Lets list the most notable.

Getting help

If you run into problems, there are different sources for getting help

Manually enroll SLES12 systems to Redhat IdM

RHEL and Ubuntu systems leverage the ipa-client software to easily enrolled them to a Redhat IdM system. Unfortunately SLES12 lacks the required packages. Nevertheless, SLES12 systems can be enrolled manually. This article is about how to achieve this.

Why using IPA for SLES systems?

Most organizations are not pure RHEL or pure SLES shops, the reality shows a heterogeneous mix of Linux distributions in corporate data centers. It makes sense to use the same authentication and authorization system to manage them.

Disclaimer

All the “special” behavior of SLES12 is based on SP2 without any patches, I do not have a SLES subscription for this test. Some of this behavior may have been fixed.

Before touching any system, please have a valid backup ready, just in case.

Preparation work

IPA is picky when it comes to host names, they must be fully qualified. Unfortunately, the default for SLES systems is to use the short host name, this must be changed first, otherwise the functionality will be limited (besides that short host names are a potential security thread).

sles12sp2:~ # hostnamectl set-hostname $(hostname -f)

In case hostname -f does not work, check if the fully qualified host name is set to the primary IP address in /etc/hosts and try again.

Unfortunately this will not survive a reboot, a dirty hack is needed. If someone has a better idea, please let me know.

sles12sp2:~ # echo 'echo $(hostname -f) > /proc/sys/kernel/hostname' >> /etc/init.d/boot.local

Please ensure that system time is correct as Kerberos is picky about having the time in sync with the KDC.

Install the required software

SLES12 comes with the basic IPA libraries and the sssd plugin needed. It just lacks the ipa-client.

sles12sp2:~ # zypper install sssd-ipa sssd-tools sssd-krb5 krb5-client sssd-ad

All dependencies will be installed automatically.

Enable sssd start at boot time, as it is not by default

sles12sp2:~ # systemctl enable sssd

Remove nscd, caching will be done by sssd.

sles12sp2:~ # zypper remove nscd

Log out and in again to get /usr/lib/mit/ in the PATH environment.

Adding the host to IPA

[root@ipa1 ~]# ipa host-add --ip-address=192.168.100.115 sles12sp2.example.com 
----------------------------------
Added host "sles12sp2.example.com"
----------------------------------
  Host name: sles12sp2.example.com
  Principal name: host/sles12sp2.example.com@EXAMPLE.COM
  Principal alias: host/sles12sp2.example.com@EXAMPLE.COM
  Password: False
  Keytab: False
  Managed by: sles12sp2.example.com
[root@ipa1 ~]# 

Generating the Kerberos Keytab and copy it to the destination host

[root@ipa1 ~]# ipa-getkeytab -s ipa1.example.com -p host/sles12sp2.example.com@EXAMPLE.COM -k sles12sp2.example.com.keytab
Keytab successfully retrieved and stored in: sles12sp2.example.com.keytab
[root@ipa1 ~]#  

Copy the Keytab to the system:

[root@ipa1 ~]# scp sles12sp2.example.com.keytab sles12sp2.example.com:/etc/krb5.keytab

Ensure ownership and permissions are set correctly

sles12sp2:~ # chmod 0600 /etc/krb5.keytab
sles12sp2:~ # chown root:root /etc/krb5.keytab

Configuration

Usually Yast is a quite nice tool to configure a SLES system. Unfortunately Yast is very confusing when it comes to SSSD configuration. Lets do it manually.

Get the IPA CA certificate

sles12sp2:~ # mkdir /etc/ipa
sles12sp2:~ # wget http://ipa1.example.com/ipa/config/ca.crt -O /etc/ipa/ca.crt

/etc/krb5.conf

sles12sp2:~ # cat > /etc/krb5.conf << EOF

[plugins]
 localauth = {
  module = sssd:/usr/lib64/sssd/modules/sssd_krb5_localauth_plugin.so
 }


[libdefaults]
  default_realm = EXAMPLE.COM
  dns_lookup_realm = true
  dns_lookup_kdc = true
  rdns = false
  dns_canonicalize_hostname = false
  ticket_lifetime = 24h
  forwardable = true
  udp_preference_limit = 0
  canonicalize = true
  default_ccache_name = KEYRING:persistent:%{uid}

[realms]
  EXAMPLE.COM = {
    pkinit_anchors = FILE:/etc/ipa/ca.crt
    pkinit_pool = FILE:/etc/ipa/ipa.crt

  }

[domain_realm]
  .example.com = EXAMPLE.COM
  example.com = EXAMPLE.COM
  $(hostname) = EXAMPLE.COM
EOF

/etc/sssd/sssd.conf/

sles12sp2:~ # cat > /etc/sssd/sssd.conf << EOF
[domain/example.com]

cache_credentials = True
krb5_store_password_if_offline = True
ipa_domain = example.com
id_provider = ipa
auth_provider = ipa
access_provider = ipa
ipa_hostname = $(hostname)
chpass_provider = ipa
ipa_server = _srv_, ipa1.example.com
ldap_tls_cacert = /etc/ipa/ca.crt
[sssd]
services = nss, sudo, pam, ssh

domains = example.com
[nss]
homedir_substring = /home

[pam]

[sudo]

[autofs]

[ssh]

[ifp]

[secrets]
EOF

Ensure ownership and permissions are correct:

sles12sp2:~ # chown root:root /etc/sssd/sssd.conf
sles12sp2:~ # chmod 600 /etc/sssd/sssd.conf

Restart sssd

sles12sp2:~ # systemctl restart sssd

nsswitch.conf and PAM

Enable sssd

sles12sp2:~ # pam-config --add --sss

Enable automatic homedir creation on first login

sles12sp2:~ # pam-config --add --mkhomedir --mkhomedir-umask=0077

Change nsswitch.conf to use sssd

sles12sp2:~ # sed -i 's/passwd: compat/passwd: compat sss/g' /etc/nsswitch.conf
sles12sp2:~ # sed -i 's/group:  compat/group: compat sss/g' /etc/nsswitch.conf
sles12sp2:~ # echo "sudoers: sss" >> /etc/nsswitch.conf

Configure sshd and ssh to use GSSAPI for authentication

sles12sp2:~ # cat >> /etc/ssh/sshd_config << EOF
GSSAPIAuthentication yes
EOF
sles12sp2:~ # cat >> /etc/ssh/ssh_config << EOF
GSSAPIAuthentication yes
EOF

Reboot to ensure its all working and caches are clean

sles12sp2:~ # reboot

Further readings

Conclusion

Using IPA for authenticating users on SLES systems works, but it is not as comfortable as with RHEL, Fedora and Ubuntu. Suse should include the ipa-client in its distribution.

Enrolling SLES systems is not easy to automate without the ipa-client, probably Ansible could help here.

The functionality is almost the same to that for RHEL7, HBAC (host based access control) is working as expected, the same applies to centralized sudoers. Unfortunately the sssd-tools are quite outdated, sss_cache -E will not delete the sudoers cache. Suse should rebase sssd to the latest upstream version. Suse customers can file a request for enhancement in the SUSE Customer Center 😉

Have fun 🙂

Configure SSSD to work on IPv6-only Hosts

SSSD is used for the client side of IPA and other centralized Identity Management Services. Unfortunately it does not behave as it should. The default is to look up first IPv4 addresses and if that fails IPv6 should be used. Well, if IPv4 fails, the whole request fails and you got weird error messages when joining an IPA domain.

As the pool for IPv4 addresses is depleted, IPv6 is getting more and more important. Thus, IPv6-only hosts are on the rise.

Here is an example error message from the IPA client.

[root@ipv6host ~]# ipa-client-install
[output ommited] 
SSSD enabled
Configured /etc/openldap/ldap.conf
Unable to find 'admin' user with 'getent passwd admin@example.com'!
Unable to reliably detect configuration. Check NSS setup manually.
[output ommited]

The host itself gets properly joined to the IPA domain and authentication works with Kerberos but you can not log in because SSSD fails.

Workaround

Configure SSSD to only use IPv6. This is done in /etc/sssd/sssd.conf

[domain/example.com]
lookup_family_order = ipv6_only
cache_credentials = True
krb5_store_password_if_offline = True
ipa_domain = example.com
id_provider = ipa
auth_provider = ipa
access_provider = ipa
ipa_hostname = ipv6host.example.com
chpass_provider = ipa
ipa_server = _srv_, ipa1.example.com
ldap_tls_cacert = /etc/ipa/ca.crt
[sssd]
services = nss, sudo, pam, ssh

domains = example.com
[nss]
homedir_substring = /home

[pam]

[sudo]

[autofs]

[ssh]

[pac]

[ifp]

Solution

At the moment there is no solution yet (just the workaround described), but its addressed at the SSSD project team, as you can see in https://pagure.io/SSSD/sssd/issue/2128 and https://bugzilla.redhat.com/show_bug.cgi?id=1021435

Happy IPv6-ing 🙂

Using IPA for user authentication and RBAC in Ansible Tower

Ansible is a great orchestration tool. Ansible Tower is the enterprise version of Ansible adding features like a WebUI, RestAPI and others.

Tower has also some features like role-based access control allowing to control which user is allowed to run which playbooks on which infrastructure, servers and so on.

In larger environments, this is not done manually but using a centrally managed Identity Management System such as Redhat IdM with IPA or Microsoft Active Directory.

This post covers how to set up Ansible Tower to leverage the use of IPA features in a simple way. Its going to use one Organizaion.

License needed

Unfortunately Tower is not yet an open source with an upstream project, this will still take some time, please be patient. Please ensure you got a license for the “standard” or “premium” edition of Ansible Tower since this is a requirement for LDAP integration, please see https://www.ansible.com/tower-editions.

Preparation in IPA

Create a bind user for Ansible

ldapmodify -x -D 'cn=Directory Manager' -W <<EOF
dn: uid=ansible,cn=sysaccounts,cn=etc,dc=example,dc=com
changetype: add
objectclass: account
objectclass: simplesecurityobject
uid: system
userPassword: supersecret
passwordExpirationTime: 20380119031407Z
nsIdleTimeout: 0
EOF

Lets create the group in IPA

ipa2:~# ipa group-add --nonposix ansible
ipa2:~# ipa group-add-member ansible --users=jdoe

The default behavior is that every user in IPA/LDAP can log in to Tower, if not assigned to a Team, the user has no privileges, however, this is not something you want. Therefore the group “ansible” is used to restrict logins to users of that group.

LDAP tree

Non-Kerberized Web application usually query the compat LDAP tree for authentication. However, the compat tree does not expose all the required LDAP attributes (yet) I.e sn or mail are missing. There is a request for enhancement pending, see also https://bugzilla.redhat.com/show_bug.cgi?id=1362272.

We are going to customize the LDAP queries for the “normal” LDAP tree in IPA. Unfortunately this means it only works with with users created in IPA. If you are using a cross domain trust with Microsoft Active directory, the AD users are not able to log in in Ansible Tower. In such a case, set up Ansible Tower to query AD directly.

Configuring Ansible Tower

Edit the file /etc/tower/conf.d/ldap.py and add/edit the following lines to be able to look up users and groups:

AUTH_LDAP_SERVER_URI = 'ldap://ipa2.example.com:389'
AUTH_LDAP_BIND_DN = 'uid=ansible,cn=sysaccounts,cn=etc,dc=example,dc=com'
AUTH_LDAP_BIND_PASSWORD = 'supersecret'
AUTH_LDAP_USER_SEARCH = LDAPSearch(
    'cn=users,cn=accounts,dc=example,dc=com',
    ldap.SCOPE_SUBTREE,
    '(uid=%(user)s)',
AUTH_LDAP_USER_ATTR_MAP = {
    'first_name': 'givenName',
    'last_name': 'sn',
    'email': 'mail',
}
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
    'cn=groups,cn=accounts,dc=example,dc=com',    # Base DN
    ldap.SCOPE_SUBTREE,
    '(objectClass=group)',
)

If you want to prevent any user to login to Ansible Tower, you can restrict it to one group, in this example a user must be member of the group ansible to be able to log in. This is strongly recommended

AUTH_LDAP_REQUIRE_GROUP = 'cn=ansible,cn=groups,cn=accounts,dc=example,dc=com'

You can also define a ipa group which gets automatically the superuser role assigned.

AUTH_LDAP_USER_FLAGS_BY_GROUP = {
    'is_superuser': 'cn=admins,cn=groups,cn=accounts,dc=example,dc=com',
}

Next will be how to auto assign users to teams, this will be done later in a separate post.

Have fun 🙂