#!/bin/bash
#
# Configure a Debian Edu system with 'LTSP-Server' profile' for both diskless
# workstations and also thin clients (using X2Go).

# LTSP has been re-written in 2019. LTSP5 has been discontinued and isn't available
# any longer in the Debian archive, the known tools are gone. This script takes care
# for the special Debian Edu requirements.

# The configuration below applies to a Debian Edu machine in the internal
# backbone network with two NICs. This system needs to be registered w/ GOsa² in
# case it isn't the main server (i.e. a 'combined' server).
# Kerberized NFS is used (with krb5i as default), see:
# https://wiki.debian.org/DebianEdu/Documentation/Bullseye/HowTo/Administration#Kerberized_NFS
# The modified system provides a separate LTSP client network attached to eth1.
#
# Author/Copyright:	Wolfgang Schweer <wschweer@arcor.de>
# Licence:		GPL2+
# first edited:	2019-11-21
# last edited:	2021-04-26

set -e

# usage
if [ -z "$1" ] ; then
	echo "Use $0 -h or $0 --help for more information"
	exit 0
fi

if [ "$1" = "-h" ] || [ "$1" = "--help" ] ; then
	cat <<EOF

Usage information:

debian-edu-ltsp-install --arch <amd64|i386> --dist <stable|testing|sid> --dns_server <10.0.2.2|dns server ip> --diskless_workstation <yes|no> --thin_type <bare|display|desktop>

Turn a Debian Edu workstation into an LTSP server for both diskless
workstations and thin clients.

--arch       takes effect for a thin client chroot setup, default value is the system architecture one.

--dist       takes effect for a thin client chroot setup, default value is the one the LTSP server is running.

--dns_server defaults to 10.0.2.2 if unset.

--diskless_workstation defaults to yes if omitted.

--thin_type  has no default value. These are available:
    bare:    preconfigured x2go client running via 'startx' as user 'thin' with sound and
             client side mass storage support.
    display: x2gothinclient running in display mode.
    desktop: x2gothinclient running in minidesktop mode.

Example 1: 'debian-edu-ltsp-install --arch amd64' creates diskless workstation support.
Example 2: 'debian-edu-ltsp-install --arch amd64 --dist bullseye --thin_type bare' creates diskless workstation and 64-bit thin client support.
            LTSP client boot defaults to diskless workstation.
Example 3: 'debian-edu-ltsp-install --arch i386 --diskless_workstation no --thin_type bare' creates additional 32-bit thin client support. 

This script applies to a system with two NICs on the internal backbone network.

EOF
	exit 0
fi

if [ -r /etc/debian-edu/config ] ; then
    . /etc/debian-edu/config
fi

# Grab dist value for both testing and stable release cases.
if grep -q / /etc/debian_version ; then
	dist=$(cat /etc/debian_version | cut -d/ -f1)
else
	dist=$(lsb_release -sc)
fi
# Set default values.
arch="$(dpkg --print-architecture)"
dns_server="10.0.2.2"
diskless_workstation="yes"
thin_type=""

while [ $# -gt 0 ] ; do
  case "$1" in
    --arch) arch="$2" ; shift ;;
    --dist) dist="$2" ; shift ;;
    --dns_server) dns_server="$2"  ; shift ;;
    --diskless_workstation) diskless_workstation="$2" ; shift ;;
    --thin_type) thin_type="$2" ; shift ;;
  esac
  shift
done

kernel_arch="$arch"

if [ "i386" == "$arch" ] ; then
	#kernel_arch="686-pae"
	# next one optimal for very old TC machines w/o PAE.
	kernel_arch="686"
fi

