Skip to content

Setting up FreeIPA, KDC and LDAP

Charles Hedrick edited this page Nov 15, 2024 · 27 revisions

Return to main: Kerberos

In our site, this documentation should never be needed. It shows how I set up the initial server. However in no conceivable situation would we start from scratch this way. Even for a major upgrade, where we have to reinstall the servers from scratch, we pull the data from an existing server. In case of a massive failure, we'd use a backup copy of one of the VMs, get it running, and pull the data from there.

However it's still useful to know what ACLs I created, what new LDAP structures, I created, etc. So I'm continuing to maintain this page. Every time I do something outside for normal IPA operations (creating users, and groups, etc) I add it to the documentation here.

But for setting up a server, see the separate page on setting up a new server. We now have a simplified way, where it's mostly done by Ansible.

I am updating this page as we add new services, new ACLs, etc. So in principle it should be possible to reproduce our existing setup. However I can't imagine any situation where we'd actually do that, unless we have to move from IPA to something else.

Table of Contents

WARNING

The obvious installation process, documented below, ,produces a slightly invalid configuration. The default install include a CA with a self-signed cert. We followed a process to install a commercial cert for LDAP and HTTPS. It works fine, but the upgrade scripts assume that if you have a CA, your LDAP and HTTPS certs come from your own CA. The upgrade script fails in our configuration. This is only on krb1. The process for producing a replica by default doesn't copy the CA, so the replica is CA-less and doesn't have this issue. There's some reason to think that krb1 wouldn't even be able to reboot cleanly.

Redhad has a fix, which it to change the certificate's nickname to the one expected by the upgrade script, Server-Cert

Do certutil -L -d /etc/httpd/alias to see what the actual nickname is. It is probably not krb1-cert, but more likely the long name starting in CN-krb1.

certutil -L -d /etc/httpd/alias -n 'krb1-cert' -a -o ~/krb1.cert
certutil -D -d /etc/httpd/alias -n 'krb1-cert'
certutil -A -d /etc/httpd/alias -n "Server-Cert" -t u,u,u -i ~/krb1.cert
emacs /etc/httpd/conf.d/nss.conf
change NSSNickname to Server-Cert

certutil -d /etc/dirsrv/slapd-CS-RUTGERS-EDU/ -L -a -n 'CN=krb1.cs.rutgers.edu,OU=SAS,O="Rutgers, The State University of New Jersey",STREET=43 College Avenue,STREET=Room 226A,L=New Brunswick,ST=NJ,postalCode=08901,C=US' -o ~/krb1.cert2
certutil -d /etc/dirsrv/slapd-CS-RUTGERS-EDU/ -D -n 'CN=krb1.cs.rutgers.edu,OU=SAS,O="Rutgers, The State University of New Jersey",STREET=43 College Avenue,STREET=Room 226A,L=New Brunswick,ST=NJ,postalCode=08901,C=US'
certutil -A -d /etc/dirsrv/slapd-CS-RUTGERS-EDU/ -n "Server-Cert" -t u,u,u -i ~/krb1.cert2

emacs /etc/dirsrv/slapd-CS-RUTGERS-EDU/dse.ldif
change nsSSLPersonalitySSL to Server-Cert

You should also remove the requests to update the original certificates:

getcert list -d /etc/dirsrv/slapd-CS-RUTGERS-EDU -n Server-Cert
; find requestid 20171002175930 in output
getcert stop-tracking -i 20171002175930

getcert list -d /etc/httpd/alias/ -n Server-Cert
getcert stop-tracking -i 20171002175931

Somehow in the process of doing all this I had lost a symlink

mkdir /etc/systemd/system/pki-tomcatd.target.wants
ln -s /lib/systemd/system/[email protected] /etc/systemd/system/pki-tomcatd.target.wants/[email protected]

In one try I also messed up permissions. To restore.

cd /etc/httpd/alias
chgrp apache *.db
chgrp apache kra-agent.pem
chgrp apache pwdfile.txt
chmod 660 pwdfile.txt

Other issues

In that state you can restart and upgrade. But attempts to install a replica failed. So the section on setting up a backup server.

Intro

It includes an MIT Kerberos KDC with a plugin for two factor authentication (free clients), LDAP, and a web GUI. It has a DNS component available, which allows for easy setup of new clients including putting them into DNS. I haven't turned on DNS.

The initial goal was just to use this system for authentication, but we've now moved users, groups, netgroup and host ssh keys to it.

Two-factor authentication only works for Centos 7 (maybe 6). However if you get a TGT under Centos 7 you can ssh to any system that supports Kerberos, and your tickets will work there.

