#!/bin/bash
# Distributed under the terms of the GNU General Public License v2
# Written by: Cory Oldford
# Purpose: Install LTSP and related services under Gentoo Linux.
# Todo:
# - make memtest optional.
# - optional installation of memtest from portage.
# - optional memtest server bootloader option.
# - figure out how to autodetect dhcp range.
# - add code to create /opt/ltsp symlink for easy ltsp version switches and testing.
# - make use of ltsp swap files directory
# - look at tightening xinetd security
# - make freedos optional
# - optional inclusion of user specified directory in freedos floppy image
# - add xdm, gdm, or kdm installation/configuration code.
# - finalize lts.conf to allow booting to a graphical login.
# Set the interface.
interface='eth0'
# Leave this set to auto if you want the ip settings determined automagically.
interface_detect='auto'
# If interface_detect is set to auto the network configuration
# will be auto configured based on the output of ifconfig, route and dnsdomainname.
# If this value is set to anything other than auto you must
# uncomment the interface_* variables and configure to your needs.
# interface_ip='192.168.0.224'
# interface_broadcast='192.168.0.255'
# interface_netmask='255.255.255.0'
# interface_gateway='192.168.0.1'
# interface_network_ip='192.168.0.0'
# interface_domain='pebcak.ca'
# interface_nameservers='192.168.0.1,www.pebcak.ca'
# variables to set to your needs.
chroot_dir='/chroot'
packages='syslog-ng xinetd tftp-hpa dhcp nfs-utils ltsp'
emerge_command='emerge -va --usepkg --buildpkg --oneshot'
# urls for extra features.
# todo allow user to opt not to install the memtest and/or
# freedos extras by not setting the following variables
# todo memtest_url='portage' to allow grabbing memtest from portage
# todo add optional code to add memtest to servers boot options
memtest_url='http://www.memtest.org/download/1.55.1/memtest86+-1.55.1.bin.gz'
freedos_url='http://www.fdos.org/ripcord/beta9sr1/disksets/ODIN/fdodin91.img'
# Syslog-ng configuration
syslog_seed_config='/usr/portage/app-admin/syslog-ng/files/syslog-ng.conf.gentoo'
syslog_net_source='source net { udp(); };'
syslog_net_destination='destination netlog { file("/var/log/ltsp.messages"); };'
syslog_net_log='log { source(net); destination(netlog); };'
# tftp variables:
tftp_chroot="$chroot_dir/tftpboot"
tftp_user='tftp'
#tftp_args="-u $tftp_user -s $tftp_chroot" # Normal
tftp_args="-vvvvv -u $tftp_user -s $tftp_chroot" # Super verbose
#tftp_args="-vvvvv -u $tftp_user -s $tftp_chroot -r blksize" # fix for some Intel stacks.
# dhcp variables:
dhcp_opts='-q'
dhcp_chroot="$chroot_dir/dhcp"
dhcp_lease_default=1592000
dhcp_lease_max=1592000
dhcp_default_range='192.168.0.144 192.168.0.169' # todo find a way to set this if interface_detect='auto'
# ltsp variables
# todo: add code to create an /opt/ltsp symlink to allow for quick version changes.
ltsp_version=`qpkg -I -v -nc ltsp | awk -F - '{print $3}'`
# If interface_detect is auto configure based on ifconfig, route, dnsdomainname output and /etc/resolv.conf
# Todo: add code to verify detected settings with user prior to continuing.
if [ $interface_detect ] && [ $interface_detect = 'auto' ] ; then
# Grab the ip, broadcast and netmask from the output of ifconfig <interface>
interface_ifconfig=`ifconfig $interface`
interface_ip=`echo "$interface_ifconfig" | awk '/Bcast/ { x=substr($2, 6) } END { print x }'`
interface_broadcast=`echo "$interface_ifconfig" | awk '/Bcast/ { x=substr($3, 7) } END { print x }'`
interface_netmask=`echo "$interface_ifconfig" | awk '/Bcast/ { x=substr($4, 6) } END { print x }'`
# Grab the network ip and gateway from the output of route -n
interface_route=`route -n`
interface_gateway=`echo "$interface_route" | awk '/UG/ && /'$interface'/ { print $2 }'`
interface_network_ip=`echo "$interface_route" | awk '! /UG/ && /'$interface'/ { print $1 }'`
# Grab the domainname and nameservers
interface_domain=`dnsdomainname`
interface_nameservers=`awk '/nameserver/ {num_ns++; if(num_ns == 1) {ns=$2} else {ns=sprintf("%s,%s",ns,$2)}} END {print ns}' /etc/resolv.conf`
fi
# Display a nice green asterisk followed by $1
function display_info () {
echo -e "\n \033[32;01m*\033[0m" "$1"
}
# Get the last line number starting with $1 in file $2
function get_last_occur () {
awk 'BEGIN{x=0}{if($1 == "'$1'") x = NR}END{print x}' $2
}
# Insert line $1 after line $2 in file $3
function insert_line_after () {
echo "Inserting \"$1\" into $3 after line $2"
newline=`echo -E $1 | awk '{ gsub(/"/, "\\\\\""); print }'`
newconfig=`awk '{if(NR=='$2'){print $0; print "'"$newline"'"} else print $0 }' $3`
echo "$newconfig" > $3
}
# Swap line $1 with $2 in file $3
function swap_line () {
echo "swapping $1 with \"$2\" in file $3"
cat $3 | awk '{if(NR=='$1'){print "'"$2"'"} else print $0 }' > $3
}
# stop services to be installed if currently running
for x in syslog-ng portmap nfs dhcp xinetd; do /etc/init.d/$x stop; done;
# Install the necessary packages
display_info "Installing $packages"
echo 'net-misc/ltsp snmp -nas esd audiofile' >> /etc/portage/package.use
$emerge_command $packages
# Copy seed syslog-ng.conf to /etc/syslog-ng/syslog-ng.conf
display_info "Creating /etc/syslog/syslog-ng.conf using $syslog_seed_config as a base."
cp -v $syslog_seed_config /etc/syslog-ng/syslog-ng.conf
# Check if syslog is configured to accept udp connections.
# If the system logger is already configured to accept udp connections
# assume the user has already configured syslog-ng appropriately for network logging.
target=`awk 'BEGIN{x=0}/udp()/{if($1 == "source") x = NR}END{print x}' /etc/syslog-ng/syslog-ng.conf`
if [ $target -eq 0 ]; then
echo "Syslog-ng not configured for network logging"
target=`get_last_occur source /etc/syslog-ng/syslog-ng.conf`
insert_line_after "$syslog_net_source" $target /etc/syslog-ng/syslog-ng.conf
target=`get_last_occur destination /etc/syslog-ng/syslog-ng.conf`
insert_line_after "$syslog_net_destination" $target /etc/syslog-ng/syslog-ng.conf
target=`get_last_occur log /etc/syslog-ng/syslog-ng.conf`
insert_line_after "$syslog_net_log" $target /etc/syslog-ng/syslog-ng.conf
else
echo "Syslog-ng seems to configured to accept udp connections"
fi
# Create log directories and files using /var/log entries in syslog-ng
syslog_files=`awk -F \" '/\/var\/log/ && !/# / && !/tty/ {print $2}' /etc/syslog-ng/syslog-ng.conf`
syslog_dirs=`for x in $syslog_files; do dirname $x; done | sort | uniq`
display_info "Creating the syslog-ng directories and files"
for x in $syslog_dirs; do mkdir -p --verbose $x; done
echo $syslog_files
touch $syslog_files
# create /etc/exports and ltsp swap directory
# todo: actually put /var/opt/ltsp/swapfiles to use
display_info "Creating default /etc/exports and ltsp swap directory"
cat <<- EOF > /etc/exports
/opt/ltsp-$ltsp_version/i386 $interface_network_ip/$interface_netmask(ro,no_root_squash,async)
/var/opt/ltsp/swapfiles $interface_network_ip/$interface_netmask(rw,no_root_squash,async)
EOF
cat /etc/exports
mkdir -p --verbose /var/opt/ltsp/swapfiles
# create default /etc/xinetd.d/tftp
display_info "Creating default /etc/xinetd.d/tftp file."
cat <<- EOF > /etc/xinetd.d/tftp
service tftp
{
disable = no
socket_type = dgram
protocol = udp
wait = yes
user = root
group = $tftp_user
server = `which in.tftpd`
server_args = $tftp_args
}
EOF
cat /etc/xinetd.d/tftp
# setup xinetd
# todo: look at locking things down more.
display_info "Configuring xinetd."
cat <<- EOF > /etc/xinetd.conf
defaults
{
instances = 60
log_type = SYSLOG authpriv info
log_on_success = HOST PID
log_on_failure = HOST
cps = 25 30
}
includedir /etc/xinetd.d
EOF
cat /etc/xinetd.conf
# create chroot directory
rm -rf $chroot_dir
mkdir -p --verbose $chroot_dir
# Move /tftpboot to tftp chroot
display_info "Moving /tftpboot to $chroot_dir directory"
mv -v /tftpboot $chroot_dir
mv -v $chroot_dir/tftpboot/pxelinux.cfg $chroot_dir/tftpboot/pxe
# The pxelinux included with ltsp is lacking some nice extras like
# menu.c32, memdisk and chain.c32 emerging syslinux temporarily will resolve this
display_info "Emerging syslinux and copying pxelinux bootloader and related files to tftp chroot."
$emerge_command syslinux
cp -v /usr/lib/syslinux/pxelinux.0 $tftp_chroot/pxe
cp -v /usr/lib/syslinux/{memdisk,menu.c32,chain.c32} $tftp_chroot/pxe
display_info "Removing syslinux as required files have been obtained"
emerge -Ca syslinux
# might as well grab memtest as a boot option..
# todo: make this optional
# todo: add optional code to grab this from portage and include it in the servers bootloader options
display_info "Grabbing memtest+ "
wget -O memtest86+.gz $memtest_url
gunzip memtest86+.gz
mv -v memtest86+ $tftp_chroot/pxe/memtest86+
# I'll grab a freedos odin floppy image as a boot option.
# todo: make this optional
# todo: add optional code to add the contents of a directory to the floppy image
display_info "Grabbing freedos "
wget -O freedos $freedos_url
mv -v freedos $tftp_chroot/pxe/freedos
# create the default pxelinux config file
# todo: add code to only add freedos and memtest if user wants.
display_info "Creating $tftp_chroot/pxe/pxelinux.cfg/default"
cat <<- EOF > $tftp_chroot/pxe/pxelinux.cfg/default
default menu.c32
prompt 0
timeout 100
ontimeout harddrive
menu title Choose a boot option....
label ltsp
menu label ^ltsp
kernel `basename $tftp_chroot/pxe/bzImage-*`
append init=/linuxrc rw root=/dev/ram0 initrd=`basename $tftp_chroot/pxe/initrd-*`
label floppy
menu label ^floppy
kernel chain.c32
append fd0
label harddrive
menu label ^harddrive
kernel chain.c32
append hd0
label memtest
menu label ^memtest
kernel memtest86+
label freedos
menu label free^dos
kernel memdisk
append initrd=freedos
EOF
cat $tftp_chroot/pxe/pxelinux.cfg/default
# create tftp group and user account.
display_info "Creating $tftp_user group and user."
groupadd $tftp_user
useradd -d $tftp_chroot -s /bin/false -G $tftp_user -g $tftp_user $tftp_user
# change ownership of tftp chroot and files.
display_info "Changing ownership of $tftp_chroot to $tftp_user:$tftp_user."
chown -R $tftp_user:$tftp_user $tftp_chroot
# configure dhcp chroot
display_info "Configuring dhcp chroot: $dhcp_chroot"
dhcp_version=`qpkg -I -v -nc dhcp | awk '/dhcp-/ {print substr($0,10)}'`
ebuild /var/db/pkg/net-misc/$dhcp_version/$dhcp_version.ebuild config
touch $dhcp_chroot/var/lib/dhcp/dhcpd.leases
ln -s /dev/log $dhcp_chroot/dev/log
# change ownership of dhcp chroot
display_info "Changing ownership of $dhcp_chroot to dhcp:dhcp."
chown -R dhcp:dhcp $dhcp_chroot
# configure dhcp
display_info "Creating default $dhcp_chroot/etc/conf.d/dhcp file."
cat <<- EOF > /etc/conf.d/dhcp
IFACE="$interface"
DHCPD_OPTS="$dhcp_opts"
CHROOT="$dhcp_chroot"
export LD_PRELOAD="/usr/lib/libresolv.so /usr/lib/libnss_dns.so"
EOF
cat /etc/conf.d/dhcp
# create dhcpd.conf
display_info "Creating $dhcp_chroot/etc/dhcp/dhcpd.conf"
cat <<- EOF > $dhcp_chroot/etc/dhcp/dhcpd.conf
ddns-update-style none;
authoritative;
option pxelinuxmagic code 208 = string;
option pxelinuxpathprefix code 210 = text;
option pxelinuxreboottime code 211 = unsigned integer 32;
use-host-decl-names on;
group "network-boot"{
class "PXE" {
match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
}
class "Etherboot" {
match if substring (option vendor-class-identifier, 0, 9) = "Etherboot";
}
if exists dhcp-parameter-request-list {
option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d2,d3);
}
option pxelinuxmagic f1:00:74:7e;
option pxelinuxpathprefix "/pxe/";
option pxelinuxreboottime 30;
filename "/pxe/pxelinux.0";
}
shared-network "local" {
subnet $interface_network_ip netmask $interface_netmask {
default-lease-time $dhcp_lease_default;
max-lease-time $dhcp_lease_max;
option subnet-mask $interface_netmask;
option broadcast-address $interface_broadcast;
option routers $interface_gateway;
option domain-name-servers $interface_nameservers;
option domain-name "$interface_domain";
option root-path "$interface_ip:/opt/ltsp-$ltsp_version/i386";
range $dhcp_default_range;
}
}
EOF
cat $dhcp_chroot/etc/dhcp/dhcpd.conf
# Necessary because of Gentoo bug 39809 :(
if ! [ -e /opt/ltsp-$ltsp_version/i386/dev ]; then
display_info "Gentoo bug 39809 detected creating necessary dirs to compensate"
mkdir --verbose /opt/ltsp-$ltsp_version/i386/{dev,oldroot,proc}
fi
# We need a basic lts.conf.
cat <<- EOF > /opt/ltsp-$ltsp_version/i386/etc/lts.conf
[DEFAULT]
SERVER = $interface_ip
RUNLEVEL = 3
SCREEN_01 = "shell"
EOF
# Add services to default runlevel and start.
for x in syslog-ng nfs dhcp xinetd; do rc-update add $x default; /etc/init.d/$x start; done;