|
| 1 | +--- |
| 2 | +title: LDAP |
| 3 | +author: |
| 4 | + - m1cr0man |
| 5 | +tags: |
| 6 | + - ldap |
| 7 | + - icarus |
| 8 | + - daedalus |
| 9 | +--- |
| 10 | + |
| 11 | +# LDAP - `m1cr0man` |
| 12 | + |
| 13 | +LDAP is our directory service. It stores usernames, passwords, UIDs, quotas, and other user specific info. |
| 14 | + |
| 15 | +LDAP's structure is different to most other database systems. If you are not familiar with it, I recommend investing some time into looking at how schemas and distinguished names work. |
| 16 | + |
| 17 | +## Deployment |
| 18 | + |
| 19 | +- OpenLDAP is deployed with Nix to Daedalus and Icarus |
| 20 | +- Daedalus is the master, Icarus is slaved to it and can be used as a read only failover |
| 21 | +- `ldap.internal` and `ldap2.internal` are slaved to Daedalus + Icarus respectively |
| 22 | +- Both servers store their data in `/var/db/openldap` |
| 23 | +- The ldap.secret, which should **ALWAYS** have permissions `400`, and owned by the openldap user, is stored in `/var/secrets`. It is not automatically created and must be copied when setting up new hosts |
| 24 | +- `rb-ldap` and `useradm` are wrappers around LDAP that are custom built |
| 25 | + |
| 26 | +## Redbrick Special Notes |
| 27 | + |
| 28 | +- The root user password is in the passwordsafe |
| 29 | +- The OID for most of the schema is [DCU's](http://www.oid-info.com/cgi-bin/display?oid=1.3.6.1.4.1.9736&submit=Display&action=display) |
| 30 | +- The configs that exist for NixOS were mostly ported from our last |
| 31 | + LDAP server ([`paphos`](../hardware/paphos.md)) to maintain compatibility |
| 32 | +- At the time of writing, LDAP is not configured with TLS |
| 33 | +- There are 2 scripts to manage quotas on /storage that run on the server serving NFS (`zfsquota` and `zfsquotaquery`). They are covered under the NFS documentation. |
| 34 | +- There's a user in ldap called testing, for testing. The password is in `pwsafe`. |
| 35 | + |
| 36 | +## Operation |
| 37 | + |
| 38 | +The `ldap*` suite of commands can be used to manage LDAP. Their man pages are very well documented, but we've provided most common operations below. |
| 39 | + |
| 40 | +Note that the ldap.secret is a crypted file, and not equal to the actual password you need to run ldap commands. |
| 41 | + |
| 42 | +### Ldapsearch Recipes |
| 43 | + |
| 44 | +`ldapsearch` can be used with and without authenticating as root. Without root, some fields (such as the password hash, altmail) will be hidden. |
| 45 | + |
| 46 | +```bash |
| 47 | +# Dump the entire LDAP database in LDIF form, which can be used as a form of backup |
| 48 | +ldapsearch -b o=redbrick -xLLL -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt |
| 49 | + |
| 50 | +# Find a user by name, and print their altmail |
| 51 | +ldapsearch -b o=redbrick -xLLL -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt uid=m1cr0man altmail |
| 52 | + |
| 53 | +# Find quotas for all users edited by m1cr0man |
| 54 | +ldapsearch -b o=redbrick -xLLL updatedby=m1cr0man quota |
| 55 | + |
| 56 | +# Find all member's usernames |
| 57 | +ldapsearch -b o=redbrick -xLLL objectClass=member uid |
| 58 | + |
| 59 | +# Find all expired users. Notice here that you can query by hidden fields, but you can't read them |
| 60 | +ldapsearch -b o=redbrick -xLLL 'yearsPaid < 1' uid |
| 61 | +``` |
| 62 | + |
| 63 | +### Ldapmodify Recipes |
| 64 | + |
| 65 | +You can instead pass a file with `-f` when necessary. |
| 66 | + |
| 67 | +To test a command add `-n` for no-op mode. |
| 68 | + |
| 69 | +Changing `updatedby` and `updated` is added to each command as good practise. |
| 70 | + |
| 71 | +```bash |
| 72 | +# Add quota info to a user |
| 73 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt << EOF |
| 74 | +dn: uid=testing,ou=accounts,o=redbrick |
| 75 | +changetype: modify |
| 76 | +add: quota |
| 77 | +quota: 3G |
| 78 | +- |
| 79 | +replace: updatedby |
| 80 | +updatedby: $USER |
| 81 | +- |
| 82 | +replace: updated |
| 83 | +updated: $(date +'%F %X') |
| 84 | +EOF |
| 85 | + |
| 86 | +# Change a user's shell |
| 87 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt << EOF |
| 88 | +dn: uid=testing,ou=accounts,o=redbrick |
| 89 | +changetype: modify |
| 90 | +replace: loginShell |
| 91 | +loginShell: /usr/local/shells/disusered |
| 92 | +- |
| 93 | +replace: updatedby |
| 94 | +updatedby: $USER |
| 95 | +- |
| 96 | +replace: updated |
| 97 | +updated: $(date +'%F %X') |
| 98 | +EOF |
| 99 | + |
| 100 | +# Update yearsPaid |
| 101 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt << EOF |
| 102 | +dn: uid=testing,ou=accounts,o=redbrick |
| 103 | +changetype: modify |
| 104 | +replace: yearsPaid |
| 105 | +yearsPaid: 1 |
| 106 | +- |
| 107 | +replace: updatedby |
| 108 | +updatedby: $USER |
| 109 | +- |
| 110 | +replace: updated |
| 111 | +updated: $(date +'%F %X') |
| 112 | +EOF |
| 113 | +``` |
| 114 | + |
| 115 | +### Ldapadd Recipes |
| 116 | + |
| 117 | +Occasionally you'll need to add people or things to ldap manually, such as a user you're recreating from backups, or a reserved system name such as a new machine. This is where ldapadd comes in. |
| 118 | + |
| 119 | +```bash |
| 120 | +# Create a file to read the new entry from |
| 121 | +cat > add.ldif << EOF |
| 122 | +dn: uid=redbrick,ou=reserved,o=redbrick |
| 123 | +uid: redbrick |
| 124 | +description: DNS entry |
| 125 | +objectClass: reserved |
| 126 | +objectClass: top |
| 127 | +EOF |
| 128 | + |
| 129 | +# Import the ldif |
| 130 | +ldapadd -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt -f add.ldif |
| 131 | + |
| 132 | +# Note if you are importing a full ldif onto a new server, use slapadd instead |
| 133 | +# Ensure slapd is not running first |
| 134 | +slapadd -v -l backup.ldif |
| 135 | +``` |
| 136 | + |
| 137 | +### Other Recipes |
| 138 | + |
| 139 | +On a yearly basis, the `yearsPaid` fields must be incremented for every users, and last year's newbies need to be not newbies anymore. |
| 140 | + |
| 141 | +Remember to take off `-n` when you are ready to rock. |
| 142 | + |
| 143 | +Adding the `updated` and `updatedby` fields from above to these queries would be a good idea. |
| 144 | + |
| 145 | +```bash |
| 146 | +# Decrement yearsPaid |
| 147 | +# WARNING NOT IDEMPOTENT, RUN ONCE |
| 148 | +ldapsearch -b o=redbrick -xLLL -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt objectClass=member yearsPaid |\ |
| 149 | +tee yearsPaid-$(date +'%F').backup.ldif |\ |
| 150 | +awk '/yearsPaid/ { print "changetype: modify\nreplace: yearsPaid\nyearsPaid: " $2 - 1 } ! /yearsPaid/ {print $0}' |\ |
| 151 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt -n |
| 152 | + |
| 153 | +# De-newbie last year's users |
| 154 | +ldapsearch -b o=redbrick -xLLL -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt newbie=TRUE dn |\ |
| 155 | +tee newbie-$(date +'%F').backup.ldif |\ |
| 156 | +awk '/^dn/ {print $0"\nchangetype: modify\nreplace: newbie\nnewbie: FALSE\n"}' |\ |
| 157 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt -n |
| 158 | + |
| 159 | +# Set quotas of users without quotas |
| 160 | +ldapsearch -b o=redbrick -xLLL '(&(objectClass=posixAccount)(!(quota=*)))' dn |\ |
| 161 | +awk '/^dn/ {print $0"\nchangetype: modify\nadd: quota\nquota: 2G\n"}' |\ |
| 162 | +ldapmodify -x -D cn=root,ou=ldap,o=redbrick -y /path/to/passwd.txt -n |
| 163 | +``` |
| 164 | + |
| 165 | +## Troubleshooting |
| 166 | + |
| 167 | +First off, it's worth calling out that if you are coming here to find help with a client side issue, chances are the DNS rule applies: |
| 168 | + |
| 169 | +>It's probably not LDAP |
| 170 | +
|
| 171 | +With that out of the way, here's some things to check - in order. |
| 172 | + |
| 173 | +### Check Reachability of LDAP |
| 174 | + |
| 175 | +Run from the master and also from the problem client. It should return `m1cr0man`'s details. If you get an `invalid credentials` or `object not found` check that the LDAP auth config hasn't changed. If you get a connection error then restart the service. |
| 176 | + |
| 177 | +```bash |
| 178 | +ldapsearch -h ldap.internal -p 389 -xLLL -b o=redbrick uid=m1cr0man |
| 179 | +``` |
| 180 | + |
| 181 | +### Verify LDAP Can Be Written to |
| 182 | + |
| 183 | +Get the password from the passwordsafe. Run this from the master. |
| 184 | + |
| 185 | +```bash |
| 186 | +ldapmodify -D cn=root,ou=ldap,o=redbrick -x -y filewithpwd.txt << EOF |
| 187 | +dn: uid=m1cr0man,ou=accounts,o=redbrick |
| 188 | +changetype: modify |
| 189 | +replace: quota |
| 190 | +quota: 3G |
| 191 | +EOF |
| 192 | +``` |
| 193 | + |
| 194 | +Run the command from the first troubleshooting step to verify the value changed. |
| 195 | + |
| 196 | +If it fails with an auth issue, triple check your password file (it should contain the plain text password). If it fails with a non-auth issue, then check the service logs. |
| 197 | + |
| 198 | +### Enable Debug Logging |
| 199 | + |
| 200 | +OpenLDAP produces a nice set of logs when the `loglevel` is _not_ set. |
| 201 | + |
| 202 | +Remove `loglevel` from `extraConfig` in the Nix config and switch, then run this command to tail the logs: |
| 203 | + |
| 204 | +```bash |
| 205 | +journalctl -fu openldap |
| 206 | +``` |
| 207 | + |
| 208 | +### Re-syncing Secondary LDAP Server(s) |
| 209 | + |
| 210 | +In the event a secondary server becomes out of sync with the master, it can be synced by stopping the server, deleting its database files, then restarting the server. Do this after ensuring that `config.redbrick.ldapSlaveTo` is set correctly. |
0 commit comments