osixia / docker-openldap

OpenLDAP container image 🐳🌴
MIT License
4.01k stars 974 forks source link

Integrated kerberos KDC configuration #319

Open beargiles opened 5 years ago

beargiles commented 5 years ago

I'm trying to integrate Kerberos KDC configuration and wanted to add an issue to track progress, ask help, and probably vent a bit. :-) I've checked the earlier kerberos issues.

Ideally this will be integrated into the one-time setup and controlled by an env var like 'USE_KDC=1'

Here's what I have so far... oddly the openldap container fails the first time I run docker-compose up but succeeds afterwards. I'll update this later unless I get so frustrated with Docker that I end up finishing this up with an ansible script.

Preparation

  1. Download the kerberos source (apt-get source krb5). /usr/share/doc/krb5-kdc-ldap is similar but doesn't include kerberos.openldap.ldif. That file strips out some things already present in a standard openldap configuation.

  2. Copy src/plugins/kdb/ldap/libkdb_ldap/kerberos.schema to bootstrap/schema

  3. Copy src/plugins/kdb/ldap/libkdb_ldap/kerberos.openldap.ldif to bootstrap/ldif

  4. Create krb5 directory

  5. Add krb5/kdc.conf

    [libdefaults]
        default_realm = EXAMPLE.COM
    
        kdc_timesync = 1
        ccache_type = 4
        forwardable = true
        proxiable = true
    
    [realms]
        EXAMPLE.COM = {
                kdc = localhost
                admin_server = localhost
                default_domain = example.com
        }
    
    [domain_realm]
        .example.com = EXAMPLE.COM
        example.com = EXAMPLE.COM
  6. Add krb5/kdc.conf

    [kdcdefaults]
        kdc_ports = 750,88
    
    [realms]
        EXAMPLE.COM = {
            database_module = LDAP
            # database_name = /var/lib/krb5kdc/principal
            admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
            # 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 aes128-cts:normal
            default_principal_flags = +preauth
        }
    
    [dbmodules]
        LDAP = {
            db_library = kldap
            ldap_kerberos_container_dn = cn=krbContainer,dc=example,dc=com
            ldap_kdc_dn = cn=admin,dc=example,dc=com
            ldap_kadmind_dn = cn=admin,dc=example,dc=com
            ldap_service_password_file = /etc/krb5kdc/ldap.stash
            ldap_servers = ldapi:///
        }
  7. Add this to Dockerfile

    ADD krb5/krb5.conf /etc/krb5.conf
    ADD krb5/kdc.conf /etc/krb5kdc/kdc.conf

Configuration

This is still done manually. I don't know if there's already a hook, similar to bootstrap, that allows us to run an arbitrary script before or after the openldap server is up.

Note: best practices would probably include creating an LDAP entry specifically for the KDC and KADMIND admin roles instead of reusing cn=admin.

  1. Add ou=users. (And ou=systems ?)

  2. Stash ldap key. I don't know if you can specify the

    kdb5_ldap_util stashsrvpw cn=admin,dc=example,dc=com
  3. Initialize the KDC entries in LDAP

    kdb5_ldap_util -D cn=admin,dc=snapdevteam,dc=com create -subtrees ou=users,dc=example,dc=com -r EXAMPLE.COM -s
  4. Start service

    sudo service krb5-kdc start

Verify

  1. Start container

  2. Verify /etc/ldap/slapd.d/cn=config/cn=schema contains cn={5}kerberos.ldif.

  3. Run

    ldapsearch -L -x -D cn=admin,dc=example,dc=com -b dc=example,dc=com -W

You should see entries for

system:

cn=krbContainer
cn=EXAMPLE.COM,cn=krbContainer
krbPrincipalName=K/M@EXAMPLE.COM,cn=EXAMPLE.COM,...
krbPrincipalName=krbtgt/EXAMPLE.COM,cn=EXAMPLE.COM,...

people:

krbPrincipalName=kadmin/adminEXAMPLE.COM,cn=EXAMPLE.COM,...
krbPrincipalName=kadmin/changepw@EXAMPLE.COM,cn=EXAMPLE.COM,...
krbPrincipalName=kadmin/history@EXAMPLE.COM,cn=EXAMPLE.COM,...

servers:

krbPrincipalName=kadmin/ldap-hosts@EXAMPLE.COM,cn=EXAMPLE.COM,...
krbPrincipalName=kiprop/ldap-hosts@EXAMPLE.COM,cn=EXAMPLE.COM,...

Questions

  1. How do I turn on indexing by krbPrincipalName?

  2. How do I add the ACL file that restricts access to LDAP entries:

    access to dn.base=""
        by * read
    
    access to dn.base="cn=Subschema"
        by * read
    
    access to attrs=userPassword,userPKCS12
        by self write
        by * auth
    
    access to attrs=shadowLastChange
        by self write
        by * read
    
    # Providing access to realm container
    access to dn.subtree= "cn=EXAMPLE.COM,cn=krbcontainer,dc=example,dc=com"
        by dn.exact="cn=kdc-service,dc=example,dc=com" write
        by dn.exact="cn=adm-service,dc=example,dc=com" write
        by * none
    
    # Providing access to principals, if not underneath realm container
    access to dn.subtree= "ou=users,dc=example,dc=com"
        by dn.exact="cn=kdc-service,dc=example,dc=com" write
        by dn.exact="cn=adm-service,dc=example,dc=com" write
        by * none
    
    access to *
        by * read

Notes

  1. Some documentation also refers to misc.schema and misc.ldif.
  2. The cosine.schema at https://github.com/openldap/opendap seems to be a lot bigger than the cosine.schema bundled in the Debian package. Among other things the former one seems to contain some PKI (X.509 certificates) absent from the latter. That's important since we often want LDAP + Kerberos + public key.
beargiles commented 5 years ago

Progress update....

I decided it would be easier to create a fork and pull request than list a bunch of suggested changes. The way the code is shaking out is:

  1. Stuff that should always be added since there's minimal impact and it's a good start if people want to tweak the files later.

The krb5 and kdc files are the standard files with appropriate changes so that we can use substitution.

There are three new envvars.

  1. Stuff that's debatable and doesn't require a running LDAP server.

On the plus side startup.sh knows the LDAP admin password. On the minus side anyone who's not using KDC is not going to be happy to have the admin password stashed, esp. if it's in plaintext.

  1. Stuff that should be conditional AND requires the LDAP server to be running.

I'm not deeply knowledgeable about LDAP schemas - I think we can have a mix of entries that do/don't list the objectClass krbPrincipal but I'm not sure. Unfortunately that class lists krbPrincipalName as a mandatory attribute so I can't add it to these ou's without making a pretty critical assumption.

Recommendation

Add #1 in startup.sh. It has minimal impact and is a good base if anyone wants to tweak the config later.

Add #2 and #3 with a post-startup script triggered by a 'USE_KRB' envvar. It should be in a place that's easily overridden, similar to assets/config/ldif/custom. An arbitrary number of scripts should be supported - the 'USE_KRB' should be tested in this script.

Open Questions

  1. How to handle calling the extra post-startup scripts. I'll probably submit a pull request with the script explicitly called and let you generalize it.

  2. krb5_ldap_util prompts the console for the admin password to stash and for the new master Kerberos password. I'm pretty sure that can be handled in the bash script - I've pinged my coworkers on this.

beargiles commented 5 years ago

Yay, figured out how to update the ldap admin password stash.

  coproc kdb5_ldap_util stashsrvpw cn=admin,${LDAP_BASE_DN}
  echo ${LDAP_ADMIN_PASSWORD} >&${COPROC[1]}
  echo ${LDAP_ADMIN_PASSWORD} >&${COPROC[1]}

I can do the same thing when creating the ldap entries but it needs to be executed when the ldap server is up.

The total changes to date to startup.sh are

  #
  # Kerberos
  #
  if [ -z "$KRB_REALM" ]; then
    KRB_REALM=${LDAP_DOMAIN^^}
  fi

  function create_krb5_config (){
    local FILE=$1
    log-helper debug "Processing file ${FILE}"
    sed -i "s|{{ LDAP_DOMAIN }}|${LDAP_DOMAIN}|g" $FILE
    sed -i "s|{{ LDAP_BASE_DN }}|${LDAP_BASE_DN}|g" $FILE
    sed -i "s|{{ KRB_REALM }}|${KRB_REALM}|g" $FILE
    cp $FILE $2
  }

  #
  # Create configuration files
  #
  create_krb5_config ${CONTAINER_SERVICE_DIR}/slapd/assets/config/krb5/krb5.conf /etc/krb5.conf
  create_krb5_config ${CONTAINER_SERVICE_DIR}/slapd/assets/config/krb5/kdc.conf /etc/krb5kdc/kdc.conf

  # -- going beyond this point should be conditional

  #
  # Cache credentials
  # Create initial KDC entries (requires running LDAP server)
  #
  coproc kdb5_ldap_util stashsrvpw cn=admin,${LDAP_BASE_DN}
  echo ${LDAP_ADMIN_PASSWORD} >&${COPROC[1]}
  echo ${LDAP_ADMIN_PASSWORD} >&${COPROC[1]}

  #  kdb5_ldap_util -D cn=admin,${LDAP_BASE_DN} -w ${LDAP_ADMIN_PASSWORD} -H ldapi:// create \
  #       -subtrees ou=users,${LDAP_BASE_DN}:ou=servers,${LDAP_BASE_DN} -sscope SUB -r ${KRB_REALM}
beargiles commented 5 years ago

I've created a pull request with my changes. https://github.com/osixia/docker-openldap/pull/322

I've been successfully running kinit and kadmin (not just kadmin.local) on both the docker container and my desktop (with appropriate /etc/krb5.conf entries).

It's mostly automatic. There's only one manual step required since it can't be done until the LDAP server is up. It should only be run once and at first glance startup.sh is only run before the server comes up. The simple bash script is at /usr/local/bin/01-kdc.sh. It would be much better if it could be run automatically, conditional on something like ENABLE_KDC.

I also updated the documentation with some brief notes on both enabling a KDC and using Kerberos authentication when connecting to the LDAP server.

Source at https://github.com/beargiles/docker-openldap, 'kdc' branch.

whiskerch commented 5 years ago

Hi @beargiles

I've been looking at your PR as we need to run kerberos for our hdfs cluster,

A couple of changes I made to get the container running without intervention:

kdc.conf - line 5 - you have your realm hard coded. I changed the value to {{ KRB_REALM }} to pick up the correct realm name

startup.sh - slapd comes up quickly for me so I run the 01-kdc.sh 10 seconds later with this command: sleep 10 && /usr/local/bin/01-kdc.sh & I'm planning on making it smarter (adding retries, polling to see if the service comes up etc) but for the moment it does the job for me.

tsmethurst commented 4 years ago

Commenting here to stay in the loop.

deskoh commented 4 years ago

I've created a pull request with my changes. #322

I've been successfully running kinit and kadmin (not just kadmin.local) on both the docker container and my desktop (with appropriate /etc/krb5.conf entries).

It's mostly automatic. There's only one manual step required since it can't be done until the LDAP server is up. It should only be run once and at first glance startup.sh is only run before the server comes up. The simple bash script is at /usr/local/bin/01-kdc.sh. It would be much better if it could be run automatically, conditional on something like ENABLE_KDC.

I also updated the documentation with some brief notes on both enabling a KDC and using Kerberos authentication when connecting to the LDAP server.

Source at https://github.com/beargiles/docker-openldap, 'kdc' branch.

@beargiles Thanks for your great work!

For manual running of 01-kdc.sh, do consider taking a look here.

I was doing a Keycloak demo with MIT Kerberos / OpenLDAP and managed to get something working based on your work. See here for the script to start KDC and Kadmin.

ygalblum commented 2 years ago

@beargiles, thanks for your effort, it is exactly what I am looking for. But, is this effort still active? If not for merging here, is your fork ready?