Difference between revisions of "Talos"

From CSLabsWiki
(change contact)
Line 1: Line 1:
|ip_addr =
|ip_addr =
|contact_person = [[User:Rinaldaj|Anthony R.]], [[User:pop1040|Tom Withiam]], [[User:Jared|Jared Dunbar]]
|contact_person = ''to be determined; contact a member''
|last_update = ''Sept 2017''
|last_update = ''Sept 2017''
|services = LDAP, DHCP, DNS, Kerberos
|services = LDAP, DHCP, DNS, Kerberos

Latest revision as of 19:20, 3 December 2020

IP Address(es):
Contact Person: to be determined; contact a member
Last Update: Sept 2017
Services: LDAP, DHCP, DNS, Kerberos

Hostname: talos.cslabs.clarkson.edu, talos.cosi.clarkson.edu, dns1.cslabs.clarkson.edu, dns1.cosi.clarkson.edu
Operating system: Debian
NIC 1: Clarkson Network
MAC: ?
CPU: ?
RAM: ?

Talos is our authentication, DNS, and DHCP server. It hosts an LDAP database used to serve user information and credentials, the latter used by a Kerberos administration and KDC server it also serves. It is authoritative over the cslabs.clarkson.edu, 128.153.144/23, and 128.153.146/24 zones in DNS and DHCP, and is responsible for certain BOOTP information (like declaring the PXE servers). As the zone DNS server, it is necessarily at, one of two addresses which OIT expects to delegate DNS (the other being - see Atlas).

It had a troubling Summer of 2017, with the motherboard dying during a reboot after a removal of old accounts in mid May 2017, and then was in a mix of being up and down for about 8 weeks before being stable again in mid-July 2017, on new hardware. During this time, it was moved between 4 different VMs, before finally being moved to Bennu's old chassis (and nuking Fog). The SSD went to Mirror as a cache drive in the ZFS.

Thoughts are to move the service to a cluster of small SoC hardware with gigabit nics and have multiple backups all synced at all times.

The DNS and DHCP configurations are both stored on GitLab and used to version control the configurations, similar to the Ziltoid configuration for iptables version history.

The Central Authentication system is handled by Thomas W. and Anthony R.

The DHCP and DNS system is handled by User:Jared


Start with a minimal installation of Debian Jessie, and proceed to install some useful packages; e.g., htop, vim, sudo. This should be enough to get through the next few steps.

Setting up firewall and other stuff

Setting up LDAP

Before You Begin

If you'd like or are in a hurry, you can skip to Installation below. This may contain some valuable information for setup or troubleshooting, however.

First, a foreword about LDAP: the Lightweight Directory Access Protocol is a protocol designed to provide a speedy, reliable way for networked machines to retrieve small records (that are often ubiquitously needed for various purposes--not the least of which identification across clients). LDAP records (or entries, as I will use interchangeably) are arranged in a hierarchical manner in a one-to-many relationship--much like filesystem hierarchies. These records are identified by a unique string, reminiscent of an absolute file path, called the distinguished name, or DN, which is used consistently to refer to the record throughout the architecture. DNs, as written out, consist of one or more components, each with a key and value separated by an equal sign '=', and joined together with a comma ',' separator. DNs can (and regularly do) have embedded spaces. The base DN for an LDAP database is usually derived from a domain name--a sufficiently unique identifier--as a series of domain components, or DCs, which will look something like dc=example,dc=com for the hypothetical "example.com" domain. Records below the base DN are arranged with more specific components on the right, such as cn=admin,dc=example,dc=com. It's worth mentioning that you will encounter CN (common name), OU (organizational unit), and other more domain-specific keys such as uid and olcDatabase.

