From 8fdd7fbb811e940536ba991698d8a171cbfa8ab9 Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Fri, 28 Mar 2025 05:45:49 +0100
Subject: [PATCH] Various improvements.

---
 bookworm/aptmark/all                          |  14 ++
 bookworm/aptmark/server                       |   4 +
 bookworm/aptmark/user                         |  17 +++
 .../apt/apt.conf.d/99_minimize_dependencies   |   4 +
 bookworm/etc/all/apt/sources.list             |   4 +
 bookworm/etc/server/ssh/sshd_config           | 123 ++++++++++++++++++
 bookworm/home/all/.bash_profile               |   8 ++
 bookworm/home/all/.bashrc                     |  29 +++++
 .../home/all/.plomlib.sh/file_dot_variants    |   7 +
 bookworm/home/all/.profile                    |  20 +++
 bookworm/home/root/.shell_prompt_color        |   1 +
 bookworm/preseed.cfg                          |  20 +--
 bookworm/scripts/init_server_access.sh        |  22 ++++
 bookworm/scripts/lib/constants_server         |   2 +
 bookworm/scripts/lib/copy_dirtree             |  22 ++++
 bookworm/scripts/lib/determine_ip             |  25 ++++
 bookworm/scripts/lib/install_aptmarkeds       |  15 +++
 bookworm/scripts/lib/mark_nonrequireds_auto   |  15 +++
 bookworm/scripts/setup_server.sh              |  59 +++++++++
 testing/aptmark/all                           |  15 +--
 testing/aptmark/user                          |  18 +--
 .../apt/apt.conf.d/99_minimize_dependencies   |   5 +-
 testing/home/all/.bash_profile                |   9 +-
 testing/home/all/.bashrc                      |  30 +----
 .../home/all/.plomlib.sh/file_dot_variants    |   8 +-
 testing/home/all/.profile                     |  21 +--
 testing/home/root/.shell_prompt_color         |   2 +-
 testing/scripts/lib/copy_dirtree              |   1 +
 testing/scripts/lib/determine_ip              |   1 +
 testing/scripts/lib/install_aptmarkeds        |   1 +
 testing/scripts/lib/mark_nonrequireds_auto    |   1 +
 testing/scripts/setup_desktop.sh              |  89 +------------
 testing/scripts/setup_secrets.sh              |   2 +-
 33 files changed, 422 insertions(+), 192 deletions(-)
 create mode 100644 bookworm/aptmark/all
 create mode 100644 bookworm/aptmark/server
 create mode 100644 bookworm/aptmark/user
 create mode 100644 bookworm/etc/all/apt/apt.conf.d/99_minimize_dependencies
 create mode 100644 bookworm/etc/all/apt/sources.list
 create mode 100644 bookworm/etc/server/ssh/sshd_config
 create mode 100644 bookworm/home/all/.bash_profile
 create mode 100644 bookworm/home/all/.bashrc
 create mode 100644 bookworm/home/all/.plomlib.sh/file_dot_variants
 create mode 100644 bookworm/home/all/.profile
 create mode 100644 bookworm/home/root/.shell_prompt_color
 create mode 100755 bookworm/scripts/init_server_access.sh
 create mode 100644 bookworm/scripts/lib/constants_server
 create mode 100644 bookworm/scripts/lib/copy_dirtree
 create mode 100644 bookworm/scripts/lib/determine_ip
 create mode 100644 bookworm/scripts/lib/install_aptmarkeds
 create mode 100644 bookworm/scripts/lib/mark_nonrequireds_auto
 create mode 100755 bookworm/scripts/setup_server.sh
 mode change 100644 => 120000 testing/aptmark/all
 mode change 100644 => 120000 testing/aptmark/user
 mode change 100644 => 120000 testing/etc/all/apt/apt.conf.d/99_minimize_dependencies
 mode change 100644 => 120000 testing/home/all/.bash_profile
 mode change 100644 => 120000 testing/home/all/.bashrc
 mode change 100644 => 120000 testing/home/all/.plomlib.sh/file_dot_variants
 mode change 100644 => 120000 testing/home/all/.profile
 mode change 100644 => 120000 testing/home/root/.shell_prompt_color
 create mode 120000 testing/scripts/lib/copy_dirtree
 create mode 120000 testing/scripts/lib/determine_ip
 create mode 120000 testing/scripts/lib/install_aptmarkeds
 create mode 120000 testing/scripts/lib/mark_nonrequireds_auto