The package also includes sssd, a daemon designed to support for PAM and nsswitch. We may want to use it, but the server can be used with pam_krb5 and nss_ldap. The main issue with not using sssd is support for one-time passwords, but there are solutions to that.

FIREWALL

By default LCSR allows ssh from within Rutgers. The default rules are at /etc/sysconfig/iptables.

I suggest starting with iptables narrowed a bit. Here's what I started with as the main rule set in /etc/sysconfig/iptables

-A INPUT  -s 128.6.26.0/24 -p tcp -m tcp --dport 22 -j ACCEPT 
-A INPUT  -s 128.6.4.9 -p tcp -m tcp -j ACCEPT 
-A INPUT  -p tcp -m tcp --dport 22 -j DROP 
The second rule allows all access from the primary server

Now for full setup:

The firewall loads from /etc/sysconfig/iptables. Here are my current rules: rules

This uses two ipsets:

  • lcsr - all Kerberos clients
  • staff - staff systems, current just 128.6.26
ipsets are not installed by default. This will show how to use them:
yum install ipset
yum install ipset-service
systemctl start ipset
systemctl enable ipset
ipset create staff hash:ip
ipset create lcsr hash:ip
ipset add staff 128.6.26.0/24
"service ipset save" will persist the definitions.

I run a script every 10 min that updates lcsr to include all hosts in the database. On the primary a 10 min delay may not be acceptable, but on the backup I think it normally will be.

FREEIPA

Make sure ntp is working. We've had issues with it. I'm currently using just ntp.rutgers.edu.

to get enough entropy. otherwise install is very slow

sudo yum install -y haveged
sudo systemctl start haveged.service

yum install freeipa-server

ipa-server-install.
defaults work

no DNS

realm CS.RUTGERS.EDU [upper case]
password for both dir server and kdc is the same, but is not in this file for obvious reasons

You should probably kinit as admin then create a user for yourself