Records each possess attributes, a multi-map of string values to a sequence of string values. While it is entirely possible to have arbitrary attributes, records in LDAP normally have properties dictated by a schema. Such a schema consists of attribute types, which define the names of attributes with the kinds of values that can be stored in an attribute (its syntax), its sorting/collation order, its comparison semantics, whether or not it needs to be unique per record, and so forth, as well as classes, which consist of a set of required attribute types (by name), and a set of optional attribute types. For example, an attribute type is something like a "userPassword", which stores an Octet String, is compared via Octet String comparison, and need not be unique to a record, while an object class is something like "posixUser" which MAY have a "userPassword" but MUST have a "uidNumber" and a "gidNumber" (both of which have syntax Integer). Classes have the additional property that they may be structural, auxiliary, or abstract. A record must have one, and only one, structural class, but can possess zero or more auxiliary classes. Classes can participate in inheritance; any may inherit arbitrarily from abstract classes, whereas auxiliary and structural classes may only inherit from their own kind (in addition to abstract). For example, consider the following Python code:

class X(object):

class Mixin(object):

class Y(X, Mixin):

y = Y()

If we consider y to represent a record in the LDAP database, then Y would be its singular structural class, which inherits from the structural class X. Auxiliary classes are not well-represented in this language, except perhaps as well-known attributes defined on the object y itself. Beyond the properties that class Y defines (here absolutely none), the Mixin class can be thought of as an abstract class from which Y also inherits some properties. All of these ultimately inherit from object, whose analogue in LDAP is top, a class which is simultaneously (and confoundingly) abstract and structural--the only such class.

It would be wise to familiarize yourself with some important classes, amongst them "posixAccount", "posixGroup", "organizationalUnit", and "simpleSecurityObject", amongst others. These and many more are defined in various RFCs and included with the OpenLDAP installation (under cn=schema,cn=config, described metareferentially through LDAP access). A web-based tool for browsing local schemas will be installed much later in this tutorial.

Access to the database is controlled through a process called binding. It should be noted that LDAP makes no distinction between users and records; a connecting client can connect to any record it so chooses. Of course, the most obvious things to bind to are user accounts, which are represented as first-class objects in most major LDAP schemas. In the simple case, one can use "simpleSecurityObject" with OpenLDAP to make any object in any part of the database be bindable, but posixAccounts are more likely to be used regularly. A connection that is not yet bound is said to be anonymous.

Access control is another topic entirely. You should be well aware of OpenLDAP's access control mini-language, which reads fairly straightforward. Amongst other things to be aware of, some important points are:

  • The first matching "to" clause is selected, in the order it is specified in the database. In particular, a "to *" clause will cancel all further searches, because it will immediately match everything. As a rule of thumb, specify more specific "to" clauses first, followed by more generic ones, with "to *" at the end.
  • The "by" clauses match similarly; place "by *" at the end of the list.
  • Giving an access level of "none" (or, equivalently, omitting it entirely) will cause results (when trying to search a record) indistinguishable from that record not existing. You may want to be the root account to be sure you're getting the entire picture.

olcAccess lines are a little cumbersome; you are well advised to Keep It Simple, Stupid.

Finally, LDIF--the LDAP Data Interchange Format. It is standardized as RFC2849, which does a good job of being one of the more concise RFCs I've read (the examples at the end are particularly informative). Basically, the syntax is as follows:

  • As with most script-like languages, lines whose first non-whitespace character is a pound-sign "#" are ignored, and blank lines have no effect on parsing or performance outside of a production.
  • Record indications are headed by a "dn: " field, followed by the DN, on its own line. From there on, all attributes and modifications are applied to that DN. For non-empty LDIF files, this must be the first non-comment, non-blank line.
  • Attributes are indicated similarly, with key, ": ", and value, all strings. If the value is too long, it may wrap onto continuation lines, which begin with exactly one space. If the value contains possibly illegal characters, such as non-ASCII ordinals, control sequences that may disrupt the parser, and so forth, the key may be followed instead by two colons ":: ", and the value will be base64 encoded. Wrapping similarly applies to base64-encoded values. Multi-valued attributes are ordered in the order in which they appear, unless they have a leading integer in {angle brackets} preceding the value, in which case they are first stably sorted by that integer in ascending order. Outputs from ldapsearch will always contain this integer for multi-valued attributes.
  • Some attributes have special behavior when passed through certain tools; ldapmodify is the most prominent of these. This command expects the "changetype: " attribute to be one of "add", "replace", or "delete", which is then to be followed by one or more "add: ", "replace: ", or "delete: " attributes, specifying a data attribute name (which must then follow that specification for all but delete), terminated by a dash "-" on a line of its own. Multiple additions and/or replacements may be specified this way, but (AFAIK) they must consistently be the same operation.
  • While not technically LDIF (and there is an LDIF way of specifying this), ldapmodrdn uses a format without any colon-separated headers in which each pair of non-blank, non-comment lines is, in order:
    • An existent DN.
    • A relative DN (RDN), whose first n components will replace the first n components of the existent DN.


