diff --git a/.gitignore b/.gitignore index 8ac91c5..367b238 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /osbuild/ /contestops/certs/ +/contestops/backups* /contestops/local.known_hosts diff --git a/contestops/configure-machines.sh b/contestops/configure-machines.sh index 470ec37..c3d8fd6 100755 --- a/contestops/configure-machines.sh +++ b/contestops/configure-machines.sh @@ -3,7 +3,7 @@ set -ex # Disable WiFi. -parallel-ssh -x "-F local.ssh_config" -h hostlist nmcli radio wifi off +# parallel-ssh -x "-F local.ssh_config" -h hostlist nmcli radio wifi off # Create hosts file so we don't need DNS. parallel-scp -x "-F local.ssh_config" -h hostlist ./config-hosts /etc/hosts diff --git a/contestops/readme.md b/contestops/readme.md index a9e5623..14a3f5d 100644 --- a/contestops/readme.md +++ b/contestops/readme.md @@ -2,8 +2,8 @@ Here are instructions and various scripts and files for running contests. -The setup consists of a machine for each contestant, a machine running the grader, and an admin machine. -All these should be connected through a network, preferably wired. +The setup consists of a machine for each contestant, a machine running the grader, and an admin machine (e.g. your personal laptop). +All these should be connected through a network. The grader can be a machine accessible over the internet or in the local network. ## Grader setup @@ -17,7 +17,7 @@ sudo apt install ntpsec ``` Configure the grader to accept client certificates. -The CA certificate (`certs/ca.pem`) is generated as part of the admin setup. +The CA certificate (`certs/ca.pem`) is generated as part of the admin setup below. ## Contestant machine setup @@ -36,15 +36,30 @@ The OS is loaded into RAM during boot, so you can remove the stick once the boot ## Network setup If there is not already an existing network, you need to set it up yourself. -Connect all contestant machines and the admin machine to a network switch with LAN cables. -If you use multiple switches, don't forget to also link the switches together. +You can use either WiFi or wired Ethernet. +WiFi has the advantage that you don't need to bring and install all those Ethernet cables, and avoids the ugly cable mess. -If the grader must be accessed over the internet, you can connect the admin machine to WiFi or USB tethering with a phone. -You can then share the internet with the local network. +**Ethernet:** +Connect all contestant machines and the admin machine to a network switch with Ethernet cables. +If you use multiple switches, don't forget to also link the switches together, such that the entire network forms a tree. -If you have Gnome, go to Network settings, click on the gear on the Ethernet connection, go to IPv4 tab, and select "Shared to other computers". +**WiFi:** +The contestant OS already has a WiFi pre-configured. +The SSID is `contest`, and the password is configured in the `contestant_wifi_password` variable in `os/config/config.toml`. +Configure the access point with this SSID and password, and the machines will connect automatically. -If you have docker installed, this doesn't work yet, because docker blocks routing. +For larger contests (10 or more contestants) it's recommended to use a dedicated device as the Internet router. +SOI has a FRITZ!Box 4040 which can be used for this. +Here, you can easily get the list of IP addresses of contestant machines from the web interface. +It's best to connect the admin machine to the router box over LAN instead of WiFi, such that admin traffic only takes one wireless hop instead of two. + +For smaller contests, you can also use the admin machine as a router. +For Ethernet, if you have Gnome, go to Network settings, click on the gear on the Ethernet connection, go to IPv4 tab, and select "Shared to other computers". +For WiFi, you can set up a WiFi hotspot in the WiFi settings. +However, with a hotspot enabled, Gnome does not let you connect to a WiFi network for Internet access at the same time, even though the hardware would in many cases support it. +Instead, you can connect a phone to WiFi and to your laptop with USB and enable USB tethering on the phone. + +If you have Docker installed, this doesn't work yet, because docker blocks routing. You can fix it by running the following commands. ```bash @@ -81,6 +96,8 @@ sudo apt install golang-cfssl ./create-certs.sh ``` +## Before the contest + Edit `local.ssh_config` and create an entry with hostname and IP address for each contestant machine. You can get the IP address by running `ip addr` in a terminal on the contestant machine. @@ -118,7 +135,16 @@ Assign users to machines. ./assign-user.sh contestant02 binna1 ``` +You may want to test on one machine that the certificate was properly installed and the grader is accessible. +This should only be done after the machines are configured and users assigned, but with a contest lock start time before the current time. +You can then set the start time and apply the contest lock config again. + +```bash +parallel-scp -x "-F local.ssh_config" -h hostlist ./contest-lock.json /etc/contest-lock.json +``` + Start periodic backup of contestant machines. +Backups are stored every 2 minutes in the folder `backups`. ```bash ./backup-create.sh timer @@ -138,6 +164,19 @@ replacing `contestant03` in the commands below with the spare machine hostname. rsync -e "ssh -F local.ssh_config" -av --chown contestant:contestant backups/contestantxx/xxxx/ contestant03:/home/contestant/ ``` +## After the contest + +Stop the periodic backup command with Ctrl+C. +Rename the `backups` folder to e.g. `backups-day1`. + +You can shut down all machines. + +``` +parallel-ssh -x "-F local.ssh_config" -h hostlist poweroff +``` + +Machines should be shut down and booted again from USB stick between contests (e.g. between practice and actual contest) to ensure all data is erased. + ## Contest lock screen The contest lock screen is a gnome extension which can lock the screen and show a countdown until the contest starts. diff --git a/os/build.py b/os/build.py index a072ca8..8bae320 100755 --- a/os/build.py +++ b/os/build.py @@ -138,6 +138,8 @@ def main(): # Add our own configuration on top. run(["cp", "-rT", f"{script_dir}/layers/participant", "config"]) + if args.variant != "training-installer": + run(["cp", "-rT", f"{script_dir}/layers/live", "config"]) run(["cp", "-rT", f"{script_dir}/layers/{args.variant}", "config"]) if args.variant == "training-installer": @@ -172,6 +174,9 @@ def main(): mkdir("config/includes.chroot/root/.ssh") run(["cp", f"{script_dir}/config/contestant_authorized_keys", "config/includes.chroot/root/.ssh/authorized_keys"]) + edit_file("config/includes.chroot/etc/NetworkManager/system-connections/contest.nmconnection", + lambda s: s.replace("@wifi_password@", config["contestant_wifi_password"])) + # Configure boot options. grub_boot_options = '\n'.join( f'menuentry "{option["label"]}" {{\n' diff --git a/os/config-example/config.toml b/os/config-example/config.toml index ce0ff38..13dc93b 100644 --- a/os/config-example/config.toml +++ b/os/config-example/config.toml @@ -4,3 +4,6 @@ install_admin_password = "$y$j9T$h5VhMd4KkdmbxdZD1gO0N/$1hvwZgO8pQw13Xd6jaNXbtkb # Example password: soi contestant_root_password = "$y$j9T$h5VhMd4KkdmbxdZD1gO0N/$1hvwZgO8pQw13Xd6jaNXbtkbqVOC4W/ia/KXOcCGYvB" + +# WiFi passwords must be at least 8 characters +contestant_wifi_password = "12345678" diff --git a/os/layers/contestant/hooks/live/2010-contestant.hook.chroot b/os/layers/contestant/hooks/live/2010-contestant.hook.chroot index 1e82426..e65b6fd 100755 --- a/os/layers/contestant/hooks/live/2010-contestant.hook.chroot +++ b/os/layers/contestant/hooks/live/2010-contestant.hook.chroot @@ -21,10 +21,10 @@ $DISABLE_DESKTOP /usr/share/applications/gnome-bluetooth-panel.desktop $DISABLE_DESKTOP /usr/share/applications/gnome-online-accounts-panel.desktop $DISABLE_DESKTOP /usr/share/applications/gnome-sharing-panel.desktop -# Enable the live system configuration script at boot. -systemctl enable live-config.service - # Disable kexec-tools services. # We want to load kexec manually, and execution of kexec is already done by systemd. systemctl disable kexec-load.service systemctl disable kexec.service + +# Restrict access to the config which contains the WiFi password. +chmod og= /etc/NetworkManager/system-connections/contest.nmconnection diff --git a/os/layers/contestant/includes.chroot/etc/NetworkManager/system-connections/contest.nmconnection b/os/layers/contestant/includes.chroot/etc/NetworkManager/system-connections/contest.nmconnection new file mode 100644 index 0000000..eb3fe88 --- /dev/null +++ b/os/layers/contestant/includes.chroot/etc/NetworkManager/system-connections/contest.nmconnection @@ -0,0 +1,20 @@ +[connection] +id=contest +uuid=b4b09615-f7b9-4777-baa0-7812d58a01dd +type=wifi + +[wifi] +mode=infrastructure +ssid=contest + +[wifi-security] +auth-alg=open +key-mgmt=wpa-psk +psk=@wifi_password@ + +[ipv4] +method=auto + +[ipv6] +addr-gen-mode=default +method=auto diff --git a/os/layers/contestant/includes.chroot/etc/polkit-1/rules.d/50-lockdown.rules b/os/layers/contestant/includes.chroot/etc/polkit-1/rules.d/50-lockdown.rules index 16b8d86..d349da9 100644 --- a/os/layers/contestant/includes.chroot/etc/polkit-1/rules.d/50-lockdown.rules +++ b/os/layers/contestant/includes.chroot/etc/polkit-1/rules.d/50-lockdown.rules @@ -7,7 +7,8 @@ polkit.addRule(function (action, subject) { if ( action.id.indexOf("org.freedesktop.ModemManager1.") === 0 || - action.id.indexOf("org.freedesktop.NetworkManager.") === 0 || + (action.id.indexOf("org.freedesktop.NetworkManager.") === 0 && + action.id !== "org.freedesktop.NetworkManager.wifi.scan") || action.id === "org.freedesktop.login1.hibernate" || action.id === "org.freedesktop.packagekit.system-network-proxy-configure" || action.id.indexOf("org.freedesktop.udisks2.") === 0 diff --git a/os/layers/contestant/includes.chroot/usr/share/gnome-shell/extensions/contest-lock@soi.ch/extension.js b/os/layers/contestant/includes.chroot/usr/share/gnome-shell/extensions/contest-lock@soi.ch/extension.js index d73ab3a..92c3d37 100644 --- a/os/layers/contestant/includes.chroot/usr/share/gnome-shell/extensions/contest-lock@soi.ch/extension.js +++ b/os/layers/contestant/includes.chroot/usr/share/gnome-shell/extensions/contest-lock@soi.ch/extension.js @@ -10,6 +10,7 @@ const { const Background = imports.ui.background; const Layout = imports.ui.layout; const Main = imports.ui.main; +const ScreenShield = imports.ui.screenShield; // half of the time for which a frame is displayed const HALF_FRAME_TIME_MS = 8; @@ -254,6 +255,14 @@ function activate () { return; } + // Monkeypatch disable regular lock screen deactivation. + // It is possible to trigger this function while contest lock is active, + // by pressing Ctrl+Alt+F1. This switches to GDM, which, after the automatic + // login timeout, switches back to our session and emits an Unlock signal. + // If we did not disable it here, this would then partially unlock the screen, + // such that you can interact with the Gnome Shell but not with applications. + ScreenShield.ScreenShield.prototype.deactivate = () => {}; + actor.show(); Main.sessionMode.pushMode('unlock-dialog'); diff --git a/os/layers/live/hooks/live/2005-live.hook.chroot b/os/layers/live/hooks/live/2005-live.hook.chroot new file mode 100644 index 0000000..6e1f34b --- /dev/null +++ b/os/layers/live/hooks/live/2005-live.hook.chroot @@ -0,0 +1,13 @@ +#!/bin/bash + +set -eu + +# Enable the live system configuration script at boot. +systemctl enable live-config.service + +# Disable automatic apt update. +systemctl disable apt-daily.timer +systemctl disable apt-daily-upgrade.timer + +# Disable autostart of Gnome Software background process. +rm /etc/xdg/autostart/org.gnome.Software.desktop diff --git a/os/layers/contestant/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock b/os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock similarity index 100% rename from os/layers/contestant/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock rename to os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock diff --git a/os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disable-search-providers b/os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disable-search-providers new file mode 100644 index 0000000..59e3763 --- /dev/null +++ b/os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disable-search-providers @@ -0,0 +1,4 @@ +# Disable Gnome Software search provider. +# This is needed to stop Gnome Software from always running in the background. +[org/gnome/desktop/search-providers] +disabled = ['org.gnome.Software.desktop'] diff --git a/os/layers/contestant/includes.chroot/etc/dconf/db/local.d/00-disallow-updates b/os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disallow-updates similarity index 100% rename from os/layers/contestant/includes.chroot/etc/dconf/db/local.d/00-disallow-updates rename to os/layers/live/includes.chroot/etc/dconf/db/local.d/00-disallow-updates diff --git a/os/layers/live/includes.chroot/etc/skel/.config/Code/User/settings.json b/os/layers/live/includes.chroot/etc/skel/.config/Code/User/settings.json new file mode 100644 index 0000000..bf438f9 --- /dev/null +++ b/os/layers/live/includes.chroot/etc/skel/.config/Code/User/settings.json @@ -0,0 +1,4 @@ +{ + // Limit cache size to 800 MB to stop it from eating all your RAM. + "C_Cpp.intelliSenseCacheSize": 800 +} diff --git a/os/layers/contestant/includes.chroot/etc/systemd/system/live-config.service b/os/layers/live/includes.chroot/etc/systemd/system/live-config.service similarity index 100% rename from os/layers/contestant/includes.chroot/etc/systemd/system/live-config.service rename to os/layers/live/includes.chroot/etc/systemd/system/live-config.service diff --git a/os/layers/participant/hooks/live/2000-participant.hook.chroot b/os/layers/participant/hooks/live/2000-participant.hook.chroot index ae7268e..a10bb88 100755 --- a/os/layers/participant/hooks/live/2000-participant.hook.chroot +++ b/os/layers/participant/hooks/live/2000-participant.hook.chroot @@ -32,6 +32,10 @@ for ext in *; do fi done popd +# Some extensions contain binaries which must be made executable. +# Because the files are now owned by root, extensions can't do that themselves. +# This just makes all files executable. +chmod --recursive +x /usr/local/lib/vscode-extensions/ # Enable codeblocks template. sed -i 's|// project wizards|RegisterWizard(wizProject, _T("soi"), _T("A SOI task"), _T("Console"));|' /usr/share/codeblocks/templates/wizard/config.script diff --git a/os/layers/participant/package-lists/participant.list.chroot b/os/layers/participant/package-lists/participant.list.chroot index cac11fb..a35a1c5 100644 --- a/os/layers/participant/package-lists/participant.list.chroot +++ b/os/layers/participant/package-lists/participant.list.chroot @@ -26,6 +26,8 @@ gcc g++ gdb ddd valgrind python3 pypy3 evince gnome-terminal konsole xterm byobu make cmake nautilus-extension-gnome-terminal file-roller +# video codecs for grader gifs +libavcodec59 # for drawing on screenshots drawing # documentation diff --git a/os/layers/training-live/hooks/live/2010-training-live.hook.chroot b/os/layers/training-live/hooks/live/2010-training-live.hook.chroot deleted file mode 100644 index 7398a8d..0000000 --- a/os/layers/training-live/hooks/live/2010-training-live.hook.chroot +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -set -eu - -# Enable the live system configuration script at boot. -systemctl enable live-config.service diff --git a/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock b/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock deleted file mode 100644 index e71be0e..0000000 --- a/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disable-screensaver-lock +++ /dev/null @@ -1,3 +0,0 @@ -# Disable lock on blank screen -[org/gnome/desktop/screensaver] -lock-enabled = false diff --git a/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disallow-updates b/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disallow-updates deleted file mode 100644 index 556d13c..0000000 --- a/os/layers/training-live/includes.chroot/etc/dconf/db/local.d/00-disallow-updates +++ /dev/null @@ -1,5 +0,0 @@ -# Disable "Updates available" notifications and auto updates. -# Updates which require reboot are useless on live systems, -# and other updates would be installed on each boot. -[org/gnome/software] -allow-updates = false diff --git a/os/layers/training-live/includes.chroot/etc/systemd/system/live-config.service b/os/layers/training-live/includes.chroot/etc/systemd/system/live-config.service deleted file mode 100644 index a8b5058..0000000 --- a/os/layers/training-live/includes.chroot/etc/systemd/system/live-config.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=custom configuration of live system during boot. -Before=basic.target -After=local-fs.target systemd-tmpfiles-setup.service -DefaultDependencies=no -ConditionKernelCommandLine=boot=live - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/usr/local/bin/live-config - -[Install] -WantedBy=basic.target diff --git a/os/readme.md b/os/readme.md index 2da5b13..9fc0411 100644 --- a/os/readme.md +++ b/os/readme.md @@ -93,9 +93,10 @@ Here is a list of features. - timezone - list of locales - bootloader background image -- `training-live` +- `live` (all live variants) - disable lock on blank screen - disable software update notifications +- `training-live` - automatic login - sudo without password - `training-installer` @@ -111,8 +112,6 @@ Here is a list of features. - `contestant` - disable bluetooth - disable sleep - - disable lock on blank screen - - disable software update notifications - disable some panels in gnome-control-center - disable automatic mounting of storage media - polkit rules which block changing network settings and mounting storage media (it prompts for the root password) @@ -120,6 +119,7 @@ Here is a list of features. - install and configure ssh server - set root password - set `authorized_keys` for root + - contest WiFi connection - automatic login - set browser homepage and bookmarks to https://contest.soi.ch - Gnome Shell extension which displays the user name in the top bar