Annotation of wikisrc/tutorials/openldap_authentication_on_netbsd.mdwn, revision 1.5

1.2       schmonz     1: **Contents**
                      2: 
                      3: [[!toc levels=3]]
                      4: 
                      5: #  Author's note 
                      6: 
                      7: This document really describes (what I remember of installing) my system, with tidbits I've forgotten from various sources on the net. I can't guarantee that following this document you'll get a working system, but I hope it will provide some insights into how the thing is supposed to work. 
                      8: 
                      9: Staffan Thom´en <duck@shangtai.net>
                     10: 
                     11: #  Server setup 
                     12: 
                     13: First things first, you'll need to set up an openldap server somewhere, this is fairly straightforward, as it's available in pkgsrc. The tricky bit is really configuring the ACL:s, since the openldap logs are incredibly hard to read. Generally it's probably a good idea to firewall it from outside and worry about the ACL setup later if you want to do things like let other departments to see your users or let the public see contact information for example. 
                     14: 
                     15: An example config file is included in the package (${LOCALBASE}/etc/opeldap/slapd.conf), and the only thing that really has to be added is to include some schemas for user authentication: 
                     16:     
                     17:     cosine.schema
                     18:     inetorgperson.schema
                     19:     nis.schema
                     20:     
                     21: 
1.3       tron       22: These are (in pkgsrc-2014Q1) installed in ${LOCALBASE}/share/examples/openldap/schema, and can just be included from there, and tells the server which record keys (as in key-value pairs) it shall accept. 
1.2       schmonz    23: 
                     24: And that really is it for the server bit. Next comes testing it out with a few ldap commands. 
                     25: 
                     26: The basic commands of talking directly with the ldap database are ldapadd, ldapmodify and ldapsearch. These are in the openldap-client package, so you won't have to install the entire server on a client machine. 
                     27: 
