aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCullum Smith <cullum@sacredheartsc.com>2024-09-25 21:38:13 -0400
committerCullum Smith <cullum@sacredheartsc.com>2024-09-25 21:38:13 -0400
commitcd1ce69f104686bbb33e049c2c4c112e78febd36 (patch)
tree6654eaf12145b918cd217dcdf9b95a0060a60b7b
parent93994080d976d1fd98a22422a549fe371a2bcae3 (diff)
downloadinfrastructure-cd1ce69f104686bbb33e049c2c4c112e78febd36.tar.gz
finish idm client stuff
-rw-r--r--files/etc/krb5.conf.common29
-rw-r--r--files/etc/login.conf.freebsd3
-rw-r--r--files/usr/local/etc/nslcd.conf.common14
-rw-r--r--files/usr/local/etc/openldap/.krb5/config.idm_server2
-rw-r--r--files/usr/local/etc/openldap/ldap.conf.common9
-rw-r--r--files/usr/local/etc/openldap/slapd.ldif.idm_server2
-rw-r--r--files/usr/local/etc/ssh/sshd_config.freebsd2
-rw-r--r--files/usr/local/var/krb5kdc/kadm5.acl.idm_server4
-rw-r--r--lib/40-user111
-rw-r--r--lib/60-kerberos51
-rw-r--r--lib/60-ldap30
-rw-r--r--scripts/common/10-vars3
-rw-r--r--scripts/hostclass/idm_server/10-slapd34
-rw-r--r--scripts/hostclass/idm_server/30-kdc6
-rw-r--r--scripts/hostclass/idm_server/90-idm16
-rw-r--r--scripts/os/freebsd/50-idm114
-rw-r--r--vars/common5
-rw-r--r--vars/hostclass/freebsd_hypervisor1
-rw-r--r--vars/hostclass/idm_server4
-rw-r--r--vars/hostname/pkg13
20 files changed, 371 insertions, 72 deletions
diff --git a/files/etc/krb5.conf.common b/files/etc/krb5.conf.common
new file mode 100644
index 0000000..aa2c11f
--- /dev/null
+++ b/files/etc/krb5.conf.common
@@ -0,0 +1,29 @@
+[libdefaults]
+ default_realm = ${realm}
+ dns_lookup_kdc = true
+ dns_lookup_realm = false
+ allow_weak_crypto = false
+ permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
+ default_client_keytab_name = /var/db/keytabs/%{euid}.keytab
+ # Breaks screenlockers
+ # verify_ap_req_nofail = true
+
+[appdefaults]
+ pam = {
+ minimum_uid = 1000
+ ccache = FILE:/tmp/krb5cc_%u_XXXXXX
+ forwardable = true
+ ticket_lifetime = ${krb5_ticket_lifetime}
+ renew_lifetime = ${krb5_renew_lifetime}
+ }
+
+[realms]
+ ${realm} = {
+$(for host in $ldap_hosts; do echo "\
+ admin_server = ${host}"; done)
+ default_domain = ${domain}
+ }
+
+[domain_realm]
+ .${domain} = ${realm}
+ ${domain} = ${realm}
diff --git a/files/etc/login.conf.freebsd b/files/etc/login.conf.freebsd
index b7def42..e712b88 100644
--- a/files/etc/login.conf.freebsd
+++ b/files/etc/login.conf.freebsd
@@ -4,7 +4,7 @@ default:\\
:welcome=/var/run/motd:\\
:setenv=BLOCKSIZE=K:\\
:mail=/var/mail/$:\\
- :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\\
+ :path=/sbin /bin /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin ~/bin:\\
:nologin=/var/run/nologin:\\
:cputime=unlimited:\\
:datasize=unlimited:\\
@@ -59,6 +59,7 @@ dialer:\\
# N.B. login_getpwclass(3) will use this entry for the root account,
# in preference to 'default'.
root:\\
+ :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\\
:ignorenologin:\\
:memorylocked=unlimited:\\
:tc=default:
diff --git a/files/usr/local/etc/nslcd.conf.common b/files/usr/local/etc/nslcd.conf.common
new file mode 100644
index 0000000..6494c70
--- /dev/null
+++ b/files/usr/local/etc/nslcd.conf.common
@@ -0,0 +1,14 @@
+uid ${nslcd_user}
+gid ${nslcd_user}
+
+uri ${ldap_uri}
+
+base ${basedn}
+base passwd ${accounts_basedn}
+base group ${groups_basedn}
+
+sasl_mech GSSAPI
+
+nss_min_uid ${nslcd_min_uid}
+nss_initgroups_ignoreusers ALLLOCAL
+nss_nested_groups yes
diff --git a/files/usr/local/etc/openldap/.krb5/config.idm_server b/files/usr/local/etc/openldap/.krb5/config.idm_server
new file mode 100644
index 0000000..7a92f86
--- /dev/null
+++ b/files/usr/local/etc/openldap/.krb5/config.idm_server
@@ -0,0 +1,2 @@
+[libdefaults]
+ default_keytab_name = FILE:${slapd_keytab}
diff --git a/files/usr/local/etc/openldap/ldap.conf.common b/files/usr/local/etc/openldap/ldap.conf.common
new file mode 100644
index 0000000..b56dc94
--- /dev/null
+++ b/files/usr/local/etc/openldap/ldap.conf.common
@@ -0,0 +1,9 @@
+URI ${ldap_uri}
+BASE ${basedn}
+USE_SASL yes
+ROOTUSE_SASL yes
+SASL_MECH GSSAPI
+SASL_REALM ${realm}
+GSSAPI_SIGN yes
+GSSAPI_ENCRYPT yes
+SUDOERS_BASE ${sudo_basedn}
diff --git a/files/usr/local/etc/openldap/slapd.ldif.idm_server b/files/usr/local/etc/openldap/slapd.ldif.idm_server
index 9dc0086..d63641e 100644
--- a/files/usr/local/etc/openldap/slapd.ldif.idm_server
+++ b/files/usr/local/etc/openldap/slapd.ldif.idm_server
@@ -119,7 +119,7 @@ olcAccess: {1}to dn.base="cn=Subschema"
by * read
olcAccess: {3}to *
by dn.exact=${slapd_replicator_dn} read
- by dn.exact=krbPrincipalName=${boxconf_username},${robots_basedn} manage
+ by dn.exact=${boxconf_dn} manage
by set="[cn=${slapd_admin_role},${roles_basedn}]/member* & user" manage
by * break
olcAccess: {4}to dn.subtree=${sudo_basedn}
diff --git a/files/usr/local/etc/ssh/sshd_config.freebsd b/files/usr/local/etc/ssh/sshd_config.freebsd
index df46af6..52d9bfe 100644
--- a/files/usr/local/etc/ssh/sshd_config.freebsd
+++ b/files/usr/local/etc/ssh/sshd_config.freebsd
@@ -3,7 +3,7 @@ Include /etc/ssh/sshd_config.d/*.conf
PermitRootLogin prohibit-password
AuthorizedKeysFile .ssh/authorized_keys
AuthorizedKeysCommand /usr/local/libexec/idm-ssh-authorized-keys %u
-AuthorizedKeysCommandUser ${ssh_authzkeys_user}
+AuthorizedKeysCommandUser ${ssh_authzkeys_username}
KbdInteractiveAuthentication no
PasswordAuthentication yes
diff --git a/files/usr/local/var/krb5kdc/kadm5.acl.idm_server b/files/usr/local/var/krb5kdc/kadm5.acl.idm_server
index c2a454b..9f7507e 100644
--- a/files/usr/local/var/krb5kdc/kadm5.acl.idm_server
+++ b/files/usr/local/var/krb5kdc/kadm5.acl.idm_server
@@ -1,2 +1,2 @@
-*/admin@${realm} * * -maxlife 1h -postdateable
-${boxconf_username}@${realm} * * -maxlife 5m -postdateable
+*/admin@${realm} * *
+${boxconf_username}@${realm} * *
diff --git a/lib/40-user b/lib/40-user
index 305fab6..bb3fc05 100644
--- a/lib/40-user
+++ b/lib/40-user
@@ -29,61 +29,92 @@ add_user(){
# Add a local user if it doesn't exist.
# options: mostly same as `pw useradd`
# $1 = username
- _bcalu_homedir_mode=700
- _bcalu_create_homedir=
- _bcalu_homedir=
- _bcalu_comment=
- _bcalu_shell=/sbin/nologin
- _bcalu_pgroup=
- _bcalu_grouplist=
- _bcalu_uid=
- _bcalu_password=
-
- while getopts c:d:G:g:mM:p:s:u: _bcalu_opt; do
- case $_bcalu_opt in
- c) _bcalu_comment=$OPTARG ;;
- d) _bcalu_homedir=$OPTARG ;;
- G) _bcalu_grouplist=$OPTARG ;;
- g) _bcalu_pgroup=$OPTARG ;;
- M) _bcalu_homedir_mode=$OPTARG ;;
- m) _bcalu_create_homedir=true ;;
- p) _bcalu_password=$OPTARG ;;
- s) _bcalu_shell=$OPTARG ;;
- u) _bcalu_uid=$OPTARG ;;
+ _bcau_homedir_mode=700
+ _bcau_create_homedir=
+ _bcau_homedir=
+ _bcau_comment=
+ _bcau_shell=/sbin/nologin
+ _bcau_pgroup=
+ _bcau_grouplist=
+ _bcau_uid=
+ _bcau_password=
+
+ while getopts c:d:G:g:mM:p:s:u: _bcau_opt; do
+ case $_bcau_opt in
+ c) _bcau_comment=$OPTARG ;;
+ d) _bcau_homedir=$OPTARG ;;
+ G) _bcau_grouplist=$OPTARG ;;
+ g) _bcau_pgroup=$OPTARG ;;
+ M) _bcau_homedir_mode=$OPTARG ;;
+ m) _bcau_create_homedir=true ;;
+ p) _bcau_password=$OPTARG ;;
+ s) _bcau_shell=$OPTARG ;;
+ u) _bcau_uid=$OPTARG ;;
esac
done
shift $((OPTIND - 1))
- _bcalu_username=$1
- : ${_bcalu_homedir:="/home/${_bcalu_username}"}
- : ${_bcalu_comment:="${_bcalu_username} user"}
+ _bcau_username=$1
+ : ${_bcau_homedir:="/home/${_bcau_username}"}
+ : ${_bcau_comment:="${_bcau_username} user"}
case $BOXCONF_OS in
freebsd)
- if pw usershow "$_bcalu_username" > /dev/null 2>&1; then
- log "local user ${_bcalu_username} already exists"
+ if pw usershow "$_bcau_username" > /dev/null 2>&1; then
+ log "local user ${_bcau_username} already exists"
return 0
fi
pw useradd \
- -n "$_bcalu_username" \
- -c "$_bcalu_comment" \
- -s "$_bcalu_shell" \
- -M "$_bcalu_homedir_mode" \
- -d "$_bcalu_homedir" \
- ${_bcalu_create_homedir:+-m} \
- ${_bcalu_grouplist:+-G ${_bcalu_grouplist}} \
- ${_bcalu_pgroup:+-g ${_bcalu_pgroup}} \
- ${_bcalu_uid:+-u ${_bcalu_uid}}
-
- log "added local user ${_bcalu_username}"
+ -n "$_bcau_username" \
+ -c "$_bcau_comment" \
+ -s "$_bcau_shell" \
+ -M "$_bcau_homedir_mode" \
+ -d "$_bcau_homedir" \
+ ${_bcau_create_homedir:+-m} \
+ ${_bcau_grouplist:+-G ${_bcau_grouplist}} \
+ ${_bcau_pgroup:+-g ${_bcau_pgroup}} \
+ ${_bcau_uid:+-u ${_bcau_uid}}
+
+ log "added local user ${_bcau_username}"
;;
*)
- die "add_local_user unimplemented for ${BOXCONF_OS}"
+ die "add_user unimplemented for ${BOXCONF_OS}"
;;
esac
- if [ -n "${_bcalu_password}" ]; then
- set_password "$_bcalu_user" "$_bcalu_password"
+ if [ -n "${_bcau_password}" ]; then
+ set_password "$_bcau_user" "$_bcau_password"
fi
}
+
+add_group(){
+ # Add a local group if it doesn't exist.
+ # options: mostly same as `pw groupadd`
+ # $1 = groupname
+ _bcag_gid=
+
+ while getopts g: _bcag_opt; do
+ case $_bcag_opt in
+ g) _bcag_gid=$OPTARG ;;
+ esac
+ done
+ shift $((OPTIND - 1))
+
+ _bcag_groupname=$1
+
+ case $BOXCONF_OS in
+ freebsd)
+ if pw groupshow "$_bcag_groupname" > /dev/null 2>&1; then
+ log "local group ${_bcag_groupname} already exists"
+ return 0
+ fi
+
+ pw groupadd -n "$_bcag_groupname" ${_bcag_gid:+-g ${_bcag_gid}}
+ log "added local group ${_bcag_groupname}"
+ ;;
+ *)
+ die "add_group unimplemented for ${BOXCONF_OS}"
+ ;;
+ esac
+}
diff --git a/lib/60-kerberos b/lib/60-kerberos
new file mode 100644
index 0000000..a323e94
--- /dev/null
+++ b/lib/60-kerberos
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+_boxconf_kadmin() {
+ case $BOXCONF_OS in
+ freebsd) _boxconf_kadmin=/usr/local/bin/kadmin ;;
+ *) _boxconf_kadmin=kadmin ;;
+ esac
+
+ "$_boxconf_kadmin" -p "$boxconf_username" -w "$boxconf_password" "$@"
+}
+
+_boxconf_kinit(){
+ case $BOXCONF_OS in
+ freebsd) /usr/local/bin/kinit "$@" ;;
+ *) kinit "$@" ;;
+ esac
+}
+
+add_principal(){
+ # Create a kerberos principal, if it doesn't already exist.
+ # Arguments are the same as MIT kadmin' add_principal.
+ # Final argument must be the principal name.
+ eval "_kap_princ=\$$#"
+ _boxconf_kadmin get_principal "$_kap_princ" \
+ || _boxconf_kadmin add_principal "$@"
+}
+
+ktadd(){
+ # Add a principal's keys to a keytab.
+ # Arguments are the same as MIT kadmin's ktadd.
+ _kkta_ktarg=false
+ _kkta_keytab=/etc/krb5.keytab
+ eval "_kkta_princ=\$$#"
+
+ # Extract the keytab argument from $@.
+ for _kkta_arg; do
+ if [ "$_kkta_ktarg" = true ]; then
+ _kkta_keytab=$_kkta_arg
+ break
+ else
+ case $_kkta_arg in
+ -k|-keytab) _kkta_ktarg=true ;;
+ esac
+ fi
+ done
+
+ # Check if we can kinit with the keytab. If not, get fresh keys.
+ if ! _boxconf_kinit -kt "$_kkta_keytab" -c MEMORY: "$_kkta_princ" 2>/dev/null; then
+ _boxconf_kadmin ktadd "$@"
+ fi
+}
diff --git a/lib/60-ldap b/lib/60-ldap
index 37c0c0a..d262849 100644
--- a/lib/60-ldap
+++ b/lib/60-ldap
@@ -4,10 +4,16 @@ ldap_add(){
# Add a DN if it doesn't already exist. Takes ldif-formatted attributes on stdin.
# $1 = the DN
_ldap_add_dn=$1; shift
- if ldapsearch -QLLL -s base -b "$_ldap_add_dn" dn > /dev/null 2>&1; then
+ if ldap_search -s base -b "$_ldap_add_dn" dn > /dev/null 2>&1; then
log "${_ldap_add_dn} already exists"
else
- { printf 'dn: %s\n' "$_ldap_add_dn"; cat; } | ldapadd -Q "$@"
+ { printf 'dn: %s\n' "$_ldap_add_dn"; cat; } | {
+ if [ "${BOXCONF_LDAP_SASL:-}" = true ]; then
+ ldapadd -Q "$@"
+ else
+ ldapadd -ZZ -D "$boxconf_dn" -w "$boxconf_password" "$@"
+ fi
+ }
fi
}
@@ -15,19 +21,33 @@ ldap_modify(){
# Modify a DN. Takes ldif-formatted attributes on stdin.
# $1 = the DN
_ldap_modify_dn=$1; shift
- { printf 'dn: %s\nchangetype: modify\n' "$_ldap_modify_dn"; cat; } | ldapmodify -Q "$@"
+ { printf 'dn: %s\nchangetype: modify\n' "$_ldap_modify_dn"; cat; } | {
+ if [ "${BOXCONF_LDAP_SASL:-}" = true ]; then
+ ldapmodify -Q "$@"
+ else
+ ldapmodify -ZZ -D "$boxconf_dn" -w "$boxconf_password" "$@"
+ fi
+ }
}
ldap_delete(){
# Delete a DN.
# $1 = the DN
- ldapdelete -Q "$@"
+ if [ "${BOXCONF_LDAP_SASL:-}" = true ]; then
+ ldapdelete -Q "$@"
+ else
+ ldapdelete -ZZ -D "$boxconf_dn" -w "$boxconf_password" "$@"
+ fi
}
ldap_search(){
# Perform an LDAP search
# $1..$N = same as ldapsearch.
- ldapsearch -QLLL "$@"
+ if [ "${BOXCONF_LDAP_SASL:-}" = true ]; then
+ ldapsearch -QLLL "$@"
+ else
+ ldapsearch -o ldif_wrap=no -LLLZZ -D "$boxconf_dn" -w "$boxconf_password" "$@"
+ fi
}
ldap_add_attribute(){
diff --git a/scripts/common/10-vars b/scripts/common/10-vars
index 2f0dc9d..3b01cd2 100644
--- a/scripts/common/10-vars
+++ b/scripts/common/10-vars
@@ -5,6 +5,7 @@ if [ "${idm_bootstrap:-}" = true ]; then
pkg_host=$pkg_host_ip
else
: ${resolvers:="$(echo "$idm_server_list" | awk '{print $3}')"}
+ : ${pkg_host:="pkg.${domain}"}
fi
idm_hostnames=$(echo "$idm_server_list" | awk '{print $1}')
@@ -32,3 +33,5 @@ dns_basedn="ou=dns,${basedn}"
kdc_basedn="cn=kdc,${basedn}"
mail_basedn="ou=mail,${basedn}"
mail_domains_basedn="ou=domains,${mail_basedn}"
+
+boxconf_dn="krbPrincipalName=${boxconf_username}@${realm},${robots_basedn}"
diff --git a/scripts/hostclass/idm_server/10-slapd b/scripts/hostclass/idm_server/10-slapd
index 204c405..83cdbb6 100644
--- a/scripts/hostclass/idm_server/10-slapd
+++ b/scripts/hostclass/idm_server/10-slapd
@@ -37,6 +37,17 @@ pkg install -y \
# Create ZFS dataset for OpenLDAP DB.
create_dataset -o "mountpoint=${slapd_data_dir}" "${state_dataset}/openldap-data"
+# To prevent a circular dependency in poudriere, we have to make a special "set"
+# of packages for the IDM hosts in which cyrus-sasl-gssapi is built with the
+# Heimdal libraries in base, rather than MIT.
+#
+# Heimdal does not support the KRB5_KTNAME environment variable with slapd.
+# However, you *can* specify a keytab by creating a ~/.krb5/config file in
+# the slapd user's home directory.
+pw user mod "$slapd_user" -d "$slapd_conf_dir"
+install_directory -m 0755 "${slapd_conf_dir}/.krb5"
+install_template -m 0644 "${slapd_conf_dir}/.krb5/config"
+
# Copy TLS certificate for LDAP server.
install_certificate -o "$slapd_user" -g "$slapd_user" slapd "$slapd_tls_cert"
install_certificate_key -o "$slapd_user" -g "$slapd_user" slapd "$slapd_tls_key"
@@ -69,18 +80,6 @@ if [ ! -d "${slapd_conf_dir}/slapd.d" ]; then
chown -R "${slapd_user}:${slapd_user}" "${slapd_conf_dir}/slapd.d"
fi
-# Enable OpenLDAP in /etc/rc.conf, and start it.
-# Note: whatever LDAP IP you specified in $slapd_server_list must be present in
-# the `-h` argument to slapd. That's how slapd figures out its own server ID.
-sysrc -v \
- slapd_enable=YES \
- slapd_cn_config=YES \
- slapd_flags="-h '${slapd_ldapi_uri}/ ldap://0.0.0.0/ ldaps://0.0.0.0/ ldaps://${BOXCONF_DEFAULT_IPV4}/'" \
- slapd_sockets="$slapd_socket" \
- slapd_krb5_ktname="$slapd_keytab"
-
-service slapd restart
-
# Copy the LDAP client configs.
install_template -m 0644 "${slapd_conf_dir}/ldap.conf"
@@ -90,11 +89,20 @@ install_template -m 0644 /usr/local/lib/sasl2/slapd.conf
# Allow slapd to read the saslauthd socket.
install_directory -m 0750 -o "$saslauthd_user" -g "$slapd_user" "$saslauthd_runtime_dir"
-# Enable and start saslauthd.
+# Enable OpenLDAP in /etc/rc.conf, and start it.
+# Note: whatever LDAP IP you specified in $slapd_server_list must be present in
+# the `-h` argument to slapd. That's how slapd figures out its own server ID.
sysrc -v \
+ slapd_enable=YES \
+ slapd_cn_config=YES \
+ slapd_flags="-h '${slapd_ldapi_uri}/ ldap://0.0.0.0/ ldaps://0.0.0.0/ ldaps://${BOXCONF_DEFAULT_IPV4}/'" \
+ slapd_sockets="$slapd_socket" \
+ slapd_krb5_ktname="$slapd_keytab" \
saslauthd_flags='-a kerberos5' \
saslauthd_enable=YES
+
service saslauthd restart
+service slapd restart
# Create directory tree.
if is_primary_server; then
diff --git a/scripts/hostclass/idm_server/30-kdc b/scripts/hostclass/idm_server/30-kdc
index abe040a..9347ed0 100644
--- a/scripts/hostclass/idm_server/30-kdc
+++ b/scripts/hostclass/idm_server/30-kdc
@@ -35,9 +35,3 @@ sysrc -v \
service kdc restart
service kadmind restart
-
-# Create the boxconf administrative user.
-if is_primary_server; then
- kadmin.local get_principal -terse "$boxconf_username" \
- || kadmin.local add_principal -pw "$boxconf_password" -x "containerdn=${robots_basedn}" "$boxconf_username"
-fi
diff --git a/scripts/hostclass/idm_server/90-idm b/scripts/hostclass/idm_server/90-idm
index 0a28491..adfdf36 100644
--- a/scripts/hostclass/idm_server/90-idm
+++ b/scripts/hostclass/idm_server/90-idm
@@ -40,11 +40,11 @@ associatedDomain: ${rdns}
EOF
# Create host principal.
-kadmin.local get_principal -terse "host/${fqdn}" \
+kadmin.local get_principal "host/${fqdn}" \
|| kadmin.local add_principal -nokey -x "dn=cn=${BOXCONF_HOSTNAME},${hosts_basedn}" "host/${fqdn}"
# Create ldap service principal.
-kadmin.local get_principal -terse "ldap/${fqdn}" \
+kadmin.local get_principal "ldap/${fqdn}" \
|| kadmin.local add_principal -nokey -x "containerdn=${services_basedn}" "ldap/${fqdn}"
# Create state dataset to persist keytabs across OS rebuilds.
@@ -89,3 +89,15 @@ ln -snfs "${slapd_conf_dir}/ldap.conf" /usr/local/etc/ldap.conf
install_file -m 0555 \
/usr/local/libexec/idm-ssh-known-hosts \
/usr/local/libexec/idm-ssh-authorized-keys
+
+# Create the boxconf administrative user.
+if is_primary_server && ! ldap_dn_exists "$boxconf_dn"; then
+ ldap_add "$boxconf_dn" <<EOF
+objectClass: krbPrincipal
+objectClass: simpleSecurityObject
+krbPrincipalName: ${boxconf_username}@${realm}
+userPassword: {SASL}${boxconf_username}@${realm}
+EOF
+
+ kadmin.local change_password -pw "$boxconf_password" "$boxconf_username"
+fi
diff --git a/scripts/os/freebsd/50-idm b/scripts/os/freebsd/50-idm
new file mode 100644
index 0000000..ea94082
--- /dev/null
+++ b/scripts/os/freebsd/50-idm
@@ -0,0 +1,114 @@
+#!/bin/sh
+
+if [ "${idm_bootstrap:-}" = true ] || [ "${enable_idm:-}" = false ]; then
+ return 0
+fi
+
+# Create state dataset to persist keytabs across OS rebuilds.
+create_dataset -o "mountpoint=${keytab_dir}" "${state_dataset}/keytabs"
+
+# Install packages.
+pkg install -y \
+ cyrus-sasl-gssapi \
+ nss-pam-ldapd-sasl \
+ openldap26-client \
+ pam_krb5 \
+ perl5 \
+ p5-perl-ldap \
+ p5-Authen-SASL
+
+# Configure PAM/NSS integration.
+install_file -m 0644 \
+ /etc/nsswitch.conf \
+ /etc/pam.d/sshd
+
+install_template -m 0644 \
+ /etc/krb5.conf \
+ /etc/nscd.conf \
+ /usr/local/etc/openldap/ldap.conf \
+ /usr/local/etc/nslcd.conf
+
+# Create ldap.conf symlink.
+ln -snfv /usr/local/etc/openldap/ldap.conf /usr/local/etc/ldap.conf
+
+# Create host object (if it doesn't exist).
+ldap_add "cn=${BOXCONF_HOSTNAME},${hosts_basedn}" <<EOF
+objectClass: device
+objectClass: domainRelatedObject
+objectClass: ldapPublicKey
+cn: ${BOXCONF_HOSTNAME}
+associatedDomain: ${fqdn}
+$(cat /usr/local/etc/ssh/ssh_host_*_key.pub | cut -d' ' -f-2 | sed 's/^/sshPublicKey: /')
+description: $(uname -mrs) ${BOXCONF_HOSTCLASS}
+EOF
+
+# Create A record.
+ldap_add "dc=${BOXCONF_HOSTNAME},dc=${domain},${dns_basedn}" <<EOF
+objectClass: dNSDomain
+objectClass: domainRelatedObject
+dc: ${BOXCONF_HOSTNAME}
+aRecord: ${BOXCONF_DEFAULT_IPV4}
+associatedDomain: ${fqdn}
+EOF
+
+# Create PTR record.
+rdns=$(ip2rdns "$BOXCONF_DEFAULT_IPV4")
+ldap_add "dc=${rdns%%.*},dc=${rdns#*.},${dns_basedn}" <<EOF
+objectClass: dNSDomain2
+objectClass: domainRelatedObject
+dc: ${rdns%%.*}
+pTRRecord: ${fqdn}
+associatedDomain: ${rdns}
+EOF
+
+# Create CNAME records.
+for cname in ${cnames:-}; do
+ ldap_add "dc=${cname},dc=${domain},${dns_basedn}" <<EOF
+objectClass: dNSDomain
+objectClass: domainRelatedObject
+dc: ${cname}
+cNAMERecord: ${fqdn}
+associatedDomain: ${cname}.${domain}
+EOF
+done
+
+# Update attributes that may have changed.
+ldap_modify "cn=${BOXCONF_HOSTNAME},${hosts_basedn}" <<EOF
+replace: sshPublicKey
+$(cat /usr/local/etc/ssh/ssh_host_*_key.pub | cut -d' ' -f-2 | sed 's/^/sshPublicKey: /')
+-
+replace: description
+description: $(uname -mrs) ${BOXCONF_HOSTCLASS}
+EOF
+
+# Create host principal and keytab.
+add_principal -nokey -x "dn=cn=${BOXCONF_HOSTNAME},${hosts_basedn}" "host/${fqdn}"
+ktadd -k "${keytab_dir}/host.keytab" "host/${fqdn}"
+ln -snfv "${keytab_dir}/host.keytab" /etc/krb5.keytab
+
+# Create symlinks so host keytab can be used to aquire a TGT on-the-fly.
+ln -snfv host.keytab "${keytab_dir}/$(id -u "$nslcd_user").keytab"
+ln -snfv host.keytab "${keytab_dir}/${ssh_authzkeys_uid}.keytab"
+ln -snfv host.keytab "${keytab_dir}/0.keytab"
+
+# Create local group for host keytab access.
+add_group -g "$host_keytab_gid" "$host_keytab_groupname"
+chgrp "$host_keytab_groupname" "${keytab_dir}/host.keytab"
+chmod 640 "${keytab_dir}/host.keytab"
+pw usermod -n "$nslcd_user" -G "$host_keytab_groupname"
+
+# Copy IDM helper scripts for SSH.
+install_file -m 0555 \
+ /usr/local/libexec/idm-ssh-known-hosts \
+ /usr/local/libexec/idm-ssh-authorized-keys
+
+# Create user for running SSH AuthorizedKeysCommand.
+add_user -u "$ssh_authzkeys_uid" -g "$host_keytab_groupname" "$ssh_authzkeys_username"
+
+# Enable and start nslcd/nscd.
+sysrc -v \
+ nslcd_enable=YES \
+ nscd_enable=YES
+
+service nslcd restart
+service nscd restart
diff --git a/vars/common b/vars/common
index 20c7976..5c22f09 100644
--- a/vars/common
+++ b/vars/common
@@ -31,12 +31,15 @@ bootstrap_resolvers='1.1.1.1'
desktop_type=kde
graphics_type=intel
boxconf_username='s-boxconf'
+host_keytab_groupname=hostkeytab
+host_keytab_gid=788
krb5_ticket_lifetime=24h
krb5_renew_lifetime=7d
nslcd_min_uid=1000
nscd_ttl=600
nscd_negative_ttl=20
-ssh_authzkeys_user=_authzkeys
+ssh_authzkeys_uid=789
+ssh_authzkeys_username=sshkeys
tcp_buffer_size=2097152 # suitable for 1 GigE
nginx_nofile=2048
diff --git a/vars/hostclass/freebsd_hypervisor b/vars/hostclass/freebsd_hypervisor
index c38452f..f3d6ac1 100644
--- a/vars/hostclass/freebsd_hypervisor
+++ b/vars/hostclass/freebsd_hypervisor
@@ -1,5 +1,6 @@
#!/bin/sh
enable_pf=false
+enable_idm=false
smtp_host=${smtp_host_ip}
resolvers=$bootstrap_resolvers
diff --git a/vars/hostclass/idm_server b/vars/hostclass/idm_server
index dec58b7..6389567 100644
--- a/vars/hostclass/idm_server
+++ b/vars/hostclass/idm_server
@@ -1,8 +1,12 @@
#!/bin/sh
+BOXCONF_LDAP_SASL=true
+
allowed_tcp_ports='ssh ldap ldaps domain kerberos-sec kerberos-adm'
allowed_udp_ports='domain kerberos-sec kpasswd'
+enable_idm=false
+
kdc_master_key='changeme'
ssh_authorized_keys_user=nobody
diff --git a/vars/hostname/pkg1 b/vars/hostname/pkg1
new file mode 100644
index 0000000..59a3f84
--- /dev/null
+++ b/vars/hostname/pkg1
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+cnames=pkg