Setting up OpenLDAP

For this purpose, the instructions on the Debian Wiki are invaluable, though somewhat flawed. Presented here is a slightly more instruction-oriented and pedantic version.

First, become root (sudo or su). Though it is not mentioned much, all the commands in the setup assume you are root.

From a root terminal, apt-get install slapd ldap-utils. During this process, you may be prompted for an administrator password for the database. It does not, however, prompt you about certain other things, like the base DN for the main database. After the packages are installed, I recommend running dpkg-reconfigure -p low slapd and verifying the results. It may tell you that it will overwrite the database previously created--that's OK. Remember to set the password to something memorable, but you probably won't need it for a little while.

Using ldap-utils

Now would be a good time to familiarize yourself with the tools in ldap-utils. They mostly take the same options, permitting you to log in in various ways:

  • -H <URI> sets the URI to attempt to contact the server on.
  • -Y <AUTH> sets the SASL authentication method.
  • -D <DN> sets the DN to bind to (very probably "cn=admin,<your_dc_components>" after the setup)
  • -W will instruct the utility to prompt for a bind password, and is best used with -D above.

The exact method you choose will probably heavily depend on the URI in -H. In particular, ldapi:/// represents a Unix domain socket, and allows one to log in using the UIDs present on the local machine. By default, root has access to both the config database (at cn=config) and the regular database (under your dc components). As root, you will generally want to use <ldapcommand> -H ldapi:// -Y EXTERNAL to perform work. **Do not use this as a non-root user, especially on cn=config**; the default privileges restrict anyone but root from even knowing about the existence of the cn=config base DN.

Eventually, for some work (or testing), you might want to log in as the administrator you set up. This is usually accomplished with <ldapcommand> -H ldap://localhost/ -D "cn=admin,<dc_components>" -W. You will be prompted for the password you entered during setup. It should be noted that OpenLDAP, by default, will add this user DN to a special config attribute on the database called "olcRootDN", which means that (1) the password is dictated by "olcRootPW" on this database, and not necessarily by the password in "userPassword", and (2) cn=admin will have unrestricted access regardless of the settings of "olcAccess" on that database. (More on that later.)

The commands that are most important in this set are:

  • ldapsearch, which takes -b <baseDN> and an optional -s <scope>, which searches for the baseDN and returns results in LDIF format. Scope may be one of:
    • sub (default if omitted): returns the subtree--the baseDN and all of its descendants.
    • one: returns the baseDN and all of its immediate children.
    • children: returns only the children of the baseDN.
    • base: returns only the baseDN.
  • ldapmodify, which takes -f <file> (or input from stdin) and alters the database according to a modification (usually addition, replacement, or deletion of attributes).
  • ldapadd, which takes similar to the above, but adds records to the database.
  • ldapmodrdn, which moves records.
  • ldapdelete, which deletes records.

You may want to read through the search results in entirety to get a feeling for LDIF. Note well that ldapmodify with replacement will replace all values of a multivalue attribute, and that you are responsible for ensuring the correct ordering afterward--it cannot be used for insertion or replacement of a single element. For more on the syntax, I direct you to the man pages--each of which has at least one simple, contrived example that will get you pointed in the right direction. Keep in mind that, returning valid LDIF, ldapsearch may be used to transfer or convert databases in mass as well.