1.3       tron       28: Options you'll be using alot like -b (base) and -H (host URI) can conveninently be stuck in a client configuration file, ${PKG_SYSCONFBASE}/openldap/ldap.conf, which will save you time and aggravation from having to type them all the time. 
1.2       schmonz    29: 
                     30: To talk to your ldap server, try running ldapsearch; 
                     31:     
                     32:     % ldapsearch -H ldap://my.server/
                     33:     
                     34: 
                     35: This really means dump everything, but since we've nothing in the database it will respond with an error. 
                     36: 
                     37: To set this database up for user authentication, we'll need to lay down some structure. LDAP is generally a hierachial database of records with key-value pairs. We'll first need to tell it about our organisation and then add a user. 
                     38: 
                     39: Here we'll be using ldapadd, which reads a format called ldif. It is a flat text format that looks something like this: 
                     40:     
                     41:      dn: cn=example,dc=org
                     42:      objectClass: dcObject
                     43:      objectClass: organization
                     44:      objectClass: top
                     45:      o: Example Organisation
                     46:      dc: example
                     47:     
                     48:     
                     49:      dn: ou=groups,dc=example,dc=org
                     50:      objectClass: top
                     51:      objectClass: organizationalUnit
                     52:      ou: groups
                     53:     
                     54:     
                     55:      dn: ou=people,dc=example,dc=org
                     56:      objectClass: top
                     57:      objectClass: organizationalUnit
                     58:      ou: people
                     59:     
                     60: 
                     61: The text above define three records, they start with a distinguished name of the record (dn:), which is a unique identifier for the record. 
                     62: 
                     63: "cn=example,dc=org" is the root of this organisation, with a common name (cn) example and a domain component (dc) of org. Next come the objectClass lines which tells us that this is domain component object, an organisation object and a top-level object. We then have an organisation (o:) line which is a descriptive text and finally a domain component line (dc:) which is the stored value for the dc (same as in the distinguished name). 
                     64: 
                     65: Following this are two records which define something called in ldap terms organisational units, and as you see from the dn:, essentially two branches of the main tree. They are here to be used for the user groups (yes, like /etc/groups) and the actual users. 
                     66: 
                     67: If you want you can just stick all of this in one file (even the user below) and add it with ldapadd -f file.ldif, this will create the initial structure of your database. 
                     68: 
                     69: Adding a group and then a user user is no more difficult, you just have to fill out the right fields. 
                     70:     
                     71:     dn: cn=ldapusers,ou=groups,dc=example,dc=org
                     72:     objectClass: top
                     73:     objectClass: posixGroup
                     74:     cn: ldapusers
                     75:     gidNumber: 101
                     76:     memberUid: bill
                     77:     memberUid: george
                     78:     
                     79: 
                     80: A group named ldapusers with the number 101, and the secondary users bill and george (these are of course not required). 
                     81:     
                     82:     dn: uid=test,ou=people,dc=example,dc=org
                     83:     objectClass: top
                     84:     objectClass: posixAccount
                     85:     objectClass: inetOrgPerson
                     86:     uid: test
                     87:     uidNumber: 2000
                     88:     gidNumber: 101
                     89:     o: Example Organisation
                     90:     cn: Test User
                     91:     givenName: Test
                     92:     sn: User
                     93:     gecos: Test User,3b,+358800128128,+35801234567
                     94:     loginShell: /bin/ksh
                     95:     homeDirectory: /home/test
                     96:     mail: test@example.org
                     97:     displayName: El Magnifico Test User
                     98:     
                     99: 
                    100: A user with the uid test, belonging to group ldapusers (101); o: is the same as the root record above and the others apart from sn (surname) is fairly obvious. The GECOS field contains comma separated values, apparently it's pulled straight into the client system. 
                    101: 
                    102: The fields actually required by the schemes are: 
                    103:     
                    104:     uid
                    105:     uidNumber
                    106:     gidNumber
                    107:     cn
                    108:     sn
                    109:     homeDirectory
                    110:     
                    111: 
                    112: LDAP can store multiple roots and each user entry for example can be more than just the login information, as above it also mentions email, phone numbers and so on for our test user, and it can also include binary data like a mugshot and them playing the corporate theme on banjo. As far as authentication is concerned, we've got what we want though. 
                    113: 
                    114: So far so good, this should not cause much trouble to set up, I believe I've covered everything required; the thing I had most problem with in relation to the database itself was that it was so unstructured, you have to provide all the structure yourself. 
                    115: 
                    116: #  Client Setup 
                    117: 
                    118: In order to log in on a NetBSD system we need to provide two things, a way for the system to authenticate you and a way for it to find out what your group, user id, etc. is. 
                    119: 
                    120: The first part of this, authentication is taken care of by PAM (or possibly by some BSD auth scheme, but this is not yet implemented as far as I know.) 
                    121: 
                    122: The second part is done via libc and the NSS subsystem. 
                    123: 
                    124: In order to do this, we need to provide some plugins for either system that enables LDAP support in them. The plugins are in pkgsrc and are called 
                    125:     
                    126:     security/pam-ldap
                    127:     
                    128: 
                    129: and 
                    130:     
                    131:     databases/nss_ldap
                    132:     
                    133:     