ipa user-add --uid=1003 hedrick
then 
ipa passwd hedrick
with temp password (you'll have to change it)
now kdestroy
then kinit as yourself and change your password

login to the web page, pick your own page

I suggest creating something like hedrick.admin Kinit as admin, then do

ipa group-add-member admins --users=YOU.admin
You'll want to require 2FA, which is a checkbox in the GUI. Adding someone to the admins group makes them an admin, with the same access as admin. I recommend not using the actual admin once you've done that setup.

Once 2FA is set, until I come up with a better approach, you can kinit in the following weird way:

klist - if you don't have a ticket do kinit as a non-2FA user
copy the cache name, e.g. KEYRING:persistent:1003:krb_ccache_ZZarlR1
kinit -T KEYRING:persistent:1003:krb_ccache_ZZarlR1 YOU.admin
type your password following by the one time token on the same line immediately after the password

The -T is needed because where there's 2FA, the password has to be sent in the clear. The normal Kerberos protocol won't work except with simple passwords. To secure that, you need a credentials cache. The credential in the cache is used to do encryption. The credentials don't have to be yours; they just have to be valid. Hence the approach above has you login as your non-admin user and use the credentials cache to armor the request for your admin login. I will set up a better approach shortly.

  • Logout of admin and login with your admin account
  • pick authenticatin, OTP tokens
  • Create a new one and do add and edit. That will show the QR code.
  • Install freeotp on your phone and scan the token. At the top of the app there's an icon that looks like a QR code. Pick it and point the camera at the screen
  • Now go back to your .admin entry and set the box to require TFA. I actually had to go back in as admin to do that. Not sure why.
  • freeotp has an issue. Sometimes you have to kill it and restart it to get it to display a code
for feeds to work we have to be able to dump all users. Create a user scripts.admin. give it user-admin role (this is in the gui)
ipa user-add or ipa user-mod --random, then ipa-getkeytab
Put a keytab in /etc/scripts.keytab. 
in kadmin.local:  modprinc -pwexpire 2030-01-01 scripts.admin
verify you can do kinit -k -t /etc/scripts.keytab scripts.admin
that's the way we'll do things in scripts now make sure it can list all of our users:
ipa user-mod --setattr=nsLookThroughLimit=100000 --setattr=nssizelimit=100000 --setattr=nstimelimit=100000 scripts.admin
but had to go into GUI and set search limit to 100000 also. As user admin, this user can manage users, but it's not a full admin
  • In gui, IPA Server, RBAC, pull down choose permissino and System: Modify Users
  • Under effective attributes add gidnumber, uidnumber, homedirectory
  • The UI is marginal. Check the result by doing as admin
  • ipa permission-show 'System: Modify Users'
  • Should see "Included attributes: gidnumber, uidnumber, homedirectory"
ipa permission-mod should do this, but I couldn't make it work for more than one attribute This is needed for the feed script to be able to update all properties. I'm running it as User Administrator, not Admin

turn on creation of private groups. We currently expect that.

ipa-managed-entries -e "UPG Definition" enable
ipa-managed-entries -e "NGP Definition" disable
systemctl restart dirsrv.target
To get a high expiration time you have to hack on ldap.
ldapmodify -x -D "uid=admin,cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu" -W -f ~/mod
with admin password
Here is ~/mod for a year. 

Also need to change the property in the GUI for kerberos ticket policy. In Policy, Kerberos ticket pokicy. (This may not be needed after the ldap change.)

And the he renew time in /var/kerberos/krb5kdc/kdc.conf. the renewtime in the client's krb5.conf is a default, which kinit can override. Also individual users can be set in the GUI or LDAP. There's already a renewtime in the CS.RUTGERS.EDU section. Just change it to 365d.

I'd restart the server. systemctl restart krb5kdc

dn: krbPrincipalName=K/[email protected],cn=CS.RUTGERS.EDU,cn=kerberos,dc=cs,dc=rutgers,dc=edu
changetype: modify
replace: krbMaxRenewableAge
krbMaxRenewableAge: 31536000
-

dn: krbPrincipalName=krbtgt/[email protected],cn=CS.RUTGERS.EDU,cn=kerberos,dc=cs,dc=rutgers,dc=edu
changetype: modify
replace: krbMaxRenewableAge
krbMaxRenewableAge: 31536000
-

dn: cn=CS.RUTGERS.EDU,cn=kerberos,dc=cs,dc=rutgers,dc=edu
changetype: modify
replace: krbMaxRenewableAge
krbMaxRenewableAge: 31536000
-

Note that tickets by default aren't renewable. The defaults are set in the libdefaults section of /etc/krb5.conf. I'd be incline to set

 ticket_lifetime = 24h
 renew_lifetime = 365d
though on the server side that has no effect other than for command line users on the server itself.

In Policy / Password policies

Default password lifetine is 90 days. Current opinion says expiring passwords isn't useful, so I'm setting it to 18250 days, which is 50 years. I believe numbers larger than that won't work, though I haven't tried it.

Currently there's a minimum change time of 1 hour. I'm not sure this is needed, but I'm not currently changing it. The default is not to keep password history so you can change a password to what it was before. Since I'm disabling expiration, there's no reason to change.

Follwing are the same as University policies, stricter than the freeipa defaults

  • 3 character classes
  • min length 10
Following are ipa default, but different from University:
  • max failures 6 in 60 sec
  • lockout 10 min
The University uses 50 failures with a longer lockout. They're concerned about mail readers that check in automatically failing after a forced password change and locking the user out permanently. We're not doign mail and we're not forcing password changes, so for the moment I'm leaving the defaults here.

New passwords are set to expire immediately. That applies to accounts created with ipa user-add. We'll be activating by a web app, so I'm not sure this is an issue. I've created lots of users by sync from NIS, with the same password, so I really do want to force a change if one of them manages to login.

In /etc/sysconfig/network, comment out the domain name entry. We don't need NIS, and it confuses scripts.

To allow us to add the "host" and various date attribute to users and groups:

ipa config-mod --addattr=ipaUserObjectClasses=hostObject
ipa config-mod --addattr=ipaGroupObjectClasses=hostObject
ipa config-mod --addattr=ipaGroupObjectClasses=request
ipa permission-mod "System: Read User Standard Attributes" --includedattrs=host
ipa permission-mod "System: Read Groups" --includedattrs=host --includedattrs=dateOfCreate --includedattrs=dateOfModify
ipa permission-mod "System: Modify Groups" --includedattrs=host --includedattrs=dateOfCreate --includedattrs=dateOfModify
ipa permission-mod "System: Modify Users" --includedattrs=host
Unfortunately you still have to add the object to existing users or groups, though new ones get it, e.g.
ipa group-mod ipausers --addattr=objectclass=hostobject
You can then add the host to entries:
ipa group-mod ipausers --addattr=host=ilab
Normally the objectclass will be added when the user or group is created

Similarly, needed to add csRutgersEduPerson, put this in addattr

dn: cn=schema
changetype: modify
add: attributetypes
AttributeTypes: ( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE
-VALUE )
attributeTypes: ( 1.3.6.1.4.1.10962.2.4.2 NAME 'csRutgersEduCredservKeytab' DE
 SC 'Encoded keytab for credserv' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGI
 N ( 'cs.rutgers.edu' 'user defined' ) )
attributeTypes: ( 1.3.6.1.4.1.10962.2.4.1 NAME 'csRutgersEduCredservRule' DESC
  'Authorization for credserv' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN (
  'cs.rutgers.edu' 'user defined' ) )
attributeTypes: ( 1.3.6.1.4.1.10962.2.4.4 NAME 'dellEmcGroup' DESC 'Role for Dell switches'
  EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
-
add: objectclasses
objectClasses: ( 1.3.6.1.4.1.10962.2.4.3 NAME 'csRutgersEduPerson' SUP top AUX
 ILIARY MAY ( csRutgersEduCredservRule $ csRutgersEduCredservKeytab $ sAMAccountName $ dellEmcGroup) X-ORIGIN
  ( 'cs.rutgers.edu' 'user defined' ) )
ldapmodify -ZZ -x -D "cn=Directory Manager" -W -H ldap://localhost -f addattr
ipa config-mod --addattr=ipaUserObjectClasses=csRutgersEduPerson
fails for some reason, so do ldapmodify on this:
dn: cn=ipaConfig,cn=etc,dc=cs,dc=rutgers,dc=edu
changetype:modify
add:ipaUserObjectClasses
ipaUserObjectClasses:csRutgersEduPerson

If there are existing users, need to add the objectclass to them. This objectclass is needed for credserv and for the Windows uid/gid mapping.

In GUI,

  • Add Permission Rutgers Write Credserv Attributes, which gives all on csRutgersEduCredservRule and csRutgersEduCredservKeytab.
  • Add Privildge Rutgers Credserv Data Management with that permission
  • Add Role Rutgers Credserv Service with that privilege
  • Add credserv/krb1.cs.rutgers.edu to that role; also krb2 when it's created
  • Edit Permission System: Read User Standard Attributes, add sAMAccountName
To allow the UI to manipulate samaccountname, as well as attributes we use for group management, and give it the right value by default, create a file /usr/lib/python2.7/site-packages/ipaserver/plugins/rutgers.py. In addition, it supplies a default GID and email address, and some other things.
from ipaserver.plugins import user
from ipaserver.plugins import group
from ipalib.parameters import Str
from ipalib import _

user.user.takes_params = user.user.takes_params + (
 Str('samaccountname?',
 cli_name='windowsname',
 label=_('Windows account name'),
 ),
)
user.user.default_attributes.append('samaccountname')

group.group.takes_params = group.group.takes_params + (
 Str('businesscategory*',
 cli_name='categories',
 label=_('Categories (specify "login" for login group)'),
 ),
)
group.group.default_attributes.append('businesscategory')

group.group.takes_params = group.group.takes_params + (
 Str('host*',
 cli_name='hosts',
 label=_('Hosts (use as cluster names for login group)'),
 ),
)
group.group.default_attributes.append('host')

def useradd_precallback(self, ldap, dn, entry, attrs_list,
*keys, **options):
    if 'samaccountname' not in entry.keys():
        entry['samaccountname'] = entry['uid']
    if 'mail' not in entry.keys():
        entry['mail'] = entry['uid'] + '@rutgers.edu'
    if 'gidnumber' not in entry.keys():
        entry['gidnumber'] = '1234'
    return dn

user.user_add.register_pre_callback(useradd_precallback, first=True)

# For some reason this ignores the text in cli_name and label

group.group.takes_params = group.group.takes_params + (
 Str('owner*',
 cli_name='owners',
 label=_('Owners'),
 normalizer=lambda value: value if ',' in value else 'uid=' + value + ',cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu',
 ),
)

group.group.default_attributes.append('owner')

and then restart httpd. This needs to be done on each server. Clients get the list of attributes, etc, from the server, so they don't need modification.

Setting up IPA installs sssd. You'll want to check authentication and nsswitch.conf afterwards.

  • I suggest leaving pam with both ldap and sss enabled. If you loving with your kerberos password you'll get a Kerberos ticket. That's preferred. But ldap pointing to ldap.rutgers.edu will give you a backup way in if the iPad server is down.
  • I suggest setting nsswitch.conf so password, shadow and group is just "files." That way only users in /etc/passwd can actually login.
To allow users in group group-manager to create and modify groups: use these files with ldapmodify. group-manager can create and modify groups, but not make them login groups. login-manager can make them login groups. Currently everyone is in group-manager.
dn: cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (targattrfilters="add=objectClass:(|(objectClass=groupOfNames)(objectclas
 s=top)(objectclass=posixgroup)(objectclass=ipausergroup)(objectclass=ipaobjec
 t)(objectclass=nestedGroup)(objectclass=hostobject)(objectclass=request))") (target="ldap:///cn=gr
 oups,cn=accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Create Group";
 allow (read,search,add) (groupdn="ldap:///cn=group-manager,cn=groups,cn=accou
 nts,dc=cs,dc=rutgers,dc=edu"); )
aci: (targetfilter=(objectClass=groupOfNames))(targetattr="member||owner||date
 OfCreate||dateOfModify")(target="ldap:///cn=groups,cn=accounts,dc=cs,dc=rutge
 rs,dc=edu") (version 3.0; acl "Modify Own Group"; allow (all) (userattr="crea
 torsName#USERDN" or userattr="owner#USERDN"); )
aci: (targetfilter=(objectClass=groupOfNames))(targetattr="member||owner||host
 ||businesscategory||dateOfCreate||dateOfModify")(target="ldap:///cn=groups,cn
 =accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Modify Own Group"; all
 ow (all) (userattr="creatorsName#USERDN" or userattr="owner#USERDN") and grou
 pdn="ldap:///cn=login-manager,cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu";
  )

dn: cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (targetfilter=(objectClass=groupOfNames))(targetattr="creatorsName")
 (target="ldap:///cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Read creator of Own Group"; 
 allow (read, search) (userattr="creatorsName#USERDN" or userattr="owner#USERDN"); )

dn: cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (targetfilter=(objectClass=groupOfNames))(target="ldap:///cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Delete Own Group"; allow (delete) (userattr="creatorsName#USERDN" or userattr="owner#USERDN"); )

dn: cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype:modify
add:aci
aci: (targetattr = "dellemcgroup")(version 3.0; acl "read dell group"; allow (read,compare,search) userdn="ldap:///all";)
-

The first ACI allows anyone in group group-manager to read, search or add items under cn=groups, with objectclass limited to those shown (those used for groups). Without the targattrfilters constraint, they could create users. It's the object class, not the location of an entry in the tree that defines whether it's a user, a group, or something else.

The second ACI allows the person who created the group, or anyone listed as a owner to change member, owner, host or businesscategory attributes. Note that businesscategory=login is used to define something as a login group, so that needs to be controlled. If you decide to let anyone create a group, you'll need an aci that limits who can change the businesscategory, or anyone could let anyone login.

The third ACI allows the creator or owner to see the creator attribute, which is needed to do searches. It's not normally visible.

The fourth ACI allows the creator or owner to delete the group. Note that if they do so they will receive a spurious error message from ipa group-del. That's because it will try to delete any Kerberos group policy for the group. There won't be any, but the attempt will fail. It's harmless.

The fifth ACI allows anyone logged in to read the dellemcgroup attribute. This is used for access control of Dell switches.

Any DN that needs to see all users will need an increased search limit. E.g. the account cleanup tool will need this. I'm using wordpress.cs.rutgers.edu for testing so

ipa host-mod wordpress.cs.rutgers.edu --setattr=nsLookThroughLimit=-1
ipa host-mod wordpress.cs.rutgers.edu --setattr=nsSizeLimit=-1

To allow scripts.admin to read who has tokens, use this with ldapmodify:

dn: cn=otp,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (target="ldap:///cn=otp,dc=cs,dc=rutgers,dc=edu")(targetattr="managedBy||
 ipatokenUniqueID||ipatokenOwner||objectclass")(targetfilter = "objectclass=ip
 atoken")   (version 3.0; acl "read token"; allow (read,search,compare) groupd
 n="ldap:///cn=User Administrator,cn=roles,cn=accounts,dc=cs,dc=rutgers,dc=edu
 ";)

I was unable to get the permissions system to do this.

To allow users to create and manage their own hosts, add

dn: cn=computers,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (targetfilter=(objectClass=ipahost))(targetattr="managedby")
 (target="ldap:///cn=computers,cn=accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Modify Own ManagedBy"; 
 allow (all) (userattr="creatorsName#USERDN"); )
This lets the creator of a host add a managedby entry. That will then let the manager (normally them) get a key table for it and do other things. To add a service, kinit using the host's key table and do ipa service-add and ipa-getkeytable.

Creating password policy for hadoop users. By default if we create a user and get a key tablek the key table is expired. This policy is like the default, but doesn't expire users. So we create the user put them in this group, and then get the key table. (pw policies are associated with groups). It prompted for a priority. I answered 1.

ipa pwpolicy-add nochange --maxlife=0 --minlife=0 --history=0 --minclasses=1 --minlength=10 --maxfail=10 --failinterval=60 --lockouttime=600

To allow NFS servers to see the key table with their existing nfs keys.

dn: cn=computers,cn=accounts,dc=cs,dc=rutgers,dc=edu
changetype: modify
add:aci
aci: (targetfilter=(objectClass=csRutgersEduPerson))(targetattr="csRutgersEduCredservKeytab")(target="ldap:///cn=computers,cn=accounts,dc=cs,dc=rutgers,dc=edu") (version 3.0; acl "Read Own Keytab"; allow (read) (userattr="managedBy#USERDN"); )

The entry must be modified by admin to have objectclass=csRutgersEduPerson and then the csRutgersEduCredservKeytab entry itself

SID generation

IPA now uses Microsoft-style SIDs. They set up a mapping from the UIDs and GIDs that they generate to SIDs. But we have old UIDs and GIDs that aren't specified in their mapping. With default settings, Kerberos won't get you get a ticket if you don't have a SID. To fix this:

ipa idrange-add CS.RUTGERS.EDU_low_id_range --base-id=1 --range-size=200000 --ri
d-base=200000000 --secondary-rid-base=300000000
ipa idrange-add CS.RUTGERS.EDU_mid_id_range --base-id=600000 --range-size=200000
 --rid-base=400000000  --secondary-rid-base=500000000

ipa config-mod --add-sids --enable-sid

The ipa config-mod --add-sldapsearch -b cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu '(&(gidnumber=*)(!(ipaNTSecurityIdentifier=*)))' ids runs a task that adds SIDs that aren't there. Check /var/log/dirsrv/slapd-CS-RUTGERS-EDU/errors to make sure it worked. If you have the same number that's both a UID and GID, it's supposed to use the secondary RID base for one of them to maintain uniqueness. Initially that didn't work. I got errors for the overlap and had to fix the SIDs manually. In a test with a newer version that problem didn't exist. Try

ldapsearch -b cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu '(&(uid=*)(!(ipaNTSecurityIdentifier=*)))'
ldapsearch -b cn=users,cn=accounts,dc=cs,dc=rutgers,dc=edu '(&(gidnumber=*)(!(ipaNTSecurityIdentifier=*)))'
<pre>
to make sure all were converted.


===DHCP schema===


To support DHCP, loaded the schema. From a system with a copy of the ISC DHCP server installed, there's a file /etc/openldap/schema/dhcp.schema. It is the schema in Openldap format.

To convert it, use https://fedorapeople.org/groups/389ds/binaries/ol-schema-migrate.pl

./ol-schema-migrate.pl -b /etc/openldap/schema/dhcp.schema > dhcp.ldif

In the resulting file, remove comment lines, and add at the beginning

<pre>
dn: cn=schema
changetype: modify
add: attributeTypes

After the attributes before the objectclasses add

-
add:objectClasses

There should be no blank lines. The line with the hyphen ends the block of attributetypes.

ldapmodify -ZZ -x -D "cn=Directory Manager" -W -H ldap://localhost -f dhcp.ldif

with the LDAP admin password.

You will also want to index two attributes, to speed up searches done by the DHCP code. Created index.ldif

dn: cn=dhcpHWAddress,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
changetype: add
cn: dhcpHWAddress
nsIndexType: pres
nsIndexType: eq
nsSystemIndex: false
objectClass: top
objectClass: nsIndex

dn: cn=dhcpClassData,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
changetype: add
cn: dhcpClassData
nsIndexType: pres
nsIndexType: eq
nsSystemIndex: false
objectClass: top
objectClass: nsIndex

and load using

ldapmodify -ZZ -x -D "cn=Directory Manager" -W -H ldap://localhost -f index
sudo db2index.pl -D "cn=Directory Manager" -n userRoot -t dhcpHWAddress -w -
You must do this on each server. You'll also want to do db2index for dhcpClassData if there is any existing data.

DHCP config:

ldap-server                 "krb1.cs.rutgers.edu";
ldap-port                   389;
# We do an anonymous bind                                                                                                                              
# ldap-username             "cn=directorymanagerloginname";                                                                                            
# ldap-password             "mypassword";                                                                                                              
ldap-base-dn                "ou=dhcp,dc=cs,dc=rutgers,dc=edu";
ldap-method                 dynamic;
ldap-debug-file             "/var/log/dhcp-ldap-startup.log";
ldap-dhcp-server-cn         "server";

Initial setup:

dn: ou=dhcp,dc=cs,dc=rutgers,dc=edu
ou: dhcp
objectClass: top
objectClass: organizationalUnit
description: DHCP Servers
aci: (target="ldap:///ou=dhcp,dc=cs,dc=rutgers,dc=edu")(targetattr=*)(version 3.0; acl "Manage DHCP Data";
 allow (all) (groupdn="ldap:///cn=dhcp-manager,cn=groups,cn=accounts,dc=cs,dc=rutgers,dc=edu"); )
aci: (target="ldap:///ou=dhcp,dc=cs,dc=rutgers,dc=edu")(targetattr=*)(version 3.0; acl "Read DHCP Data";
 allow (read,search) (userdn="ldap:///anyone"); )


dn: cn=server,ou=dhcp,dc=cs,dc=rutgers,dc=edu
cn: server
objectClass: top
objectClass: dhcpServer
dhcpServiceDN: cn=config,ou=dhcp,dc=cs,dc=rutgers,dc=edu

dn: cn=config, ou=dhcp,dc=cs,dc=rutgers,dc=edu
cn: config
objectClass: top
objectClass: dhcpService
dhcpPrimaryDN:  cn=server,ou=dhcp,dc=cs,dc=rutgers,dc=edu
dhcpStatements: ddns-update-style none
dhcpStatements: get-lease-hostnames true
dhcpStatements: use-host-decl-names true

dn: cn=128.6.26.0,cn=config,ou=dhcp,dc=cs,dc=rutgers,dc=edu
cn: 128.6.26.0
objectClass: top
objectClass: dhcpSubnet
objectClass: dhcpOptions
dhcpNetMask: 24
dhcpStatements: default-lease-time 600
dhcpStatements: max-lease-time 7200
dhcpOption: subnet-mask 255.255.255.0
dhcpOption: routers 128.6.26.1
dhcpOption: domain-name-servers 128.6.1.1
dhcpOption: domain-name "rutgers.edu"

dn: cn=farside.lcsr.rutgers.edu,cn=config,ou=dhcp,dc=cs,dc=rutgers,dc=edu
cn: farside.lcsr.rutgers.edu
objectClass: top
objectClass: dhcpHost
dhcpHWAddress: ethernet 00:0c:29:af:cb:ca
dhcpStatements: fixed-address 128.6.26.62

DHCP hosts are maintained by the account web application. I have also added commands to the ipa command-line tool to maintain all the LDAP data. See dhcp.py in the ansible setup. This code requires each kdc to have a copy of dhcpd. That's installed an a working directory created by the ansible script kdc2.yml. In addition to needing dhcpd installed, you need the directory /var/lib/dhcptest, protected 01777. (dhcpd is run in an enviornment with its own /tmp. You could probably put the files in /tmp, but debugging is hard because you can't see that from outside httpd.)

pw quality rules

See Setting up pw quality check

Services

Credserv

Run credserv.

I'm assuming you've already added the attributes csRutgersEduCredservRule and csRutgersEduCredservKeytab. That only has to be done on one server. New servers inherit it.

It authenticates as a Kerberos service, so it needs a service principal. That goes in /etc/krb5.keytab, per instructions below.

ipa service-add credserv/krb1.cs.rutgers.edu
ipa-getkeytab -p credserv/krb1.cs.rutgers.edu -k /etc/krb5.keytab
ipa role-add-member "Rutgers Credserv Service" --services=credserv/krb1.cs.rutgers.edu

This assumes that the role Rutgers Credserv Service already exists. If not, see the man page for credserv for host to create it. It has permission to read and write csRutgersEduCredservRule and csRutgersEduCredservKeytab.

Also need /etc/krb5.anonymous.keytab and /etc/krb5.tgt.keytab. They are created from anonymous.user and krbtgt/CS.RUTGERS.EDU. The second file is very security-sensitive. It should never go anyplace other than the KDCs.

I extracted those keytables using kadmin.local, the ktadd command. Make very sure to use the -norandkey option, or it will change the keys. That will disable the whole system, and won't be easy to restore. I'd copy them from an existing server, and on the first server do this before going into production.

Here's the section in krb5.conf for it:

[appdefaults]
  credserv= {
    impersonate=/etc/krb5.tgt.keytab
    admingroup=admins
    ldapurl=ldaps://krb2.cs.rutgers.edu,ldaps://krb1.cs.rutgers.edu
    ldapbase=cn=accounts,dc=cs,dc=rutgers,dc=edu
  }
impersonate points to a key table for the TGT. Without impersonate it will use keytables for users stored in ldap. Impersonate is necessary if you need to support users with two-factor authentication. Using key tables is kind of a hack, but the impersonate code was added later.

admingroup is a group for people who can look at and change other users' data. I'm using admins, which is a group that all the admins belong to. If you want to delegate this to other users, you can use a different group.

ldapurl should point to the local server. I'm not sure whether adding a second server is actually a good idea.

ldapbase is the base for your users' information.

a service file for systemd is included in the source.

chrony

I recommend installing chrony. /etc/chrony.conf is installed with it. Remove the default centos servers and add

server 128.6.1.1 iburst
initstepslew 10 128.6.1.1

The initstepslew says to adjust the clock immediately rather than gradually if the time is off by more than 10 sec. This is the reason for using chrony rather than ntp. Ntp will leave the clock off for 900 sec, and may exit if it's too far off. Kerberos servers absolutely must have the right time, or two-factor auth won't work, and with some clients, kerberos won't either.

systemctl disable ntpd
systemctl enable chronyd

Note that starting ipa will start ntp. That will kill chrony. That's OK. We just need chrony to run once to set the clock if it's too far off. We currently have a cron job on all systems that will restart chrony eventually. That will kill ntp. That's fine too.

Commercial cert

When you set up IPA it will generate its own certificate. It will be used for both ldap and http. A CA cert goes into /etc/ipa/ca.cert.

In the long run you want to use a commercial certificate. I got back from the certificate people a key, a cert, and a CA chain. Installing them was a hair-raising experience.

Note that on the backup, the intermediates are already there, because they're copied from the primary. Hence you can go directly to ipa-server-certinstall. This is probably true for renewal as well.

  • Before you can install the actual cert, you need to install all the CA certificates for the chain.
  • Take apart the intermediate certificates. If you look at the file you'll see it has 3 obvious sections.
  • Use openssl x509 --in FILE --text to look at them
  • Starting at the top level, install each one using
ipa-cacert-manage -p PASSWORD -n NICKNAME -t C,, install FILE
; PASSWORD is the admin password for the system. 
; NICKNAME is a word you pick to identify the CA. 
; FILE is the file with the data
  • ipa-certupdate ; this actually updates the CA database with the new certs
  • ipa-server-certinstall -w -d mysite.key mysite.crt, i.e. the files with the key and the actual cert for this system
  • systemctl restart httpd.service
This will probably fail. In /etc/httpd/conf.d/nss.conf, it adds a line like
NSSNickname "CN=krb1.cs.rutgers.edu,OU=SAS,O="Rutgers, The State University of New Jersey",STREET=43 College Avenue,STREET=\
Room 226A,L=New Brunswick,ST=NJ,postalCode=08901,C=US"    
Unfortunately this is a syntax error. To fix it, add \ before the "'s inside the string.

Make sure you can restart httpd.service cleanly. I do *not* recommend rebooting the system until that's fixed.

Now

systemctl restart [email protected]

At this point, in an ideal world, you're done. However I found that after doing this the system would no longer come up after reboot. After about 10 min it will come up, but

systemctl -a
will show several services failed. It may also come up without the firewall on, which is a concern.

There appears to be a race condition between dbus and polkit. I fixed it by editing /usr/lib/systemd/system/dbus.service and adding a line:

ExecStartPre=/bin/sleep 2
ExecStart=/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/bin/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.Reloa\
The first (with sleep) is what I added. At that point the system should come up.

This appears to be a known issue with how systemd interacts with the dbus. It's been fixed upstream, but it may be a while before we see it.

Setting up to update iptables

There's a cron job, /usr/local/scripts/hosts2fw/update

#!/bin/sh

export PATH=/sbin:/bin:/usr/sbin:/usr/bin
export KRB5CCNAME=KEYRING:persistent:0:hosts2fw
kinit -k -t /etc/scripts.keytab [email protected]

ipa host-find | grep "Host name:" | awk 'BEGIN{FS=":"}{gsub(" ", "", $2); print 
"[" $2 "]"}' | xargs -L 1 ipset add lcsr -exist
service ipset save

kdestroy

It runs every 10 min from cron. However for ansible there are times when we need to do it immediately. I've added a user "useradd syncipt -u 990 -g 990" to run it. The user's shell is /home/syncipt/update. In that user's home directory we have

.k5login

enroll/[email protected]

update

#!/bin/sh
sudo /usr/local/scripts/hosts2fw/update

/etc/sudoers.d/syncipt

syncipt ALL=NOPASSWD:/usr/local/scripts/hosts2fw/update

and in /etc/ssh/sshd_config before the other match block

match user syncipt
AuthenticationMethods gssapi-with-mic

This allows the sync to be triggered by principal enroll/[email protected]

TLS

To support old code we have temporarily had to support TLS1.0. The system won't support SSL. TLS1.0 is the lowest it will go. However we've configured it for SSL2, because attempts to configure TLS1.0 failed. The system doesn't actually use SSL.

In /etc/crypto-policies/back-ends/nss.config, change "tls-version-min=tls1.2" to "tls-version-min=ssl2".

Stop the system. In /etc/dirsrv/slap*/dse.ldif change cn=encryption,cn=config to have the following

nsSSL3: on
nsSSL2: on
sslVersionMin: SSL2

Restart. This can probably be done with ldapmodify.

TODO

Immediate

  • Document how to set up client to use both servers, and test backup
Later
  • Optimize hosts2fw script to not re-add all hosts
  • Clean up nightly dumps of LDAP data
  • Create script to add hosts