Self Hosted: Episode 6 - Authentication

Software used:

  • FreeBSD 14.2 (jail)
  • OpenLDAP 2.6 (from ports)
It has been said: "Three people can keep a secret, if two are dead." Fortunately, it doesn't need to come to that.

Authentication

As with many things in computing there needs to be a source of truth. This is true for user accounts. Where we have one account, we will need more. Good, bad, or indifferent, the defacto way of keeping user and group information is using LDAP.

Update DNS with new address

To begin with we will need to be able to resolve the new server. To do this update the example.org and 30.168.192.in-addr.arpa zone files on the DNS servers respectively.

ldap     IN      A      192.168.30.13

record for example.org zone file

13      IN      PTR      ldap.example.org.

record for the 30.168.192.in-addr.arpa zone file

Don't forget to update the zone serial number and restart the named service.

Setup LDAP server

Just like with the CA server, the LDAP server will be setup using a jail. If you have not already setup the Jail infrastructure review Episode 3 - Setting up FreeBSD Jail. For this jail we will be using the following file.

# LDAP server jail file

ldap {
	# STARTUP / LOGGING
	exec.start = "/bin/sh /etc/rc";
	exec.stop = "/bin/sh /etc/rc.shutdown";
	exec.consolelog = "/var/log/jail_console_${name}.log";

	# PERMISSIONS
	allow.raw_sockets;
	exec.clean;
	mount.devfs;
	allow.reserved_ports = true;

	# HOSTNAME / PATH
	host.hostname = "${name}";
	path = "/usr/local/jails/containers/${name}";

	# NETWORK
	vnet;
	vnet.interface = "${epair}b";

	# NETWORKS/INTERFACES
	$id = "12";
	$ip = "192.168.30.${id}";
	$mask = "255.255.255.0";
	$gateway = "192.168.30.1";
	$bridge = "net-dmz";
	$epair = "epair${id}";

	exec.prestart += "ifconfig ${epair} create up";
	exec.prestart += "ifconfig ${epair}a up descr jail:${name}";
	exec.prestart += "ifconfig ${bridge} addm ${epair}a up";
	exec.start    += "ifconfig ${epair}b ${ip} netmask ${mask} up";
	exec.start    += "route add default ${gateway}";
	exec.poststop += "ifconfig ${bridge} deletem ${epair}a";
	exec.poststop += "ifconfig ${epair}a destroy";
}

Start the LDAP jail

service jail start ldap

Install the LDAP server packages

pkg -j ldap -y update
pkg -j ldap -y install openldap-server openldap-client

Connect to the new jail

jexec ldap /bin/sh

Edit ldap config file

The LDAP server has a server configuration files called slapd.conf located in the /usr/local/etc/openldap directory. This file consists of several parts defined in the config file. Each part has been commented.

# Import Schemas to support
include         /usr/local/etc/openldap/schema/core.schema
include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema
include         /usr/local/etc/openldap/schema/nis.schema

# specify paths for restarting
pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args

# load the DB module for storate
modulepath      /usr/local/libexec/openldap
moduleload      back_mdb

# Set access controls on different keys
# NOTE: rootdn can always read and write EVERYTHING!
# Access Control
access to attrs=userPassword
        by group/groupOfNames/member="ou=services,dc=example,dc=org" read
        by self write
        by * none
access to attrs=description
        by group/groupOfNames/member="ou=services,dc=example,dc=org" read
        by self write
        by * none

# TLS
TLSCACertificateFile /usr/local/share/certs/example.org_root_ca.pem
TLSCertificateFile /usr/local/etc/ssl/ldap_fullchain.pem
TLSCertificateKeyFile /usr/local/etc/ssl/ldap.key
#TLSVerifyClient never

# enable database config definitions
database config
# configure storage DB settings
database        mdb
maxsize         1073741824
suffix          "dc=example,dc=org"
rootdn          "cn=ldapadmin,dc=example,dc=org"
rootpw          change_me_please!
directory       /var/db/openldap-data

# Indices to maintain
index   objectClass     eq
index   cn,sn,mail      pres,eq,approx,sub
index   uid             eq

# enable DB monitoring
database monitor

/usr/local/etc/openldap/slapd.conf

Get LDAP certificate

Get a new certificate from the CA for the LDAP server

Add and trust the root cert

test -d /usr/local/share/certs || mkdir -p /usr/local/share/certs
fetch --no-verify-peer --no-verify-hostname -o /usr/local/share/certs/root_ca.crt https://certauth.example.org/roots.pem
certctl rehash

commands to install and trust the CA certificate

Install the acme package

pkg install -y acme.sh

command to install the acme.sh package

Generate the certificate.