1.3       tron      134: The latest version of these packages (pkgrsc-2014Q2 and newer) will automatically created the necessary symbolic links in /usr/lib and /usr/lib/security to be able to use these modules. For older version you will have create a symbolic link from /usr/lib/nss_ldap.so.0 to ${LOCALBASE}/lib/nss_ldap.so.1 and from /usr/lib/security/pam_ldap.so to ${LOCALBASE}/lib/security/pam_ldap.so
1.2       schmonz   135: 
                    136: Before we go any further, I'd like to introduce some security into the mix; up til now we've talked to the ldap server without any limitations and what's called anonymous binds, i.e. not logged in. 
                    137: 
                    138: XXX can anonymous binds actually write to a db without ACLs? 
                    139: 
                    140: This is an ldap user, just like the test user outlined above, since the ldap database can authenticate against itself. (You don't have to, but I haven't explored the other possibilities such as SASL) 
                    141: 
                    142: So we'll create a user called nss 
                    143:     
                    144:     dn: cn=nss,dc=example,dc=org
                    145:     objectClass: top
                    146:     objectClass: inetOrgPerson
                    147:     o: Example Organisation
                    148:     cn: nss
                    149:     sn: manager
                    150:     
                    151: 
                    152: We'll attach a password so that not just anyone can connect, and also change our LDAP configuration slightly so that we use encrypted passwords. 
                    153:     
                    154:     userPassword: {SSHA}w5aocfmGgZqq3h8AjvaZiw8WKdrRTjTi
                    155:     
                    156: 
                    157: To generate this password I use (bundled with openldap-server) slapdpasswd 
                    158:     
                    159:     % slappasswd -h "{SSHA}"
                    160:     
                    161:     
                    162: 
                    163: And in slapd.conf add 
                    164:     
                    165:     passsword-hash {SSHA}
                    166:     
                    167: 
                    168: And of course you'll need to change the secret for the rootpw into something encrypted. 
                    169: 
                    170: Note that the traffic between the ldap client and the server is still not (that is if you've been following this document) encrypted so this might be best to perform locally. 
                    171: 
                    172: This user will be used for ACL filtering later. 
                    173: 
1.3       tron      174: Next we'll need to configure the LDAP part of the plugins, a convenience here is that since both the plugins are made by the same people, they can share a configuration file. They will look for ${PKG_SYSCONFBASE}/nss_ldap.conf and ${PKG_SYSCONFBASE}/pam_ldap.conf, but linking them to the same file will let you have just one place to configure (and protect for your ldap user password) 
1.2       schmonz   175: 
                    176: The important bits in this file is the base setting and the uri for your ldap server: 
                    177:     
                    178:     base dc=example,dc=org
                    179:     
                    180:     
                    181:     uri ldap://my.server/
                    182:     
                    183: 
                    184: Next we need to tell it who it should contact the ldap database as: 
                    185:     
                    186:     binddn cn=nss,dc=example,dc=org
                    187:     
                    188:     
                    189: And if you want to be able to change passwords as root without knowing the user's password in advance (with passwd, using ldapmodify you can still just set it, if you bind with the credentials to do it (see ACLs).) 
                    190: 
                    191: I haven't mentioned this user before, it's the database's root user, allowed to do anything; 
                    192:     
                    193:     rootbinddn cn=root,dc=example,dc=org
                    194:     
                    195: 
1.3       tron      196: The password for this will not be in this file, but in a separate file called ${PKG_SYSCONFBASE}/nss_ldap.secret or for pam; ${PKG_SYSCONFBASE}/pam_ldap.secret 
1.2       schmonz   197: 
                    198:   * ) not sure about this, but my system has both, linked together 
                    199: 
                    200: Finally we will set the password exchange method to exop; 
                    201:     
                    202:     pam_password exop
                    203:     
                    204: 
                    205: This is the OpenLDAP extended method and while the passwords will still be sent in the clear, they are encrypted with the database's scheme in the database. 
                    206: 
                    207: So while you can use ldapsearch to get the data (though ACLs can prevent this if properly set up) it will still only be a hash. 
                    208: 
                    209: That's it for configuring the plugins so far. 
                    210: 
                    211: #  NSS 
                    212: 
                    213: The next change we will need to do is to enable the ldap module in nsswitch.conf: 
                    214: 
                    215: Change 
                    216:     
                    217:     group:     files
                    218:     ...
                    219:     passwd:    files
                    220:     
                    221: 
                    222: To 
                    223:     
                    224:     group:     files ldap
                    225:     ...
                    226:     passwd:    files ldap
1.3       tron      227:     ...
                    228:     netgroup:   files ldap
1.2       schmonz   229: 
1.3       tron      230: This will enable you to have local accounts as well as LDAP users. You could test this out now, by running the getent program; 
1.2       schmonz   231:     
                    232:     % getent group
                    233:     
                    234: 
                    235: Will present you with a list of all the groups in the system, with the ldap group 'ldapusers' we created earlier tacked on to the end of the list. 
                    236:     
                    237:     % getent passwd
                    238:     
                    239: 
                    240: And this will show you the user list, with the ldap user 'test' at the end. 
                    241: 
                    242: #  PAM 
                    243: 
                    244: PAM keeps it's configuration files in /etc/pam.d/, these are divided into individual files per each pam service in the system; most are just including system but some need special attention. 
                    245: 
                    246: On my system I have the following changes from the stock netbsd setup: 
                    247: 
                    248: ##  /etc/pam.d/sshd 
                    249:     
                    250:     #
                    251:     # PAM configuration for the "sshd" service
                    252:     #
                    253:      
                    254:     # auth
                    255:     auth            required        pam_nologin.so  no_warn
                    256:     auth            sufficient      pam_krb5.so     no_warn try_first_pass
