Sep 052012
 

This post will guide you through a simple setup to confine users defined in LDAP using Security-Enhanced Linux. SELinux on RHEL6/Debian Wheezy comes with confined user roles which allows quick and easy setup for restricting users who are given access to your machines.

# semanage login -l

Login Name                SELinux User              MLS/MCS Range            

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

Looking at these default roles, you can see that not a whole lot is restricted on a cleanly-installed machine. Unfortunately, this means that any users you add will be logging in with an unconfined_r role which essentially delegates access controls to systems other than SELinux.

Depending on the role that this machine will play in your organization, you probably want to set the default to something other than unconfined_u. The various default SELinux users can be found in /etc/selinux/targeted/contexts/users/.

# ls -l /etc/selinux/targeted/contexts/users/
total 24
-rw-r--r--. 1 root root 253 Jun 18 09:01 guest_u
-rw-r--r--. 1 root root 389 Jun 18 09:01 root
-rw-r--r--. 1 root root 514 Jun 18 09:01 staff_u
-rw-r--r--. 1 root root 578 Jun 18 09:01 unconfined_u
-rw-r--r--. 1 root root 353 Jun 18 09:01 user_u
-rw-r--r--. 1 root root 307 Jun 18 09:01 xguest_u

For our purposes, we’ll go ahead and set the __default__ selinux user to user_u. To do this, you simply run semanage login -m -s user_u -r s0 __default__. You can see the change below.

# semanage login -l

Login Name                SELinux User              MLS/MCS Range            

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

Now, any user who does not have an explicit entry in the output above will be confined to user_u. Great! So, what about potential privileged users who may need sudo access? The user_u profile is inadequate for these types of users as it’s very restrictive. A more appropriate role would be something like staff_u. My user account only exists in LDAP.

# grep -c solj /etc/passwd
0
# getent passwd solj
solj:*:1000:2000:Sol Jerome:/home/solj:/bin/bash

This is okay. The first thing to do is add a login mapping for the user.

semanage login -a -s staff_u solj

Now, logging in as that user should give you the proper security context.

$ id
uid=1000(solj) gid=2000(sysadmin) groups=2000(sysadmin) context=staff_u:staff_r:staff_t:s0-s0:c0.c1023

Okay, now the last part is to give this person sudo access. My sudo configuration resides in LDAP. Therefore, I will need to add 2 new sudoOptions in order to allow my user to transition properly to sysadm_r:sysadm_t. The following is an example of the type of errors you will see if these options are not present.

$ sudo -i
-bash: /root/.bash_profile: Permission denied
-bash-4.1#

The correct sudoers entry should look like the following.

cn=solj,ou=sudoers,dc=example,dc=com
cn: solj
objectClass: sudoRole
objectClass: top
sudoHost: ALL
sudoRunAs: ALL
sudoUser: solj
sudoCommand: ALL
sudoOption: role=sysadm_r
sudoOption: type=sysadm_t

And now when I run sudo, I am able to transition no problem.

$ sudo -i
# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=staff_u:sysadm_r:sysadm_t:s0-s0:c0.c1023
 Posted by at 18:03
May 032012
 

This post will cover post-installation steps necessary to go from a completely unmanaged machine to a machine that is setup to be an LDAP server with a basic DIT. This will also setup phpldapadmin for web-based administration of your LDAP directory.

Note: I use nginx here simply because I find it easier to deal with. There’s no requirement for it and you may find it easier to use apache.

The post-install script used to setup the LDAP server is below. The reason this is used is because there are a lot of one time things that happen during the installation of an LDAP server and I have not yet been able to represent some of these events in bcfg2. The script below depends on some files that are hosted on another web server. I will provide the necessary files needed below.

The custom php packages are available from http://blog.famillecollet.com/pages/Config-en. The reason for using these packages is that php-fpm is not available from the stock RHEL repositories or from EPEL. Since I am already familiar with php-fpm and I prefer to use it, I decided to simply download only the necessary packages rather than use the entire repository.

#!/bin/bash

# ssl settings
WEBCERT="/etc/pki/tls/certs/phpldapadmin.crt"
WEBKEY="/etc/pki/tls/private/phpldapadmin.key"
SLAPDCERT="/etc/openldap/cacerts/slapd.crt"
SLAPDMASTERCERT="/etc/openldap/cacerts/slapd-master.crt"
SLAPDKEY="/etc/pki/tls/private/slapd.key"
SSLSUBJ="/C=Country Code/ST=Some State/L=City/O=Organization Name/OU=Organizational Unit Name/CN=${HOSTNAME}"