# Common Debian Edu specific configuration (dirs and HERE documents), only minor
# difference for thin clients and diskless workstation (in ltsp.conf), see below.
# Next two lines are needed in case of a separate LTSP server.
ETH0_IP="$(hostname -I | cut -d' ' -f1)"
ETH1_IP="$(hostname -I | cut -d' ' -f2)"
ETH1_IP=${ETH1_IP:-"192.168.0.254"}
HOSTNAME="$(hostname -s)"
if [ ! -d /etc/ltsp/client ] ; then
	mkdir -p /etc/ltsp/client/init

	# We are using NFS/Kerberos (krb5i) and autofs instead of sshfs for home dirs.
	touch  /etc/ltsp/client/init/54-pam.sh

	# Debian Edu wants a greeter w/o user list, i.e. don't modify existing config.
	touch  /etc/ltsp/client/init/55-display-manager.sh

	# make ipxe menu entries more user friendly.
	cat <<EOF > /etc/ltsp/ltsp.conf
# /bin/sh -n
# LTSP configuration file
# Documentation=man:ltsp.conf(5)

[server]
# Provide a full menu name for thin/bare-amd64.img
IPXE_BARE_AMD64_IMG="Plain X2Go Thin Client (64-Bit)"
# Provide a full menu name for thin/bare-i386.img
IPXE_BARE_I386_IMG="Plain X2Go Thin Client (very old machines, 32-Bit)"

# Provide a full menu name for thin/display-amd64.img
IPXE_DISPLAY_AMD64_IMG="Display Mode X2Go Thin Client (64-Bit)"
# Provide a full menu name for thin/display-i386.img
IPXE_DISPLAY_I386_IMG="Display Mode X2Go Thin Client (very old machines, 32-Bit)"

# Provide a full menu name for thin/desktop-amd64.img
IPXE_DESKTOP_AMD64_IMG="Desktop Mode X2Go Thin Client (64-Bit)"
# Provide a full menu name for thin/desktop-i386.img
IPXE_DESKTOP_I386_IMG="Desktop Mode X2Go Thin Client (very old machines, 32-Bit)"

# Provide a full menu name for x86_64.img
IPXE_X86_64_IMG="Diskless Workstation (64-Bit)"
# Provide a full menu name for x86_32.img
IPXE_X86_32_IMG="Diskless Workstation (32-Bit)"

##### Set default boot value ######
# Default value is x86_64 or x86_32 (arch specific, Diskless Workstation)
# Plain X2Go Thin Client (64-Bit)
#DEFAULT_IMAGE="bare-amd64"
# Plain X2Go Thin Client (32-Bit)
#DEFAULT_IMAGE="bare-i386"
# Display Mode X2Go Thin Client (64-Bit)
#DEFAULT_IMAGE="display-amd64"
# Desktop Mode X2Go Thin Client (64-Bit)
#DEFAULT_IMAGE="desktop-amd64"
# Install Debian Edu/amd64 (64-Bit)
#DEFAULT_IMAGE="amd64"
# Install Debian Edu/i386  (32-Bit)
#DEFAULT_IMAGE="i386"

##### Set default iPXE menu timeout ######
# Default value is 5000 milliseconds, i.e. 5 seconds.
#MENU_TIMEOUT="5000"
# Don't show the menu.
#MENU_TIMEOUT="-1"

# Debian Edu specific
DNS_SERVER=10.0.2.2
SEARCH_DOMAIN=intern
ADD_IMAGE_EXCLUDES="/etc/ltsp/image-local.excludes"
# This takes effect for hosts behind the LTSP eth1 interface.
NAT=1

# In the special [clients] section, parameters for all clients can be defined.
# Most ltsp.conf parameters should be placed here.
[clients]
POST_INIT_PRINTER="cp /etc/ltsp/p910d.conf /etc/default/p910d"
HOSTS_1="10.0.2.2 tjener.intern tjener"
HOSTS_2="$ETH1_IP $HOSTNAME"
EOF
fi

# Debian Edu specific common additional image excludes; for diskless
# workstations the /skole mountpoint (for autofs) needs to be clean.
# This applies for both a combined server and a separate LTSP server.
# For a combined server image the autofs service needs to be enabled (see below).
if echo "$PROFILE" | grep -Eq 'Workstation|LTSP-Server' ; then
	cat <<EOF > /etc/ltsp/image-local.excludes