1.3       tron      257:     auth            sufficient      pam_ldap.so     no_warn try_first_pass
1.2       schmonz   258:     # pam_ssh has potential security risks.  See pam_ssh(8).
                    259:     #auth           sufficient      pam_ssh.so      no_warn try_first_pass
                    260:     auth            required        pam_unix.so     no_warn try_first_pass
                    261:      
                    262:     # account
                    263:     account         required        pam_krb5.so
                    264:     account         required        pam_login_access.so
                    265:     account         required        pam_unix.so
                    266:      
                    267:     # session
                    268:     # pam_ssh has potential security risks.  See pam_ssh(8).
                    269:     #session        optional        pam_ssh.so
                    270:     session         required        pam_permit.so
                    271:      
                    272:     # password
                    273:     password        sufficient      pam_krb5.so     no_warn try_first_pass
1.3       tron      274:     password        sufficient      pam_ldap.so     no_warn try_first_pass
1.2       schmonz   275:     password        required        pam_unix.so     no_warn try_first_pass
                    276:     
                    277: 
                    278: ##  /etc/pam.d/su 
                    279:     
                    280:     #
                    281:     # PAM configuration for the "su" service
                    282:     #
                    283:      
                    284:     # auth
                    285:     auth            sufficient      pam_rootok.so           no_warn
                    286:     auth            sufficient      pam_self.so             no_warn
                    287:     auth            sufficient      pam_ksu.so              no_warn try_first_pass
1.3       tron      288:     #auth           sufficient      pam_group.so            no_warn group=rootauth root_only fail_safe authenticate
1.2       schmonz   289:     auth            requisite       pam_group.so            no_warn group=wheel root_only fail_safe
1.3       tron      290:     auth            sufficient      pam_ldap.so             no_warn try_first_pass
1.2       schmonz   291:     auth            required        pam_unix.so             no_warn try_first_pass nullok
                    292:      
                    293:     # account
                    294:     account         required        pam_login_access.so
                    295:     account         include         system
                    296:      
                    297:     # session
                    298:     session         required        pam_permit.so
                    299:     
                    300: 
                    301: ##  /etc/pam.d/system 
                    302:     
1.5     ! tron      303:     # $NetBSD: openldap_authentication_on_netbsd.mdwn,v 1.4 2014/06/04 20:56:19 tron Exp $
1.2       schmonz   304:     #
                    305:     # System-wide defaults
                    306:     #
                    307:      
                    308:     # auth
                    309:     auth            sufficient      pam_krb5.so             no_warn try_first_pass
1.3       tron      310:     auth            sufficient      pam_ldap.so             no_warn try_first_pass
1.2       schmonz   311:     auth            required        pam_unix.so             no_warn try_first_pass nullok
                    312:      
                    313:     # account
                    314:     account         required        pam_krb5.so
                    315:     account         required        pam_unix.so
                    316:     
                    317:     # session
                    318:     session         required        pam_lastlog.so          no_fail no_nested
                    319:      
                    320:     # password
1.3       tron      321:     password        sufficient      pam_krb5.so             no_warn try_first_pass
                    322:     password        sufficient      pam_ldap.so             no_warn try_first_pass
                    323:     password        sufficient      pam_unix.so             no_warn try_first_pass
1.2       schmonz   324:     password        required        pam_deny.so             prelim_ignore
                    325:     
                    326: 
                    327: The last bit here with pam_deny, is a bit special, it is what enables you to change passwords for both local users and those in the ldap database with the passwd command. pam_deny with the prelim_ignore flag is needed, else pam will will fail in the preliminary phase (it is always run trough twice) and you will not be able to change passwords. 
                    328: 
                    329: In order to use this you need to patch your pam_deny (/usr/src/lib/libpam/modules/pam_deny.c) with the patch by Edgar Fuß <ef@math.uni-bonn.de>: 
                    330: 
                    331: <http://mail-index.netbsd.org/tech-userlevel/2007/08/29/0001.html>
                    332: 
                    333: The original message describing the problem is here: 
                    334: 
                    335: <http://mail-index.netbsd.org/tech-userlevel/2007/08/25/0006.html>
                    336: 
                    337: 
