Archive
Blog - Technical - posts for March 2014
Mar 30 2014
Installation: OpenLDAP + Active Directory
I have an environment which mixes some users on Active Directory with some users who have straight Linux accounts. This doesn't always work well with authentication mechanisms, though, as going with standard mechanisms for one or the other has its own set of quirks (ignoring that going with one or the other would also leave out users). Authenticating against AD's LDAP can be a little strange, due to Windows Server's quirks. Authenticating against passwd/shadow requires either getting PAM working (which can be finicky) or running daemons as root (which is dangerous).
So instead, combine the two using a different tool! OpenLDAP supports use of multiple database definitions, of which one is a proxy. And on top of that, you can join them together by making one subordinate to the other so that both databases are searched.
Fortunately, this MSDN blog article has a lot of information that applies on connecting OpenLDAP to AD. But:
- The MSDN blog article is rather vague about how to set up LDAPS. There is a Technet article, but it refers to a specific MachineKey without explaining how to identify it.
- Figuring out the MachineKey for a certificate is described over here, on the Directory Services Team blog.
- Windows Server Core doesn't have Explorer to modify file/directory permissions, so you also need to know how to use icacls.
- Windows Server only passes back the client certificate, and unfortunately not the full certificate chain, so you end up needing to specify TLS_CACERT instead of just TLS_CACERTDIR.
The MSDN blog article primarily concerned about saslauthd access, though, rather than OpenLDAP connectivity, so this only gets us as far as having the infrastructure to query AD over LDAPS (which is valuable, though!).
If you put together several pieces of information out there, it's possible to now set up OpenLDAP with all the pieces we need. Inconveniently, public documentation seems to be largely out of date, and very fragmented. A lot of it refers to using slapd.conf, but that's been deprecated in favour of slapd.d. Yet oddly enough, users on openldap-technical seem to be perfectly okay with taking a slapd.conf, converting it to cn=config format, then shoving it in....
Notes from here on were gathered on CentOS 6.x - if you're following along, you may need to make allowances for your own distribution.
Pieces of documentation used to put together the OpenLDAP configuration (starting with slapd.conf, then converting to LDIF):
- Integrate Active Directory and OpenLDAP
- Overall documentation on putting together slapd.conf, and particularly the pointer about subordinate.
- OpenLDAP Server
- The Ubuntu OpenLDAP server tutorial, very useful for how to go about setting up the backend database, and part of the reason I selected HDB instead of BDB, which remains the default on CentOS.
Using these pages as a basis, I pulled together a slapd.conf looking roughly like this (if you want to use it as a basis for your own slapd.conf, you'll need to pay attention to all TODO entries):
include /etc/openldap/schema/cosine.schema
include /etc/openldap/schema/inetorgperson.schema
include /etc/openldap/schema/microsoft.schema
include /etc/openldap/schema/nis.schema
# Allow LDAPv2 client connections. This is NOT the default.
allow bind_v2
pidfile /var/run/openldap/slapd.pid
argsfile /var/run/openldap/slapd.args
modulepath /usr/lib64/openldap
moduleload rwm
# The next three lines allow use of TLS for encrypting connections using a
# dummy test certificate which you can generate by running
# /usr/libexec/openldap/generate-server-cert.sh. Your client software may balk
# at self-signed certificates, however.
TLSCACertificatePath /etc/openldap/certs
TLSCertificateFile "\"OpenLDAP Server\""
TLSCertificateKeyFile /etc/openldap/certs/password
# TODO: Replace all instances of dc=domain,dc=local with your own.
defaultsearchbase "dc=domain,dc=local"
database config
access to *
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by * none
# enable server status monitoring (cn=monitor)
database monitor
access to *
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
by dn.exact="cn=admin,dc=domain,dc=local" read
by * none
###
# database definitions##
#
database ldap
suffix "cn=users,dc=domain,dc=local"
uri "ldap://ad.domain.local"
subordinate
# TODO: Replace with proper credentials.
idassert-bind
bindmethod=simple
binddn="cn=bindbot,cn=Users,dc=domain,dc=local"
credentials=""
idassert-authzFrom "*"
overlay rwm
rwm-map objectclass account user
rwm-map attribute uid sAMAccountname
rwm-map attribute cn cn
rwm-map attribute sn sn
rwm-map attribute uidNumber uidNumber
rwm-map attribute gidNumber gidNumber
rwm-map attribute homeDirectory unixHomeDirectory
rwm-map attribute loginShell loginShell
rwm-map attribute mail mail
rwm-map attribute *
access to dn.subtree="dc=domain,dc=local"
by * read
database hdb
suffix "dc=domain,dc=local"
checkpoint 512 30
rootdn "cn=admin,dc=domain,dc=local"
# Cleartext passwords, especially for the rootdn, should
# be avoided. See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
# rootpw secret
# rootpw {crypt}ijFYNcSNctBYg
# TODO: Add a rootpw.
rootpw {SSHA}
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory /var/lib/ldap
# Indices to maintain for this database
index objectClass eq,pres
index ou,cn,mail,surname,givenname eq,pres,sub
index uidNumber,gidNumber,loginShell eq,pres
index uid,memberUid eq,pres,sub
index nisMapName,nisMapEntry eq,pres,sub
You may have noticed that there's a reference to a microsoft.schema at the top. That's to help resolve some otherwise unresolved LDAP types. The file follows, with the first two entries from Apple, of all places, and the third being a localized (i.e. using a local Object Identifier) version of the standard NIS homeDirectory entry):
NAME 'user'
SUP organizationalPerson
STRUCTURAL )
attributetype ( 1.2.840.113556.1.4.221
NAME 'sAMAccountName'
SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
SINGLE-VALUE )
attributetype ( 1.1.2.1.1
NAME 'unixHomeDirectory'
SYNTAX '1.3.6.1.4.1.1466.115.121.1.26'
SINGLE-VALUE )
The rewrite/remap entries in slapd.conf for the AD LDAP proxy refer to a few non-standard properties. These are controlled via the Unix Attributes tab in AD, but in Windows Server 2012 R2, that's enabled by installing Identity Management for UNIX.
Once the slapd.conf is ready to go, it's pretty simple to generate a slapd.d directory:
$ slaptest -f slapd.conf.reference -F slapd.d
This generates a series of LDIF files which can be massaged a bit more, which is where this documentation comes in very handy:
- LDAP for Rocket Scientists
- The ZYTRAX guide to LDAP and LDIF was absolutely key to figuring out how to get slapd.d and LDIF working properly.
Combined with the above documentation, I generated three LDIF files (which, again, you'll need to change with your own information):
objectClass: olcSchemaConfig
cn: microsoft
olcAttributeTypes: {0}( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX 1.
3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
olcAttributeTypes: {1}( 1.1.2.1.1 NAME 'unixHomeDirectory' EQUALITY caseExactI
A5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcObjectClasses: {0}( 1.2.840.113556.1.5.9 NAME 'user' SUP organizationalPers
on STRUCTURAL )
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib64/openldap
olcModuleLoad: {0}rwm
dn: olcDatabase={2}ldap,cn=config
objectClass: olcDatabaseConfig
objectClass: olcLDAPConfig
olcDatabase: {2}ldap
# TODO: Replace all instances of dc=domain,dc=local with your own.
olcSuffix: cn=users,dc=domain,dc=local
olcSubordinate: TRUE
olcDbURI: "ldap://ad.domain.local"
olcDbRebindAsUser: TRUE
# TODO: Replace with proper credentials.
olcDbIDAssertBind: bindmethod=simple binddn="cn=bindbot,cn=users,dc=domain,dc=
local" credentials="" tls_cacert=/etc/openldap/certs/ad.crt
olcDbIDAssertAuthzFrom: *
olcAccess: {0}to dn.subtree="dc=domain,dc=local" by * read
dn: olcOverlay=rwm,olcDatabase={2}ldap,cn=config
objectClass: olcOverlayConfig
objectClass: olcRwmConfig
olcOverlay: rwm
olcRwmMap: {0}objectclass account user
olcRwmMap: {1}attribute uid sAMAccountname
olcRwmMap: {2}attribute cn cn
olcRwmMap: {3}attribute givenname givenName
olcRwmMap: {4}attribute sn sn
olcRwmMap: {5}attribute uidNumber uidNumber
olcRwmMap: {6}attribute gidNumber gidNumber
olcRwmMap: {7}attribute homeDirectory unixHomeDirectory
olcRwmMap: {8}attribute loginShell loginShell
olcRwmMap: {9}attribute mail mail
olcRwmMap: {10}attribute *
dn: olcDatabase={3}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {3}hdb
olcSuffix: dc=domain,dc=local
olcDbDirectory: /var/lib/ldap
olcRootDN: cn=admin,dc=domain,dc=local
# TODO: Add a rootpw.
olcRootPW: {SSHA}
olcDbConfig: set_cachesize 0 2097152 0
olcDbConfig: set_lk_max_objects 1500
olcDbConfig: set_lk_max_locks 1500
olcDbConfig: set_lk_max_lockers 1500
olcDbIndex: objectClass eq
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcAccess: to attrs=userPassword by dn="cn=admin,dc=domain,dc=local" write by anonymous auth by self write by * none
olcAccess: to attrs=shadowLastChange by self write by * read
olcAccess: to dn.base="" by * read
olcAccess: to * by dn="cn=admin,dc=domain,dc=local" write by * read
dn: olcOverlay=glue,olcDatabase={3}hdb,cn=config
objectClass: olcOverlayConfig
olcOverlay: glue
changetype: modify
add: olcTLSCACertificateFile
# TODO: The location of your domain server's CA certificate.
olcTLSCACertificateFile: /etc/openldap/certs/ad.crt
-
replace: olcTLSCertificateFile
# TODO: The location of your LDAP server's public certificate.
olcTLSCertificateFile: /etc/openldap/certs/ldap.domain.local.crt
-
replace: olcTLSCertificateKeyFile
# TODO: The location of your LDAP server's private key.
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap.domain.local.key
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
# TODO: Replacer all instances of dc=domain,dc=local with your own.
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=externa
l,cn=auth" read by dn.base="cn=admin,dc=domain,dc=local" read
by * none
# Create top-level object in domain
dn: dc=domain,dc=local
objectClass: top
objectClass: dcObject
objectClass: organization
# TODO: Your organization's name.
o: Organization
# TODO: Your top-level domain component.
dc: domain
# Admin user
dn: cn=admin,dc=domain,dc=local
objectclass: simpleSecurityObject
objectclass: organizationalRole
cn: admin
description: LDAP administrator
# TODO: Add a user password.
userPassword: {SSHA}
dn: ou=people,dc=domain,dc=local
objectClass: organizationalUnit
ou: people
dn: ou=groups,dc=domain,dc=local
objectClass: organizationalUnit
ou: groups
Importing the LDIFs is relatively straightforward, although cleaning up what we don't want is a good idea:
$ rm -f /etc/openldap/slapd.d/cn\=config/olcDatabase\={2}bdb.ldif
# Remove the existing LDAP database, if it exists.
$ rm -f /var/lib/ldap/*
$ ldapadd -v -Y EXTERNAL -H ldapi:/// -f backend.ldif
$ ldapmodify -v -Y EXTERNAL -H ldapi:/// -f admin.ldif
# The next command will require your administrator's LDAP password.
$ ldapadd -x -D cn=admin,dc=domain,dc=local _W -f frontend.ldif
When you put your certificate files in /etc/openldap/certs, if you're running with SELinux (which you probably should), make sure that the file security contexts are correct, particularly for the CA certificate (otherwise, OpenLDAP won't be able to connect to your AD server):
Known Issues:
The connection to AD is unencrypted. I'm working on how to have it connect via ldaps:// instead. ldapsearch works without a problem, so it's puzzling why slapd fails. Further updates to come if/when I figure this out. Bitten by SELinux! See note above about restorecon.
Mar 28 2014
The return of iOS
Within the next few weeks, I expect that I'll be back on iOS. It's been a while since I've used it, but I've had experience with a number of different mobile operating systems at this point:
- Android (up through 4.2)
- DangerOS
- iOS (up through 5, occasional exposure up through 7)
- Windows Phone (7)
- Windows Mobile (up through 6.5)
Up until now, I've been using Android on a Samsung Note II. Which has gone reasonably well, but a few things have caused me to give up on Android:
- Replacing the home screen with Google Now.
- My device has been relatively stable... except for the past couple of months, it's been hard-locking repeatedly. Sometimes several times a day.
- I looked at a Note II because it was an attempt at trying something different with a mobile device. Unfortunately, several of the unique aspects just haven't worked out:
- Stylus: I've barely used it.
- Screen size: Larger than most phones, but not big enough to use for couch browsing.
- Popup browser: Although active in Android 4.0, it was disabled in Android 4.2. Unfortunately, one of the things I liked.
In general, though, I've concluded that Android, at least at this point in time, isn't for me. At least some chunk of that is due to Google's business practices (Android is open... except when it's not). So back to a different walled garden for me - but at least it's one that doesn't pretend otherwise.
Mar 16 2014
An exercise in bad documentation
I'm... irritated. I've just spent several hours trying to figure out how to have Jetty use an SSL certificate generated wholly externally, all because the Jetty documentation is... less than complete.
The start line:
- One PKCS12 file with a X.509 certificate, its private key, and its chain of trust, encrypted with a password.
- Generated from Windows Server, but the answer isn't "Don't use Windows". Yes, it's sometimes a perfectly valid answer. But not this time.
Things not obvious:
- jetty-ssl.xml refers to KeyStorePassword (easy enough), KeyManagerPassword (what's that?), and TrustStorePassword (huh?).
- The Jetty instructions for importing a PKCS12 file only talk about keystore passwords. But that leads to a situation where Jetty complains it can't read the necessary keys.
- If you'd like to leverage the default Java Jetty keystore (particularly if you have a secure system), you need to know the keystore's password. Which isn't provided anywhere... although an Internet search will tell you.
- After much digging around, maybe it's because for the key pair, the private key can have a password? Except having a blank password doesn't work, and setting it to the keystore's password doesn't work. Odd.
- The SSL documentation for Jetty 8 refers to a key pair password. What happened to that configuration key?
- So... somehow, "password" became "KeyManagerPassword". But what's the default password for that? It takes another Internet search to find that. Or maybe you're just supposed to look in the source code?
Spending several hours over this makes me not happy.
P.S. There's conveniently a command to import a PKCS12 file directly into a keystore. You can use it to rename the key in the process, since Jetty wants a key called... jetty.
Mar 15 2014
Internal scattershot
A challenge when performing service refactoring is that you now need to start worrying a lot more about connection security. When everything's on one box, and you're connecting via ports on localhost, you don't generally need to worry much. However, once you start splitting out services, relying on trust and unencrypted connections becomes less viable.
Here are some notes regarding PostgreSQL, as an example:
- Your users should be already set up with encrypted passwords (you did, right?).
- Next, you need to prepare SSL certificates for PostgreSQL, assuming that your installation has SSL support (CentOS compiles it in by default), install them in the correct location, and enable them in postgresql.conf.
- If your internal CA is based on Windows Server, it's a little trickier to generate the certificate and its private key. I prefer to generate the certificate request on the CA with an .inf file, import the certificate on the CA, export the public and private key from the CA, and then convert the .pfx file into a .crt and .key file on the target host.
- Once you have your SSL certificates up and running, the next step is to define PostgreSQL connection restrictions (using hostssl, of course).
- From there, it's a matter of adjusting your database connection strings appropriately.
- PHP uses the sslmode parameter, although it's odd that the value list is in a comment and not in the documentation.
- JDBC uses the ssl parameter, although since Java keeps its own CA database, you may need to import your CA certificate into the Java truststore.
Mar 11 2014
Linux networking miscellania
A few interesting things I've stumbled across while finalizing some of my migration preparations from my previous server to the new one
- If you have multiple network adapters, your default route might not be the one you want. At least in CentOS, you can configure that in /etc/sysconfig/network with GATEWAY and GATEWAYDEV. This could be the problem if you're finding that you can't connect to your system (ping, ssh, etc.) even when it's capable of reaching the outside world... and even more, that if you disable your internal network adapter, everything "magically works." netstat -r/route -e might reveal that your default gateway isn't the one you think it is.
- Brute force ssh attacks aren't fun, and they've been pretty common for a while. True, they're probably not going to hack into your box (You have disabled root ssh access, right? And you don't have any of the standard accounts they try to hack in with?), but they can be rather annoying. Fortunately, it's actually pretty easy to use iptables to limit the number of ssh connections per minute.