# misc settings
LDAPDIR="/root/ldap-setup"
HTTPDIR="http://web.server/ldap"
LDIFDIR="${HTTPDIR}/ldif"
RPMS="${HTTPDIR}/rpms/php-5.3.8-5.el6.remi.x86_64.rpm
${HTTPDIR}/rpms/php-cli-5.3.8-5.el6.remi.x86_64.rpm
${HTTPDIR}/rpms/php-common-5.3.8-5.el6.remi.x86_64.rpm
${HTTPDIR}/rpms/php-fpm-5.3.8-5.el6.remi.x86_64.rpm
${HTTPDIR}/rpms/php-ldap-5.3.8-5.el6.remi.x86_64.rpm
openldap-clients
openldap-servers
autofs"

PASSWD="changeme"
SLAPPASSWD=""
BCFG2PASSWD=""

selinux-disable()
{
    #FIXME: remove when bcfg2 selinux policy works properly
    setenforce 0
}

selinux-enable()
{
    #FIXME: remove when bcfg2 selinux policy works properly
    setenforce 1
}

inst-packages()
{
    echo -n "Installing custom php packages for phpldapadmin..."
    yum -y --nogpgcheck install ${RPMS} >/dev/null
    # FIXME: update the kernel (kernel panics when not done here)
    yum -y update kernel >/dev/null
    echo "done"
}

gen-ssl-certs()
{
    /usr/bin/openssl req -batch -new -x509 -nodes \
        -subj "${SSLSUBJ}" \
        -out ${WEBCERT} \
        -keyout ${WEBKEY} -days 3600 >/dev/null
    /usr/bin/openssl req -batch -new -x509 -nodes \
        -subj "${SSLSUBJ}" \
        -out ${SLAPDCERT} \
        -keyout ${SLAPDKEY} -days 3600 >/dev/null

    cacertdir_rehash /etc/openldap/cacerts
}

get-passwds()
{
    # setup ldap admin password
    echo -n "Please enter a new ldap admin password: "
    read -s PASSWD
    # get bcfg2 password
    echo -n "Please enter the bcfg2 password (can be found in /etc/bcfg2.conf on an existing client): "
    read -s BCFG2PASSWD
    echo
}

gen-slappasswd()
{
    if [ -x /usr/sbin/slappasswd ]
    then
        SLAPPASSWD=$(/usr/sbin/slappasswd -s ${PASSWD})
    else
        echo "Failed to find slappasswd. Aborting."
        exit 1
    fi
}

setup-ldap()
{
    /usr/bin/curl -o ${LDAPDIR}/fix-admin-account.ldif ${LDIFDIR}/fix-admin-account.ldif
    /usr/bin/curl -o ${LDAPDIR}/new-ldap-setup.ldif ${LDIFDIR}/new-ldap-setup.ldif
    /usr/bin/curl -o ${LDAPDIR}/base.ldif ${LDIFDIR}/base.ldif
    sed -i "s|PWREPLACE|${SLAPPASSWD}|" ${LDAPDIR}/fix-admin-account.ldif ${LDAPDIR}/new-ldap-setup.ldif
    # this seems wrong. if someone knows how to do this better, please inform me.
    echo "olcRootPW: ${SLAPPASSWD}" >> /etc/openldap/slapd.d/cn=config/olcDatabase={0}config.ldif
    /bin/cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
    chown -R ldap. /var/lib/ldap
    /sbin/service slapd start && sleep 1 # FIXME: how do you do this properly?
    ldapadd -w ${PASSWD} -x -D "cn=config" -f ${LDAPDIR}/fix-admin-account.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/new-ldap-setup.ldif
    ldapadd -w ${PASSWD} -x -D "cn=Manager,dc=uh,dc=edu" -f ${LDAPDIR}/base.ldif
}

add-sudo()
{
    /usr/bin/curl -o ${LDAPDIR}/sudo-index.ldif ${LDIFDIR}/sudo-index.ldif
    cp /usr/share/doc/$(rpm -q sudo --qf "%{NAME}"-"%{VERSION}")/schema.OpenLDAP /etc/openldap/schema/sudo.schema
    restorecon -F -R -v /etc/openldap/schema
    mkdir ${LDAPDIR}/sudo-ldap
    echo "include /etc/openldap/schema/sudo.schema" > ${LDAPDIR}/sudo-ldap/sudoschema.conf
    slapcat -f ${LDAPDIR}/sudo-ldap/sudoschema.conf -F /tmp \
            -n0 -s "cn={0}sudo,cn=schema,cn=config" > ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif
    sed -i 's/{0}sudo/sudo/' ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif
    head -n-8 ${LDAPDIR}/sudo-ldap/sudo-tmp.ldif > ${LDAPDIR}/sudo-ldap/sudo.ldif
    echo -e "\n$(cat ${LDAPDIR}/sudo-index.ldif)" >> ${LDAPDIR}/sudo-ldap/sudo.ldif # add in our sudo index
    rm ${LDAPDIR}/sudo-index.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/sudo-ldap/sudo.ldif
}

add-autofs()
{
    cp /usr/share/doc/$(rpm -q autofs --qf "%{NAME}"-"%{VERSION}")/autofs.schema /etc/openldap/schema/autofs.schema
    restorecon -F -R -v /etc/openldap/schema
    mkdir ${LDAPDIR}/autofs
    echo "include /etc/openldap/schema/core.schema" > ${LDAPDIR/autofs/autofs.conf
    echo "include /etc/openldap/schema/cosine.schema" >> ${LDAPDIR/autofs/autofs.conf
    echo "include /etc/openldap/schema/autofs.schema" >> ${LDAPDIR/autofs/autofs.conf
    slapcat -f ${LDAPDIR}/autofs/autofs.conf -F /tmp \
            -n0 -s "cn={2}autofs,cn=schema,cn=config" > ${LDAPDIR}/autofs/autofs-tmp.ldif
    sed -i 's/{2}autofs/autofs/' ${LDAPDIR}/autofs/autofs-tmp.ldif
    head -n-8 ${LDAPDIR}/autofs/autofs-tmp.ldif > ${LDAPDIR}/autofs/autofs.ldif
    ldapadd -w ${PASSWD} -x -D "cn=admin,cn=config" -f ${LDAPDIR}/autofs/autofs.ldif
}

import-db()
{
    while true; do
        echo -n "Is this machine a master or a slave? [m/s] "
        read status
        case $status in
            m*|M*)
                /usr/bin/curl -o ${LDAPDIR}/olcaccess.ldif ${LDIFDIR}/olcaccess.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncprov-module.ldif ${LDIFDIR}/syncprov-module.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncprov.ldif ${LDIFDIR}/syncprov.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/olcaccess.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncprov-module.ldif
                ldapadd -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncprov.ldif
                break
            ;;
            s*|S*)
                # grab master SSL certificate
                /usr/bin/curl -o ${SLAPDMASTERCERT} ${HTTPDIR}/slapd-master.crt
                cacertdir_rehash /etc/openldap/cacerts

                /usr/bin/curl -o ${LDAPDIR}/olcaccess-slave.ldif ${LDIFDIR}/olcaccess-slave.ldif
                /usr/bin/curl -o ${LDAPDIR}/syncrepl.ldif ${LDIFDIR}/syncrepl.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/olcaccess-slave.ldif
                ldapmodify -w ${PASSWD} -D "cn=admin,cn=config" -f ${LDAPDIR}/syncrepl.ldif
                break
            ;;
            *)
                echo "Invalid response."
            ;;
        esac
    done
}

run-bcfg2()
{
    /usr/sbin/bcfg2 -vqe -S https://bcfg2.server:6789 -x ${BCFG2PASSWD} --ca-cert=/etc/bcfg2.ca -r packages
    /usr/sbin/bcfg2 -vqer packages
}

selinux-disable
mkdir -p ${LDAPDIR}
get-passwds
inst-packages
gen-ssl-certs
gen-slappasswd
setup-ldap
add-sudo
import-db
run-bcfg2
selinux-enable
echo "Setup complete. Please reboot."

Here are the accompanying ldif files needed.

fix-admin-account.ldif

# Set password for cn=admin,cn=config
dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: PWREPLACE
-
replace: olcRootDN
olcRootDN: cn=admin,cn=config

ldif/new-ldap-setup.ldif

# create modules area
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib64/openldap

# set access for the monitor db.
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="cn=Manager,dc=yourcompany,dc=com" read by * none

# change LDAP domain, password and access rights.
dn: olcDatabase={2}bdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=yourcompany,dc=com
-
replace: olcRootDN
olcRootDN: cn=Manager,dc=yourcompany,dc=com
-
replace: olcRootPW
olcRootPW: PWREPLACE

# setup SSL
dn: cn=config
changetype:modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/pki/tls/private/slapd.key
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/cacerts/slapd.crt
-
replace: olcTLSCipherSuite
olcTLSCipherSuite: HIGH:MEDIUM:-SSLv2

base.ldif

# setup basic tree
dn: dc=yourcompany,dc=com
dc: uh
objectClass: top
objectClass: domain

dn: ou=People,dc=yourcompany,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit

dn: ou=Group,dc=yourcompany,dc=com
ou: Group
objectClass: top
objectClass: organizationalUnit

dn: cn=replicator,dc=yourcompany,dc=com
cn: replicator
objectClass: organizationalRole
objectClass: simpleSecurityObject
objectClass: top
description: LDAP replication user
userPassword: changeme

ldif/sudo-index.ldif

# add sudo index
dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: sudoUser eq