LDAP is a bit tedious to work with on its own, if you can't tell; the LDIF format, while standardized and textual, is quite verbose, and not easy to work with even in fairly powerful text editors (like vim :). More powerful users may prefer to use LDAP bindings in their preferred languages; OpenLDAP is itself a C library, and bindings are available for various scripting languages, including Python and PHP. Web UIs are also available, but many (as FreeIPA and others) impose additional structure on the LDAP database and introduce various schemas that may or may not interoperate well with the base standards. I prefer the direct access affored by phpldapadmin--written with said scripting language bindings--since it doesn't try to parse or specialize any of the data, and likely won't fail in unexpected ways if I end up breaking something.

First things first, in your root console, apt-get install phpldapadmin. This will also install apache2 if you haven't gotten that already. In any case, the apache2 instance will be configured such that phpldapadmin will be mounted on your site at /phpldapadmin/. I recommend changing this, as it is a well known path and likely to be hammered by various automated password crackers (which are probably readily aware of the existence of a cn=admin,<dc_components> account on LDAP anyway). To do so, go into /etc/apache2/conf-enabled and find phpldapadmin.conf, then find within that the following lines (near the top):

# Define /phpldapadmin alias, this is the default
<IfModule mod_alias.c>
    Alias /phpldapadmin /usr/share/phpldapadmin/htdocs

Change the first argument of the Alias as you see fit, then run /etc/init.d/apache2 restart in your root shell.

