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