aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/10-bootstrapping.md2
-rw-r--r--files/etc/krb5.conf.idm_server27
-rw-r--r--files/etc/nscd.conf.freebsd27
-rw-r--r--files/etc/nsswitch.conf.freebsd10
-rw-r--r--files/etc/pam.d/sshd.freebsd17
-rw-r--r--files/etc/resolv.conf.common1
-rw-r--r--files/etc/resolv.conf.idm_server2
-rw-r--r--files/usr/local/etc/nslcd.conf.idm_server14
-rw-r--r--files/usr/local/etc/openldap/ldap.conf.idm_server2
-rw-r--r--files/usr/local/etc/openldap/slapd.ldif.idm_server4
-rw-r--r--files/usr/local/etc/pdns/pdns.conf.idm_server2
-rw-r--r--files/usr/local/etc/poudriere.d/idm-make.conf.pkg_repository4
-rw-r--r--files/usr/local/etc/poudriere.d/idm-pkglist.pkg_repository1
-rw-r--r--files/usr/local/etc/poudriere.d/make.conf.pkg_repository4
-rw-r--r--files/usr/local/etc/poudriere.d/pkglist.pkg_repository1
-rw-r--r--files/usr/local/etc/ssh/ssh_config.freebsd (renamed from files/etc/ssh/ssh_config.freebsd)0
l---------files/usr/local/etc/ssh/ssh_config.freebsd_hypervisor (renamed from files/etc/ssh/ssh_config.freebsd_hypervisor)0
-rw-r--r--files/usr/local/etc/ssh/ssh_config.no_idm (renamed from files/etc/ssh/ssh_config.no_idm)0
l---------files/usr/local/etc/ssh/ssh_config.roadwarrior_laptop (renamed from files/etc/ssh/ssh_config.roadwarrior_laptop)0
-rw-r--r--files/usr/local/etc/ssh/sshd_config.freebsd (renamed from files/etc/ssh/sshd_config.freebsd)2
l---------files/usr/local/etc/ssh/sshd_config.freebsd_hypervisor (renamed from files/etc/ssh/sshd_config.freebsd_hypervisor)0
-rw-r--r--files/usr/local/etc/ssh/sshd_config.no_idm (renamed from files/etc/ssh/sshd_config.no_idm)2
-rw-r--r--files/usr/local/etc/unbound/unbound.conf.idm_server68
-rw-r--r--files/usr/local/libexec/idm-ssh-authorized-keys.common43
-rw-r--r--files/usr/local/libexec/idm-ssh-known-hosts.common51
-rw-r--r--files/usr/local/libexec/idm-update-unbound-blocklists.idm_server32
-rw-r--r--files/usr/local/var/krb5kdc/kadm5.acl.idm_server2
-rw-r--r--files/usr/local/var/krb5kdc/kdc.conf.idm_server23
-rw-r--r--lib/60-ldap12
-rw-r--r--scripts/common/10-vars5
-rw-r--r--scripts/hostclass/desktop66
-rw-r--r--scripts/hostclass/idm_server/10-slapd13
-rw-r--r--scripts/hostclass/idm_server/20-powerdns2
-rw-r--r--scripts/hostclass/idm_server/30-kdc41
-rw-r--r--scripts/hostclass/idm_server/40-unbound40
-rw-r--r--scripts/hostclass/idm_server/90-idm96
-rw-r--r--scripts/os/freebsd/10-sysctls4
-rw-r--r--scripts/os/freebsd/30-ssh32
-rw-r--r--scripts/os/freebsd/41-ssh40
-rw-r--r--vars/common10
-rw-r--r--vars/hostclass/idm_server4
-rw-r--r--vars/hostclass/roadwarrior_laptop1
-rw-r--r--vars/os/freebsd2
43 files changed, 639 insertions, 70 deletions
diff --git a/docs/10-bootstrapping.md b/docs/10-bootstrapping.md
index cda5c80..bb3082f 100644
--- a/docs/10-bootstrapping.md
+++ b/docs/10-bootstrapping.md
@@ -70,7 +70,7 @@ are also set with this command.
Now you are ready to build all the packages and create the repository. `boxconf`
assumes that any host named `pkg[0-1]` has the `pkg_repository` hostclass.
- ./boxconf -e idm_bootstrap=true 10.99.99.4
+ ./boxconf -e idm_bootstrap=true -s 10.99.99.4 pkg1
Substitute whatever IP you chose for the `pkg1` jail as necessary. Note that it
will take a while to build all the packages for the first time.
diff --git a/files/etc/krb5.conf.idm_server b/files/etc/krb5.conf.idm_server
new file mode 100644
index 0000000..422d0e4
--- /dev/null
+++ b/files/etc/krb5.conf.idm_server
@@ -0,0 +1,27 @@
+[libdefaults]
+ default_realm = ${realm}
+ dns_lookup_kdc = false
+ dns_lookup_realm = false
+ allow_weak_crypto = false
+ permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
+ 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} = {
+ kdc = ${fqdn}
+ admin_server = ${fqdn}
+ default_domain = ${domain}
+ }
+
+[domain_realm]
+ .${domain} = ${realm}
+ ${domain} = ${realm}
diff --git a/files/etc/nscd.conf.freebsd b/files/etc/nscd.conf.freebsd
new file mode 100644
index 0000000..9105cb5
--- /dev/null
+++ b/files/etc/nscd.conf.freebsd
@@ -0,0 +1,27 @@
+enable-cache passwd yes
+positive-time-to-live passwd ${nscd_ttl}
+negative-time-to-live passwd ${nscd_negative_ttl}
+
+enable-cache group yes
+positive-time-to-live group ${nscd_ttl}
+negative-time-to-live group ${nscd_negative_ttl}
+
+enable-cache hosts yes
+positive-time-to-live hosts ${nscd_ttl}
+negative-time-to-live hosts ${nscd_negative_ttl}
+
+enable-cache services yes
+positive-time-to-live services ${nscd_ttl}
+negative-time-to-live services ${nscd_negative_ttl}
+
+enable-cache protocols yes
+positive-time-to-live protocols ${nscd_ttl}
+negative-time-to-live protocols ${nscd_negative_ttl}
+
+enable-cache rpc yes
+positive-time-to-live rpc ${nscd_ttl}
+negative-time-to-live rpc ${nscd_negative_ttl}
+
+enable-cache networks yes
+positive-time-to-live networks ${nscd_ttl}
+negative-time-to-live networks ${nscd_negative_ttl}
diff --git a/files/etc/nsswitch.conf.freebsd b/files/etc/nsswitch.conf.freebsd
new file mode 100644
index 0000000..19c9f22
--- /dev/null
+++ b/files/etc/nsswitch.conf.freebsd
@@ -0,0 +1,10 @@
+group: files cache ldap
+hosts: files cache dns
+netgroup: files
+networks: files
+passwd: files cache ldap
+shells: files
+services: files
+protocols: files
+rpc: files
+sudoers: files ldap
diff --git a/files/etc/pam.d/sshd.freebsd b/files/etc/pam.d/sshd.freebsd
new file mode 100644
index 0000000..57b281b
--- /dev/null
+++ b/files/etc/pam.d/sshd.freebsd
@@ -0,0 +1,17 @@
+# auth
+auth sufficient /usr/local/lib/security/pam_krb5.so try_first_pass
+auth required pam_unix.so no_warn try_first_pass
+
+# account
+account required pam_nologin.so
+account required /usr/local/lib/security/pam_krb5.so
+account required pam_login_access.so
+account required pam_unix.so
+
+# session
+session required /usr/local/lib/security/pam_krb5.so
+session required pam_permit.so
+
+# password
+password sufficient /usr/local/lib/security/pam_krb5.so try_first_pass
+password required pam_unix.so no_warn try_first_pass
diff --git a/files/etc/resolv.conf.common b/files/etc/resolv.conf.common
index 24c2044..475ea9d 100644
--- a/files/etc/resolv.conf.common
+++ b/files/etc/resolv.conf.common
@@ -1,3 +1,2 @@
$(printf 'nameserver %s\n' $resolvers)
domain ${domain}
-options timeout:1
diff --git a/files/etc/resolv.conf.idm_server b/files/etc/resolv.conf.idm_server
new file mode 100644
index 0000000..9a87112
--- /dev/null
+++ b/files/etc/resolv.conf.idm_server
@@ -0,0 +1,2 @@
+nameserver 127.0.0.1
+domain ${domain}
diff --git a/files/usr/local/etc/nslcd.conf.idm_server b/files/usr/local/etc/nslcd.conf.idm_server
new file mode 100644
index 0000000..1f74779
--- /dev/null
+++ b/files/usr/local/etc/nslcd.conf.idm_server
@@ -0,0 +1,14 @@
+uid ${nslcd_user}
+gid ${nslcd_user}
+
+uri ${slapd_ldapi_uri}
+
+base ${basedn}
+base passwd ${accounts_basedn}
+base group ${groups_basedn}
+
+sasl_mech EXTERNAL
+
+nss_min_uid ${nslcd_min_uid}
+nss_initgroups_ignoreusers ALLLOCAL
+nss_nested_groups yes
diff --git a/files/usr/local/etc/openldap/ldap.conf.idm_server b/files/usr/local/etc/openldap/ldap.conf.idm_server
index 3b285f7..a3e18f2 100644
--- a/files/usr/local/etc/openldap/ldap.conf.idm_server
+++ b/files/usr/local/etc/openldap/ldap.conf.idm_server
@@ -1,4 +1,4 @@
-URI ldapi:///
+URI ${slapd_ldapi_uri}
BASE ${basedn}
USE_SASL yes
ROOTUSE_SASL yes
diff --git a/files/usr/local/etc/openldap/slapd.ldif.idm_server b/files/usr/local/etc/openldap/slapd.ldif.idm_server
index 784c63a..9dc0086 100644
--- a/files/usr/local/etc/openldap/slapd.ldif.idm_server
+++ b/files/usr/local/etc/openldap/slapd.ldif.idm_server
@@ -119,8 +119,8 @@ olcAccess: {1}to dn.base="cn=Subschema"
by * read
olcAccess: {3}to *
by dn.exact=${slapd_replicator_dn} read
- by dn.exact=uid=${idm_admin_username},${robots_basedn} manage
- by group/groupOfMembers/member=cn=${idm_admin_groupname},${groups_basedn} manage
+ by dn.exact=krbPrincipalName=${boxconf_username},${robots_basedn} manage
+ by set="[cn=${slapd_admin_role},${roles_basedn}]/member* & user" manage
by * break
olcAccess: {4}to dn.subtree=${sudo_basedn}
by dn.children=${hosts_basedn} read
diff --git a/files/usr/local/etc/pdns/pdns.conf.idm_server b/files/usr/local/etc/pdns/pdns.conf.idm_server
index fc63bd6..2d49dc3 100644
--- a/files/usr/local/etc/pdns/pdns.conf.idm_server
+++ b/files/usr/local/etc/pdns/pdns.conf.idm_server
@@ -4,7 +4,7 @@
#
# You must set ldap-bindmethod=gssapi (?!) for this to work. This behavior doesn't
# seem to be documented anywhere, but hey, it's nice!
-ldap-host=ldapi:///
+ldap-host=${slapd_ldapi_uri}
ldap-bindmethod=gssapi
ldap-basedn=${dns_basedn}
diff --git a/files/usr/local/etc/poudriere.d/idm-make.conf.pkg_repository b/files/usr/local/etc/poudriere.d/idm-make.conf.pkg_repository
index 1d5ce20..ad5304f 100644
--- a/files/usr/local/etc/poudriere.d/idm-make.conf.pkg_repository
+++ b/files/usr/local/etc/poudriere.d/idm-make.conf.pkg_repository
@@ -4,8 +4,8 @@ DEFAULT_VERSIONS+=${poudriere_default_versions:-}
MAKE_JOBS_NUMBER=${poudriere_make_jobs_number}
# Global port options
-OPTIONS_UNSET=TEST DEBUG GSSAPI_HEIMDAL GSSAPI_BASE GSSAPI_NONE HEIMDAL NLS DOCS AVAHI LIBWRAP MYSQL MSQLND ODBC READLINE PULSEAUDIO UPNP BASH ZSH INFO ALSA SAMBA WAYLAND PLATFORM_WAYLAND PIPEWIRE
-OPTIONS_SET=GSSAPI GSSAPI_MIT NONFREE LIBEDIT
+OPTIONS_UNSET=TEST DEBUG GSSAPI_HEIMDAL GSSAPI_BASE GSSAPI_NONE HEIMDAL HEIMDAL_BASE NLS DOCS AVAHI LIBWRAP MYSQL MSQLND ODBC READLINE PULSEAUDIO UPNP BASH ZSH INFO ALSA SAMBA WAYLAND PLATFORM_WAYLAND PIPEWIRE TCP_WRAPPERS
+OPTIONS_SET=GSSAPI GSSAPI_MIT MIT NONFREE LIBEDIT
# Per-port options
dns_powerdns_SET=OPENLDAP
diff --git a/files/usr/local/etc/poudriere.d/idm-pkglist.pkg_repository b/files/usr/local/etc/poudriere.d/idm-pkglist.pkg_repository
index 86c102e..9504faa 100644
--- a/files/usr/local/etc/poudriere.d/idm-pkglist.pkg_repository
+++ b/files/usr/local/etc/poudriere.d/idm-pkglist.pkg_repository
@@ -12,6 +12,7 @@ net/py-python-ldap
net/rsync
security/cyrus-sasl2-saslauthd
security/krb5
+security/openssh-portable
security/pam_krb5@mit
security/pam_mkhomedir
security/sudo
diff --git a/files/usr/local/etc/poudriere.d/make.conf.pkg_repository b/files/usr/local/etc/poudriere.d/make.conf.pkg_repository
index 8348621..b0ae948 100644
--- a/files/usr/local/etc/poudriere.d/make.conf.pkg_repository
+++ b/files/usr/local/etc/poudriere.d/make.conf.pkg_repository
@@ -4,8 +4,8 @@ DEFAULT_VERSIONS+=${poudriere_default_versions:-}
MAKE_JOBS_NUMBER=${poudriere_make_jobs_number}
# Global port options
-OPTIONS_UNSET=TEST DEBUG GSSAPI_HEIMDAL GSSAPI_BASE GSSAPI_NONE HEIMDAL NLS DOCS AVAHI LIBWRAP MYSQL MSQLND ODBC READLINE PULSEAUDIO UPNP BASH ZSH INFO ALSA SAMBA WAYLAND PLATFORM_WAYLAND PIPEWIRE
-OPTIONS_SET=GSSAPI GSSAPI_MIT NONFREE LIBEDIT
+OPTIONS_UNSET=TEST DEBUG GSSAPI_HEIMDAL GSSAPI_BASE GSSAPI_NONE HEIMDAL HEIMDAL_BASE NLS DOCS AVAHI LIBWRAP MYSQL MSQLND ODBC READLINE PULSEAUDIO UPNP BASH ZSH INFO ALSA SAMBA WAYLAND PLATFORM_WAYLAND PIPEWIRE TCP_WRAPPERS
+OPTIONS_SET=GSSAPI GSSAPI_MIT MIT NONFREE LIBEDIT
# Per-port options
databases_akonadi_SET=MYSQL
diff --git a/files/usr/local/etc/poudriere.d/pkglist.pkg_repository b/files/usr/local/etc/poudriere.d/pkglist.pkg_repository
index 80fc5e5..157ae8e 100644
--- a/files/usr/local/etc/poudriere.d/pkglist.pkg_repository
+++ b/files/usr/local/etc/poudriere.d/pkglist.pkg_repository
@@ -19,6 +19,7 @@ security/cyrus-sasl2-saslauthd
security/kstart
security/krb5@default
security/krb5@ldap
+security/openssh-portable
security/pam_krb5@mit
security/pam_mkhomedir
security/sshpass
diff --git a/files/etc/ssh/ssh_config.freebsd b/files/usr/local/etc/ssh/ssh_config.freebsd
index 9be624a..9be624a 100644
--- a/files/etc/ssh/ssh_config.freebsd
+++ b/files/usr/local/etc/ssh/ssh_config.freebsd
diff --git a/files/etc/ssh/ssh_config.freebsd_hypervisor b/files/usr/local/etc/ssh/ssh_config.freebsd_hypervisor
index 338cdba..338cdba 120000
--- a/files/etc/ssh/ssh_config.freebsd_hypervisor
+++ b/files/usr/local/etc/ssh/ssh_config.freebsd_hypervisor
diff --git a/files/etc/ssh/ssh_config.no_idm b/files/usr/local/etc/ssh/ssh_config.no_idm
index 97f3ba8..97f3ba8 100644
--- a/files/etc/ssh/ssh_config.no_idm
+++ b/files/usr/local/etc/ssh/ssh_config.no_idm
diff --git a/files/etc/ssh/ssh_config.roadwarrior_laptop b/files/usr/local/etc/ssh/ssh_config.roadwarrior_laptop
index 338cdba..338cdba 120000
--- a/files/etc/ssh/ssh_config.roadwarrior_laptop
+++ b/files/usr/local/etc/ssh/ssh_config.roadwarrior_laptop
diff --git a/files/etc/ssh/sshd_config.freebsd b/files/usr/local/etc/ssh/sshd_config.freebsd
index c933741..df46af6 100644
--- a/files/etc/ssh/sshd_config.freebsd
+++ b/files/usr/local/etc/ssh/sshd_config.freebsd
@@ -13,4 +13,4 @@ GSSAPICleanupCredentials yes
UsePAM yes
UseDNS no
-Subsystem sftp /usr/libexec/sftp-server
+Subsystem sftp /usr/local/libexec/sftp-server
diff --git a/files/etc/ssh/sshd_config.freebsd_hypervisor b/files/usr/local/etc/ssh/sshd_config.freebsd_hypervisor
index 355377d..355377d 120000
--- a/files/etc/ssh/sshd_config.freebsd_hypervisor
+++ b/files/usr/local/etc/ssh/sshd_config.freebsd_hypervisor
diff --git a/files/etc/ssh/sshd_config.no_idm b/files/usr/local/etc/ssh/sshd_config.no_idm
index f38720c..8a15559 100644
--- a/files/etc/ssh/sshd_config.no_idm
+++ b/files/usr/local/etc/ssh/sshd_config.no_idm
@@ -7,4 +7,4 @@ PasswordAuthentication yes
UsePAM yes
UseDNS no
-Subsystem sftp /usr/libexec/sftp-server
+Subsystem sftp /usr/local/libexec/sftp-server
diff --git a/files/usr/local/etc/unbound/unbound.conf.idm_server b/files/usr/local/etc/unbound/unbound.conf.idm_server
new file mode 100644
index 0000000..762fe09
--- /dev/null
+++ b/files/usr/local/etc/unbound/unbound.conf.idm_server
@@ -0,0 +1,68 @@
+server:
+ module-config: "respip validator iterator"
+ verbosity: 1
+ num-threads: ${unbound_threads}
+ interface: 0.0.0.0
+ interface: ::0
+ do-ip6: no
+ prefer-ip6: no
+ prefetch: yes
+ prefetch-key: yes
+ so-rcvbuf: 425984
+ do-not-query-localhost: no
+ access-control: 0.0.0.0/0 allow
+ access-control: ::0/0 allow
+
+ cache-max-negative-ttl: ${unbound_cache_max_negative_ttl}
+ rrset-cache-size: ${unbound_rrset_cache_size}
+ msg-cache-size: ${unbound_msg_cache_size}
+ msg-cache-slabs: ${unbound_slabs}
+ rrset-cache-slabs: ${unbound_slabs}
+ infra-cache-slabs: ${unbound_slabs}
+ key-cache-slabs: ${unbound_slabs}
+
+ private-address: 192.168.0.0/16
+ private-address: 169.254.0.0/16
+ private-address: 172.16.0.0/12
+ private-address: 10.0.0.0/8
+
+ domain-insecure: "${domain}"
+$([ -z "$unbound_insecure_domains" ] || printf ' domain-insecure: "%s"\n' $unbound_insecure_domains)
+
+ local-zone: "10.in-addr.arpa" nodefault
+ local-zone: "16.172.in-addr.arpa" nodefault
+ local-zone: "17.172.in-addr.arpa" nodefault
+ local-zone: "18.172.in-addr.arpa" nodefault
+ local-zone: "19.172.in-addr.arpa" nodefault
+ local-zone: "20.172.in-addr.arpa" nodefault
+ local-zone: "21.172.in-addr.arpa" nodefault
+ local-zone: "22.172.in-addr.arpa" nodefault
+ local-zone: "23.172.in-addr.arpa" nodefault
+ local-zone: "24.172.in-addr.arpa" nodefault
+ local-zone: "25.172.in-addr.arpa" nodefault
+ local-zone: "26.172.in-addr.arpa" nodefault
+ local-zone: "27.172.in-addr.arpa" nodefault
+ local-zone: "28.172.in-addr.arpa" nodefault
+ local-zone: "29.172.in-addr.arpa" nodefault
+ local-zone: "30.172.in-addr.arpa" nodefault
+ local-zone: "31.172.in-addr.arpa" nodefault
+ local-zone: "168.192.in-addr.arpa" nodefault
+
+$([ -z "$unbound_local_zones" ] || printf ' local-zone: "%s" typetransparent\n' $unbound_local_zones)
+
+ private-domain: "${domain}"
+$([ -z "$unbound_local_zones" ] || printf ' private-domain: "%s"\n' $unbound_local_zones)
+
+$([ -z "$unbound_local_data" ] || printf ' local-data: "%s"\n' $unbound_local_data)
+
+$(echo "$unbound_blocklists" | while read -r name _url; do
+ [ -n "$name" ] && printf "rpz:\n name: %s\n zonefile: ${unbound_blocklist_dir}/%s.zone\n" "$name" "$name"; done)
+
+stub-zone:
+ name: "${domain}"
+ stub-addr: 127.0.0.1@${pdns_port}
+$(printf "\
+stub-zone:
+ name: \"%s\"
+ stub-addr: 127.0.0.1@${pdns_port}
+" $reverse_dns_zones)
diff --git a/files/usr/local/libexec/idm-ssh-authorized-keys.common b/files/usr/local/libexec/idm-ssh-authorized-keys.common
new file mode 100644
index 0000000..d18b199
--- /dev/null
+++ b/files/usr/local/libexec/idm-ssh-authorized-keys.common
@@ -0,0 +1,43 @@
+#!/usr/local/bin/perl
+
+use strict;
+use warnings;
+
+use Net::LDAP;
+use Net::LDAP::Util qw(escape_filter_value);
+use Authen::SASL;
+
+open my $fh, '<', '/usr/local/etc/openldap/ldap.conf' or quit($!);
+my %config;
+while (<$fh>) {
+ chomp;
+ next if /^#/;
+ my @pair = split(' ', $_, 2);
+ next unless (@pair == 2);
+ $config{$pair[0]} = $pair[1];
+}
+close($fh);
+
+my $mech = $config{SASL_MECH} // 'GSSAPI';
+my $uri = $config{URI} // quit('URI not specified');
+my $basedn = $config{BASE} // quit('BASE not specified');
+
+@ARGV == 1 or die "usage: $0 USERNAME\n";
+my $username = $ARGV[0];
+
+my $conn = Net::LDAP->new($uri, version => '3') or die "$0: $@";
+my $sasl = Authen::SASL->new($mech);
+my $status = $conn->bind(sasl => $sasl);
+$status->code and die "$0: ".$status->error;
+
+my $search = $conn->search(
+ scope => 'sub',
+ base => "ou=accounts,$basedn",
+ filter => '(&(objectClass=posixAccount)(sshPublicKey=*)(uid=' . escape_filter_value($username) . '))',
+ attrs => ['sshPublicKey']);
+$search->code and die "$0: ".$search->error;
+
+exit 0 if $search->entries == 0;
+die "$0: multiple LDAP entries returned for user: $username\n" if $search->entries > 1;
+
+print "$_\n" foreach (($search->entries)[0]->get_value('sshPublicKey'));
diff --git a/files/usr/local/libexec/idm-ssh-known-hosts.common b/files/usr/local/libexec/idm-ssh-known-hosts.common
new file mode 100644
index 0000000..78b48fc
--- /dev/null
+++ b/files/usr/local/libexec/idm-ssh-known-hosts.common
@@ -0,0 +1,51 @@
+#!/usr/local/bin/perl
+
+use strict;
+use warnings;
+
+use Net::LDAP;
+use Net::LDAP::Util qw(escape_filter_value);
+use Authen::SASL;
+
+sub quit {
+ # Prints an error message and exits with code 0.
+ # NB: If KnownHostsCommand returns nonzero, the entire SSH connection aborts,
+ # which isn't what we want.
+ print STDERR "$0: $_[0]" if @_ > 0;
+ exit 0
+}
+
+@ARGV == 1 or die "usage: $0 HOSTNAME\n";
+my $hostname = $ARGV[0];
+
+open my $fh, '<', '/usr/local/etc/openldap/ldap.conf' or quit($!);
+my %config;
+while (<$fh>) {
+ chomp;
+ next if /^#/;
+ my @pair = split(' ', $_, 2);
+ next unless (@pair == 2);
+ $config{$pair[0]} = $pair[1];
+}
+close($fh);
+
+my $mech = $config{SASL_MECH} // 'GSSAPI';
+my $uri = $config{URI} // quit('URI not specified');
+my $basedn = $config{BASE} // quit('BASE not specified');
+
+my $conn = Net::LDAP->new($uri, version => '3') or quit($@);
+my $sasl = Authen::SASL->new($mech);
+my $status = $conn->bind(sasl => $sasl);
+$status->code and quit($status->error);
+
+my $search = $conn->search(
+ scope => 'sub',
+ base => "ou=hosts,ou=accounts,$basedn",
+ filter => '(&(sshPublicKey=*)(associatedDomain=' . escape_filter_value($hostname) . '))',
+ attrs => ['sshPublicKey']);
+$search->code and quit($search->error);
+
+quit if $search->entries == 0;
+quit("multiple LDAP entries returned for host: $hostname\n") if $search->entries > 1;
+
+print "$hostname $_\n" foreach (($search->entries)[0]->get_value('sshPublicKey'));
diff --git a/files/usr/local/libexec/idm-update-unbound-blocklists.idm_server b/files/usr/local/libexec/idm-update-unbound-blocklists.idm_server
new file mode 100644
index 0000000..c33b909
--- /dev/null
+++ b/files/usr/local/libexec/idm-update-unbound-blocklists.idm_server
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -eu -o pipefail
+
+prog=$(basename "$(readlink -f "$0")")
+usage="${prog} BLOCKLIST_DIR
+ Blocklist URLs are read from stdin."
+
+die() {
+ printf '%s: %s\n' "$prog" "$*" 1>&2
+ exit 1
+}
+
+usage(){
+ printf 'usage: %s\n' "$usage" 1>&2
+ exit 2
+}
+
+[ $# -eq 1 ] || usage
+case $1 in
+ -h|--help) usage ;;
+esac
+
+[ -d "$1" ] || die "not a directory: ${1}"
+
+cd "$1"
+
+find . -maxdepth 1 -type f -exec rm {} +
+
+while read -r name url; do
+ [ -n "$url" ] && curl -sSfL -o "${name}.zone" "$url"
+done
diff --git a/files/usr/local/var/krb5kdc/kadm5.acl.idm_server b/files/usr/local/var/krb5kdc/kadm5.acl.idm_server
new file mode 100644
index 0000000..c2a454b
--- /dev/null
+++ b/files/usr/local/var/krb5kdc/kadm5.acl.idm_server
@@ -0,0 +1,2 @@
+*/admin@${realm} * * -maxlife 1h -postdateable
+${boxconf_username}@${realm} * * -maxlife 5m -postdateable
diff --git a/files/usr/local/var/krb5kdc/kdc.conf.idm_server b/files/usr/local/var/krb5kdc/kdc.conf.idm_server
new file mode 100644
index 0000000..ab16965
--- /dev/null
+++ b/files/usr/local/var/krb5kdc/kdc.conf.idm_server
@@ -0,0 +1,23 @@
+[realms]
+ ${realm} = {
+ database_module = openldap_ldapconf
+ key_stash_file = ${kdc_master_key_path}
+ max_life = ${kdc_max_life}
+ max_renewable_life = ${kdc_max_renewable_life}
+ default_principal_flags = +preauth
+ }
+
+[dbdefaults]
+ ldap_kerberos_container_dn = ${kdc_basedn}
+ ldap_kdc_sasl_mech = EXTERNAL
+ ldap_kadmind_sasl_mech = EXTERNAL
+ ldap_conns_per_server = 5
+
+[dbmodules]
+ openldap_ldapconf = {
+ ldap_servers = ${slapd_ldapi_uri}
+ db_library = kldap
+ }
+
+[logging]
+ default = SYSLOG
diff --git a/lib/60-ldap b/lib/60-ldap
index bc5bcff..37c0c0a 100644
--- a/lib/60-ldap
+++ b/lib/60-ldap
@@ -24,6 +24,12 @@ ldap_delete(){
ldapdelete -Q "$@"
}
+ldap_search(){
+ # Perform an LDAP search
+ # $1..$N = same as ldapsearch.
+ ldapsearch -QLLL "$@"
+}
+
ldap_add_attribute(){
# Add a single attribute value to an object if it's not already present.
# $1 = DN
@@ -54,3 +60,9 @@ ldap_rdn_value(){
# $1 = DN
echo "$1" | sed -E 's/^[^=]+=([^,]+),.*$/\1/'
}
+
+ldap_dn_exists(){
+ # Return 0 if DN exists, else 1.
+ # $1 = DN
+ ldap_search -s base -b "$1" dn > /dev/null 2>&1
+}
diff --git a/scripts/common/10-vars b/scripts/common/10-vars
index 3cfbd8f..2f0dc9d 100644
--- a/scripts/common/10-vars
+++ b/scripts/common/10-vars
@@ -9,6 +9,9 @@ fi
idm_hostnames=$(echo "$idm_server_list" | awk '{print $1}')
+fqdn="${BOXCONF_HOSTNAME}.${domain}"
+: ${smtp_host:="smtp.${domain}"}
+
realm=$(echo "$domain" | tr '[:lower:]' '[:upper:]')
basedn=$(echo "$domain" | sed -e 's/^/dc=/' -e 's/\./,dc=/g')
@@ -26,6 +29,6 @@ roles_basedn="ou=roles,${groups_basedn}"
automount_basedn="ou=automount,${basedn}"
sudo_basedn="ou=sudo,${basedn}"
dns_basedn="ou=dns,${basedn}"
-kdc_basedn="ou=dns,${basedn}"
+kdc_basedn="cn=kdc,${basedn}"
mail_basedn="ou=mail,${basedn}"
mail_domains_basedn="ou=domains,${mail_basedn}"
diff --git a/scripts/hostclass/desktop b/scripts/hostclass/desktop
index 2a85f16..561fb8d 100644
--- a/scripts/hostclass/desktop
+++ b/scripts/hostclass/desktop
@@ -10,11 +10,13 @@ pkg install -y \
eclipse \
firefox \
git \
+ gnupg \
krb5 \
i3 \
libreoffice \
libva-intel-media-driver \
networkmgr \
+ password-store \
py${python_version}-pip \
stow \
terminus-font \
@@ -30,9 +32,11 @@ pkg install -y \
case $desktop_type in
i3)
pkg install \
+ dunst \
i3 \
i3lock \
- i3status
+ i3status \
+ profanity
;;
kde)
pkg install \
@@ -100,3 +104,63 @@ esac
# On some graphics cards, kern.vt.suspendswitch=1 (the default) breaks graphics
# acceleration after resuming from sleep.
set_sysctl kern.vt.suspendswitch="${vt_suspendswitch:-1}"
+
+# Fix xterm-256color termcap
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=280679
+cat <<'EOF' | tic -o /usr/local/share/site-terminfo -
+xterm-256color|xterm with 256 colors,
+ am, bce, ccc, km, mc5i, mir, msgr, npc, xenl,
+ colors#0x100, cols#80, it#8, lines#24, pairs#0x10000,
+ acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+ bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
+ clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
+ csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
+ cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C,
+ cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
+ cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
+ dl=\E[%p1%dM, dl1=\E[M, ech=\E[%p1%dX, ed=\E[J, el=\E[K,
+ el1=\E[1K, flash=\E[?5h$<100/>\E[?5l, home=\E[H,
+ hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@,
+ il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS,
+ initc=\E]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
+ invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~,
+ kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D,
+ kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, ka1=\EOw,
+ ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z,
+ kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
+ kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP, kf10=\E[21~,
+ kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q,
+ kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~,
+ kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~,
+ kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~,
+ kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S,
+ kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~,
+ kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~,
+ kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q,
+ kf39=\E[1;6R, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~,
+ kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~,
+ kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~,
+ kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q,
+ kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~,
+ kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~,
+ kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~,
+ kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~,
+ kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~,
+ kind=\E[1;2B, kmous=\E[<, knp=\E[6~, kpp=\E[5~,
+ kri=\E[1;2A, mc0=\E[i, mc4=\E[4i, mc5=\E[5i, meml=\El,
+ memu=\Em, mgc=\E[?69l, nel=\EE, oc=\E]104\007,
+ op=\E[39;49m, rc=\E8, rep=%p1%c\E[%p2%{1}%-%db,
+ rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B,
+ rmam=\E[?7l, rmcup=\E[?1049l\E[23;0;0t, rmir=\E[4l,
+ rmkx=\E[?1l\E>, rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m,
+ rs1=\Ec\E]104\007, rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7,
+ setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
+ setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
+ sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
+ sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
+ smcup=\E[?1049h\E[22;0;0t,
+ smglr=\E[?69h\E[%i%p1%d;%p2%ds, smir=\E[4h,
+ smkx=\E[?1h\E=, smm=\E[?1034h, smso=\E[7m, smul=\E[4m,
+ tbc=\E[3g, u6=\E[%i%d;%dR, u7=\E[6n,
+ u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd,
+EOF
diff --git a/scripts/hostclass/idm_server/10-slapd b/scripts/hostclass/idm_server/10-slapd
index dc52a58..204c405 100644
--- a/scripts/hostclass/idm_server/10-slapd
+++ b/scripts/hostclass/idm_server/10-slapd
@@ -10,10 +10,13 @@
: ${slapd_syncrepl_session_log:='1000'}
: ${slapd_syncrepl_cleanup_age:='7'}
: ${slapd_syncrepl_cleanup_interval:='1'}
+: ${slapd_admin_role:='role-ldap-admin'}
slapd_user=ldap
slapd_data_dir=/var/db/openldap-data
slapd_conf_dir=/usr/local/etc/openldap
+slapd_socket=/var/run/openldap/ldapi
+slapd_ldapi_uri="ldapi://$(echo "$slapd_socket" | sed 's|/|%2f|g')"
slapd_tls_cert="${slapd_conf_dir}/slapd.crt"
slapd_tls_key="${slapd_conf_dir}/slapd.key"
slapd_replicator_tls_cert="${slapd_conf_dir}/replicator.crt"
@@ -72,8 +75,8 @@ fi
sysrc -v \
slapd_enable=YES \
slapd_cn_config=YES \
- slapd_flags="-h 'ldapi://%2fvar%2frun%2fopenldap%2fldapi/ ldap://0.0.0.0/ ldaps://0.0.0.0/ ldaps://${BOXCONF_DEFAULT_IPV4}/'" \
- slapd_sockets="/var/run/openldap/ldapi" \
+ 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
@@ -151,6 +154,12 @@ objectClass: organizationalUnit
ou: $(ldap_rdn_value "$roles_basedn")
EOF
+ # cn=role-ldap-admin,ou=roles,ou=groups,ou=accounts,dc=example,dc=com
+ ldap_add "cn=${slapd_admin_role},${roles_basedn}" <<EOF
+objectClass: groupOfMembers
+cn: ${slapd_admin_role}
+EOF
+
# ou=automount,dc=example,dc=com
ldap_add "$automount_basedn" <<EOF
objectClass: organizationalUnit
diff --git a/scripts/hostclass/idm_server/20-powerdns b/scripts/hostclass/idm_server/20-powerdns
index 4d42ee9..26abe52 100644
--- a/scripts/hostclass/idm_server/20-powerdns
+++ b/scripts/hostclass/idm_server/20-powerdns
@@ -40,7 +40,7 @@ objectClass: domainRelatedObject
dc: ${domain}
${pdns_soa_record}
${pdns_ns_records}
-$(echo "$idm_server_list" | awk '{print "aRecord: "$2}')
+$(echo "$idm_server_list" | awk '{print "aRecord: "$3}')
associatedDomain: ${domain}
EOF
diff --git a/scripts/hostclass/idm_server/30-kdc b/scripts/hostclass/idm_server/30-kdc
index 4921688..abe040a 100644
--- a/scripts/hostclass/idm_server/30-kdc
+++ b/scripts/hostclass/idm_server/30-kdc
@@ -1,12 +1,43 @@
#!/bin/sh
+kdc_conf_dir=/usr/local/var/krb5kdc
+kdc_master_key_path="${kdc_conf_dir}/master_key"
+
+: ${kdc_max_life:='24h'}
+: ${kdc_max_renewable_life:='7d'}
+
# Install MIT kerberos.
pkg install -y krb5
+# Generate the system kerberos configuration.
+install_template -m 0644 /etc/krb5.conf
+ln -snfv /etc/krb5.conf /usr/local/etc/krb5.conf
+
+# Generate KDC configuration files.
+install_template -m 0644 \
+ "${kdc_conf_dir}/kdc.conf" \
+ "${kdc_conf_dir}/kadm5.acl"
+
+# If the realm does not exist in LDAP, create it. Otherwise, stash the master key.
+if is_primary_server && ! ldap_dn_exists "$kdc_basedn"; then
+ kdb5_ldap_util -P "$kdc_master_key" create -subtrees "$accounts_basedn" -sscope SUB -s
+elif ! [ -f "$kdc_master_key_path" ]; then
+ kdb5_util -P "$kdc_master_key" stash
+fi
+
+# Start the KDC and kadmind.
+sysrc -v \
+ kdc_program=/usr/local/sbin/krb5kdc \
+ kadmind_program=/usr/local/sbin/kadmind \
+ kdc_flags="" \
+ kdc_enable=YES \
+ kadmind_enable=YES
+
+service kdc restart
+service kadmind restart
+
+# Create the boxconf administrative user.
if is_primary_server; then
- # ou=kdc,dc=example,dc=com
- ldap_add "$kdc_basedn" <<EOF
-objectClass: organizationalUnit
-ou: $(ldap_rdn_value "$dns_basedn")
-EOF
+ 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/40-unbound b/scripts/hostclass/idm_server/40-unbound
new file mode 100644
index 0000000..39f1317
--- /dev/null
+++ b/scripts/hostclass/idm_server/40-unbound
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+unbound_user=unbound
+unbound_conf_dir=/usr/local/etc/unbound
+unbound_blocklist_dir="${unbound_conf_dir}/blocklists"
+unbound_blocklist_url_file="${unbound_conf_dir}/blocklist_urls"
+
+: ${unbound_blocklist_urls:=''}
+: ${unbound_cache_max_negative_ttl:='60'}
+: ${unbound_rrset_cache_size:='104857600'} # 100 MB
+: ${unbound_msg_cache_size:='52428800'} # 50 MB
+: ${unbound_slabs:='2'}
+: ${unbound_insecure_domains:=''}
+: ${unbound_local_zones:=''}
+: ${unbound_local_data:=''}
+: ${unbound_blocklists:=''}
+: ${unbound_threads:="$nproc"}
+
+# Install unbound recursive resolver.
+pkg install -y unbound
+
+# Generate unbound configuration.
+install_directory -m 0755 -o "$unbound_user" "$unbound_blocklist_dir"
+install_template -m 0644 "${unbound_conf_dir}/unbound.conf"
+
+# Download blocklists.
+echo "$unbound_blocklists" | tee "$unbound_blocklist_url_file"
+install_file -m 0755 /usr/local/libexec/idm-update-unbound-blocklists
+su -m "$unbound_user" -c "/usr/local/libexec/idm-update-unbound-blocklists ${unbound_blocklist_dir} < ${unbound_blocklist_url_file}"
+
+# Enable and start unbound.
+sysrc -v unbound_enable=YES
+service unbound restart
+
+# Now we are ready to us unbound as the local resolver.
+install_template -m 0644 /etc/resolv.conf
+
+# Update blocklists with a cron job.
+echo "@daily root su -m ${unbound_user} -c \"/usr/local/libexec/idm-update-unbound-blocklists ${unbound_blocklist_dir} < ${unbound_blocklist_url_file}\" && service unbound reload" \
+ | tee /etc/cron.d/idm-update-unbound-blocklists
diff --git a/scripts/hostclass/idm_server/90-idm b/scripts/hostclass/idm_server/90-idm
index 7881f14..0a28491 100644
--- a/scripts/hostclass/idm_server/90-idm
+++ b/scripts/hostclass/idm_server/90-idm
@@ -1,9 +1,91 @@
#!/bin/sh
-# Create host object for this server
-# Create ldap service principal for this server
-# Create A record
-# Create PTR record
-# Create boxconf user
-# Create sudo rules
-# Create admin group
+# Create host object.
+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
+
+# 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 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 host principal.
+kadmin.local get_principal -terse "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 add_principal -nokey -x "containerdn=${services_basedn}" "ldap/${fqdn}"
+
+# Create state dataset to persist keytabs across OS rebuilds.
+create_dataset -o "mountpoint=${keytab_dir}" "${state_dataset}/keytabs"
+
+# Export host keytab.
+[ -f "${keytab_dir}/host.keytab" ] || kadmin.local ktadd -k "${keytab_dir}/host.keytab" -q "host/${fqdn}"
+ln -snfv "${keytab_dir}/host.keytab" /etc/krb5.keytab
+
+# Export slapd keytab.
+[ -f "$slapd_keytab" ] || kadmin.local ktadd -k "$slapd_keytab" -q "ldap/${fqdn}"
+chown "$slapd_user" "$slapd_keytab"
+
+# Install PAM/NSS integration packages.
+pkg install -y \
+ nss-pam-ldapd-sasl \
+ 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 \
+ /usr/local/etc/nslcd.conf \
+ /etc/nscd.conf
+
+sysrc -v \
+ nslcd_enable=YES \
+ nscd_enable=YES
+
+service nslcd restart
+service nscd restart
+
+# Create ldap.conf symlink.
+ln -snfs "${slapd_conf_dir}/ldap.conf" /usr/local/etc/ldap.conf
+
+# Copy IDM helper scripts for SSH.
+install_file -m 0555 \
+ /usr/local/libexec/idm-ssh-known-hosts \
+ /usr/local/libexec/idm-ssh-authorized-keys
diff --git a/scripts/os/freebsd/10-sysctls b/scripts/os/freebsd/10-sysctls
index a59d54f..865544a 100644
--- a/scripts/os/freebsd/10-sysctls
+++ b/scripts/os/freebsd/10-sysctls
@@ -67,8 +67,8 @@ if [ "$BOXCONF_VIRTUALIZATION_TYPE" != jail ]; then
net.inet.tcp.minmss=536 \
security.bsd.hardlink_check_gid=0 \
security.bsd.hardlink_check_uid=0 \
- security.bsd.see_other_gids=0 \
- security.bsd.see_other_uids=0 \
+ security.bsd.see_other_gids="$see_other_uids" \
+ security.bsd.see_other_uids="$see_other_uids" \
security.bsd.unprivileged_read_msgbuf=0 \
vfs.zfs.min_auto_ashift=12
diff --git a/scripts/os/freebsd/30-ssh b/scripts/os/freebsd/30-ssh
deleted file mode 100644
index 7a57943..0000000
--- a/scripts/os/freebsd/30-ssh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-
-# Create state dataset to persist SSH host keys across OS rebuilds.
-create_dataset -o "mountpoint=${ssh_host_key_dir}" "${state_dataset}/ssh"
-
-# If the state dataset contains existing host keys, symlink them into
-# /etc/ssh.
-#
-# If not, this is the first time we are building this box, so copy the
-# autogenerated host keys to the state partition.
-for key in \
- ssh_host_ecdsa_key \
- ssh_host_ed25519_key \
- ssh_host_rsa_key
-do
- [ -f "${ssh_host_key_dir}/${key}" ] || \
- mv -v "/etc/ssh/${key}" "/etc/ssh/${key}.pub" "$ssh_host_key_dir"
-
- ln -snvf "${ssh_host_key_dir}/${key}" "/etc/ssh/${key}"
- ln -snvf "${ssh_host_key_dir}/${key}.pub" "/etc/ssh/${key}.pub"
-done
-
-# Copy SSH configs.
-install_directory -m 0755 /etc/ssh/sshd_config.d
-
-install_template -m 0644 \
- /etc/ssh/sshd_config \
- /etc/ssh/ssh_config
-
-# Restart sshd.
-sysrc -v sshd_enable=YES
-service sshd restart
diff --git a/scripts/os/freebsd/41-ssh b/scripts/os/freebsd/41-ssh
new file mode 100644
index 0000000..7352fc3
--- /dev/null
+++ b/scripts/os/freebsd/41-ssh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Install and use ssh from ports.
+pkg install -y openssh-portable
+
+# Disable base sshd and enable the one from ports.
+sysrc -v \
+ sshd_enable=NO \
+ openssh_enable=YES
+
+# Create state dataset to persist SSH host keys across OS rebuilds.
+create_dataset -o "mountpoint=${ssh_host_key_dir}" "${state_dataset}/ssh"
+
+# If the state dataset contains existing host keys, symlink them into
+# /etc/ssh.
+#
+# If not, this is the first time we are building this box, so copy the
+# autogenerated host keys to the state partition.
+service openssh keygen
+for key in \
+ ssh_host_ecdsa_key \
+ ssh_host_ed25519_key \
+ ssh_host_rsa_key
+do
+ [ -f "${ssh_host_key_dir}/${key}" ] || \
+ mv -v "/usr/local/etc/ssh/${key}" "/usr/local/etc/ssh/${key}.pub" "$ssh_host_key_dir"
+
+ ln -snvf "${ssh_host_key_dir}/${key}" "/usr/local/etc/ssh/${key}"
+ ln -snvf "${ssh_host_key_dir}/${key}.pub" "/usr/local/etc/ssh/${key}.pub"
+done
+
+# Copy SSH configs.
+install_directory -m 0755 /usr/local/etc/ssh/sshd_config.d
+install_template -m 0644 \
+ /usr/local/etc/ssh/sshd_config \
+ /usr/local/etc/ssh/ssh_config
+
+# Stop base sshd and start the one from ports.
+service sshd stop
+service openssh restart
diff --git a/vars/common b/vars/common
index 845b0b8..20c7976 100644
--- a/vars/common
+++ b/vars/common
@@ -6,6 +6,7 @@ email_domain=example.com
locale=en_US.UTF-8
ntp_pools='pool.ntp.org'
root_password=changeme
+boxconf_password=changeme
root_authorized_keys='ssh-ed25519 changeme
ssh-ed25519 changeme'
root_mail_alias="you@${email_domain}"
@@ -28,16 +29,13 @@ nproc=$(nproc)
allowed_tcp_ports=ssh
bootstrap_resolvers='1.1.1.1'
desktop_type=kde
-fqdn="${BOXCONF_HOSTNAME}.${domain}"
graphics_type=intel
-idm_admin_username='s-boxconf'
-idm_admin_uid='20000'
-idm_admin_groupname='sysadmins'
-idm_admin_gid='30000'
+boxconf_username='s-boxconf'
+krb5_ticket_lifetime=24h
+krb5_renew_lifetime=7d
nslcd_min_uid=1000
nscd_ttl=600
nscd_negative_ttl=20
-smtp_host="smtp.${domain}"
ssh_authzkeys_user=_authzkeys
tcp_buffer_size=2097152 # suitable for 1 GigE
diff --git a/vars/hostclass/idm_server b/vars/hostclass/idm_server
index eec6d1c..dec58b7 100644
--- a/vars/hostclass/idm_server
+++ b/vars/hostclass/idm_server
@@ -3,7 +3,9 @@
allowed_tcp_ports='ssh ldap ldaps domain kerberos-sec kerberos-adm'
allowed_udp_ports='domain kerberos-sec kpasswd'
+kdc_master_key='changeme'
+
ssh_authorized_keys_user=nobody
unbound_blocklists="\
-https://raw.githubusercontent.com/hagezi/dns-blocklists/main/unbound/pro.plus.blacklist.conf"
+hagezi-pro https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/pro.txt"
diff --git a/vars/hostclass/roadwarrior_laptop b/vars/hostclass/roadwarrior_laptop
index 1889a77..45bade8 100644
--- a/vars/hostclass/roadwarrior_laptop
+++ b/vars/hostclass/roadwarrior_laptop
@@ -1,3 +1,4 @@
#!/bin/sh
resolvers=$bootstrap_resolvers
pf_skip_interfaces=wg
+see_other_uids=1
diff --git a/vars/os/freebsd b/vars/os/freebsd
index 17e7edb..9f5f068 100644
--- a/vars/os/freebsd
+++ b/vars/os/freebsd
@@ -6,12 +6,14 @@ cx_lowest=Cmax
enable_pf=true
install_packages='sudo tmux vim'
intel_epp=50
+see_other_uids=0
export ASSUME_ALWAYS_YES=yes
acme_standalone_port=9080
acme_uid=169
keytab_dir=/var/db/keytabs
nfscbd_port=7745
+nslcd_user=nslcd
python_version=311
saslauthd_runtime_dir=/var/run/saslauthd
saslauthd_user=cyrus