1This file explains how to use the optional LDAP functionality of SUDO to 2store /etc/sudoers information. This feature is distinct from LDAP passwords. 3 4LDAP philosophy 5=============== 6As times change and servers become cheap, an enterprise can easily have 500+ 7UNIX servers. Using LDAP to synchronize Users, Groups, Hosts, Mounts, and 8others across an enterprise can greatly reduce the administrative overhead. 9 10Sudo in the past has only used a single local configuration file /etc/sudoers. 11Some have attempted to workaround this by synchronizing changes via 12RCS/CVS/RSYNC/RDIST/RCP/SCP and even NFS. Many have asked for a Hesiod, NIS, 13or LDAP patch for sudo, so here is my attempt at LDAP'izing sudo. 14 15Definitions 16=========== 17Many times the word 'Directory' is used in the document to refer to the LDAP 18server, structure and contents. 19 20Many times 'options' are used in this document to refer to sudoer 'defaults'. 21They are one and the same. 22 23Design Features 24=============== 25 26 * Sudo no longer needs to read sudoers in its entirety. Parsing of 27 /etc/sudoers requires the entire file to be read. The LDAP feature of sudo 28 uses two (sometimes three) LDAP queries per invocation. It never reads all 29 the sudoer entries in the LDAP store. This makes it especially fast and 30 particularly usable in LDAP environments. The first query is to parse 31 default options (see below). The second is to match against the username or 32 groups a user belongs to. (The special ALL tag is matched in this query 33 too.) If no match is made against the username, the third query pulls the 34 entries that match against user netgroups to compare back to the user. 35 36 * Sudo no longer blows up if there is a typo. Parsing of /etc/sudoers can 37 still blow up when sudo is invoked. However when using the LDAP feature of 38 sudo, LDAP syntax rules are applied before the data is uploaded into the 39 LDAP server, so proper syntax is always guaranteed! One can of course still 40 insert a bogus hostname or username, but sudo will not care. 41 42 * Options inside of entries now override global default options. 43 /etc/sudoers allowed for only default options and limited options associated 44 with user/host/command aliases. The syntax can be difficult for the newbie. 45 The LDAP feature attempts to simplify this and yet still provide maximum 46 flexibility. 47 48 Sudo first looks for an entry called 'cn=default' in the SUDOers container. 49 If found, the multi-valued sudoOption attribute is parsed the same way the 50 global 'Defaults' line in /etc/sudoers is parsed. 51 52 If on the second or third query, a response contains a sudoRole which 53 matches against the user, host, and command, then the matched object is 54 scanned for a additional options to override the top-level defaults. See 55 the example LDAP content below for more information. 56 57 * Visudo is no longer needed. Visudo provides locking and syntax checking 58 against the /etc/sudoers file. Since LDAP updates are atomic, locking is no 59 longer necessary. Because syntax is checked when the data is inserted into 60 LDAP, the sudoers syntax check becomes unnecessary. 61 62 * Aliases are no longer needed. User, Host, and Command Aliases were setup 63 to allow simplification and readability of the sudoers files. Since the 64 LDAP sudoer entry allows multiple values for each of its attributes and 65 since most LDAP browsers are graphical and easy to work with, original 66 aliases are no longer needed. 67 68 If you want to specify lots of users into an entry or want to have similar 69 entries with identical users, then use either groups or user netgroups. 70 Thats what groups and netgroups are for and Sudo handles this well. 71 Alternately, one can just paste them all into the LDAP record. 72 73 If you want to specify lots of hosts into an entry, use netgroups or IP 74 address matches (10.2.3.4/255.255.0.0). Thats what netgroups are for and 75 Sudo handles this well. Or just past them all into the LDAP record. 76 77 If you want to specify lots of commands, use directories or wildcards, or 78 just paste them all into LDAP. That's what it's for. 79 80 * The /etc/sudoers file can be disabled. Paranoid security administrators 81 can now disallow parsing of any local /etc/sudoers file by an LDAP 82 sudoOption 'ignore_local_sudoers'. This way all sudoers can be controlled 83 and audited in one place because local entries are not allowed. 84 In fact, if this option is included in the cn=defaults object of LDAP, 85 sudo won't even look for a /etc/sudoers file. 86 87 * The sudo binary compiled with LDAP support should be totally backward 88 compatible and be syntactically and source code equivalent to its non 89 LDAP-enabled build. 90 91 92Build instructions 93================== 94The most simplest way to build sudo with LDAP support is to include the 95'--with-ldap' option. I recommend including the '--with-pam' option on those 96system with PAM so that if you decide to use LDAP for authentication, you won't 97need to recompile sudo. 98 99 $ ./configure --with-ldap --with-pam 100 101If your ldap libraries and headers are in a non standard place, you will need 102to specify them at configure time. 103 104 $ ./configure --with-ldap=/usr/local/ldapsdk --with-pam 105 106Sudo is tested against OpenLDAP's implementation. Other LDAP implementations 107may require adding '-lldif' to SUDO_LIBS in the Makefile. 108 109Your Mileage may vary. Please let Aaron Spangler <aaron@spangler.ods.org> 110know what combinations worked best for your OS & LDAP Combinations so we can 111improve sudo. 112 113More Build Notes: 114HP-UX 11.23 (gcc3) Galen Johnson <Galen.Johnson@sas.com> 115 CFLAGS="-D__10_10_compat_code" LDFLAGS="-L/opt/ldapux/lib" 116 117Schema Changes 118============== 119Add the following schema to your LDAP server so that it may contain sudoer 120content. In OpenLDAP, simply place this into a new file and 'include' it 121in your slapd.conf and restart slapd. For other LDAP servers, provide this 122to your LDAP Administrator. Make sure to index the attribute 'sudoUser'. 123 124 125 # 126 # schema file for sudo 127 # 128 129 attributetype ( 1.3.6.1.4.1.15953.9.1.1 130 NAME 'sudoUser' 131 DESC 'User(s) who may run sudo' 132 EQUALITY caseExactIA5Match 133 SUBSTR caseExactIA5SubstringsMatch 134 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) 135 136 attributetype ( 1.3.6.1.4.1.15953.9.1.2 137 NAME 'sudoHost' 138 DESC 'Host(s) who may run sudo' 139 EQUALITY caseExactIA5Match 140 SUBSTR caseExactIA5SubstringsMatch 141 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) 142 143 attributetype ( 1.3.6.1.4.1.15953.9.1.3 144 NAME 'sudoCommand' 145 DESC 'Command(s) to be executed by sudo' 146 EQUALITY caseExactIA5Match 147 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) 148 149 attributetype ( 1.3.6.1.4.1.15953.9.1.4 150 NAME 'sudoRunAs' 151 DESC 'User(s) impersonated by sudo' 152 EQUALITY caseExactIA5Match 153 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) 154 155 attributetype ( 1.3.6.1.4.1.15953.9.1.5 156 NAME 'sudoOption' 157 DESC 'Options(s) followed by sudo' 158 EQUALITY caseExactIA5Match 159 SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) 160 161 objectclass ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL 162 DESC 'Sudoer Entries' 163 MUST ( cn ) 164 MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoOption $ 165 description ) 166 ) 167 168 # 169 # Same thing as above, but imports better into SunONE or iPlanet 170 # (remove any leading spaces and save to a seperate file) 171 # 172 173 dn: cn=schema 174 attributeTypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME 'sudoUser' DESC 'User(s) who may run sudo' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'SUDO' ) 175 attributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME 'sudoHost' DESC 'Host(s) who may run sudo' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'SUDO' ) 176 attributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME 'sudoCommand' DESC 'Command(s) to be executed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'SUDO' ) 177 attributeTypes: ( 1.3.6.1.4.1.15953.9.1.4 NAME 'sudoRunAs' DESC 'User(s) impersonated by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'SUDO' ) 178 attributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME 'sudoOption' DESC 'Options(s) followed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'SUDO' ) 179 objectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL DESC 'Sudoer Entries' MUST ( cn ) MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoOption $ description ) X-ORIGIN 'SUDO' ) 180 181 182 183Importing /etc/sudoers to LDAP 184============================== 185Importing is a two step process. 186 187Step 1: 188Ask your LDAP Administrator where to create the ou=SUDOers container. 189(An example location is shown below). Then use the provided script to convert 190your sudoers file into LDIF format. The script will also convert any default 191options. 192 193 # SUDOERS_BASE=ou=SUDOers,dc=example,dc=com 194 # export SUDOERS_BASE 195 # ./sudoers2ldif /etc/sudoers > /tmp/sudoers.ldif 196 197Step 2: 198Import into your directory server. If you are using OpenLDAP, do the following 199if you are using another directory, provide the LDIF file to your LDAP 200Administrator. An example is shown below. 201 202 # ldapadd -f /tmp/sudoers.ldif -h ldapserver \ 203 > -D cn=Manager,dc=example,dc=com -W -x 204 205Example sudoers Entries in LDAP 206=============================== 207The equivalent of a sudoer in LDAP is a 'sudoRole'. It contains sudoUser(s), 208sudoHost, sudoCommand and optional sudoOption(s) and sudoRunAs(s). 209<put an example here> 210 211Managing LDAP entries 212===================== 213Doing a one-time bulk load of your ldap entries is fine. However what if you 214need to make minor changes on a daily basis? It doesn't make sense to delete 215and re-add objects. (You can, but this is tedious). 216 217I recommend using any of the following LDAP browsers to administer your SUDOers. 218 * GQ - The gentleman's LDAP client - Open Source - I use this a lot on Linux 219 and since it is Schema aware, I don't need to create a sudoRole template. 220 http://biot.com/gq/ 221 222 * LDAP Browser/Editor - by Jarek Gawor - I use this a lot on Windows 223 and Solaris. It runs anywhere in a Java Virtual Machine including 224 web pages. You have to make a template from an existing sudoRole entry. 225 http://www.iit.edu/~gawojar/ldap 226 http://www.mcs.anl.gov/~gawor/ldap 227 http://ldapmanager.com 228 229 There are dozens of others, some open source, some free, some not. 230 231 232Configure your /etc/ldap.conf 233============================= 234The /etc/ldap.conf file is meant to be shared between sudo, pam_ldap, nss_ldap 235and other ldap applications and modules. IBM Secureway unfortunately uses 236the same filename but has a different syntax. If you need to rename where 237this file is stored, recompile SUDO with the -DLDAP_CONFIG compile option. 238 239Make sure you sudoers_base matches exactly with the location you specified 240when you imported the sudoers. Below is an example /etc/ldap.conf 241 242 # Either specify a uri or host & port 243 #host ldapserver 244 #port 389 245 # 246 # URI will override host & port settings 247 # but only works with LDAP SDK's that support 248 # ldap_initialize() such as OpenLDAP 249 uri ldap://ldapserver 250 #uri ldaps://secureldapserver 251 # 252 # must be set or sudo will ignore LDAP 253 sudoers_base ou=SUDOers,dc=example,dc=com 254 # 255 # verbose sudoers matching from ldap 256 #sudoers_debug 2 257 # 258 # optional proxy credentials 259 #binddn <who to search as> 260 #bindpw <password> 261 # 262 # LDAP Protocol Version defaults to 3 263 #ldap_version 3 264 # 265 # Define if you want to use port 389 and switch to 266 # encryption before the bind credentials are sent 267 #ssl start_tls 268 # 269 # Additional TLS options follow that allow tweaking 270 # of the SSL/TLS connection 271 # 272 #tls_checkpeer yes # verify server SSL certificate 273 #tls_checkpeer no # ignore server SSL certificate 274 # 275 # If you enable tls_checkpeer, specify either tls_cacertfile 276 # or tls_cacertdir. 277 # 278 #tls_cacertfile /etc/certs/trusted_signers.pem 279 #tls_cacertdir /etc/certs 280 # 281 # For systems that don't have /dev/random 282 # use this along with PRNGD or EGD.pl to seed the 283 # random number pool to generate cryptographic session keys. 284 # 285 #tls_randfile /etc/egd-pool 286 # 287 # You may restrict which ciphers are used. Consult your SSL 288 # documentation for which options go here. 289 # 290 #tls_ciphers <cipher-list> 291 # 292 # Sudo can provide a client certificate when communicating to 293 # the LDAP server. 294 # Tips: 295 # * Enable both lines at the same time. 296 # * Do not password protect the key file. 297 # * Ensure the keyfile is only readable by root. 298 # 299 #tls_cert /etc/certs/client_cert.pem 300 #tls_key /etc/certs/client_key.pem 301 # 302 303Debugging your LDAP configuration 304================================= 305Enable debugging if you believe sudo is not parsing LDAP the way you think it 306it should. A value of 1 shows moderate debugging. A value of 2 shows the 307results of the matches themselves. Make sure to set the value back to zero 308so that other users don't get confused by the debugging messages. This value 309is 'sudoers_debug' in the /etc/ldap.conf. 310 311Parsing Differences between /etc/sudoers and LDAP 312================================================= 313There are some subtle differences in the way sudoers is handled once in LDAP. 314Probably the biggest is that according to the RFC, LDAP's ordering is 315arbitrary and you cannot expect that Attributes & Entries are returned in 316any order. If there are conflicting command rules on an entry, the negative 317takes precedence. This is called paranoid behavior (not necessarily the 318most specific match). 319 320Here is an example: 321 322 # /etc/sudoers: 323 # Allow all commands except shell 324 johnny ALL=(root) ALL,!/bin/sh 325 # Always allows all commands because ALL is matched last 326 puddles ALL=(root) !/bin/sh,ALL 327 328 # LDAP equivalent of Johnny 329 # Allows all commands except shell 330 dn: cn=role1,ou=Sudoers,dc=my-domain,dc=com 331 objectClass: sudoRole 332 objectClass: top 333 cn: role1 334 sudoUser: johnny 335 sudoHost: ALL 336 sudoCommand: ALL 337 sudoCommand: !/bin/sh 338 339 # LDAP equivalent of Puddles 340 # Notice that even though ALL comes last, it still behaves like 341 # role1 since the LDAP code assumes the more paranoid configuration 342 dn: cn=role2,ou=Sudoers,dc=my-domain,dc=com 343 objectClass: sudoRole 344 objectClass: top 345 cn: role2 346 sudoUser: puddles 347 sudoHost: ALL 348 sudoCommand: !/bin/sh 349 sudoCommand: ALL 350 351Another difference is that negations on the Host are User (or Runas) are 352currently ignorred. For example, these attributes do not work how they first 353seem. If you desperately want this to be changed, contact Aaron Spangler 354(aaron@spangler.ods.org). 355 356 # does not match all but joe 357 # rather, does not match anyone 358 sudoUser: !joe 359 360 # does not match all but joe 361 # rather, matches everyone including Joe 362 sudoUser: ALL 363 sudoUser: !joe 364 365 # does not match all but web01 366 # rather, matches all hosts including web01 367 sudoHost: ALL 368 sudoHost: !web01 369 370 371Configure your /etc/nsswitch.conf 372================================= 373At the time of this writing, sudo does not consult nsswitch.conf for the 374search order. But if it did, it would look like this: 375This might be implemented in the future. For now just skip this step. 376 377 sudoers: files ldap 378