skole/*
EOF
	cat <<EOF > /etc/ltsp/p910d.conf
# Use attached USB printer (default is /dev/lp0, i.e. parallel port printer).
P910ND_OPTS=" -f /dev/usb/lp0"
# Debian specific (set to 1 to enable daemon start, default is 0)
P910ND_START=1
# See 'man p910nd' for details and settings.
EOF
fi

# FIXME: On the main server even more additional excludes might be useful.
if echo "$PROFILE" | grep -Eq 'Main-Server' ; then
	cat <<EOF >> /etc/ltsp/image-local.excludes
usr/lib/apache2
usr/lib/exim4
usr/lib/icinga
usr/log/samba/*
usr/log/squid/*
var/cache/apache2/*
var/cache/apt/*
var/cache/bind/*
var/cache/debconf/*
var/cache/etckeeper/*
var/cache/gosa/*
var/cache/icinga/*
var/cache/munin/*
var/cache/nscd/*
var/cache/samba/*
var/lib/apache2/*
var/lib/cfengine3/*
var/lib/dbus/*
var/lib/dhcp/*
var/lib/dpkg/*
var/lib/exim4/*
var/lib/icinga/*
var/lib/munin/*
var/lib/munin-node/*
var/lib/nfs/*
var/log/cfengine/*
var/log/installer/*
var/log/munin/*
var/log/samba/*
var/log/squid/*
var/mail/*
var/log/*.gz
var/spool/squid
EOF
fi

# Needed for thin client auto login user.
mkdir -p /etc/ltsp/getty@tty1.service.d
cat <<EOF > /etc/ltsp/getty@tty1.service.d/override.conf
[Service]
ExecStart=
ExecStart=-/usr/sbin/agetty -a thin --noclear %I $TERM
RestartSec=10
EOF

# Needed for thin client autofs setup (USB mass storage support (rw mode).
mkdir -p /etc/ltsp/autofs
cat <<EOF > /etc/ltsp/autofs/extra.autofs
/- /etc/auto.usb0 --mode=0777 --timeout=3
EOF
cat <<EOF > /etc/ltsp/autofs/auto.usb0
/usb0 -fstype=auto,rw,user,umask=000 :/dev/sda1
EOF

# Needed for thin client auto login configuration (startx).
mkdir -p /etc/ltsp/skel
cat <<EOF > /etc/ltsp/skel/.profile
while true ; do
	startx
done
EOF

# Needed for thin client auto login configuration (x2goclient start).
cat <<EOF > /etc/ltsp/skel/.xinitrc
exec x2goclient --no-menu --add-to-known-hosts --no-session-edit --close-disconnect
EOF

# Needed for thin client x2goclient configuration.
mkdir -p /etc/ltsp/skel/.x2goclient
cat <<EOF > /etc/ltsp/skel/.x2goclient/printing
[General]
pdfview=false
showdialog=true

[CUPS]
defaultprinter=

[print]
command=lpr
ps=false
startcmd=false
stdin=false

[view]
command=xpdf
open=true
EOF

# Needed for thin client (x2goclient preconfigured session).
cat <<EOF > /etc/ltsp/skel/.x2goclient/sessions
[default]
autologin=false
clipboard=both
command=XFCE
defsndport=true
directrdp=false
directrdpsettings=
directxdmcp=false
directxdmcpsettings=
display=1
dpi=96
export="/usb0:1;"
fstunnel=true
fullscreen=true
height=600
host=$(hostname -s)
icon=/usr/share/icons/hicolor/64x64/apps/x2goclient.png
iconvfrom=ISO8859-1
iconvto=UTF-8
krbdelegation=false
krblogin=false
maxdim=false
multidisp=false
name=Debian Edu Thin Client
pack=16m-jpeg
print=true
published=false
quality=9
rootless=false
setdpi=true
sndport=4713
sound=true
soundsystem=pulse
soundtunnel=true
speed=4
sshport=22
sshproxyautologin=false
startsoundsystem=true
type=auto
useiconv=false
usekbd=true
usesshproxy=false
width=800
xdmcpclient=Xnest
xdmcpserver=localhost
xinerama=false
EOF

# Needed for thin client x2goclient configuration.
cat <<EOF > /etc/ltsp/skel/.x2goclient/settings
[toolbar]
show=false
EOF

# Specific settings needed if BD ISO image is used for installation inside d-i.
# First part of next condition: Looking for file created by base-installer and
# removed at the end of the d-i run.
if [ -e /etc/apt/apt.conf.d/00IgnoreTimeConflict ] && grep -q BD /etc/apt/sources.list ; then
	BD_ISO="true";
	device="$(grep media/cdrom /etc/fstab | cut -d' ' -f1)"
	mirror="file:///media/cdrom/"
else
	mirror="http://deb.debian.org/debian"
fi

# Create thin client chroot and generate image.
export DEBIAN_FRONTEND=noninteractive
if ! [ "" == "$thin_type" ] && [ ! -d /srv/ltsp/thin/"$thin_type"-"$arch"/etc/ltsp ] ; then
	mkdir -p /srv/ltsp/thin/"$thin_type"-"$arch"
	# Install common thin client packages.
debootstrap --arch="$arch" --no-check-gpg --variant=minbase --include=linux-image-"$kernel_arch" \
	"$dist" /srv/ltsp/thin/"$thin_type"-"$arch" "$mirror"
	chroot /srv/ltsp/thin/"$thin_type"-"$arch"/ apt clean
	mount /dev/pts -t devpts /srv/ltsp/thin/"$thin_type"-"$arch"/dev/pts
	mount proc -t proc /srv/ltsp/thin/"$thin_type"-"$arch"/proc
	mount tmpfs -t tmpfs /srv/ltsp/thin/"$thin_type"-"$arch"/tmp
	mkdir -p /srv/ltsp/thin/"$thin_type"-"$arch"/tmp/user/0
	if [ "true" == "$BD_ISO" ] ; then
		mkdir -p /srv/ltsp/thin/"$thin_type"-"$arch"/media/cdrom
		mount $device /srv/ltsp/thin/"$thin_type"-"$arch"/media/cdrom
		echo "deb [trusted=yes] $mirror $dist main" > /srv/ltsp/thin/"$thin_type"-"$arch"/etc/apt/sources.list
	fi
	chroot /srv/ltsp/thin/"$thin_type"-"$arch"/ apt -y -qq install education-thin-client p910nd
	# Install case specific additional packages.
	if [ "display" == "$thin_type" ] ; then
		chroot /srv/ltsp/thin/"$thin_type"-"$arch"/ apt -y -qq install x2gothinclient-displaymanager
	fi
	if [ "desktop" == "$thin_type" ] ; then
		chroot /srv/ltsp/thin/"$thin_type"-"$arch"/ apt -y -qq install x2gothinclient-minidesktop \
		x2gothinclient-cdmanager x2gothinclient-usbmount \
		firefox-esr-l10n-"$LANGCODE"
	fi
	if [ "true" == "$BD_ISO" ] ; then
		umount /srv/ltsp/thin/"$thin_type"-"$arch"/media/cdrom
	fi
	umount /srv/ltsp/thin/"$thin_type"-"$arch"/dev/pts
	umount /srv/ltsp/thin/"$thin_type"-"$arch"/proc
	umount /srv/ltsp/thin/"$thin_type"-"$arch"/tmp
	rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"/tmp/user
	if [ "bare" == "$thin_type" ] ; then
		rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"/var/cache/apt
		rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"/var/cache/debconf
		rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"/var/cache/man
		rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"/var/lib/dpkg
	fi
	cp /etc/locale.gen /srv/ltsp/thin/"$thin_type"-"$arch"/etc/
	cp /etc/default/locale /srv/ltsp/thin/"$thin_type"-"$arch"/etc/default
	chroot /srv/ltsp/thin/"$thin_type"-"$arch" locale-gen
	cp /etc/default/keyboard /srv/ltsp/thin/"$thin_type"-"$arch"/etc/default
	cp /etc/default/console-setup /srv/ltsp/thin/"$thin_type"-"$arch"/etc/default
	chroot /srv/ltsp/thin/"$thin_type"-"$arch" setupcon -k

	# Customize X2Go client for Debian Edu use.
	if [ "display" == "$thin_type" ] || [ "desktop" == "$thin_type" ] ; then
		cp /etc/ltsp/skel/.x2goclient/sessions /srv/ltsp/thin/"$thin_type"-"$arch"/etc/x2go/x2gothinclient_sessions
	fi
	# Firefox-ESR customization for Debian Edu.
	if [ "desktop" == "$thin_type" ] ; then
		cp /etc/environment /srv/ltsp/thin/"$thin_type"-"$arch"/etc
		cp /etc/firefox-esr/debian-edu.js /srv/ltsp/thin/"$thin_type"-"$arch"/etc/firefox-esr
		cp /etc/firefox-esr/debian-edu-homepage-ldap.js /srv/ltsp/thin/"$thin_type"-"$arch"/etc/firefox-esr
		cp /etc/ssl/certs/Debian-Edu_rootCA.crt /srv/ltsp/thin/"$thin_type"-"$arch"/etc/ssl/certs
		cat <<EOF > /srv/ltsp/thin/"$thin_type"-"$arch"/usr/share/firefox-esr/distribution/policies.json
{
  "policies": {
    "Certificates": {
      "ImportEnterpriseRoots": true,
      "Install": [
        "/etc/ssl/certs/Debian-Edu_rootCA.crt"
      ]
    },
    "NewTabPage": false,
    "OverrideFirstRunPage": "",
    "SearchEngines": {
      "Add": [
        {
          "IconURL": "https://duckduckgo.com/favicon.ico",
          "Method": "POST",
          "Name": "DuckDuckGo",
          "SuggestURLTemplate": "https://duckduckgo.com/ac/?q={searchTerms}&type=list",
          "URLTemplate": "https://duckduckgo.com/?q={searchTerms}"
        }
      ],
      "Default": "DuckDuckGo"
    }
  }
}
EOF
	fi

	ltsp image /srv/ltsp/thin/"$thin_type"-"$arch"

	# Remove chroot now that the image has been built (to save space)
	rm -rf /srv/ltsp/thin/"$thin_type"-"$arch"

	# Create a runtime user for x2go login terminal; configure autofs (USB storage support).
	if [ "bare" == "$thin_type" ] ; then
		cat <<EOF >> /etc/ltsp/ltsp.conf
POST_INIT_THIN_USER='useradd -G disk -m -d /run/home/thin -k /etc/ltsp/skel -r thin'

POST_INIT_SYSTEMD='mkdir /etc/systemd/system/getty@tty1.service.d && \
cp /etc/ltsp/getty@tty1.service.d/override.conf /etc/systemd/system/getty@tty1.service.d'

POST_INIT_AUTOFS='cp /etc/ltsp/autofs/extra.autofs /etc/auto.master.d && \
cp /etc/ltsp/autofs/auto.* /etc'
EOF
	fi

	# Create the ltsp.img file and move it to where it belongs.
	ltsp initrd
	mv /srv/tftp/ltsp/ltsp.img /srv/tftp/ltsp/"$thin_type"-"$arch"/ltsp.img

	# Clean up ltsp.conf from image specific items.
	sed -i '/POST_INIT/d' /etc/ltsp/ltsp.conf
fi

# Generate image for diskless workstation.
if [ "yes" == "$diskless_workstation"  ] ; then
	if echo "$PROFILE" | grep -Eq 'Main-Server' ; then
		# The image is a copy of the main server's fs. On the server, autofs
		# is disabled, but it is needed for diskless workstations.
		# OTOH some services need to be disabled, i.e. 'masked'.
		cat <<EOF >> /etc/ltsp/ltsp.conf
PRE_INIT_MAIN_SERVER="systemctl enable autofs"
POST_INIT_USE_FQDN="sed -i '/10.0.2.2/ s/server/tjener.intern tjener/' /etc/hosts"
MASK_SYSTEM_SERVICES="apache2 named cups dovecot etckeeper exim4 squid tftpd-hpa \
icinga2 nmbd smbd systemd-journald xrdp krb5-kdc mariadb cfengine3 isc-dhcp-server"
EOF
	else
		cat <<EOF >> /etc/ltsp/ltsp.conf
MASK_SYSTEM_SERVICES="etckeeper exim4 tftpd-hpa isc-dhcp-server xrdp"
EOF
	fi
	# Temporary workaround needed in some cases for configuring the resolver inside
	# the SquashFS image.
	rm -f /etc/resolv.conf
	echo "nameserver $dns_server" > /etc/resolv.conf
	echo "search intern" >> /etc/resolv.conf
	# Create SqashFS image.
	ltsp image /,,/boot,subdir=boot,,/usr,subdir=usr,,/var,subdir=var
	# Revert resolver workaround from above.
	rm -f /etc/resolv.conf
	ln -s /run/resolvconf/resolv.conf /etc/resolv.conf
	# next modification avoids ltsp command error if lot of images are available.
	ALL_IMAGES=1 ltsp kernel
	ltsp initrd
	if uname -r | grep -q 686 ; then
		mv /srv/tftp/ltsp/ltsp.img /srv/tftp/ltsp/x86_32/ltsp.img
	else
		mv /srv/tftp/ltsp/ltsp.img /srv/tftp/ltsp/"$(uname -m)"/ltsp.img
	fi
	# Clean up ltsp.conf from specific items.
	sed -i '/PRE_INIT_MAIN/d' /etc/ltsp/ltsp.conf
	sed -i '/MASK_SYSTEM/d' /etc/ltsp/ltsp.conf
fi

# Get rid of additional excludes just in case they exist (main server).
rm -rf /etc/ltsp/image-local.excludes
touch /etc/ltsp/image-local.excludes

# Use legacy network interfaces names.
if ! grep -q net.ifnames /etc/default/grub ; then
    sed -i 's/quiet/net.ifnames=0 quiet/' /etc/default/grub
    update-grub
fi

# Tweak network interfaces file to match the use case.
if echo "$PROFILE" | grep -Eq 'Main-Server' ; then
	cat <<EOF > /etc/network/interfaces
auto eth0 eth1
iface eth0 inet static
	address 10.0.2.2
	gateway 10.0.0.1
	dns-search intern
	dns-nameserver 127.0.0.1

iface eth1 inet static
	address $ETH1_IP
EOF
	else
	cat <<EOF > /etc/network/interfaces
auto eth0 eth1
iface eth0 inet static
	address $ETH0_IP
	gateway 10.0.0.1
	dns-search intern
	dns-nameservers 10.0.2.2

iface eth1 inet static
	address $ETH1_IP
EOF
fi

# Next line is needed upon upgrade from LTSP5.
sed -i '/ltsp/d' /etc/exports

# Adjust NFS exports for separate LTSP servers.
if ! echo "$PROFILE" | grep -Eq 'Main-Server' ; then
	rm -f /etc/exports.d/edu.exports
fi

# Configure NFS
ltsp nfs

# Restart nfs-kernel-server and give feedback about exports if running this
# script outside of the Debian Installer environment.
if [ ! -x /sbin/start-stop-daemon.REAL ] ; then
	exportfs -rav
fi

# Generate the LTSP specific iPXE menu
ltsp ipxe

# Add PXE installation related entries to iPXE menu.
if ! grep -q main-server /srv/tftp/ltsp/ltsp.ipxe && \
    [ -f /srv/tftp/debian-installer/amd64/linux ] ; then
	echo "Modifying $tftpdir/ltsp/ltsp.ipxe"
	sed -i '/^menu.*/ a item\
item --gap                        Installation:\
item --key a amd64                Install Debian Edu/amd64 (64-Bit)\
item --key i i386                 Install Debian Edu/i386  (32-Bit)\
item\
' /srv/tftp/ltsp/ltsp.ipxe
	cat /srv/tftp/debian-edu/install.cfg >>/srv/tftp/ltsp/ltsp.ipxe
fi

# Make sure /srv/tftp/ltsp/ltsp.ipxe is Debian Edu specific.
sed -i 's#ltsp/ltsp.img#ltsp/${img}/ltsp.img#' /srv/tftp/ltsp/ltsp.ipxe