By now, if you hit the IP address of your LDAP server in your web browser, you should see either the default page (if you just installed apache2 with phpldapadmin) or whatever was mounted on your root before. Navigate to whatever you changed the Alias to (or /phpldapadmin if you didn't), and you should see a login present. (If not, try to fix that before continuing; doing so is beyond the scope of this document.)

Of course, phpldapadmin has some unfortunate defaults, using the well-known example domain "example.com" (dc=example,dc=com) as its default base DN. We'll need to change that. Navigate yourself to /etc/phpldapadmin and open config.php, searching for a line like the following:

/* Array of base DNs of your LDAP server. Leave this blank to have phpLDAPadmin
   auto-detect it for you. */

First off, if the line is commented with either # or //, uncomment it. Then proceed to change the DN in the single quotes to a DN of your desire. If you'd like, you can comma-separate more DNs (in single quotes) to add them. I like to add cn=config to this, and find that commenting the line out (to leave it blank) won't add this base for you.

While we're in here, it would be a good idea to change the URI as well; find:

/* Examples:
           (Unix socket at /usr/local/var/run/ldap) */

and (since we're on the same machine) change the URI to ldapi:///. This will prevent unnecessary network traffic and encodings by using a Unix domain socket for IPC.

Finally, you may want to find this line as well:

/* The DN of the user for phpLDAPadmin to bind with. For anonymous binds or
   'cookie','session' or 'sasl' auth_types, LEAVE THE LOGIN_DN AND LOGIN_PASS
   BLANK. If you specify a login_attr in conjunction with a cookie or session
   auth_type, then you can also specify the bind_id/bind_pass here for searching
   the directory for users (ie, if your LDAP server does not allow anonymous
   binds. */
#  $servers->setValue('login','bind_id','cn=Manager,dc=example,dc=com');

and either comment it out (as the line below it) or change it to a proper account. Note that I do not recommend revealing your cn=admin DN, so I recommend either commenting it out, or replacing it with a "template" that will be used to populate the login name field (like "cn=,ou=users,<dc_components>") so that regular users have an easier time logging in.

Administrating cn=config

If you've added cn=config to your root DNs, you will need to permission them properly. This deserves its own subsection, because it will probably be the first (and hopefully last) time you have to get your hands dirty with the ldap-utils tools. Get back on your root terminal, and run something to the effect of ldapsearch -Y EXTERNAL -H ldapi:/// -b 'olcDatabase={0}config,cn=config'. The following should spit out on your terminal:

# {0}config, config
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcRootDN: cn=admin,cn=config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by * break

This means that only local root can do anything (including even see) this database. We would like to make sure that something we can bind to remotely can do this configuration (we're not running a web browser on our headless server, right?). Luckily, it's pretty easy; retrieve your last command with Up and redirect its output to a file (anywhere--/tmp is a good place); then, edit it until it looks something like this:

# {0}config, config
dn: olcDatabase={0}config,cn=config
changetype: replace
replace: olcAccess

olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by dn.exact="cn=admin,<dc_components>" manage by * break

The green sections are bits you'll want to add yourself. And, yes, you'll want to delete all the other cruft as well. Once you're done doing that, write the file back out and run it through ldapmodify -Y EXTERNAL -H ldapi:/// -f <file>. The changes will be commited, and your cn=admin account should have access to cn=config now (for remote configuration).

Setting up SSL

Note: While these instructions suffice for a quick-and-dirty setup, it should be noted that COSI now has its own CA certificate, and it is used properly in the real setup. See that page for more details.

If you're going to be using phpldapadmin over an untrusted or insecure network, it's generally a good idea to setup SSL/TLS on the webserver. In the most recent configuration, it's pretty simple; in your root terminal, run a2enconf ssl to create the relevant symlinks. Then, create a certificate. It's a bit of a process to go through to create a real CA and a real service certificate, or to get it from some other CA, so (for now) we can stick with a simple self-signed certificate, which can be made with the somewhat arduous command line:

openssl req -new -x509 -days 1000 -keyout key/phpldapadmin.key -out crt/phpldapadmin.crt -nodes

This more or less breaks down to the following:

  • -new creates a certificate request or certificate,
  • -x509 makes it into a certificate instead of a request,
  • -days controls its validity period,
  • -keyout is where you want the key to go,
  • -out is where you want the certificate generated to go,
  • -nodes prevents the key from being encrypted--Apache will need to read it plainly. You'll probably want to chmod 600 this key.

Finally, find your default site in /etc/apache2/sites-enabled and add anywhere in the VirtualHost specification:

SSLEngine on
SSLCertificateFile "/path/to/crt/phpldapadmin.crt"
SSLCertificateKeyFile "/path/to/key/phpldapadmin.key"

Restart Apache (/etc/init.d/apache2 restart) and direct your browser to that server with the HTTPS protocol. You will be warned about an invalid certificate (browsers do not like self signed certificates even with all else good), but once you confirm an exception, your communication with the server will be properly encrypted.

Now that you have a key and certificate, you can also take a moment to setup LDAPS--which will be important if you plan on using LDAP authentication for remote services, as this means passwords may traverse the network. The easiest way I've found to do so is to stop the service (/etc/init.d/slapd stop), and find the file /etc/ldap/slapd.d/cn=config.ldif, adding amongst the LDIF (in proper format, which isn't too hard):

olcTLSCertificateFile: /path/to/crt/phpldapadmin.crt
olcTLSCertificateKeyFile: /path/to/key/phpldapadmin.key

Then, in /etc/default/slapd, add "ldaps:///" to the SLAPD_HOSTS. Once you start slapd, you should be able to confirm (e.g., with lsof -i tcp) that it is listening on LDAPS.

Configuring the database

Now that you have the ability to administrate all the important parts of your database from the web interface, you can drop your root shell and grab your web browser. Direct it to the site (HTTPS, preferably) and log in as your cn=admin account. We can move on to the fun part now :)

Presumably, you have access to cn=config now, and it should be in phpldapadmin. Go into olcDatabase={1}whatever,cn=config and make sure that the olcAccess lines look reasonable (they're generally pretty readable). If you want to change something, feel free to do so now or later. If you look down in that record, you'll also see olcRootDN, initialized to your cn=admin account. This means you will never lose administrative access to the database when bound as that DN, so keep in mind that you will be overriding access restrictions while doing this.

Now would be a fun time to start adding users and groups--we'll use posixAccounts and posixGroups, respectively. Since posixAccounts require a mandatory GID, we'll be creating a group first.

It should be noted that, with the posix* accounts, resolving authentication groups is non-trivial; posixAccount objects hold no information as to their group membership--all members are listed in the groups. This means you should keep the number of groups small, as the number of requests needed to resolve a group and its users is constant time (one request), but the time required to resolve a user and its groups is linear with the total number of groups. Keep that in mind.

Before we add a posixGroup, let's get organized. Create an Organizational Unit (OU) under your dc components, and call it "groups" or something of the sort. (Think of an OU as a filesystem directory.) Now, under that OU, create a new (child) entry, and let it be a posixGroup.

From within phpldapadmin, you can click on the "Schema" link to see the schemas of the LDAP classes and attribute types--we can try this now with our posixGroup (don't worry, you can create it later, too). Click "Schemas", where you'll see an exhaustive list of object classes, and select "posixGroup" from the dropdown. It should show us that we REQUIRE "cn" (common name--the name of the group as it will appear everywhere) and gidNumber (its numeric ID), and that the attributes "description", "memberUid" (the usernames of member users--a multivalued attribute), and "userPassword" (see man newgrp) are OPTIONAL. Now that we know what we need, we can do the thing :)

Go back into ou=groups,<dc_components> and create a child entry. You can use either phpldapadmin's automatic Posix Group template, or just use Default--both are just about as easy, though the template for Posix Group will suggest a gidNumber for you. I prefer control over the GID namespace (especially because phpldapadmin chose 500, well within the system block of GIDs), so I'd use Default. Configure away--and try to choose very high numbers for your numeric IDs so they don't conflict with the lower, 1000+ IDs typically used for local accounts :)

Setting up Kerberos

Before you begin

Kerberos is a trusted-third-party PKI which provides cryptographically strong proof of authenticity for both services and users of those services. It is not based in X.509, like OpenSSL, but rather an implementation of a distinct protocol.

In the usual case, Kerberos is pretty elegantly simple; a KDC server runs two services, one for the usual Kerberos protocol (that involves distributing tickets), and one for administration (e.g., things like users changing passwords). The central entities of Kerberos are this KDC, the principals (which are like, but not exactly corresponding to users--a user can have multiple principals with different privileges, and a service can own principals too), and tickets, which are ephemeral certificates of authenticity issued by the KDC.

Kerberos' cryptography is much stronger than LDAP, in the sense that LDAP would gladly send the password for a BIND operation in plaintext if you configured it to do so (which is why setting up LDAPS is a good idea). Furthermore, as bona fide certificates, if the key for a ticket is leaked, it only remains compromised until the expiry of that ticket. Of course, the user's password can still be compromised, which will allow an attacker to get as many tickets as needed...

Configuring Kerberos to work together with LDAP requires two points of contact:

  1. LDAP may be configured as the database backend for Kerberos. Note that the storage for Kerberos principals must, in the most straightforward setup, be distinct from the set of (POSIX) users. They are two different entities.
  2. Kerberos principals will be named to coincide with LDAP users, despite the two being very different entities. Yes, this does result in a little data duplication (of usernames, and particularly grievously, of passwords), but the two systems work together surprisingly well once properly configured; LDAP provides most of the groups and passwd data, aside from the password for authentication, which is provided by Kerberos.

So, why use Kerberos if it takes more work and requires more out of the LDAP database for doing nothing more than replicating one of its present features? In one word: NFS. Kerberos tickets are the only sensible way to secure an NFS share such that an attacker with theoretically arbitrary packet inspection/injection capabilities cannot interfere with (or, if so chosen, exfiltrate) data in a file share. Because local machines are issued one--and only one--ticket per user, compromise of that machine still will only grant enough privileges to modify the share on behalf of all of the users whose tickets are present on that machine. For public machines, such as the lab machines, with which just about every user is trusted with root access, this usually limits the damage to the sole user at the time (usually the one who sudo'd), and thus exposes no additional privileges in that case. Of course, NFS isn't the only service which can be protected thusly; Kerberos tickets can be negotiated through HTTP by various UAs, and a quick search for "kerberos" in the APT repositories shows plenty of software ready to take advantage of that, and theoretically any software that links against the Kerberos libraries can use these security features to their benefit. Nonetheless, at the time of this writing, NFS and Microsoft's AD remains the only (and perhaps most important) users of the Kerberos standards and protocol.


Getting a KDC up and running is actually quite straightforward, especially on Debian. Basically, as root, apt-get install krb5-admin-server. You will probably be prompted to set up some things like a master password and a few default principals; you can safely ignore these, as we're about to switch the backends anyway. (If you don't want to use an LDAP backend, though, everything is very much already configured.)

Making it work with LDAP as a database backend requires a little more contrivance. You'll want to also get the krb5-kdc-ldap package, which will contain some tools for helping set up the database, particularly kdb5_ldap_util. You'll want to go ahead and run that now, using a command somewhat like:

kdb5_ldap_util create -subtrees "ou=kerberos,dc=my,dc=domain,dc=com" -r "MY.DOMAIN.COM" -D "cn=admin,dc=my,dc=domain,dc=com"

Where ou=kerberos,dc=my,dc=domain,dc=com is the subtree that will hold the realm (and is to be created--if it exists already, this will fail!). Make sure that cn=admin,dc=my,dc=domain,dc=com is replaced with an actual LDAP account with permissions to edit the database in that way (namely, creating some new subtrees). MY.DOMAIN.COM will be, of course, the Kerberos realm.

During this process, you will be prompted to enter a master password. Make a very strong one and keep it in a very safe place.

With that underway, we need to tell the administrative servers to use LDAP as their backend. Find the /etc/krb5kdc/kdc.conf file and make it look something like this (on Talos, at least):

    kdc_ports = 750,88

        database_module = openldap_ldapconf
        acl_file = /etc/krb5kdc/kadm5.acl
        key_stash_file = /etc/krb5kdc/stash
        kdc_ports = 750,88
        max_life = 10h 0m 0s
        max_renewable_life = 7d 0h 0m 0s
        master_key_type = des3-hmac-sha1
        supported_enctypes = aes256-cts:normal arcfour-hmac:normal des3-hmac-sha1:normal des-cbc-crc:normal des:normal des:v4 des:norealm des:onlyrealm des:afs3
        default_principal_flags = +preauth

    openldap_ldapconf = {
        db_library = kldap
        ldap_kerberos_container_dn = "cn=kerberos,dc=my,dc=domain,dc=com"
        ldap_kdc_dn = "cn=kerberos,ou=services,dc=my,dc=domain,dc=com"
        ldap_kadmind_dn = "cn=kerberos,ou=services,dc=my,dc=domain,dc=com"
        ldap_service_password_file = /etc/krb5kdc/service.keyfile
        ldap_server = ldapi:///

Note the existence of a cn=kerberos,ou=services,... bind DN. This should be an account that has access to change this part of the LDAP database and a reasonably strong password; you'll want to make this account elsewhere (and you can make it whatever bind DN you like, of course). The password itself will be stashed in the various, user-secret files stash and service.keyfile, by using the following command:

kdb5_ldap_util stashsrvpw "cn=kerberos,ou=services,dc=my,dc=domain,dc=com"

With that in place, you should check kinit and kpasswd to make sure they're working fine, and you have an operational KDC!

Populating Kerberos

At this point, you might have an LDAP database with a few user entries, but probably not any operational principals. This is something you'll want to fix. Get into a terminal and run kadmin.local--since you're doing this on the actual administrative server, it will bypass any restrictions and immediately give you an administrative prompt. From here, let's add a principal:

kadmin: addprinc username

It should prompt you for a password, grouse a little about using a sane default, and proceed to make the principal. That's really all there is to it.

At some point, you'll want to probably make some administrative accounts so you can do kadmin normally. If you have a default principal on a machine (such as "username" in this case), MIT Kerberos will automatically assume that you want to elevate to "username/admin" to do administrative work. Indeed, the default installation of MIT Kerberos has "*/admin" be administrative, so let's get to it:

kadmin: addprinc username/admin

Now wherever you may be in the network, you should be able to kadmin -p username/admin and do administrative things. You'll need this later to enroll hosts for GSS services. Until then, go ahead and have fun with adding any other users that need accounts--users in your LDAP database may be a good place to start. (A script for automating this is left as an exercise to the reader, in part because it will require the invention of automatic passwords.)


GSSAPI is an interesting beast that requires not only user principals as you've been adding above, but also machine credentials. In particular, the clients generally have to have a key on them that simultaneously authenticates (and identifies) them as well as provides them secure access to the services. The server, on the other hand, needs its own key of this sort. Now that you can kadmin from anywhere, you can go ahead and make them from anywhere, but you'll want to know the naming scheme first:

GSSAPI keys for services have principals of the form "<service-name>/<dns-name>", whereas machine keys for clients usually just use "host" as the service. For example, NFS servers use "nfs" as a service name, so you'll probably want to spit out a couple of keys like this:


...and so forth. If you don't have differential access for hosts on your network (as you probably shouldn't--user credentials should be the ones verified), you can take a shortcut and just make one host key for the entire domain:


You can look at man rpc.gssd on Debian to see what formats of machine key the GSS daemon will look for in the key tables (keytabs).

Adding these keys can be done with addprinc, but you probably don't need to care about the password. You can ask kadmin to create a random password that you don't care about with:

kadmin: addprinc -randkey nfs/nfs-server.domain.com

...and so forth with the rest of the keys.

Now, on the hosts that you want to add these to the keytabs (yes, you will need to use kadmin on those machines, locally, with your credentials), use the following to import the key into the default keytab:

kadmin: ktadd nfs/nfs-server.domain.com

That should basically do it for this step! Give out your host and service keys as needed--keys are pretty cheap :)

Kerberizing NFS

We've come to the important part--we need NFS to work with Kerberos security. It is at this point that I will mention that this setup was done on a different machine--namely, Bacon, which was running FreeBSD 10.1 as opposed to Linux--furthermore, this particular FreeBSD box was very much non-standard to begin with. The default on FreeBSD is Heimdal Kerberos, so you'll want to somehow get and make sure you can run MIT Kerberos (the default, it seems, on the Linuces). The two systems don't interoperate well, especially in user interface, so be careful with your $PATH. I'll assume that the MIT tools are in your path, but it's worth mentioning that, on Bacon, the tools need to be absolutely referenced in /usr/local/bin/.

If you haven't done it already, or if you've done it wrong, use the steps above to get the service key for NFS into the local keytab. Then, modify the /etc/exports file to include the following parameter on all lines that you want to export with proper protection:


The colon (:) is a separator character, and this forms a list; other valid types are sys and none, which provide little (if any) cryptographically-guaranteed security. The Kerberos securities are given as krb5, which just verifies the user for the RPC tunnel (somewhat vulnerable to MitM attachs), krb5i which adds a little overhead but guarantees message integrity, and krb5p which adds a lot of overhead but encrypts messages sent over the network. More often than not, krb5i is fairly suitable.

Restart NFS, and make sure that the GSS daemon (gssd on FreeBSD) is running before you restart NFS--FreeBSD's nfsd requires this.

Go to your clients, and make sure the keytabs contain the host keys as mentioned. Again, start rpc.gssd (as it is on Debian), and make sure it is running somewhere. You may wish to pass it -f -v -v -v -v in another terminal so as to watch debugging output for troubleshooting; now try this:

mount -t nfs4 -o sec=krb5i nfs-server.domain.com:/mount/point/ /mnt

If all goes well, this should mount the remote /mount/point/ at the local /mnt. For troubleshooting, you can add -v to get more output from the mount command.

Working with PAM, NSS, and NFS

This content has been moved; see How to add Kerberos to a Debian Machine.