diff --git a/bookworm/aptmark/all b/bookworm/aptmark/all
new file mode 100644
index 0000000..3605a65
--- /dev/null
+++ b/bookworm/aptmark/all
@@ -0,0 +1,14 @@
+# connectivity: ifupdown seems necessary everyhwere, isc-dhcp-client unpredictably so
+ifupdown
+isc-dhcp-client
+# git for the setup directory; cloning works with ca-certificates
+ca-certificates
+git
+# to avoid constant warnings about no locale being found
+locales
+# extremely useful for basic network debugging; missed these more than once in an emergency
+netcat-traditional
+iputils-ping
+# to set the time
+ntpsec-ntpdate
+#
diff --git a/bookworm/aptmark/server b/bookworm/aptmark/server
new file mode 100644
index 0000000..70e8d2b
--- /dev/null
+++ b/bookworm/aptmark/server
@@ -0,0 +1,4 @@
+# so we can log in at all …
+openssh-server
+# firewalling
+nftables
diff --git a/bookworm/aptmark/user b/bookworm/aptmark/user
new file mode 100644
index 0000000..1ae2972
--- /dev/null
+++ b/bookworm/aptmark/user
@@ -0,0 +1,17 @@
+# stuff we want on any system with interactive plom user account
+#
+# ping won't work for user without this – see <https://shallowsky.com/blog/linux/ping-permissions.html>
+linux-sysctl-defaults
+# generally useful
+ack
+vim
+sudo
+less
+man-db
+manpages
+procps
+# for syncing
+borgbackup
+# for my own scripts to run
+python3-venv
+#
diff --git a/bookworm/etc/all/apt/apt.conf.d/99_minimize_dependencies b/bookworm/etc/all/apt/apt.conf.d/99_minimize_dependencies
new file mode 100644
index 0000000..4aaef79
--- /dev/null
+++ b/bookworm/etc/all/apt/apt.conf.d/99_minimize_dependencies
@@ -0,0 +1,4 @@
+APT::AutoRemove::RecommendsImportant "false";
+APT::AutoRemove::SuggestsImportant "false";
+APT::Install-Recommends "false";
+APT::Install-Suggests "false";
diff --git a/bookworm/etc/all/apt/sources.list b/bookworm/etc/all/apt/sources.list
new file mode 100644
index 0000000..72b0ffb
--- /dev/null
+++ b/bookworm/etc/all/apt/sources.list
@@ -0,0 +1,4 @@
+deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
+deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
+deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
+deb http://ftp.debian.org/debian bookworm-backports main contrib non-free non-free-firmware
diff --git a/bookworm/etc/server/ssh/sshd_config b/bookworm/etc/server/ssh/sshd_config
new file mode 100644
index 0000000..e952cb3
--- /dev/null
+++ b/bookworm/etc/server/ssh/sshd_config
@@ -0,0 +1,123 @@
+
+# This is the sshd server system-wide configuration file.  See
+# sshd_config(5) for more information.
+
+# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games
+
+# The strategy used for options in the default sshd_config shipped with
+# OpenSSH is to specify options with their default value where
+# possible, but leave them commented.  Uncommented options override the
+# default value.
+
+Include /etc/ssh/sshd_config.d/*.conf
+
+#Port 22
+#AddressFamily any
+#ListenAddress 0.0.0.0
+#ListenAddress ::
+
+#HostKey /etc/ssh/ssh_host_rsa_key
+#HostKey /etc/ssh/ssh_host_ecdsa_key
+#HostKey /etc/ssh/ssh_host_ed25519_key
+
+# Ciphers and keying
+#RekeyLimit default none
+
+# Logging
+#SyslogFacility AUTH
+#LogLevel INFO
+
+# Authentication:
+
+#LoginGraceTime 2m
+#PermitRootLogin prohibit-password
+PermitRootLogin no  # plomlompom's security rule
+#StrictModes yes
+#MaxAuthTries 6
+#MaxSessions 10
+
+#PubkeyAuthentication yes
+
+# Expect .ssh/authorized_keys2 to be disregarded by default in future.
+#AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2
+
+#AuthorizedPrincipalsFile none
+
+#AuthorizedKeysCommand none
+#AuthorizedKeysCommandUser nobody
+
+# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
+#HostbasedAuthentication no
+# Change to yes if you don't trust ~/.ssh/known_hosts for
+# HostbasedAuthentication
+#IgnoreUserKnownHosts no
+# Don't read the user's ~/.rhosts and ~/.shosts files
+#IgnoreRhosts yes
+
+# To disable tunneled clear text passwords, change to no here!
+PasswordAuthentication no
+#PermitEmptyPasswords no
+
+# Change to yes to enable challenge-response passwords (beware issues with
+# some PAM modules and threads)
+KbdInteractiveAuthentication no
+
+# Kerberos options
+#KerberosAuthentication no
+#KerberosOrLocalPasswd yes
+#KerberosTicketCleanup yes
+#KerberosGetAFSToken no
+
+# GSSAPI options
+#GSSAPIAuthentication no
+#GSSAPICleanupCredentials yes
+#GSSAPIStrictAcceptorCheck yes
+#GSSAPIKeyExchange no
+
+# Set this to 'yes' to enable PAM authentication, account processing,
+# and session processing. If this is enabled, PAM authentication will
+# be allowed through the KbdInteractiveAuthentication and
+# PasswordAuthentication.  Depending on your PAM configuration,
+# PAM authentication via KbdInteractiveAuthentication may bypass
+# the setting of "PermitRootLogin prohibit-password".
+# If you just want the PAM account and session checks to run without
+# PAM authentication, then enable this but set PasswordAuthentication
+# and KbdInteractiveAuthentication to 'no'.
+UsePAM yes
+
+#AllowAgentForwarding yes
+#AllowTcpForwarding yes
+#GatewayPorts no
+X11Forwarding yes
+#X11DisplayOffset 10
+#X11UseLocalhost yes
+#PermitTTY yes
+PrintMotd no
+#PrintLastLog yes
+#TCPKeepAlive yes
+#PermitUserEnvironment no
+#Compression delayed
+ClientAliveInterval 15
+#ClientAliveCountMax 3
+#UseDNS no
+#PidFile /run/sshd.pid
+#MaxStartups 10:30:100
+#PermitTunnel no
+#ChrootDirectory none
+#VersionAddendum none
+
+# no default banner path
+#Banner none
+
+# Allow client to pass locale environment variables
+AcceptEnv LANG LC_*
+
+# override default of no subsystems
+Subsystem	sftp	/usr/lib/openssh/sftp-server
+
+# Example of overriding settings on a per-user basis
+#Match User anoncvs
+#	X11Forwarding no
+#	AllowTcpForwarding no
+#	PermitTTY no
+#	ForceCommand cvs server
diff --git a/bookworm/home/all/.bash_profile b/bookworm/home/all/.bash_profile
new file mode 100644
index 0000000..316cf19
--- /dev/null
+++ b/bookworm/home/all/.bash_profile
@@ -0,0 +1,8 @@
+# ~/.bash_profile, if it exists, is sourced by Bash for all login shells,
+# instead of ~/.profile
+#
+# While I prefer using ~/.profile, I provide ~/.bash_profile because some
+# applications write to it even if it does not previously exist, which
+# deactivates ~/.profile if not explicitly sourced from within here.
+. "${HOME}/.profile"
+
diff --git a/bookworm/home/all/.bashrc b/bookworm/home/all/.bashrc
new file mode 100644
index 0000000..636c408
--- /dev/null
+++ b/bookworm/home/all/.bashrc
@@ -0,0 +1,29 @@
+# ~/.bashrc is sourced by Bash for any interactive shells. 
+
+# Use vim as default editor for anything.
+export VISUAL=vim
+export EDITOR="${VISUAL}"
+
+# Some helpful aliases.
+alias curlpost='curl -H "Content-Type: application/json" -X POST'
+alias ls="ls --color=auto"
+
+# Colored prompt with username, hostname, date/time, directory.
+COLOR_NUMBER=7  # default to white
+COLOR_NUMBER_FILE="${HOME}/.shell_prompt_color"
+[ -f "${COLOR_NUMBER_FILE}" ] && COLOR_NUMBER=`cat "${COLOR_NUMBER_FILE}"`
+tput_color="$(tput setaf "${COLOR_NUMBER}")$(tput bold)"
+tput_reset="$(tput sgr0)"
+# Bash confuses the line length when not told to not count escape sequences.
+if [ -n "${BASH}" ]; then
+    tput_color="\[$tput_color\]"
+    tput_reset="\[$tput_reset\]"
+fi
+PS1="${tput_color}["\$\(date\ +%Y-%m-%d/%H:%M:%S/%Z\)" $(whoami)@$(hostname):"\$\(pwd\)"]$ ${tput_reset}"
+PS2="${tput_color}> ${tput_reset}"
+PS3="${tput_color}select: ${tput_reset}"
+PS4="${tput_color}+ ${tput_reset}"
+
+# include others
+. "${HOME}/.plomlib.sh/file_dot_variants"
+file_dot_variants "${HOME}/\.bashrc"
diff --git a/bookworm/home/all/.plomlib.sh/file_dot_variants b/bookworm/home/all/.plomlib.sh/file_dot_variants
new file mode 100644
index 0000000..1c1bf69
--- /dev/null
+++ b/bookworm/home/all/.plomlib.sh/file_dot_variants
@@ -0,0 +1,7 @@
+file_dot_variants() {
+    for FILE in ${1}\.*; do
+        if [ -f "${FILE}" ]; then
+            . "${FILE}"
+        fi
+    done
+}
diff --git a/bookworm/home/all/.profile b/bookworm/home/all/.profile
new file mode 100644
index 0000000..13fa422
--- /dev/null
+++ b/bookworm/home/all/.profile
@@ -0,0 +1,20 @@
+# ~/.profile is sourced on login, with its exports inherited by all processes
+# started below it under the same login, i.e. it has a very deep effect, but
+# changes to it only apply to new login sessions (compare to e.g. ~/.bashrc
+# which by is sourced anew for every new interactive Bash, login or not).
+#
+# Notably this makes ~/.profile (outside its optional sourcing of ~/.bashrc)
+# useful for providing environment variables to non-shell applications started
+# within a login session.
+PATH_BASHRC="${HOME}/.bashrc"
+[ -n "${BASH_VERSION}" -a -f "${PATH_BASHRC}" ] && . "${PATH_BASHRC}"
+
+PATH_LOCAL_BIN="${HOME}/.local/bin"
+[ -d "${PATH_LOCAL_BIN}" ] && PATH="${PATH_LOCAL_BIN}:${PATH}"
+
+# local changes to this shell variable otherwise will get lost
+export PATH
+
+# include others
+. "${HOME}/.plomlib.sh/file_dot_variants"
+file_dot_variants "${HOME}/\.profile"
diff --git a/bookworm/home/root/.shell_prompt_color b/bookworm/home/root/.shell_prompt_color
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/bookworm/home/root/.shell_prompt_color
@@ -0,0 +1 @@
+1
diff --git a/bookworm/preseed.cfg b/bookworm/preseed.cfg
index 4b7e4f3..481658a 100644
--- a/bookworm/preseed.cfg
+++ b/bookworm/preseed.cfg
@@ -1,18 +1,18 @@
 #_preseed_V1
 
-d-i passwd/root-login boolean true
-user-setup-udeb	passwd/make-user boolean false
+d-i debian-installer/load-cdrom/title
 
 d-i netcfg/choose_interface select auto
 d-i netcfg/link_wait_timeout string 3
-d-i netcfg/wireless_security_type wpa
-#d-i netcfg/wireless_wep string
-#d-i netcfg/disable_autoconfig boolean false
+#d-i netcfg/wireless_security_type wpa
 d-i netcfg/use_autoconfig true
-d-i netcfg/wireless_essid kadatheron
+#d-i netcfg/wireless_essid kadatheron
 d-i netcfg/get_hostname string unassigned-hostname
 d-i netcfg/get_domain string unassigned-domain
 
+d-i passwd/root-login boolean true
+user-setup-udeb	passwd/make-user boolean false
+
 # d-i partman-auto/method string crypto
 d-i partman-auto-lvm/guided_size string max
 #d-i partman-auto/choose_recipe select atomic
@@ -24,16 +24,20 @@ d-i base-installer/initramfs-tools/driver-policy most
 
 # d-i pkgsel/run_tasksel boolean false
 
+d-i apt-setup/use-mirror true
 d-i mirror/protocol string http
+d-i mirror/http/proxy
 #d-i mirror/http/hostname string http.us.debian.org
+d-i apt-setup/enable-source-repositories true
 d-i apt-setup/non-free-firmware boolean true
 d-i apt-setup/non-free boolean true
 d-i apt-setup/contrib boolean true
 d-i apt-setup/services-select security, updates
 
 # d-i grub-installer/only_debian boolean true
-grub-installer/enable_os_prober_otheros_no false
-grub-installer/force-efi-extra-removable false
+d-i grub-installer/force-efi-extra-removable false
+d-i grub-installer/update-nvram true
+d-i grub-installer/enable_os_prober_otheros_no false
 
 clock-setup clock-setup/utc boolean true
 d-i finish-install/reboot_in_progress note
diff --git a/bookworm/scripts/init_server_access.sh b/bookworm/scripts/init_server_access.sh
new file mode 100755
index 0000000..1fb240d
--- /dev/null
+++ b/bookworm/scripts/init_server_access.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+set -e
+cd $(dirname "$0")
+. lib/constants_server
+. lib/expect_min_n_args
+
+PATH_KNOWN_HOSTS="${PATH_SSH}/known_hosts"
+
+expect_min_n_args 1 '(server)' "$@"
+SERVER="$1"
+
+echo '\nKnow host.\n'
+set +e
+ssh-keygen -f "${PATH_KNOWN_HOSTS}" -R "${SERVER}"
+set -e
+ssh-keyscan "${SERVER}" >> "${PATH_KNOWN_HOSTS}"
+
+echo '\nAsking for new root password.\n'
+ssh "root@${SERVER}" 'printf "\n\n" && passwd'
+
+echo '\nSetting up config repo.'
+ssh "root@${SERVER}" 'apt update && apt install -y git && git clone https://plomlompom.com/repos/clone/config'
diff --git a/bookworm/scripts/lib/constants_server b/bookworm/scripts/lib/constants_server
new file mode 100644
index 0000000..9a10aaa
--- /dev/null
+++ b/bookworm/scripts/lib/constants_server
@@ -0,0 +1,2 @@
+PATH_REL_SSH=.ssh
+PATH_SSH="${HOME}/${PATH_REL_SSH}"
diff --git a/bookworm/scripts/lib/copy_dirtree b/bookworm/scripts/lib/copy_dirtree
new file mode 100644
index 0000000..da20d4e
--- /dev/null
+++ b/bookworm/scripts/lib/copy_dirtree
@@ -0,0 +1,22 @@
+copy_dirtree() {
+    expect_min_n_args 3 '(source root, target root, tags)' "$@"
+    SOURCE_ROOT="$1"
+    TARGET_ROOT="$2"
+    shift 2
+    TAGS="$@"
+    for TAG in ${TAGS}; do
+	PATH_TAG="${SOURCE_ROOT}/${TAG}"
+	if [ ! -d "${PATH_TAG}" ]; then
+	    continue
+	fi
+	cd "${PATH_TAG}"
+        for PATH_REL in $(find . -type f,l); do
+            PATH_TARGET="${TARGET_ROOT}"$(echo "${PATH_REL}" | cut -c2-)
+            PATH_SOURCE=$(realpath "${PATH_REL}")
+            DIRECTORY=$(dirname "${PATH_TARGET}")
+            mkdir -p "${DIRECTORY}"
+            cp -L "${PATH_SOURCE}" "${PATH_TARGET}"
+        done
+        cd -
+    done
+}
diff --git a/bookworm/scripts/lib/determine_ip b/bookworm/scripts/lib/determine_ip
new file mode 100644
index 0000000..0fd0f09
--- /dev/null
+++ b/bookworm/scripts/lib/determine_ip
@@ -0,0 +1,25 @@
+determine_ip() {
+    FINAL_IP="127.0.1.1"
+    for IP in $(hostname -I); do
+        if [ $(echo "${IP}" | grep ':' | wc -l) -eq 1 ]; then
+            continue
+        fi
+        RANGE_1=$(echo "${IP}" | cut -d "." -f 1)
+        RANGE_2=$(echo "${IP}" | cut -d "." -f 2)
+        if [ "${RANGE_1}" -eq 127 ]; then
+            continue
+        elif [ "${RANGE_1}" -eq 10 ]; then
+            continue
+        elif [ "${RANGE_1}" -eq 172 ]; then
+            if [ "${RANGE_2}" -ge 16 ] && [ "${RANGE_2}" -le 31 ]; then
+                continue
+            fi
+        elif [ "${RANGE_1}" -eq 192 ]; then
+            if [ "${RANGE_2}" -eq 168 ]; then
+                continue
+            fi
+        fi
+        FINAL_IP="${IP}"
+    done
+    printf "${FINAL_IP}"
+}
diff --git a/bookworm/scripts/lib/install_aptmarkeds b/bookworm/scripts/lib/install_aptmarkeds
new file mode 100644
index 0000000..daf91d1
--- /dev/null
+++ b/bookworm/scripts/lib/install_aptmarkeds
@@ -0,0 +1,15 @@
+install_aptmarkeds() {
+    # Walk through the package names in ../aptmark/ files to ensure the respective
+    # packages are installed.
+    for TAG in $@; do
+        PATH_APTMARK_TAG="${PATH_CONF}/aptmark/${TAG}"
+        if [ ! -f "${PATH_APTMARK_TAG}" ]; then
+            continue
+        fi
+        cat "${PATH_APTMARK_TAG}" | while read LINE; do
+            if [ ! $(echo "${LINE}" | cut -c1) = "#" ]; then
+                apt-get -y -o Dpkg::Options::="--force-confnew" install "${LINE}"
+            fi
+        done
+    done
+}
diff --git a/bookworm/scripts/lib/mark_nonrequireds_auto b/bookworm/scripts/lib/mark_nonrequireds_auto
new file mode 100644
index 0000000..0b2a435
--- /dev/null
+++ b/bookworm/scripts/lib/mark_nonrequireds_auto
@@ -0,0 +1,15 @@
+mark_nonrequireds_auto() {
+    PATH_LIST_PREFIX=/tmp/list_
+    PATH_LIST_UNSORTED="${PATH_LIST_PREFIX}unsorted"
+    PATH_LIST_ALL_PACKAGES="${PATH_LIST_PREFIX}all_packages"
+    PATH_LIST_WHITE="${PATH_LIST_PREFIX}white"
+    PATH_LIST_BLACK="${PATH_LIST_PREFIX}black"
+    TOK_REQ=' required'
+    dpkg-query -Wf '${Package} ${Priority}\n' | grep "${TOK_REQ}" | sed "s/${TOK_REQ}//" > "${PATH_LIST_UNSORTED}"
+    sort "${PATH_LIST_UNSORTED}" | uniq > "${PATH_LIST_WHITE}"
+    dpkg-query -Wf '${Package}\n' > "${PATH_LIST_UNSORTED}"
+    sort "${PATH_LIST_UNSORTED}" | uniq > "${PATH_LIST_ALL_PACKAGES}"
+    comm -3 "${PATH_LIST_ALL_PACKAGES}" "${PATH_LIST_WHITE}" > "${PATH_LIST_BLACK}"
+    apt-mark auto `cat "${PATH_LIST_BLACK}"`
+    rm "${PATH_LIST_UNSORTED}" "${PATH_LIST_ALL_PACKAGES}" "${PATH_LIST_WHITE}" "${PATH_LIST_BLACK}"
+}
diff --git a/bookworm/scripts/setup_server.sh b/bookworm/scripts/setup_server.sh
new file mode 100755
index 0000000..41b4501
--- /dev/null
+++ b/bookworm/scripts/setup_server.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+set -e
+cd $(dirname "$0")
+. lib/constants_server  # PATH_REL_SSH, PATH_SSH
+. lib/copy_dirtree
+. lib/determine_ip
+. lib/expect_min_n_args
+. lib/install_aptmarkeds
+. lib/mark_nonrequireds_auto
+
+MIN_TAGS='all server'
+
+expect_min_n_args 2 '(hostname, FQDN)' "$@"
+HOSTNAME="$1"
+FQDN="$2"
+
+USERNAME=plom
+PATH_USER_HOME="/home/${USERNAME}"
+PARENT_DIR="$(dirname $(pwd))"
+DEBIAN_RELEASE="$(basename ${PARENT_DIR})"
+PATH_REPO="$(dirname ${PARENT_DIR})"
+PATH_REL_ETC=etc
+PATH_CONF="${PATH_REPO}/${DEBIAN_RELEASE}"
+PATH_CONF_ETC="${PATH_CONF}/${PATH_REL_ETC}"
+PATH_CONF_HOME="${PATH_CONF}/home"
+PATH_USER_SSH="${PATH_USER_HOME}/${PATH_REL_SSH}"
+PATH_ETC="/${PATH_REL_ETC}"
+PATH_HOSTS="${PATH_ETC}/hosts"
+
+mark_nonrequireds_auto
+install_aptmarkeds ${MIN_TAGS}
+apt -y --purge autoremove
+apt -y dist-upgrade
+
+echo '\nSetting hostname and FQDN.'
+echo "${HOSTNAME}" > "${PATH_ETC}/hostname"
+hostname "${HOSTNAME}"
+echo '127.0.0.1 localhost.localdomain localhost' > "${PATH_HOSTS}"
+echo "$(determine_ip) ${FQDN} ${HOSTNAME}" >> "${PATH_HOSTS}"
+
+echo '\nAdapting /etc to our needs.'
+copy_dirtree "${PATH_CONF_ETC}" "${PATH_ETC}"${MIN_TAGS}
+echo '\nSetting Berlin localtime.'
+ln -sf /usr/share/zoneinfo/Europe/Berlin "${PATH_ETC}/localtime"
+ntpdate-debian
+
+# # Enable firewall.
+# systemctl enable nftables.service
+
+echo "\nSetting up root user's home directory."
+copy_dirtree "${PATH_CONF_HOME}" '/root' ${MIN_TAGS} root
+
+echo "\nSetting up user ${USERNAME}."
+adduser --disabled-password --gecos "" "${USERNAME}"
+usermod -a -G sudo "${USERNAME}"
+copy_dirtree "${PATH_CONF_HOME}" "${PATH_USER_HOME}" ${MIN_TAGS} user
+mkdir -p "${PATH_USER_SSH}"
+mv "${PATH_SSH}/authorized_keys" "${PATH_USER_SSH}/"
+chown -R "${USERNAME}:${USERNAME}" "${PATH_USER_HOME}"
diff --git a/testing/aptmark/all b/testing/aptmark/all
deleted file mode 100644
index 3605a65..0000000
--- a/testing/aptmark/all
+++ /dev/null
@@ -1,14 +0,0 @@
-# connectivity: ifupdown seems necessary everyhwere, isc-dhcp-client unpredictably so
-ifupdown
-isc-dhcp-client
-# git for the setup directory; cloning works with ca-certificates
-ca-certificates
-git
-# to avoid constant warnings about no locale being found
-locales
-# extremely useful for basic network debugging; missed these more than once in an emergency
-netcat-traditional
-iputils-ping
-# to set the time
-ntpsec-ntpdate
-#
diff --git a/testing/aptmark/all b/testing/aptmark/all
new file mode 120000
index 0000000..1913f5e
--- /dev/null
+++ b/testing/aptmark/all
@@ -0,0 +1 @@
+../../bookworm/aptmark/all
\ No newline at end of file
diff --git a/testing/aptmark/user b/testing/aptmark/user
deleted file mode 100644
index 1ae2972..0000000
--- a/testing/aptmark/user
+++ /dev/null
@@ -1,17 +0,0 @@
-# stuff we want on any system with interactive plom user account
-#
-# ping won't work for user without this – see <https://shallowsky.com/blog/linux/ping-permissions.html>
-linux-sysctl-defaults
-# generally useful
-ack
-vim
-sudo
-less
-man-db
-manpages
-procps
-# for syncing
-borgbackup
-# for my own scripts to run
-python3-venv
-#
diff --git a/testing/aptmark/user b/testing/aptmark/user
new file mode 120000
index 0000000..41adec5
--- /dev/null
+++ b/testing/aptmark/user
@@ -0,0 +1 @@
+../../bookworm/aptmark/user
\ No newline at end of file
diff --git a/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies b/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies
deleted file mode 100644
index 4aaef79..0000000
--- a/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
-APT::AutoRemove::RecommendsImportant "false";
-APT::AutoRemove::SuggestsImportant "false";
-APT::Install-Recommends "false";
-APT::Install-Suggests "false";
diff --git a/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies b/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies
new file mode 120000
index 0000000..4d1145f
--- /dev/null
+++ b/testing/etc/all/apt/apt.conf.d/99_minimize_dependencies
@@ -0,0 +1 @@
+../../../../../bookworm/etc/all/apt/apt.conf.d/99_minimize_dependencies
\ No newline at end of file
diff --git a/testing/home/all/.bash_profile b/testing/home/all/.bash_profile
deleted file mode 100644
index 316cf19..0000000
--- a/testing/home/all/.bash_profile
+++ /dev/null
@@ -1,8 +0,0 @@
-# ~/.bash_profile, if it exists, is sourced by Bash for all login shells,
-# instead of ~/.profile
-#
-# While I prefer using ~/.profile, I provide ~/.bash_profile because some
-# applications write to it even if it does not previously exist, which
-# deactivates ~/.profile if not explicitly sourced from within here.
-. "${HOME}/.profile"
-
diff --git a/testing/home/all/.bash_profile b/testing/home/all/.bash_profile
new file mode 120000
index 0000000..6c35564
--- /dev/null
+++ b/testing/home/all/.bash_profile
@@ -0,0 +1 @@
+../../../bookworm/home/all/.bash_profile
\ No newline at end of file
diff --git a/testing/home/all/.bashrc b/testing/home/all/.bashrc
deleted file mode 100644
index 636c408..0000000
--- a/testing/home/all/.bashrc
+++ /dev/null
@@ -1,29 +0,0 @@
-# ~/.bashrc is sourced by Bash for any interactive shells. 
-
-# Use vim as default editor for anything.
-export VISUAL=vim
-export EDITOR="${VISUAL}"
-
-# Some helpful aliases.
-alias curlpost='curl -H "Content-Type: application/json" -X POST'
-alias ls="ls --color=auto"
-
-# Colored prompt with username, hostname, date/time, directory.
-COLOR_NUMBER=7  # default to white
-COLOR_NUMBER_FILE="${HOME}/.shell_prompt_color"
-[ -f "${COLOR_NUMBER_FILE}" ] && COLOR_NUMBER=`cat "${COLOR_NUMBER_FILE}"`
-tput_color="$(tput setaf "${COLOR_NUMBER}")$(tput bold)"
-tput_reset="$(tput sgr0)"
-# Bash confuses the line length when not told to not count escape sequences.
-if [ -n "${BASH}" ]; then
-    tput_color="\[$tput_color\]"
-    tput_reset="\[$tput_reset\]"
-fi
-PS1="${tput_color}["\$\(date\ +%Y-%m-%d/%H:%M:%S/%Z\)" $(whoami)@$(hostname):"\$\(pwd\)"]$ ${tput_reset}"
-PS2="${tput_color}> ${tput_reset}"
-PS3="${tput_color}select: ${tput_reset}"
-PS4="${tput_color}+ ${tput_reset}"
-
-# include others
-. "${HOME}/.plomlib.sh/file_dot_variants"
-file_dot_variants "${HOME}/\.bashrc"
diff --git a/testing/home/all/.bashrc b/testing/home/all/.bashrc
new file mode 120000
index 0000000..8f7b90b
--- /dev/null
+++ b/testing/home/all/.bashrc
@@ -0,0 +1 @@
+../../../bookworm/home/all/.bashrc
\ No newline at end of file
diff --git a/testing/home/all/.plomlib.sh/file_dot_variants b/testing/home/all/.plomlib.sh/file_dot_variants
deleted file mode 100644
index 1c1bf69..0000000
--- a/testing/home/all/.plomlib.sh/file_dot_variants
+++ /dev/null
@@ -1,7 +0,0 @@
-file_dot_variants() {
-    for FILE in ${1}\.*; do
-        if [ -f "${FILE}" ]; then
-            . "${FILE}"
-        fi
-    done
-}
diff --git a/testing/home/all/.plomlib.sh/file_dot_variants b/testing/home/all/.plomlib.sh/file_dot_variants
new file mode 120000
index 0000000..f616819
--- /dev/null
+++ b/testing/home/all/.plomlib.sh/file_dot_variants
@@ -0,0 +1 @@
+../../../../bookworm/home/all/.plomlib.sh/file_dot_variants
\ No newline at end of file
diff --git a/testing/home/all/.profile b/testing/home/all/.profile
deleted file mode 100644
index 13fa422..0000000
--- a/testing/home/all/.profile
+++ /dev/null
@@ -1,20 +0,0 @@
-# ~/.profile is sourced on login, with its exports inherited by all processes
-# started below it under the same login, i.e. it has a very deep effect, but
-# changes to it only apply to new login sessions (compare to e.g. ~/.bashrc
-# which by is sourced anew for every new interactive Bash, login or not).
-#
-# Notably this makes ~/.profile (outside its optional sourcing of ~/.bashrc)
-# useful for providing environment variables to non-shell applications started
-# within a login session.
-PATH_BASHRC="${HOME}/.bashrc"
-[ -n "${BASH_VERSION}" -a -f "${PATH_BASHRC}" ] && . "${PATH_BASHRC}"
-
-PATH_LOCAL_BIN="${HOME}/.local/bin"
-[ -d "${PATH_LOCAL_BIN}" ] && PATH="${PATH_LOCAL_BIN}:${PATH}"
-
-# local changes to this shell variable otherwise will get lost
-export PATH
-
-# include others
-. "${HOME}/.plomlib.sh/file_dot_variants"
-file_dot_variants "${HOME}/\.profile"
diff --git a/testing/home/all/.profile b/testing/home/all/.profile
new file mode 120000
index 0000000..b4338c4
--- /dev/null
+++ b/testing/home/all/.profile
@@ -0,0 +1 @@
+../../../bookworm/home/all/.profile
\ No newline at end of file
diff --git a/testing/home/root/.shell_prompt_color b/testing/home/root/.shell_prompt_color
deleted file mode 100644
index d00491f..0000000
--- a/testing/home/root/.shell_prompt_color
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/testing/home/root/.shell_prompt_color b/testing/home/root/.shell_prompt_color
new file mode 120000
index 0000000..c863eca
--- /dev/null
+++ b/testing/home/root/.shell_prompt_color
@@ -0,0 +1 @@
+../../../bookworm/home/root/.shell_prompt_color
\ No newline at end of file
diff --git a/testing/scripts/lib/copy_dirtree b/testing/scripts/lib/copy_dirtree
new file mode 120000
index 0000000..632d725
--- /dev/null
+++ b/testing/scripts/lib/copy_dirtree
@@ -0,0 +1 @@
+../../../bookworm/scripts/lib/copy_dirtree
\ No newline at end of file
diff --git a/testing/scripts/lib/determine_ip b/testing/scripts/lib/determine_ip
new file mode 120000
index 0000000..057f0f6
--- /dev/null
+++ b/testing/scripts/lib/determine_ip
@@ -0,0 +1 @@
+../../../bookworm/scripts/lib/determine_ip
\ No newline at end of file
diff --git a/testing/scripts/lib/install_aptmarkeds b/testing/scripts/lib/install_aptmarkeds
new file mode 120000
index 0000000..7809441
--- /dev/null
+++ b/testing/scripts/lib/install_aptmarkeds
@@ -0,0 +1 @@
+../../../bookworm/scripts/lib/install_aptmarkeds
\ No newline at end of file
diff --git a/testing/scripts/lib/mark_nonrequireds_auto b/testing/scripts/lib/mark_nonrequireds_auto
new file mode 120000
index 0000000..00c3bf2
--- /dev/null
+++ b/testing/scripts/lib/mark_nonrequireds_auto
@@ -0,0 +1 @@
+../../../bookworm/scripts/lib/mark_nonrequireds_auto
\ No newline at end of file
diff --git a/testing/scripts/setup_desktop.sh b/testing/scripts/setup_desktop.sh
index 586e57e..968e560 100755
--- a/testing/scripts/setup_desktop.sh
+++ b/testing/scripts/setup_desktop.sh
@@ -1,10 +1,14 @@
 #!/bin/sh
 set -e
 cd $(dirname "$0")
-. lib/constants  # PATH_USER_HOME, USERNAME
-. lib/expect_min_n_args
 . lib/abort
 . lib/abort_if_offline
+. lib/constants  # PATH_USER_HOME, USERNAME
+. lib/copy_dirtree
+. lib/determine_ip
+. lib/expect_min_n_args
+. lib/mark_nonrequireds_auto
+. lib/install_aptmarkeds
 
 PARENT_DIR=$(dirname $(pwd))
 DEBIAN_RELEASE=$(basename ${PARENT_DIR})
@@ -24,29 +28,6 @@ THINKPAD_NAMES="x220 w530 t490s"
 
 export DEBIAN_FRONTEND=noninteractive
 
-copy_dirtree() {
-    expect_min_n_args 3 "(source root, target root, tags)" "$@"
-    SOURCE_ROOT="$1"
-    TARGET_ROOT="$2"
-    shift 2
-    TAGS="$@"
-    for TAG in ${TAGS}; do
-	PATH_TAG="${SOURCE_ROOT}/${TAG}"
-	if [ ! -d "${PATH_TAG}" ]; then
-	    continue
-	fi
-	cd "${PATH_TAG}"
-        for PATH_REL in $(find . -type f,l); do
-            PATH_TARGET="${TARGET_ROOT}"$(echo "${PATH_REL}" | cut -c2-)
-            PATH_SOURCE=$(realpath "${PATH_REL}")
-            DIRECTORY=$(dirname "${PATH_TARGET}")
-            mkdir -p "${DIRECTORY}"
-            cp -L "${PATH_SOURCE}" "${PATH_TARGET}"
-        done
-        cd -
-    done
-}
-
 get_system_class_for() {
     for THINKPAD_NAME in $THINKPAD_NAMES; do
         if [ "$1" = "${THINKPAD_NAME}" ]; then
@@ -80,64 +61,6 @@ upgrade_from_older_release() {
     apt -y autoremove
 }
 
-determine_ip() {
-    FINAL_IP="127.0.1.1"
-    for IP in $(hostname -I); do
-        if [ $(echo "${IP}" | grep ':' | wc -l) -eq 1 ]; then
-            continue
-        fi
-        RANGE_1=$(echo "${IP}" | cut -d "." -f 1)
-        RANGE_2=$(echo "${IP}" | cut -d "." -f 2)
-        if [ "${RANGE_1}" -eq 127 ]; then
-            continue
-        elif [ "${RANGE_1}" -eq 10 ]; then
-            continue
-        elif [ "${RANGE_1}" -eq 172 ]; then
-            if [ "${RANGE_2}" -ge 16 ] && [ "${RANGE_2}" -le 31 ]; then
-                continue
-            fi
-        elif [ "${RANGE_1}" -eq 192 ]; then
-            if [ "${RANGE_2}" -eq 168 ]; then
-                continue
-            fi
-        fi
-        FINAL_IP="${IP}"
-    done
-    printf "${FINAL_IP}"
-}
-
-mark_nonrequireds_auto() {
-    PATH_LIST_PREFIX=/tmp/list_
-    PATH_LIST_UNSORTED="${PATH_LIST_PREFIX}unsorted"
-    PATH_LIST_ALL_PACKAGES="${PATH_LIST_PREFIX}all_packages"
-    PATH_LIST_WHITE="${PATH_LIST_PREFIX}white"
-    PATH_LIST_BLACK="${PATH_LIST_PREFIX}black"
-    TOK_REQ=" required"
-    dpkg-query -Wf '${Package} ${Priority}\n' | grep "${TOK_REQ}" | sed "s/${TOK_REQ}//" > "${PATH_LIST_UNSORTED}"
-    sort "${PATH_LIST_UNSORTED}" | uniq > "${PATH_LIST_WHITE}"
-    dpkg-query -Wf '${Package}\n' > "${PATH_LIST_UNSORTED}"
-    sort "${PATH_LIST_UNSORTED}" | uniq > "${PATH_LIST_ALL_PACKAGES}"
-    comm -3 "${PATH_LIST_ALL_PACKAGES}" "${PATH_LIST_WHITE}" > "${PATH_LIST_BLACK}"
-    apt-mark auto `cat "${PATH_LIST_BLACK}"`
-    rm "${PATH_LIST_UNSORTED}" "${PATH_LIST_ALL_PACKAGES}" "${PATH_LIST_WHITE}" "${PATH_LIST_BLACK}"
-}
-
-install_aptmarkeds() {
-    # Walk through the package names in ../aptmark/ files to ensure the respective
-    # packages are installed.
-    for TAG in $@; do
-        PATH_APTMARK_TAG="${PATH_CONF}/aptmark/${TAG}"
-        if [ ! -f "${PATH_APTMARK_TAG}" ]; then
-            continue
-        fi
-        cat "${PATH_APTMARK_TAG}" | while read LINE; do
-            if [ ! $(echo "${LINE}" | cut -c1) = "#" ]; then
-                apt-get -y -o Dpkg::Options::="--force-confnew" install "${LINE}"
-            fi
-        done
-    done
-}
-
 adopt_wifi_connection() {
     get_network_interfaces_last_wpa_value() {
         REGEX="^\s+wpa-${1}\s+"
diff --git a/testing/scripts/setup_secrets.sh b/testing/scripts/setup_secrets.sh
index eb2c1b3..ea9b351 100755
--- a/testing/scripts/setup_secrets.sh
+++ b/testing/scripts/setup_secrets.sh
@@ -10,7 +10,7 @@ cd $(dirname "$0")
 . lib/get_passphrase
 . lib/path_tmp_timestamped
 
-PATH_REL_SECRETS=to_usb
+PATH_REL_SECRETS=.secrets
 export PATH_SECRETS="${PATH_USER_HOME}/${PATH_REL_SECRETS}"
 
 # Mount secrets device and copy over its content.
-- 
2.30.2