export CRTP=/usr/local/etc/ssl
test -f $CRTP || mkdir -p $CRTP
acme.sh --issue --standalone -d $(hostname) --cert-file $CRTP/$(hostname -s).crt --key-file $CRTP/$(hostname -s).key --fullchain-file $CRTP/$(hostname -s)_fullchain.pem --server https://certauth.example.org/acme/acme/directory

commands to generate new certifiate

Start the LDAP server

service openldap start

command to start the ldap server

Forward logs to logging server

Create the /usr/local/etc/syslog.d directory if it doesn't exist.

[ -f /usr/local/etc/syslog.d ] || mkdir -p /usr/local/etc/syslog.d

command to create the syslog.d directory

Next, create the syslog config file

Send log messages to log server
*.*                                          @192.168.30.12

/usr/local/etc/syslog.d/remote.conf

Finally, restart syslogd

service syslogd restart

command to restart syslogd

LDAP Setup

Now that the server is running we need to setup the structure and add some user accounts.

Setup structure

#
# Simple LDAP Schema
#
dn: dc=example,dc=org
objectclass: dcObject
objectclass: organization
dc: example
o: example.org LDAP Server
description: Root entry for example.org

# First level
dn: ou=users,dc=example,dc=org
objectclass: organizationalUnit
ou: users
description: All people in organization

dn: ou=groups,dc=example,dc=org
objectclass: organizationalUnit
ou: groups
description: All groups in organization

dn: ou=domains,dc=example,dc=org
objectclass: organizationalUnit
ou: domains
description: All domains in organization

dn: ou=serviceaccounts,dc=example,dc=org
objectclass: organizationalUnit
ou: serviceaccounts
description: All sevice accounts in organization

init_schema.ldif

Load the ldif data to the openldap server

ldapadd -W -H ldap://127.0.0.1:389 -D "cn=ldapadmin,dc=example,dc=org" -f init_schema.ldif

Add a user account

Now the server is up and running let's add a user account. Updates to the LDAP server are made using LDIF files. Create a LDIF file from the example below with the appropriate user information.

Create a SSHA password

export PASS=letmein
export RND_SALT=`openssl rand -base64 6`
export PASS_HASH=`echo -n "$PASS$RND_SALT" | openssl dgst -sha1 -binary | openssl enc -base64 -A`
export LDAP_PASS_HASH=`(echo -n "$PASS_HASH" | openssl base64 -d -A; echo -n "$RND_SALT";) | openssl enc -base64 -A | awk '{print "{SSHA}"$0 }'`
echo $LDAP_PASS_HASH
unset RND_SALT
unset PASS_HASH
unset LDAP_PASS_HASH
unset PASS

shell to create a SSHA password

Edit the ldif as needed.

#
# Create User alice
#
dn: uid=2e86409ff65ff189,ou=users,dc=example,dc=org
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
uid: 2e86409ff65ff189
cn: alice
sn: user
uidNumber: 5000
gidNumber: 5000
homeDirectory: /home/alice
givenName: alice
displayName: alice user
mail: [email protected]
mail: @example.org
userPassword:: e1NTSEF9WmdwcCt6ZE90UC9KcEhXNjIxUGd5Wm5MZlV0RGExUjZNVzk2V0E9PQ=
 =
description: $2b$10$JxGxv8v2hGw4/dytUMRjbOlHrzg7wyCRqZX6YtFdAEaruH.OsIvWm

add_user.ldif

Load the user account.

ldapadd -W -H ldap://127.0.0.1:389 -D "cn=ldapadmin,dc=example,dc=org" -f add_user.ldif

Modify a user account

From time to time updates/changes are going to need to be made to a user account. Updates to LDAP also use LDIF files to preform updates. Use the follow script to create an update LDIF.

dn: uid=juser,ou=users,dc=example,dc=org
changetype: modify
replace: userPassword
userPassword: {SSHA}adsfasdfasdfadfasdfadfas
-

update_user.ldif

Load the updated password

ldapmodify -W -H ldap://127.0.0.1:389 -D "cn=ldapadmin,dc=example,dc=org" -f update_user.ldif

Delete a user account

User accounts that are no longer needed should be deleted. Use the following script to help delete an old user account.

uid=juser,ou=users,dc=example,dc=org

delete_user.ldif

Load the delete ldif

ldapdelete -W -H ldap://127.0.0.1:389 -D "cn=ldapadmin,dc=dc=example,dc=org" -f delete_user.ldif

An easier way...

While we can manipulate the LDAP users and attributes by hand I have created a few scripts that will make the daily maintenance of users much easier. These scripts can be fetched from mgraves00/misc-scripts/ldap.

Conclusion

While this seems like a lot of work to setup a self hosted environment for just one person (and it is), once we have the infrastructure setup, adding new self hosted service will be much easier.