1.5     ! tron      338: ##  /etc/pam.d/sudo 
1.4       tron      339: 
                    340:     #
                    341:     # PAM configuration for the "sudo" service
                    342:     #
                    343:     
                    344:     # auth
                    345:     auth            sufficient      pam_ldap.so             no_warn try_first_pass
                    346:     auth            required        pam_unix.so             no_warn try_first_pass nullok use_uid
                    347:     
                    348:     # account
                    349:     account         required        pam_login_access.so
                    350:     account         include         system
                    351:     
                    352:     # session
                    353:     session         required        pam_permit.so
                    354:     
                    355: This file is only required if you want to use the "sudo" package from "pkgsrc".
                    356: You will have to compile this package manually with "PKG_OPTIONS.sudo" set to
                    357: "pam" because it doesn't support PAM by default.
                    358:     
1.2       schmonz   359: #  Securing your system 
                    360: 
                    361: As far as the document goes now, this setup is unprotected in that anyone listening in to the packets travelling trough your network would be able to find the unencrypted messages of your ldap users. Not a happy thought. 
                    362: 
                    363: So we'll want to enable SSL encryption of the traffic between your clients and the server. 
                    364: 
                    365: In order to do this you will need to create an SSL certificate for your server and also distribute it to the client machines, so that they will be able to certify the authenticity of the server. 
                    366: 
                    367: We'll also need to configure slapd to use it, I put my keys in the /etc/openssl hierachy, since it seemed made for it. 
                    368:     
1.3       tron      369:     TLSCipherSuite          HIGH
1.2       schmonz   370:     TLSCertificateFile      /etc/openssl/certs/openldap.pem
                    371:     TLSCertificateKeyFile   /etc/openssl/private/openldap.pem
                    372:     TLSCACertificateFile    /etc/openssl/certs/openldap.pem
                    373:     
                    374: 
1.3       tron      375: Next we'll need to change the clients setup so that they will use encryption. Enable ssl in ${PKG_SYSCONFBASE}/{nss_,pam_}ldap.conf; 
1.2       schmonz   376:     
1.3       tron      377:     ssl start_tls
1.2       schmonz   378: 
1.3       tron      379: Next if you're like me using the ${PKG_SYSCONFBASE}/openldap/ldap.conf file, telling the client libs where to find the cert file is enough, we don't have to put it in the nss/pam config: 
1.2       schmonz   380:     
                    381:     TLS_CACERT /etc/openssl/certs/openldap.pem
                    382:     
                    383: 
                    384: If you can still use getent, encryption is happening. You can of course also tcpdump your network traffic to see what's going on. 
                    385: 
                    386: #  ACL 
                    387: 
                    388: I left access control lists of the server to the last, because they are the easiest to get wrong and often cause problems that you might attribute to other things in the various setups. 
                    389: 
                    390: The syntax is fairly straightforward; 
                    391:     
                    392:     acceess to [something] by [someone] [access]
                    393:     
                    394: 
                    395: The order is important; if something matches, later tests will not be run. 
                    396: 
                    397: The one I use looks like this: 
                    398:     
                    399:      #
                    400:      # Protect passwords from prying eyes
                    401:      #
                    402:      access to attrs=userPassword
                    403:        by dn="cn=nss,dc=example,dc=org" write
                    404:        by anonymous auth
                    405:        by self write
                    406:        by * none
                    407:      
                    408:      #
                    409:      # set read-only attributes
                    410:      #
                    411:      access to attrs=uidNumber,gidNumber,uid,homeDirectory
                    412:        by dn="cn=nss,dc=example,dc=org" write
                    413:        by self read
                    414:        by * read
                    415:      
                    416:      #
                    417:      # For all else, let the user edit his own entry and everyone else watch
                    418:      #
                    419:      access to *
                    420:        by dn="cn=nss,dc=example,dc=org" write
                    421:        by self write
                    422:        by * read
                    423:      
                    424:     
                    425: 
                    426: Note that access to the user password can be set to auth; so that the database can authenticate a user without letting them see the password hash using an anonymous bind. 
                    427: 

CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb