From e596091daf51443248a0cb427832be62552eaf27 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 28 Oct 2013 19:50:41 +0100 Subject: Reorganization. Move preseed-related stuff in ./preseed/, and vm-related stuff in ./virtualenv/. --- .gitignore | 1 - Makefile | 43 ++ include/.gitignore | 1 + include/partition.sh | 88 ++++ partition.sh | 88 ---- preseed.cfg | 57 +++ preseed.sh | 93 ++++ src/.gitignore | 3 + src/fripost-partman-udeb/Makefile | 13 + src/fripost-partman-udeb/base.sh | 527 +++++++++++++++++++++ src/fripost-partman-udeb/debian/changelog | 5 + src/fripost-partman-udeb/debian/compat | 1 + src/fripost-partman-udeb/debian/control | 21 + src/fripost-partman-udeb/debian/copyright | 7 + src/fripost-partman-udeb/debian/install | 1 + src/fripost-partman-udeb/debian/postinst | 25 + src/fripost-partman-udeb/debian/rules | 3 + src/fripost-partman-udeb/debian/templates | 84 ++++ src/fripost-partman-udeb/lib/stdbuf.c | 11 + src/fripost-postinst-udeb/debian/changelog | 5 + src/fripost-postinst-udeb/debian/compat | 1 + src/fripost-postinst-udeb/debian/control | 11 + src/fripost-postinst-udeb/debian/copyright | 7 + src/fripost-postinst-udeb/debian/install | 2 + src/fripost-postinst-udeb/debian/rules | 3 + src/fripost-postinst-udeb/debian/templates | 93 ++++ .../finish-install.d/07fripost | 275 +++++++++++ src/fripost-postinst-udeb/sshd_config | 40 ++ src/getname.sh | 28 ++ virtualenv/virt | 133 ++++++ 30 files changed, 1581 insertions(+), 89 deletions(-) delete mode 100644 .gitignore create mode 100644 Makefile create mode 100644 include/.gitignore create mode 100755 include/partition.sh delete mode 100755 partition.sh create mode 100644 preseed.cfg create mode 100755 preseed.sh create mode 100644 src/.gitignore create mode 100644 src/fripost-partman-udeb/Makefile create mode 100644 src/fripost-partman-udeb/base.sh create mode 100644 src/fripost-partman-udeb/debian/changelog create mode 100644 src/fripost-partman-udeb/debian/compat create mode 100644 src/fripost-partman-udeb/debian/control create mode 100644 src/fripost-partman-udeb/debian/copyright create mode 100644 src/fripost-partman-udeb/debian/install create mode 100755 src/fripost-partman-udeb/debian/postinst create mode 100755 src/fripost-partman-udeb/debian/rules create mode 100644 src/fripost-partman-udeb/debian/templates create mode 100644 src/fripost-partman-udeb/lib/stdbuf.c create mode 100644 src/fripost-postinst-udeb/debian/changelog create mode 100644 src/fripost-postinst-udeb/debian/compat create mode 100644 src/fripost-postinst-udeb/debian/control create mode 100644 src/fripost-postinst-udeb/debian/copyright create mode 100644 src/fripost-postinst-udeb/debian/install create mode 100755 src/fripost-postinst-udeb/debian/rules create mode 100644 src/fripost-postinst-udeb/debian/templates create mode 100755 src/fripost-postinst-udeb/finish-install.d/07fripost create mode 100644 src/fripost-postinst-udeb/sshd_config create mode 100755 src/getname.sh create mode 100755 virtualenv/virt diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 05b023b..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -authorized_keys diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..93d0c3f --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +ARCH ?= $(shell uname -m | sed -e 's/^x86_64$$/amd64/' -e 's/^i686$$/i386/') +VERSION = 7.2.0 + +ISOIMG = debian-${VERSION}-${ARCH}-netinst.iso +PRESEEDED = $(patsubst %.iso,%-preseeded.iso, ${ISOIMG}) +UDEBS = $(shell ./src/getname.sh) +CHECKSUMS = MD5 SHA1 SHA256 SHA512 + +# Preseed +iso-preseed: + make dist/${ARCH}/${PRESEEDED} +dist/${ARCH}/${PRESEEDED}: preseed.cfg $(wildcard ./include/*) dist/${ARCH}/${ISOIMG} ${UDEBS} + ./preseed.sh $@ dist/${ARCH}/${ISOIMG} ${UDEBS} + +# Get, verify +iso-get: dist/${ARCH}/${ISOIMG} +iso-checksum: $(patsubst %,iso-checksum-%, ${CHECKSUMS}) + +# Verify a single digest +iso-checksum-%: dist/${ARCH}/${ISOIMG} dist/${ARCH}/%SUMS dist/${ARCH}/%SUMS.sign + @echo Checking $* sum + @cd dist/${ARCH}; grep "\s${ISOIMG}$$" $*SUMS | $$(echo $* | sed 's@.*@/usr/bin/\L&sum@') --strict -c + gpg --verify dist/${ARCH}/$*SUMS.sign + +# Download +.PRECIOUS: dist/${ARCH}/% +dist/${ARCH}/%: + @[ -d dist/${ARCH} ] || mkdir -p dist/${ARCH} + wget -nc -P dist/${ARCH} http://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/$* + +# All udebs +udebs: ${UDEBS} + +# Single udeb; TODO: GPG sign +%.udeb: UDEB = $(shell ./src/getname.sh $*) +%.udeb: $(shell /usr/bin/git ls-files -- src/${UDEB}) + @cd src/${UDEB} && /usr/bin/debuild -i -us -uc -b -a${ARCH} + +clean: + @for d in src/*-udeb/; do rm -fv $${d%-udeb/}_*.build $${d%-udeb/}_*.changes $${d%-udeb/}_*.udeb; cd $$d && dh_clean && cd -; done + @rm -fv $(patsubst %,dist/${ARCH}/%SUMS, ${CHECKSUMS}) $(patsubst %,dist/${ARCH}/%SUMS.sign, ${CHECKSUMS}) + +.PHONY: clean cleanall iso-get iso-checksum iso-preseed udebs diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..05b023b --- /dev/null +++ b/include/.gitignore @@ -0,0 +1 @@ +authorized_keys diff --git a/include/partition.sh b/include/partition.sh new file mode 100755 index 0000000..fb56ce7 --- /dev/null +++ b/include/partition.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# +# Simple partitioning shell script. +# +# Copyright 2013 Guilhem Moulin +# +# Licensed under the GNU GPL version 3 or higher. + +set -ue + +. /lib/fripost-partman/base.sh + +# Wipe the disk +device=/dev/sda +fripost_wipe $device + +db_get fripost/encrypt +encrypt=$RET + +# Create a disk label +/sbin/parted -s $device mklabel gpt +log "Created disklabel GPT for device $device" + +# Create a EFI partition if needed; otherwise, create a partition needed +# to put GRUB on GPT disklabels. +if [ -d /proc/efi -o -d /sys/firmware/efi ]; then + part_efi=$( fripost_mkpart $device efi 256M +boot ) +else + fripost_mkpart $device bios_grub 8M +bios_grub +fi +db_set grub-installer/bootdev $device +db_fset grub-installer/bootdev seen true + +# Create boot and system partitions +part_boot=$( fripost_mkpart $device boot 64M ) +part_system=$( fripost_mkpart $device system 100% ) +/sbin/parted -s $device align-check opt ${part_system#$device} \ + || fatal "$part_system is not aligned" + + +if [ $encrypt = true ]; then + # Encrypt the system partition. We choose the key length and digest + # depending on the architecture we're on; we use AES128 and SHA-256 + # on 32-bits platforms, and AES256 and SHA-512 on 64-bits platforms. + arch=$(uname -m) + if [ "$arch" = x86_64 ]; then + keysize=256 + hash=sha512 + elif [ "$arch" = i386 -o "$arch" = i686 ]; then + keysize=128 + hash=sha256 + fi + # Note: XTS requires the key size to be doubled. + fripost_encrypt $part_system system_crypt \ + --cipher aes-xts-plain64 --key-size $(( $keysize * 2 )) --hash $hash \ + --iter-time 5000 --use-random + part_system=/dev/mapper/system_crypt +fi + + +# Ensure LVM2 is installed in the target chroot; create logical volumes +# for /, swap and /home. +apt-install lvm2 || true +vg=$(hostname) +pvcreate -ff -y $part_system +vgcreate $vg $part_system + +lvcreate -L 5G -n root $vg +lvcreate -L 1G -n swap $vg +lvcreate -l 100%FREE -n home $vg +vgchange -ay $vg + + +# Format the partitions +fripost_mkfs ext2 $part_boot -E resize=512M -m1 -b 4096 +fripost_mkfs ext4 /dev/$vg/root -b 4096 +fripost_mkfs ext4 /dev/$vg/home -b 4096 +mkswap /dev/$vg/swap + + +# Stuff the fstab and mount the devices in the target +fripost_fstab $part_boot /boot ext2 noatime +fripost_fstab /dev/$vg/root / ext4 noatime,errors=remount-ro +fripost_fstab /dev/$vg/swap none swap sw +fripost_fstab /dev/$vg/home /home ext4 noatime +fripost_mount_partitions + +# TODO: EFI: format, add to fstab, how to populate? diff --git a/partition.sh b/partition.sh deleted file mode 100755 index fb56ce7..0000000 --- a/partition.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/sh -# -# Simple partitioning shell script. -# -# Copyright 2013 Guilhem Moulin -# -# Licensed under the GNU GPL version 3 or higher. - -set -ue - -. /lib/fripost-partman/base.sh - -# Wipe the disk -device=/dev/sda -fripost_wipe $device - -db_get fripost/encrypt -encrypt=$RET - -# Create a disk label -/sbin/parted -s $device mklabel gpt -log "Created disklabel GPT for device $device" - -# Create a EFI partition if needed; otherwise, create a partition needed -# to put GRUB on GPT disklabels. -if [ -d /proc/efi -o -d /sys/firmware/efi ]; then - part_efi=$( fripost_mkpart $device efi 256M +boot ) -else - fripost_mkpart $device bios_grub 8M +bios_grub -fi -db_set grub-installer/bootdev $device -db_fset grub-installer/bootdev seen true - -# Create boot and system partitions -part_boot=$( fripost_mkpart $device boot 64M ) -part_system=$( fripost_mkpart $device system 100% ) -/sbin/parted -s $device align-check opt ${part_system#$device} \ - || fatal "$part_system is not aligned" - - -if [ $encrypt = true ]; then - # Encrypt the system partition. We choose the key length and digest - # depending on the architecture we're on; we use AES128 and SHA-256 - # on 32-bits platforms, and AES256 and SHA-512 on 64-bits platforms. - arch=$(uname -m) - if [ "$arch" = x86_64 ]; then - keysize=256 - hash=sha512 - elif [ "$arch" = i386 -o "$arch" = i686 ]; then - keysize=128 - hash=sha256 - fi - # Note: XTS requires the key size to be doubled. - fripost_encrypt $part_system system_crypt \ - --cipher aes-xts-plain64 --key-size $(( $keysize * 2 )) --hash $hash \ - --iter-time 5000 --use-random - part_system=/dev/mapper/system_crypt -fi - - -# Ensure LVM2 is installed in the target chroot; create logical volumes -# for /, swap and /home. -apt-install lvm2 || true -vg=$(hostname) -pvcreate -ff -y $part_system -vgcreate $vg $part_system - -lvcreate -L 5G -n root $vg -lvcreate -L 1G -n swap $vg -lvcreate -l 100%FREE -n home $vg -vgchange -ay $vg - - -# Format the partitions -fripost_mkfs ext2 $part_boot -E resize=512M -m1 -b 4096 -fripost_mkfs ext4 /dev/$vg/root -b 4096 -fripost_mkfs ext4 /dev/$vg/home -b 4096 -mkswap /dev/$vg/swap - - -# Stuff the fstab and mount the devices in the target -fripost_fstab $part_boot /boot ext2 noatime -fripost_fstab /dev/$vg/root / ext4 noatime,errors=remount-ro -fripost_fstab /dev/$vg/swap none swap sw -fripost_fstab /dev/$vg/home /home ext4 noatime -fripost_mount_partitions - -# TODO: EFI: format, add to fstab, how to populate? diff --git a/preseed.cfg b/preseed.cfg new file mode 100644 index 0000000..7856628 --- /dev/null +++ b/preseed.cfg @@ -0,0 +1,57 @@ +# Preseed file +# +# Copyright 2013 Guilhem Moulin +# +# Licensed under the GNU GPL version 3 or higher. + +# Locales +d-i debian-installer/locale string en_US.UTF-8 +d-i localechooser/supported-locales multiselect sv_SE.UTF-8 +d-i keymap skip-config + +# Debian archive mirror hostname (installer only): +d-i mirror/http/hostname string ftp.se.debian.org + +# Assume the clock is UTC. Always use NTP. +d-i clock-setup/utc boolean true +d-i clock-setup/ntp boolean true +d-i clock-setup/ntp-server string 0.se.pool.ntp.org +d-i time/zone string Europe/Stockholm + +# Network +d-i netcfg/choose_interface select auto +d-i netcfg/hostname string eilift +d-i netcfg/get_domain string guilhem.org +#d-i netcfg/disable_autoconfig boolean true +#d-i netcfg/get_ipaddress string 192.168.10.100 +#d-i netcfg/get_netmask string 255.255.255.0 +#d-i netcfg/get_gateway string 192.168.10.2 +#d-i netcfg/get_nameservers string 192.168.10.1 +#d-i netcfg/confirm_static boolean true + +# Users +adduser adduser/homedir-permission boolean false +d-i passwd/root-login boolean false +d-i passwd/shadow boolean true +d-i passwd/user-fullname string Guilhem Moulin +d-i passwd/username string guilhem +# TODO: should be generated by preseed.sh: echo ... | mkpasswd -s -H SHA-512 +d-i passwd/user-password-crypted password $6$QjLeZdW1O$9VymVDc5q6NctEylAqObvmP4S7FXk1ckCPaHua0JaiFSaTeTKoWxaP7bQoUWvzCPDY23Xqthg87QDBbaCY9BP0 +d-i passwd/user-default-groups string ssh + +# System +popularity-contest popularity-contest/participate boolean true +d-i base-installer/kernel/image string linux-image-amd64 +d-i base-installer/kernel/linux/initramfs-generators string initramfs-tools +# A subset of 'standard' is interesting: tasksel --task-packages standard +tasksel tasksel/first string +d-i pkgsel/include string curl bind9-host git heirloom-mailx python-apt \ + postfix vim-nox emacs23-nox +d-i pkgsel/upgrade select safe-upgrade + +d-i preseed/early_command string anna-install fripost-partman fripost-postinst +d-i preseed/late_command string /bin/in-target /usr/bin/update-alternatives --set editor /usr/bin/vim.nox +d-i fripost/partition-script string /cdrom/include/partition.sh + +d-i fripost/wipe-device string none +d-i fripost/initrd-ssh-port string 2222 diff --git a/preseed.sh b/preseed.sh new file mode 100755 index 0000000..b3d8362 --- /dev/null +++ b/preseed.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# +# Usage: preseed.sh preseeded.iso source.iso [udebs] +# +# Credits: https://wiki.debian.org/DebianInstaller/Preseed/EditIso +# https://wiki.debian.org/DebianInstaller/Modify/CD +# +# Install with 'Advanced options > Automated install' + +set -ue + +newiso=$(readlink -f "$1") +[ -e "$newiso" ] && rm -f "$newiso" + +iso="$2" +test -r "$iso" || exit 1 +shift; shift +pwd=$(pwd) + +for prog in fuseiso fusermount rsync md5sum genisoimage find; do + if ! which "$prog" >/dev/null; then + echo "Error: Missing $prog" >&2 + exit 1 + fi +done + +mountdir=$(mktemp -d) +fuseiso "$iso" "$mountdir" + +isoeditdir=$(mktemp -d) +rsync -aH --exclude=TRANS.TBL --chmod=u+w "$mountdir"/ "$isoeditdir"/ + +fusermount -u "$mountdir" +rmdir "$mountdir" + +cp preseed.cfg "$isoeditdir/" +md5sum ./preseed.cfg >> "$isoeditdir/md5sum.txt" + +isolinux=isolinux/adtxt.cfg +sed -ri "s@^\s+append\s(.*\s)?auto=true\b@& file=/cdrom/preseed.cfg @" \ + "$isoeditdir/$isolinux" +# ^ no need to update the checksum, $isolinux is not in './md5sum.txt'. + +mkdir "$isoeditdir/include" +rsync -aL ./include/ "$isoeditdir/include"/ +find ./include \( \! -name '.*' -a -type f \) -print0 | \ + xargs -r0 md5sum >> "$isoeditdir/md5sum.txt" + +# TODO: that's ugly +dist=$(for x in "$isoeditdir"/dists/*; do [ -h "$x" ] && continue; [ -d "$x" ] && echo ${x##*/} && break; done) +arch=$(for x in "$isoeditdir/dists/$dist/main"/binary-*; do test -d "$x" && echo ${x##*/binary-} && break; done) +packages="dists/$dist/main/debian-installer/binary-$arch/Packages" +while [ $# -gt 0 ]; do + cd "${1%%_*}-udeb"; shift + name="$(sed -n 's/Package:\s\s*//p' ./debian/control)" + udeb=$(cut -d' ' -f1,1 ./debian/files) + dir="pool/main/$(expr substr "$name" 1 1)/$name" + mkdir -p "$isoeditdir/$dir/" + cp ../"$udeb" "$isoeditdir/$dir/" + + cat "./debian/$name/DEBIAN/control" >> "$isoeditdir/$packages" + echo "Filename: $dir/$udeb" >> "$isoeditdir/$packages" + echo "MD5sum: $(md5sum ../"$udeb" | cut -d' ' -f1,1)" >> "$isoeditdir/$packages" + echo "SHA1: $(sha1sum ../"$udeb" | cut -d' ' -f1,1)" >> "$isoeditdir/$packages" + echo "SHA256: $(sha256sum ../"$udeb" | cut -d' ' -f1,1)" >> "$isoeditdir/$packages" + echo >> "$isoeditdir/$packages" + + cd "$isoeditdir" + md5sum "./$dir/$udeb" >> "$isoeditdir/md5sum.txt" + cd "$pwd" +done + +cd "$isoeditdir" +gzip -fk ./"$packages" + +md5sums=$(mktemp) +while read sum file; do + if [ "$file" = "./${packages}" -o \ + "$file" = "./${packages}.gz" ]; then + md5sum "$file" + else + echo "$sum $file" + fi +done < ./md5sum.txt > "$md5sums" +mv "$md5sums" "$isoeditdir/md5sum.txt" + +genisoimage -o "$newiso" -quiet -r -J \ + -no-emul-boot -boot-load-size 4 \ + -boot-info-table \ + -b isolinux/isolinux.bin \ + -c isolinux/boot.cat \ + ./ +rm -rf "$isoeditdir" diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..85b61ee --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ +*.udeb +*.build +*.changes diff --git a/src/fripost-partman-udeb/Makefile b/src/fripost-partman-udeb/Makefile new file mode 100644 index 0000000..b603c83 --- /dev/null +++ b/src/fripost-partman-udeb/Makefile @@ -0,0 +1,13 @@ +CC ?= gcc +CFLAGS ?= -Wall -x c -s + +LIBS = lib/stdbuf.so + +all: ${LIBS} + +%.so: %.c + #TODO: multiarch (-m32/-m64) + ${CC} ${CFLAGS} $< -fPIC -shared -o $@ + +clean: + rm -f ${LIBS} diff --git a/src/fripost-partman-udeb/base.sh b/src/fripost-partman-udeb/base.sh new file mode 100644 index 0000000..114e678 --- /dev/null +++ b/src/fripost-partman-udeb/base.sh @@ -0,0 +1,527 @@ +# Functions commonly used by fripost-partman shell scripts +# +# Copyright 2013 Guilhem Moulin +# +# Licensed under the GNU GPL version 3 or higher. + +true ${DEBIAN_HAS_FRONTEND:=} ${DEBCONF_REDIR:=} +. /usr/share/debconf/confmodule + +# TODO: +# * support sofware RAID (mdadm) +# * encryption without SSH daemon (eg when /home is on a separate device) +# * encryption of multiple disks with the same key (eg / on a software raid) + + +# Log a message. +log() { + logger -t fripost -- "$@" +} + +# Log a fatal error and quit. +fatal() { + logger -t fripost -p user.err -- "$@" + exit 1 +} + +# Ensure stdout is opened with line buffering. If some day stdbuf(1) is +# available in busybox, we should replace the LD_PRELOAD by 'stdbuf -oL -eL'. +stdbuf() { + LD_PRELOAD=/lib/fripost-partman/stdbuf.so "$@" +} + +# Wait for a device to be created +wait_for_device() { + local device="$1" timeout=40 + + until [ -b "$device" -o $timeout -le 0 ]; do + log "Waiting for ${device}..." + sleep 0.25 + timeout=$(( $timeout - 1 )) + done + [ -b "$device" ] || fatal "Error: $device not found!" +} + + +############################################################################## +# Wipe the disk (unless d-i's 'fripost/wipe-device' is 'none') +# +# Usage: fripost_wipe + +fripost_wipe() { + local device="$1" source bs=8192 blockdir size wipe fifo pid + blockdir="/sys/block/${device#/dev/}" + + db_get fripost/wipe-device + if [ "$RET" = none ]; then + log "Not wiping $device" + return 0 + fi + + source="/dev/$RET" + log "Want to wipe $device using source $source" + [ -b "$device" -a -d "$blockdir" -a -c "$source" ] || \ + fatal "Invalid device $device or source $source" + + size=$(( `cat $blockdir/size` * `cat $blockdir/queue/physical_block_size` )) + + wipe="dd if=$source of=$device bs=$bs" + db_subst fripost/wipe-device_progress_title DISK "$device" + db_subst fripost/wipe-device_progress_title SIZE "$size" + if [ "$source" = /dev/zero ]; then + db_subst fripost/wipe-device_progress_title WHAT "zeroes" + elif [ "$source" = /dev/random -o "$source" = /dev/urandom ]; then + db_subst fripost/wipe-device_progress_title WHAT "bytes of random data" + else + db_subst fripost/wipe-device_progress_title WHAT "bytes" + fi + db_progress START 0 $(( $size / $bs )) fripost/wipe-device_progress_title + db_subst fripost/wipe-device_progress_info COMMAND "$wipe" + db_progress INFO fripost/wipe-device_progress_info + + fifo=$(mktemp -u) + /bin/mknod "$fifo" p || exit 1 + trap 'echo kill $pid' EXIT + $wipe 2> "$fifo" & pid=$! + heartbeat $pid USR1 & + + local n records dir + while read -u 7 n records dir; do + [ "$records" = records -a "$dir" = out ] && db_progress SET ${n%+*} + done 7< "$fifo" + db_progress SET $(( $size / $bs )); sleep 0.25 + + rm -f "$fifo" + trap '' EXIT + + db_progress STOP + db_unregister fripost/wipe-device_progress_title + db_unregister fripost/wipe-device_progress_info +} + +heartbeat () { + local pid=$1 sig=${2:-SIGHUP} sleep=${3:-1} + until false; do + sleep $sleep + /bin/kill -$sig $pid 2>/dev/null || break + done +} + + + +############################################################################## +# Partitioning +# +# Usage: fripost_mkpart [flag | +flag | -flag | ...] +# +# Prints the bloc device for the new partition. can be given +# using SI prefixes T,G,M, or k. If is a percentage, it is +# relative to the free capacity. + +fripost_mkpart() { + local device="$1" name="$2" size="$3" grain=$(( 256*32 )) offset bs + local start stop part flag free + # TODO: names are supported on GPT only. What is the second argument + # of mkpart on msdos? + + bs=$(cat /sys/block/${device#/dev/}/queue/physical_block_size) + + if /sbin/parted -sm "$device" p | grep -q '^[1-9][0-9]*:'; then + # The last offset, in sector; it is *always* supposed to be aligned. + start=$( /sbin/parted -sm "$device" u s p \ + | grep '^[1-9][0-9]*:' \ + | sort -t: -nk2,2 \ + | tail -1 \ + | sed -r 's/^[1-9][0-9]*:[^:]*:([1-9][0-9]*)s:.*/\1/' ) + start=$(( 1 + $start )) + else + # There are no partitions yet + offset=$(cat /sys/block/${device#/dev/}/alignment_offset) + if [ $offset -eq 0 ]; then + # 64 is arbitrary here; $grain may be better since aligned, + # but the 1st partition is typically /boot or something + # without heavy IO, so choosing a smaller value means we get + # more space without significant performance penalty + start=64 + else + start=$(( $offset / $bs )) + fi + fi + + # Remove the SI prefix, or compute the size relative to a percentage + case "$size" in + *T) size=$(( ${size%T} * 1024**4 ));; + *G) size=$(( ${size%G} * 1024**3 ));; + *M) size=$(( ${size%M} * 1024**2 ));; + *K) size=$(( ${size%K} * 1024 ));; + *%) free=$(( `cat /sys/block/${device#/dev/}/size` - 1 - $start )) + size=$(( $bs * $free * ${size%\%} / 100 )) + ;; + *) fatal "Unknown size $size" ;; + esac + + # Round toward 0 to preserve the alignement + stop=$(( $start + $size / $bs )) + stop=$(( $stop - $stop % $grain - 1 )) + log "Creating partition '$name' on $device, between sectors $start and $stop" + [ $start -ge $stop ] && fatal "Cannot create partition: $start >= $stop" + /sbin/parted -a minimal -s $device mkpart "$name" ${start}s ${stop}s + + # The partition index that is to be created + part=$( /sbin/parted -sm $device u s p \ + | grep -m1 "^[0-9]:${start}s:" \ + | sed 's/:.*//' ) + + while [ $# -gt 3 ]; do + flag="$4" + log "Partition flag for ${device}${part} ($name): $flag" + case "$flag" in + +*) /sbin/parted -s $device set $part "${flag#+}" on ;; + -*) /sbin/parted -s $device set $part "${flag#-}" off ;; + *) /sbin/parted -s $device toggle $part "$flag" ;; + esac + shift + done + + wait_for_device "${device}${part}" + echo ${device}${part} +} + + + +############################################################################## +# Encryption +# +# Usage: fripost_encrypt [] + +fripost_encrypt() { + local device="$1" name="$2" keyfile sshHostKey import=/cdrom/include + local fifo pid + shift; shift + keyfile=$(mktemp) || exit 1 + + log "Encryting device $device and sets up a mapping $name" + + db_input high fripost/encryption-password || true + db_go + db_get fripost/encryption-password + + if [ -n "$RET" ]; then + log "The password can be found from debconf (hey, that's insecure!)" + # Since we're using the builtin, the password shouldn't be + # visible in ps(1). + echo -n "$RET" > "$keyfile" + db_set fripost/encryption-password '' + + else + log "Starting a SSH daemon to slurp the volume key..." + local type=rsa + db_subst fripost/ssh-keypair-generation_progress_title TYPE $type + db_progress START 0 1 fripost/ssh-keypair-generation_progress_title + + mkdir -pm0755 /etc/ssh/ + sshHostKey=/etc/ssh/ssh_host_rsa_key + /usr/bin/ssh-keygen -b 4096 -t $type -N '' -C $sshHostKey -f $sshHostKey + db_progress SET 1; sleep 0.25 + db_progress STOP + db_unregister fripost/ssh-keypair-generation_progress_title + + cat > /etc/ssh/sshd_config <<- EOF + Port 22 + Protocol 2 + HostKey $sshHostKey + UsePrivilegeSeparation no + + PasswordAuthentication no + ChallengeResponseAuthentication no + HostbasedAuthentication no + PubkeyAuthentication yes + + PermitRootLogin yes + AllowUsers root + StrictModes yes + + AllowAgentForwarding no + AllowTcpForwarding no + ForceCommand /bin/cat >$keyfile + EOF + + # Populate the authorized keys. + [ -d ~root/.ssh ] || mkdir -m0700 ~root/.ssh + copy_authorized_keys $import/authorized_keys ~root/.ssh/authorized_keys 'no-pty' + + # Start the SSH daemon + touch /var/log/lastlog + /usr/sbin/sshd + + # Tell the user we're ready + db_subst fripost/encryption-slurpkey_text IPv4 "$(getIPv4)" + db_subst fripost/encryption-slurpkey_text SSHFPR_SERVER \ + "$(/usr/bin/ssh-keygen -lf $sshHostKey)" + db_subst fripost/encryption-slurpkey_text SSHFPR_AUTHORIZED \ + "$(sshfprs ~root/.ssh/authorized_keys ' - ')" + + # Anything sent to the SSH is stored into $keyfile, which is our LUKS key. + until test -s "$keyfile"; do + db_settitle fripost/encryption-slurpkey_title + db_input critical fripost/encryption-slurpkey_text + db_go + done + + # Kill the daemon, we're done + log "Killing the SSH daemon..." + kill `cat /var/run/sshd.pid` || true + fi + + # Load dm-crypt + /sbin/dmsetup targets | cut -d' ' -f1 | grep -q '^crypt$' \ + || /sbin/modprobe -v dm_crypt + + log "cryptsetup options: $@" + db_progress START 0 100 fripost/cryptsetup-genkey_progress_title + db_progress INFO fripost/cryptsetup-genkey_progress_info + + fifo=$(mktemp -u) + /bin/mknod "$fifo" p || exit 1 + trap 'echo kill $pid' EXIT + stdbuf /sbin/cryptsetup -q "$@" --key-file="$keyfile" luksFormat "$device" \ + | stdbuf sed -nr 's/^Generating key \(([0-9]+)% done\)\.$/\1/p' > "$fifo" & pid=$! + + local n + while read -u 7 n; do + db_progress SET $n + done 7< "$fifo" + db_progress SET 100; sleep 0.25 + + rm -f "$fifo" + trap '' EXIT + + db_progress STOP + db_unregister fripost/cryptsetup-genkey_progress_title + db_unregister fripost/cryptsetup-genkey_progress_info + + # Open the LUKS device and set up the mapping + /sbin/cryptsetup --key-file="$keyfile" luksOpen "$device" "$name" + + log "Removing the key file" + rm -f "$keyfile" # We are on a ramdisk, so it's good enough to unlink(2) + + # Add an entry to the crypttab + printf "%-15s %-41s %-15s %s\n" \ + "$name" UUID=$(/bin/block-attr --uuid "$device") none luks \ + >> /tmp/crypttab + + local m _ + [ -d /var/lib/fripost ] || mkdir /var/lib/fripost + # The modules required to fire up dropbear and start cryptsetup in the ramdisk. + echo dm_crypt > /tmp/initramfs-modules + while read m _; do /sbin/modinfo -F filename "$m"; done < /proc/modules \ + | sed -nr "s@^/lib/modules/`uname -r`/kernel/((arch/[^/]+/)?crypto|drivers/(ata|scsi))(/.*)?/([^/]+)\.ko\$@\5@p" \ + >> /var/lib/fripost/initrd-modules + /bin/apt-install busybox cryptsetup || true + + wait_for_device "/dev/mapper/$name" +} + + +# Like ssh-keygen -lf, but for a file such as authorized_keys, which +# may contain multiple keys. +# +# Usage: sshfprs.sh file [prefix] + +sshfprs() { + local file="$1" prefix="${2:-}" pk + + while read pk; do + # /usr/bin/ssh-keygen can't read from STDIN, and the '<<<' is + # not POSIX, so we save each pubkey in a temporary file + pkf=$(mktemp) + echo "$pk" > "$pkf" + echo "${prefix}$(/usr/bin/ssh-keygen -lf $pkf)" + rm -f "$pkf" + done < "$file" +} + +# Copy an authorized_keys file, possibly adding some options. The input +# format is sshd(8)'s; see the section called "AUTHORIZED_KEYS FILE +# FORMAT" in the manpage. The character '#' cannot occur in the options. + +copy_authorized_keys() { + local from="$1" to="$2" + if [ $# -gt 2 ]; then + sed -r "s#^([^#]+\s)?(ssh-(dss|rsa)|ecdsa-sha2-nistp(256|384|521))\s#$3 \2 #" \ + "$from" > "$to" + else + cp "$from" "$to" + fi + chmod 0600 "$to" +} + + + +############################################################################## +# Add a progress bar to mkfs(8). +# +# Usage: fripost_mkfs [] + +fripost_mkfs() { + local type="$1" device="$2" fifo pid line stage step total= + shift; shift + + log "Formatting $device as $type; options $@" + db_subst fripost/mkfs_progress_title DEVICE "$device" + db_subst fripost/mkfs_progress_title TYPE "$type" + wait_for_device "$device" + + fifo=$(mktemp -u) + /bin/mknod "$fifo" p || exit 1 + trap 'echo kill $pid' EXIT + /sbin/mkfs."$type" "$@" "$device" | tr '\b' '\n' > "$fifo" & pid=$! + + while read -u 7 line; do + # XXX: The parsing has been tested with mkfs.ext{2,3,4} only. + case "$line" in + *:*/*) + [ -z "$total" ] || db_progress STOP + + stage="${line%: *}" + step="${line##*: }" + log "mkfs(8) stage '$stage', $step steps" + + total="${step#*/}" + db_progress START 0 $total fripost/mkfs_progress_title + db_progress SET ${step%/*} + + db_subst fripost/mkfs_progress_info STAGE "$stage" + db_progress INFO fripost/mkfs_progress_info + ;; + */$total) + [ -z "$total" ] || db_progress SET ${line%/*} + ;; + done) + [ -z "$total" ] || db_progress SET $total + ;; + esac + done 7< "$fifo" + + rm -f "$fifo" + trap '' EXIT + + [ -z "$total" ] || db_progress STOP + db_unregister fripost/mkfs_progress_title + db_unregister fripost/mkfs_progress_info +} + + +############################################################################## +# Set up the fstab +# +# Usage: fripost_fstab [] +# +# Note: Don't forget to run 'fripost_mount_partitions' after the last +# call to 'fripost_fstab'. + +fripost_fstab() { + local device="$1" mp="$2" type="$3" options="${4:-defaults}" pass + + # TODO: EFI should be set with pass 1 + if [ "$mp" = / ]; then + log "Got root device, $device" + pass=1 + elif [ "$type" = swap ]; then + log "Got swap device, $device" + pass=0 + else + log "Got other device, $device -> $mp" + pass=2 + fi + + # Add an entry to the fstab + fripost_fstab_addentry $device $mp $type $options 0 $pass +} + +fripost_fstab_addentry() { + local device=$1 mp=$2 type=$3 options=$4 dump=$5 pass=$6 + local fstab=/tmp/fstab + local entry="%-41s %-15s %-7s %-31s %-7s %s\n" + log "Adding fstab entry $device -> $mp" + + if ! [ -s $fstab -a -f $fstab ]; then + # No fstab has bee created yet. We copy the header from + # /lib/partman/finish.d/39create_fstab_header + cat > $fstab <<-EOF + # /etc/fstab: static file system information. + # + # Use 'blkid' to print the universally unique identifier for a device; + # this may be used with UUID= as a more robust way to name devices that + # works even if disks are added and removed. See fstab(5). + # + EOF + printf "$entry" '# ' '' '' \ + '' '' '' \ + >> $fstab + fi + + # Always use UUIDs + local uuid=$(/bin/block-attr --uuid "$device") + if [ -z "$uuid" ]; then + log "Warning: Block device $device has no UUID!" + printf "$entry" "$device" $mp $type $options $dump $pass >> $fstab + else + log "Block device $device has UUID $uuid" + printf "$entry" "UUID=$uuid" $mp $type $options $dump $pass >> $fstab + + # Fix udev's UUID mapping + if ! [ -h /dev/disk/by-uuid/"$uuid" ]; then + device=$(/bin/mapdevfs "$device") + log "Adding a symlink UUID $uuid -> $device" + ln -s ../../"${device#/dev/}" /dev/disk/by-uuid/"$uuid" + fi + fi +} + + + +############################################################################## +# Mount all entries in the fstab +# +# Usage: fripost_mount_partitions + +fripost_mount_partitions() { + local cdrom uuid mp type options _ + + # sort(1), to ensure that roots are mounted earlier, regardless of + # where they appear in the file (eg, /boot can appear before /). + sed -e 's/#.*//' -e '/^\s*$/d' -e 's/\s\s*/ /g' /tmp/fstab \ + | grep -v '\s0$' \ + | sort -t' ' -k2,2 \ + | while read uuid mp type options _; do + local dev=$(/sbin/blkid -U "${uuid#UUID=}") + log "Found fstab entry: $dev -> $mp" + wait_for_device "$dev" + if [ "$type" = swap ]; then + /sbin/swapon "$dev" + elif [ / = $(expr substr "$mp" 1 1) ]; then + mkdir "/target$mp" + /bin/mount -t "$type" -o "$options" "$dev" "/target$mp" + fi + done + + # Post-installation scripts may use the install CD as a local mirror + # for APT packages. TODO: What if it was a USB stick? A netboot? + cdrom=$( sed -rn 's#^(\S+) /cdrom.*#\1#p' /proc/mounts \ + | grep -m1 -Ev '^/dev/(loop|sd[a-z])' ) + [ -n "$cdrom" ] || fatal "Error: Is /cdrom a mountpoint?" + fripost_fstab_addentry "$cdrom" /media/cdrom0 udf,iso9660 user,noauto 0 0 + mkdir -p /target/media/cdrom0 + ln -s cdrom0 /target/media/cdrom + + # Move the fstab and crypttab where they belong + [ -d /target/etc ] || mkdir /target/etc + mv /tmp/fstab /target/etc/fstab + [ \! -f /tmp/crypttab ] || mv /tmp/crypttab /target/etc/crypttab +} + +getIPv4() { + /bin/ip addr show "${1:-eth0}" | sed -nr 's/^\s+inet\s([0-9.]{4,32}).*/\1/p' +} diff --git a/src/fripost-partman-udeb/debian/changelog b/src/fripost-partman-udeb/debian/changelog new file mode 100644 index 0000000..6146216 --- /dev/null +++ b/src/fripost-partman-udeb/debian/changelog @@ -0,0 +1,5 @@ +fripost-partman (0.0.0) unstable; urgency=low + + * Tests + + -- Guilhem Moulin Wed, 09 Oct 2013 17:33:16 +0200 diff --git a/src/fripost-partman-udeb/debian/compat b/src/fripost-partman-udeb/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/src/fripost-partman-udeb/debian/compat @@ -0,0 +1 @@ +7 diff --git a/src/fripost-partman-udeb/debian/control b/src/fripost-partman-udeb/debian/control new file mode 100644 index 0000000..043fa49 --- /dev/null +++ b/src/fripost-partman-udeb/debian/control @@ -0,0 +1,21 @@ +Source: fripost-partman +Section: debian-installer +Priority: optional +Maintainer: Guilhem Moulin +Build-Depends: debhelper (>= 7) + +Package: fripost-partman +XC-Package-Type: udeb +Architecture: all +Depends: cdebconf-udeb, parted-udeb, xfsprogs-udeb, hw-detect, md-modules, + mdadm-udeb, lvm2-udeb, dmsetup-udeb, ntfstools-udeb, xfs-modules, + ext2-modules, ext3-modules, openssh-server-udeb, cryptsetup-udeb, + crypto-dm-modules, ${misc:Depends} +Provides: created-fstab, made-filesystems, mounted-partitions, + partitioned-harddrives +Description: Partition Hard Drives using shell scripts. + . + Warning: This module *will* wipe your disk if you ask it to do so, without + giving you the chance to back out. Do not use this on systems you care about + unless you've made thorough tests with Virtual Machines. +XB-Installer-Menu-Item: 4199 diff --git a/src/fripost-partman-udeb/debian/copyright b/src/fripost-partman-udeb/debian/copyright new file mode 100644 index 0000000..4e26ce2 --- /dev/null +++ b/src/fripost-partman-udeb/debian/copyright @@ -0,0 +1,7 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: native package + +Files: * +Copyright: © 2013 Guilhem Moulin +License: GPL-3+ + diff --git a/src/fripost-partman-udeb/debian/install b/src/fripost-partman-udeb/debian/install new file mode 100644 index 0000000..885868e --- /dev/null +++ b/src/fripost-partman-udeb/debian/install @@ -0,0 +1 @@ +base.sh lib/*.so lib/fripost-partman diff --git a/src/fripost-partman-udeb/debian/postinst b/src/fripost-partman-udeb/debian/postinst new file mode 100755 index 0000000..6ba238e --- /dev/null +++ b/src/fripost-partman-udeb/debian/postinst @@ -0,0 +1,25 @@ +#! /bin/sh + +set -ue + +. /lib/fripost-partman/base.sh + +log "Starting..." +db_input high fripost/partition-script || true +db_go +db_get fripost/partition-script +script=/"${RET#/}" + +[ -f "$script" -a -x "$script" ] || \ + fatal "$script does not exist, or is not executable." + +log "Running $script" +log-output -t fripost-partman "$script" + +rv=$? +if [ $rv -ne 0 ]; then + log "Partitioning script failed; exited with $rv" + exit $rv +fi + +log "Partitioning appears to have completed successfully." diff --git a/src/fripost-partman-udeb/debian/rules b/src/fripost-partman-udeb/debian/rules new file mode 100755 index 0000000..cbe925d --- /dev/null +++ b/src/fripost-partman-udeb/debian/rules @@ -0,0 +1,3 @@ +#!/usr/bin/make -f +%: + dh $@ diff --git a/src/fripost-partman-udeb/debian/templates b/src/fripost-partman-udeb/debian/templates new file mode 100644 index 0000000..00377a5 --- /dev/null +++ b/src/fripost-partman-udeb/debian/templates @@ -0,0 +1,84 @@ +Template: debian-installer/fripost-partman/title +Type: text +Description: Partition disks using a shell script + +Template: fripost/partition-script +Type: text +Description: Which script should be used for partitioning? +Extended_description: The path should be absolute, and the script must + be executable. + +Template: fripost/wipe-device +Type: select +Default: zero +Choices: none, zero, urandom, random +Description: Which kind of data fill the disk with before encryption? +Extended_description: 'none' means disks will NOT wiped, otherwise use + /dev/{zero,urandom,random} as the source data to fill disks with. + Beware that the later two may drain the entropy pool of the system! + +Template: fripost/wipe-device_progress_title +Type: text +Description: Filling ${DISK} with ${SIZE} ${WHAT} + +Template: fripost/wipe-device_progress_info +Type: text +Description: ${COMMAND} + +Template: fripost/encrypt +Type: boolean +Default: true +Description: Should the system disk be fully encrypted? (Excluding /boot.) + +Template: fripost/encryption-password +Type: password +Default: +Description: Password for full-disk encryption? +Extended_description: If left empty, a SSH daemon will be fired and the + automatic install will be interupted, waiting for the user to dump the + password on the standard input. + +Template: fripost/ssh-keypair-generation_progress_title +Type: text +Description: Generating public/private ${TYPE} key pair + +Template: fripost/encryption-slurpkey_title +Type: note +Description: Waiting for passphrase + +Template: fripost/encryption-slurpkey_text +Type: text +Description: Press 'continue' once you have sent the key + You now need to send the encryption key for the LUKS/dm-crypt volume to + this special-purpose SSH server: + . + ssh -o UserKnownHostsFile=/dev/null -T root@${IPv4} < /path/to/key + . + To defeat MiTM-attacks, please ensure that the server fingerprint matches + . + ${SSHFPR_SERVER} + . + Key(s) that are granted access have the following fingerprint: + . + ${SSHFPR_AUTHORIZED} + . + Note: This server is ephemeral, and will be replaced with a full-blown + daemon toward the end of the installation. Using /dev/null as the + Known Hosts File is meant to tell the SSH client not to remember its + public key. + +Template: fripost/cryptsetup-genkey_progress_title +Type: text +Description: Generating volume key + +Template: fripost/cryptsetup-genkey_progress_info +Type: text +Description: This will take a while if it drains the entropy pool + +Template: fripost/mkfs_progress_title +Type: text +Description: Formatting ${DEVICE} as ${TYPE} + +Template: fripost/mkfs_progress_info +Type: text +Description: ${STAGE} diff --git a/src/fripost-partman-udeb/lib/stdbuf.c b/src/fripost-partman-udeb/lib/stdbuf.c new file mode 100644 index 0000000..13e6c06 --- /dev/null +++ b/src/fripost-partman-udeb/lib/stdbuf.c @@ -0,0 +1,11 @@ +/* All credits to https://lists.gnu.org/archive/html/bug-coreutils/2008-11/msg00138.html + * Cf. also http://www.pixelbeat.org/programming/stdio_buffering/ + * http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/stdbuf.c;h=2500c300147f30aa5d2d99fd29b5865637a11290;hb=HEAD + */ + +#include + +__attribute__ ((constructor)) void +cah9mion4ocim6ip () { + setvbuf (stdout, NULL, _IOLBF, 0); +} diff --git a/src/fripost-postinst-udeb/debian/changelog b/src/fripost-postinst-udeb/debian/changelog new file mode 100644 index 0000000..c1ea4fd --- /dev/null +++ b/src/fripost-postinst-udeb/debian/changelog @@ -0,0 +1,5 @@ +fripost-postinst (0.0.0) unstable; urgency=low + + * Tests + + -- Guilhem Moulin Wed, 17 Oct 2013 04:32:31 +0200 diff --git a/src/fripost-postinst-udeb/debian/compat b/src/fripost-postinst-udeb/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/src/fripost-postinst-udeb/debian/compat @@ -0,0 +1 @@ +7 diff --git a/src/fripost-postinst-udeb/debian/control b/src/fripost-postinst-udeb/debian/control new file mode 100644 index 0000000..e173159 --- /dev/null +++ b/src/fripost-postinst-udeb/debian/control @@ -0,0 +1,11 @@ +Source: fripost-postinst +Section: debian-installer +Priority: optional +Maintainer: Guilhem Moulin +Build-Depends: debhelper (>= 7) + +Package: fripost-postinst +XC-Package-Type: udeb +Architecture: all +Depends: fripost-partman, ${misc:Depends} +Description: Post-install scripts (e.g., install dropbear in the initramfs) diff --git a/src/fripost-postinst-udeb/debian/copyright b/src/fripost-postinst-udeb/debian/copyright new file mode 100644 index 0000000..4e26ce2 --- /dev/null +++ b/src/fripost-postinst-udeb/debian/copyright @@ -0,0 +1,7 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: native package + +Files: * +Copyright: © 2013 Guilhem Moulin +License: GPL-3+ + diff --git a/src/fripost-postinst-udeb/debian/install b/src/fripost-postinst-udeb/debian/install new file mode 100644 index 0000000..5426071 --- /dev/null +++ b/src/fripost-postinst-udeb/debian/install @@ -0,0 +1,2 @@ +finish-install.d/* /usr/lib/finish-install.d +sshd_config /var/lib/fripost diff --git a/src/fripost-postinst-udeb/debian/rules b/src/fripost-postinst-udeb/debian/rules new file mode 100755 index 0000000..cbe925d --- /dev/null +++ b/src/fripost-postinst-udeb/debian/rules @@ -0,0 +1,3 @@ +#!/usr/bin/make -f +%: + dh $@ diff --git a/src/fripost-postinst-udeb/debian/templates b/src/fripost-postinst-udeb/debian/templates new file mode 100644 index 0000000..5385ce9 --- /dev/null +++ b/src/fripost-postinst-udeb/debian/templates @@ -0,0 +1,93 @@ +Template: base-installer/progress/fripost +Type: text +Description: ${WHAT} + +Template: fripost/initrd-ssh-port +Type: string +Default: 22 +Description: On which [address:]port should dropbear listen? +Extended_description: If port is a range (e.g., 1024-65535), a random + port in that range is chosen. Leaving the question empty is equivalent + to specifying the range of registered port 1024-49151. This is only + used for remote (SSH) unlocking of encrypted disks. + +Template: fripost/dropbear-use-openssh-key +Type: boolean +Default: false +Description: Use the same key for dropbear and OpenSSH? +Extended_description: If False, generate a dedicated key for dropbear. + +Template: fripost/activate-selinux +Type: boolean +Default: true +Description: Install and activate (in enforcing mode) SELinux? +Extended_description: Note that activating SELinux requires a dummy + reboot to label all files. So if you have full-disk encryption, you'll + have to send the password twice to dropbear. + +Template: fripost/keep-media-directory +Type: boolean +Default: false +Description: Keep /media and its kids' entries in the fstab? +Extended_description: /media (and its related entries in the fstab) + can safely be removed on a headless server. + +Template: fripost/sshd-fprs_title +Type: text +Description: Reboot in progress + +Template: fripost/sshd-fprs_text +Type: note +Description: Press 'continue' to reboot on the new system + We are done! After rebooting you should be able to log in into your + new machine: + . + ssh ${USER}@${IPv4} + . + To defeat MiTM-attacks, please ensure (for instance by trying to log in + right now, although it won't be successful before the next reboot) that + the server's public key has the following fingerprint + . + ${SSHFPR_SERVER} + . + To unlock the encrypted disk, you need to send the key to the SSH + daemon living in in the initrd: + . + ssh -p ${PORT} -T root@${IPv4} < /path/to/key + . + An attacker successfully mounting a MiTM-attack could get hold of the + encryption key! It is crucial that you match this (single purpose) + server's fingerprint against + . + ${SSHFPR_INITRD} + . + Key(s) that are granted access to these two servers have the following + fingerprint: + . + ${SSHFPR_AUTHORIZED} + +Template: fripost/sshd-fprs-nodropbear_text +Type: note +Description: Press 'continue' to reboot on the new system + We are done! After rebooting you should be able to log in into your new + machine: + . + ssh ${USER}@${IPv4} + . + To defeat MiTM-attacks, please ensure (for instance by trying to log in + right now, although it won't be successful before the next reboot) that + the server's public key has the following fingerprint + . + ${SSHFPR_SERVER} + . + Key(s) that are granted access to the server have the following + fingerprint: + . + ${SSHFPR_AUTHORIZED} + +Template: fripost/final-notice +Type: boolean +Default: true +Description: Display the final notice before rebooting? +Extended_description: It's good to show SSH fingerprints, because it + defeats MiTM-attacks. diff --git a/src/fripost-postinst-udeb/finish-install.d/07fripost b/src/fripost-postinst-udeb/finish-install.d/07fripost new file mode 100755 index 0000000..55d292b --- /dev/null +++ b/src/fripost-postinst-udeb/finish-install.d/07fripost @@ -0,0 +1,275 @@ +#! /bin/sh +# +# Fripost's postinstall scripts. Should be run after setting up the +# users (06), and ideally before updating the initramfs (10). +# +# Copyright 2013 Guilhem Moulin +# +# Licensed under the GNU GPL version 3 or higher. + +# TODO: blacklist firewire-related modules, to defeat DMA-based attacks. + +set -ue + +. /lib/fripost-partman/base.sh +import=/cdrom/include + +# Update the information below the progress bar. Also, log the argument. +progress() { + log "$1" + db_subst base-installer/progress/fripost WHAT "$1" + db_progress INFO base-installer/progress/fripost +} + + +####################################################################### +# Ensure OpenSSH is installed, and generate a new key, longer than +# default. + +progress "Installing packages" +/bin/apt-install debconf initramfs-tools openssh-server +sshHostKey=/target/etc/ssh/ssh_host_rsa_key +rm -f "${sshHostKey}" "${sshHostKey}.pub" +progress "Generating public/private rsa key pair (OpenSSH)" +/bin/in-target /usr/bin/ssh-keygen -b 4096 -t rsa -N '' \ + -C "${sshHostKey#/target}" -f "${sshHostKey#/target}" + + +####################################################################### +# Put dropbear in the initrd if full disk encryption is desired. + +# Get username of the first user +db_get passwd/username +user="$RET" + +db_get fripost/encrypt +encrypt=$RET +if [ "$encrypt" = true ]; then + # Put dropbear in the initrd + progress "Installing dropbear" + /bin/apt-install dropbear + + cat /var/lib/fripost/initrd-modules >> /target/etc/initramfs-tools/modules + + rm -rf /target/etc/dropbear \ + /target/etc/initramfs-tools/etc/dropbear/dropbear_rsa_host_key + + # The default is not to copy the keys from the OpenSSH server to the + # initrd, because it's trivial for an attacker with physical access + # to the box to uncompress it and get hold of the private keys. + + db_get fripost/dropbear-use-openssh-key + if [ "$RET" = true ]; then + progress "Converting OpenSSH rsa key to dropbear format" + /bin/in-target /usr/lib/dropbear/dropbearconvert openssh dropbear \ + ${sshHostKey#/target} \ + /etc/initramfs-tools/etc/dropbear/dropbear_rsa_host_key + else + progress "Generating public/private rsa key pair (dropbear)" + /bin/in-target /usr/bin/dropbearkey -t rsa -s 4096 \ + -f /etc/initramfs-tools/etc/dropbear/dropbear_rsa_host_key + fi + + progress "Copying authorized_keys to the initrd" + rm -rf /target/etc/initramfs-tools/root/.ssh + mkdir -pm0700 /target/etc/initramfs-tools/root/.ssh + copy_authorized_keys $import/authorized_keys /target/etc/initramfs-tools/root/.ssh/authorized_keys \ + 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty,command="/bin/cat >/lib/cryptsetup/passfifo"' + + # Get the [address:]port to make dropbear listen to + db_get fripost/initrd-ssh-port + port="${RET:-1024-49151}" + min=${port%-*} + max=${port#*-} + if [ $port = "$min-$max" -a $min -le $max ]; then + # Pick a random port in the given range + port=$(/target/usr/bin/od -An -N2 -i /dev/urandom) + port=$(( $port % ($max + 1 - $min) + $min )) + fi + # See dropbear(8) for the list of options. Failure to read a keyfile + # makes dropbear disable the corresponding algorithm (including + # key-based authentication), in our case DSS/DSA. + log "Changing dropbear's options; port $port" + sed -i "s@^\s*/sbin/dropbear\$@& -d '' -sgjk -p $port@" \ + /target/usr/share/initramfs-tools/scripts/init-premount/dropbear + + # Sadly /usr/lib/finish-install.d/10update-initramfs only updates + # the ramdisk if both cryptsetup *and* console-setup are installed. + # (Cf. #694156 and #696773.) So we perform the update manually here. + progress "Generating new initramfs image" + /bin/in-target /usr/sbin/update-initramfs -u -t +fi + + +####################################################################### + +progress "Updating OpenSSH's server configuration" +rm -f /target/etc/ssh/ssh_host_dsa_key /target/etc/ssh/ssh_host_dsa_key.pub +cp /var/lib/fripost/sshd_config /target/etc/ssh/sshd_config + + +####################################################################### +# Install and activate SELinux +# TODO: would be better to have our own policy instead of amending the +# default one. + +db_get fripost/activate-selinux +if [ "$RET" = true ]; then + progress "Installing SELinux" + # Recommended packages include graphical tools... + /bin/in-target /usr/bin/debconf-apt-progress --no-progress -- \ + apt-get -y install --no-install-recommends \ + selinux-basics selinux-policy-default selinux-policy-dev auditd + progress "Activating SELinux" + /bin/in-target /usr/sbin/selinux-activate + + sed -ri 's/^#?\s*(FSCKFIX)=(yes|no)\s*(\s#.*)?$/\1=yes/' \ + /target/etc/default/rcS + + progress "Running update-grub" + grep -q '^GRUB_CMDLINE_LINUX=' /target/etc/default/grub \ + || fatal "Missing definition of 'GRUB_CMDLINE_LINUX' in /etc/default/grub" + GRUB_CMDLINE="console=tty0 security=selinux enforcing=1" + # ^ TODO: we should leave (non SELinux-related) existing + # configuration options + sed -ri "s/^(GRUB_CMDLINE_LINUX)=.*/\1=\"$GRUB_CMDLINE\"/" \ + /target/etc/default/grub + /bin/in-target /usr/sbin/update-grub + + if /bin/in-target /bin/sh -c "dpkg-query -s postfix >/dev/null 2>&1"; then + progress "Running postfix-nochroot" + echo 'SYNC_CHROOT=n' >> /target/etc/default/postfix + /bin/in-target /usr/sbin/postfix-nochroot + fi + # TODO: in a crontab: check-selinux-installation +fi + + +####################################################################### +# Remove unnecessary packages + +# TODO: check for dummy packages / RCs in a weekly crontab. +dpkg_remove=$(mktemp -p /target/tmp) +cat > "$dpkg_remove" <<- EOF + acpi + dictionaries-common + eject + ispell + laptop-detect + nano + tasksel + wamerican + wbritish +EOF +#XXX: the dummy package 'module-init-tools' is a dependency for 'acpid'. +#/usr/sbin/chroot /target /usr/bin/dpkg-query \ +# --show --showformat='${binary:Package} ${binary:Summary}\n' \ +# | sed -rn 's/^(\S+)\s.*\btransitional dummy package\b.*/\1/p' \ +# >> "$dpkg_remove" +/bin/in-target /usr/bin/xargs -a"${dpkg_remove#/target}" \ + debconf-apt-progress --no-progress -- apt-get -y autoremove --purge +rm -f "$dpkg_remove" + + +####################################################################### +# Remove /media and remove its related entries from the fstab. +# It's a bit dirty to remove what we created earlier, but /media/cdrom +# is required in the target, because apt gets some packages on that +# pool. + +db_get fripost/keep-media-directory +if [ "$RET" = false ]; then + log "Removing /media and amending the fstab" + sed -nr 's@^\S+\s+(/target/media/\S+)\s.*@\1@p' /proc/mounts \ + | while read dir; do + log "Unmounting $dir" + /bin/umount "$dir" + done + sed -ri '/^[^#[:blank:]]+\s+\/media\//d' /target/etc/fstab + + for mp in /target/media/*; do + if [ -h "$mp" ]; then + rm -f "$mp" + elif [ -d "$mp" ]; then + rmdir "$mp" + elif [ -e "$mp" ]; then + fatal "Could not remove $mp" + fi + done + rmdir /target/media +fi + + +####################################################################### +# Final notice before rebooting + +if [ "$encrypt" = false ]; then + # There is no dropbear + template=fripost/sshd-fprs-nodropbear_text +else + template=fripost/sshd-fprs_text + db_subst "$template" PORT "$port" + + # Convert the key to OpenSSH format, so we can use ssh-keygen + sshHostKey2=$(mktemp) + /usr/sbin/chroot /target /usr/bin/dropbearkey -y \ + -f /etc/initramfs-tools/etc/dropbear/dropbear_rsa_host_key \ + | grep -E '^(ssh-(dss|rsa)|ecdsa-sha2-nistp(256|384|521))' > "$sshHostKey2" + db_subst "$template" SSHFPR_INITRD "$(/usr/bin/ssh-keygen -lf $sshHostKey2)" + rm -f "$sshHostKey2" +fi +db_subst "$template" USER "$user" +db_subst "$template" IPv4 "$(getIPv4)" +db_subst "$template" SSHFPR_SERVER "$(/usr/bin/ssh-keygen -lf $sshHostKey)" +db_subst "$template" SSHFPR_AUTHORIZED "$(sshfprs $import/authorized_keys ' - ')" + +db_get fripost/final-notice +if [ "$RET" = true ]; then + # Start the SSH daemon to let the user's client to recognize the server + # hence make the weak TOFU model MiTM-immune. + progress "Starting OpenSSH" + /usr/sbin/chroot /target /usr/sbin/service ssh start + + db_settitle fripost/sshd-fprs_title + db_input critical "$template" + db_go + + # Don't show the usual "reboot in progress" notice + db_set finish-install/reboot_in_progress '' + db_fset finish-install/reboot_in_progress seen true + + progress "Stopping OpenSSH" + /usr/sbin/chroot /target /usr/sbin/service ssh stop +fi + + +####################################################################### +# Allow the user to log in via SSH at the next login. + +progress "Fixing permissions on home directories" +db_get adduser/homedir-permission || true +# Workaround for #398802 +if [ "${RET:-true}" = false ]; then + # Fix permissions for existing users + . /target/etc/adduser.conf + sed -rn "s@^([^:]+:){2}([0-9]+):([^:]*:){2}(/[^:]*):.*@\2 \4@p" /target/etc/passwd \ + | while read uuid home; do + [ $uuid -ge $FIRST_UID -a $uuid -le $LAST_UID -a -d /target"$home" ] || continue + log "Fixing permissions on $home" + chmod 0700 /target"$home" + done + + # Fix permissions for future users + sed -ri 's/^(DIR_MODE)=[0-9]+/\1=0700/' /target/etc/adduser.conf +fi + +ugid="$(sed -rn "s@^$user:[^:]*:([0-9]+:[0-9]+):.*@\1@p" /target/etc/passwd)" +home="$(sed -rn "s@^$user:([^:]*:){4}(/[^:]*):.*@\2@p" /target/etc/passwd)" + +# Create ~/.ssh/authorized_keys and fix ownership. We create it *after* +# stopping the SSH daemon to ensure that users can verify the +# fingerprint but cannot log in. +progress "Copying authorized_keys to ~$user/.ssh" +[ -d /target"$home/.ssh" ] || mkdir -m0700 /target"$home/.ssh" +copy_authorized_keys $import/authorized_keys /target"$home/.ssh/authorized_keys" +chown -R "$ugid" /target"$home/.ssh" # Probably 1000:1000, but who knows diff --git a/src/fripost-postinst-udeb/sshd_config b/src/fripost-postinst-udeb/sshd_config new file mode 100644 index 0000000..e81b272 --- /dev/null +++ b/src/fripost-postinst-udeb/sshd_config @@ -0,0 +1,40 @@ +# What ports, IPs and protocols we listen for +Port 22 +# Use these options to restrict which interfaces/protocols sshd will bind to +#ListenAddress :: +#ListenAddress 0.0.0.0 +Protocol 2 +# HostKeys for protocol version 2 +HostKey /etc/ssh/ssh_host_rsa_key +#Privilege Separation is turned on for security +UsePrivilegeSeparation yes + +# Logging +SyslogFacility AUTH +LogLevel INFO + +# Authentication: +LoginGraceTime 120 +PermitRootLogin no +AllowGroups ssh +StrictModes yes + +PubkeyAuthentication yes +#AuthorizedKeysFile %h/.ssh/authorized_keys + +# Change to yes to enable challenge-response passwords (beware issues with +# some PAM modules and threads) +ChallengeResponseAuthentication no + +# Change to no to disable tunnelled clear text passwords +PasswordAuthentication no + +X11Forwarding no +PrintMotd no +PrintLastLog yes +TCPKeepAlive yes + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +Subsystem sftp /usr/lib/openssh/sftp-server diff --git a/src/getname.sh b/src/getname.sh new file mode 100755 index 0000000..f9ed5ec --- /dev/null +++ b/src/getname.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Usage: +# getname.sh +# Returns the list of all known .udeb files. +# getname.sh [name] +# Returns the canonical name of a package name. + +set -ue + +if [ $# -gt 0 ]; then + name="${1#src/}" + case "$1" in + src/*-udeb/*) name="${name%%/*}";; + *-udeb);; + *) name="${name%.udeb}"; name="${name%%_*}-udeb";; + esac + echo $name +else + for dir in src/*-udeb/; do + if [ -r "$dir/debian/files" ]; then + echo src/$(cut -d' ' -f1 "$dir/debian/files") + else + sed -nr 's@^(\S+) \(([0-9.]+)\).*@src/\1_\2_all.udeb@p' "$dir/debian/changelog" + fi + done +fi + diff --git a/virtualenv/virt b/virtualenv/virt new file mode 100755 index 0000000..46c56dc --- /dev/null +++ b/virtualenv/virt @@ -0,0 +1,133 @@ +#!/bin/sh +# +# Define and install a libvirt domain +# +# Copyright 2013 Guilhem Moulin +# +# Licensed under the GNU GPL version 3 or higher. +# + +set -ue +root=$(dirname "$0") + +help() { + cat >&2 <<-EOF + Usage: + $0 install [] + Install the given domain, using the given image (created if + necessary) as a disk. + $0 view + Open a connection to a running domain using the Spice protocol + EOF + exit 1 +} + +get_XML_attribute() { + local domain="$1" object="$2" attribute="$3" + + /usr/bin/virsh dumpxml "$domain" \ + | /usr/bin/xmlstarlet sel -t -m /domain/"$object" -v @"$attribute" +} + +doesExist() { + /usr/bin/virsh -r list --all --name | grep -qxF "$1" +} +isActive() { + /usr/bin/virsh -r list --name | grep -qxF "$1" +} + +install() { + [ $# -eq 1 -o $# -eq 2 ] || help + local name=$1 + local disk=${2:-$root/images/${name}.qcow2} + + local darch version=$(sed -n 's/^VERSION\s*=\s*//p' $root/../preseed/Makefile) + case "${ARCH:=$(/bin/uname -m)}" in + i386|i686) darch=i386;; + x86_64) darch=amd64;; + *) darch=$ARCH;; + esac + local cdrom="$root/../preseed/dist/$darch/debian-$version-$darch-netinst-preseeded.iso" + + if doesExist "$name"; then + echo "Error: Domain $name already exists. If you want to replace the domain, run" >&2 + echo >&2 + isActive "$name" && echo " virsh destroy $name" >&2 + echo " virsh undefine $name" >&2 + exit 1 + fi + if ! [ -f "$cdrom" -a -r "$cdrom" ]; then + cat >&2 <<- EOF + Error: '$cdrom' does not exist or is not readable. + To create the preseeded image, run + + cd $root/../preseed + ARCH=$darch make iso-preseed + EOF + exit 1 + fi + + grep -q '^kvm\s' /proc/modules || echo 'WARN: KVM not available!' >&2 + + if ! [ -r "$disk" -a -r "$disk" ]; then + local size=12G + echo "Creating (sparse) $size disk image '$disk'" >&2 + /usr/bin/qemu-img create -f "${disk##*.}" \ + -o size="$size",preallocation=metadata "$disk" + fi + + # TODO: the bus should be chosen at random among sata,ide,scsi,usb + # TODO: test EFI: http://www.linux-kvm.org/page/OVMF + /usr/bin/virt-install -q \ + --name "$name" \ + --arch "$ARCH" \ + --ram 128 \ + --cdrom "$cdrom" \ + --disk "$disk",cache=writeback,bus=sata \ + --network network=default \ + --graphics spice + + cat >&2 <<- EOF + Domain '$name' has been created. As you can see in + + virsh list + + it should now be up and running. To open a graphical console, run + + $0 view $name + EOF +} + +view(){ + [ $# -eq 1 ] || help + local domain="$1" + + if ! doesExist "$domain"; then + cat >&2 <<- EOF + Error: Domain $domain does not exist. To create it, run + + ./virt install $domain + EOF + exit 1 + elif ! isActive "$domain"; then + echo "Error: Domain $domain is not active" >&2 + exit 1 + fi + + local type=$(get_XML_attribute "$domain" devices/graphics type) + local port=$(get_XML_attribute "$domain" devices/graphics port) || true + + if [ "$type" != spice -o -z "$port" ]; then + echo "Error: Could not find a valid Spice server on domain '$domain'." >&2 + exit 1 + fi + /usr/bin/spicec -h 127.0.0.1 -p $port +} + +command="${1:-}" +shift; + +case "$command" in + install|view) $command "$@";; + *) help +esac -- cgit v1.2.3