These can be changed to match your needs. In this case, anyone in the group cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com is given full access to the LDAP directory (UPDATE: Please note that the ldapadmin cn is a groupOfNames objectClass [_not_ a posixGroup]).
ldif/olcaccess.ldif

dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by dn.base="cn=replicator,dc=yourcompany,dc=com" read by * break
olcAccess: {1}to * by group.exact="cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com" write by * break
olcAccess: {2}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {3}to attrs=shadowLastChange by self write by * read
olcAccess: {4}to * by * read
-

ldif/syncprov-module.ldif

# setup syncprov module
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: {1}syncprov

You will want to modify these settings according to your replication needs.

ldif/syncprov.ldif

dn: olcOverlay={0}syncprov,olcDatabase={2}bdb,cn=config
objectClass: olcSyncProvConfig
olcOverlay: {0}syncprov
olcSpCheckpoint: 100 10
olcSpSessionlog: 100

ldif/olcaccess-slave.ldif

dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by group.exact="cn=ldapadmin,ou=yourorganizationalunit,dc=yourcompany,dc=com" write by * break
olcAccess: {1}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {2}to * by * read
-

ldif/syncrepl.ldif

dn: olcDatabase={2}bdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: {0}rid=000 provider=ldaps://ldap-master-server searchbase=dc=yourcompany,dc=com type=refreshAndPersist retry="5 5 300 +" bindmethod=simple binddn="cn=re
plicator,dc=yourcompany,dc=com" credentials="changeme" tls_cacertdir=/etc/openldap/cacerts                                                                              -

Here are the relevant bits from the ldap bundle in the bcfg2 repository

<Bundle name='ldap'>
        <Group name='ldap-server'>
                <BoundPath name='/etc/openldap/cacerts/slapd.crt' type='permissions' owner='ldap' group='ldap' perms='0600'/>
                <BoundPath name='/etc/pki/tls/private/slapd.key' type='permissions' owner='ldap' group='ldap' perms='0600'/>
                <Package name='ldapvi'/>
                <Package name='openldap-clients'/>
                <Package name='openldap-servers'/>
                        <Path name='/etc/sysconfig/ldap'/>
                        <BoundPath name='/etc/openldap/slapd.d' type='directory' owner='ldap' group='ldap' perms='0700'/>

                <Service name='slapd'/>

                <!-- phpLDAPadmin settings -->
                <Package name='php'/>
                        <BoundPath name='/var/lib/php/session' type='directory' owner='root' group='nginx' perms='0770'/>
                <Package name='php-fpm'/>
                        <Path name='/etc/php-fpm.d/www.conf'/>
                <Package name='php-ldap'/>
                <Package name='nginx'/>
                <Package name='phpldapadmin'/>
                <Service name='php-fpm'/>
                <Service name='nginx'/>
                <Path name='/etc/nginx/conf.d/phpldapadmin.conf'/>
                <Path name='/etc/openldap/ldap.conf'/>
                <Path name='/etc/phpldapadmin/config.php'/>
                <BoundPath name='/var/www/html/phpldapadmin' type='symlink' to='/usr/share/phpldapadmin/htdocs'/>
                <Path name='/usr/share/phpldapadmin/templates/creation/custom_uh.xml'/>
        </Group>
</Bundle>

The /etc/sysconfig/ldap file needs to be modified to allow LDAPS by uncommenting SLAPD_LDAPS=yes. In /etc/php-fpm.d/www.conf, you need to make sure the user/group are set to nginx (if you are using nginx as your web server).

My nginx configuration for /etc/nginx/conf.d/phpldapadmin.conf looks like this.

server {
        listen          80;
        server_name     ldap-server-hostname;
        rewrite         ^/(.*) https://ldap-server-hostname/$1 permanent;
}

server {
        listen                  443; # listen also for IPv4 traffic on "regular" IPv4 sockets
        server_name             ldap-server-hostname;
        access_log              /var/log/nginx/ssl-access.log;
        error_log               /var/log/nginx/ssl-error.log;
        root                    /var/www/html/phpldapadmin;

        ssl                     on;
        ssl_certificate         /etc/pki/tls/certs/phpldapadmin.crt;
        ssl_certificate_key     /etc/pki/tls/private/phpldapadmin.key;

        index           index.php index.html;

        location ~ \.php$ {
                fastcgi_pass    localhost:9000;
                fastcgi_index   index.php;
                fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include         fastcgi_params;
                fastcgi_param   HTTPS on;
        }
}

I needed the following lines in /etc/openldap/ldap.conf to get phpldapadmin working properly.

URI             ldaps://localhost/
TLS_CACERTDIR   /etc/openldap/cacerts
TLS_REQCERT     never

Lastly, you will need to modify /etc/phpldapadmin/config.php with appropriate values for your site.

 Posted by at 13:16