2025-06-26-13-33-49: Cronjob

This commit is contained in:
lmn-client 2025-06-26 13:33:49 +02:00
commit 29049c8da1
127 changed files with 7089 additions and 0 deletions

View file

@ -0,0 +1,2 @@
---
fvs_remove_discover: true # Remove discover to disable update_notify

View file

@ -0,0 +1,113 @@
// configure plasma defaults
function forEachWidgetInContainmentList(containmentList, callback) {
for (var containmentIndex = 0; containmentIndex < containmentList.length; containmentIndex++) {
var containment = containmentList[containmentIndex];
var widgets = containment.widgets();
for (var widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++) {
var widget = widgets[widgetIndex];
callback(widget, containment);
if (widget.type === "org.kde.plasma.systemtray") {
systemtrayId = widget.readConfig("SystrayContainmentId");
if (systemtrayId) {
forEachWidgetInContainmentList([desktopById(systemtrayId)], callback)
}
}
}
}
}
function forEachWidget(callback) {
forEachWidgetInContainmentList(desktops(), callback);
forEachWidgetInContainmentList(panels(), callback);
}
function forEachWidgetByType(type, callback) {
forEachWidget(function(widget, containment) {
if (widget.type == type) {
callback(widget, containment);
}
});
}
function widgetSetProperty(args) {
if (!(args.widgetType && args.configGroup && args.configKey)) {
return;
}
forEachWidgetByType(args.widgetType, function(widget){
widget.currentConfigGroup = [args.configGroup];
/*
//--- Delete when done debugging
const oldValue = widget.readConfig(args.configKey);
print("" + widget.type + " (id: " + widget.id + "):");
print("\t[" + args.configGroup + "] " + args.configKey + ": " +
oldValue + " => " + args.configValue + "\n");
//--- End Debug
*/
widget.writeConfig(args.configKey, args.configValue);
});
}
// configure task bar starters:
widgetSetProperty({
widgetType: "org.kde.plasma.icontasks",
configGroup: "General",
configKey: "launchers",
configValue: [
"applications:systemsettings.desktop",
"preferred://browser",
"applications:thunderbird.desktop",
"applications:libreoffice-startcenter.desktop",
"preferred://filemanager"
//"applications:org.kde.konsole.desktop",
//"applications:org.kde.discover.desktop"
],
});
// kickoff is the default menu:
/* this does not work (anymore?)
widgetSetProperty({
widgetType: "org.kde.plasma.kickoff",
configGroup: "General",
configKey: "favorites",
configValue: ["applications:libreoffice-startcenter.desktop",],
});
*/
widgetSetProperty({
widgetType: "org.kde.plasma.kickoff",
configGroup: "General",
configKey: "systemFavorites",
configValue: ["reboot", "shutdown", "logout"],
//configValue: ["logout"],
});
// prepare a folder view on the desktop:
/* 20230917 disabled for now
var allDesktops = desktops();
for (var desktopIndex = 0; desktopIndex < allDesktops.length; desktopIndex++) {
var d = allDesktops[desktopIndex];
d.addWidget("org.kde.plasma.folder", 50, 50, 456, 600)
print("Folder app generated!\n")
}
widgetSetProperty({
widgetType: "org.kde.plasma.folder",
configGroup: "General",
configKey: "url",
configValue: "/lmn/media/",
});
widgetSetProperty({
widgetType: "org.kde.plasma.folder",
configGroup: "General",
configKey: "labelMode",
configValue: "0",
});
*/
// /usr/share/plasma/shells/org.kde.plasma.desktop/contents/updates/fvs-config.js

View file

@ -0,0 +1,5 @@
[Desktop Entry]
Type=Directory
Name=FvS
Icon=face-smile-big
#X-KDE-BaseGroup=info

View file

@ -0,0 +1,12 @@
<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">
<Menu>
<Name>Applications</Name>
<Menu>
<Name>FvS</Name>
<Directory>fvs.directory</Directory>
<Include>
<Category>fvs</Category>
</Include>
</Menu>
</Menu>

View file

@ -0,0 +1,6 @@
if [[ "$UID" -gt 10000 ]] && [[ ! -f ~/.local/share/user-places.xbel.lmn ]] ; then
(sleep 30 ; lmn-patch-dolphin.sh) &
fi
if [[ "$UID" -gt 10000 ]] && [[ -f ~/.local/share/user-places.xbel ]] ; then
lmn-fixhome-dolphin.sh
fi

View file

@ -0,0 +1,20 @@
#!/usr/bin/bash
set -eu
file="${1:-$HOME/.local/share/user-places.xbel}"
[[ -e "$file" ]] || exit 0
if id | grep -q teachers; then
exit 0
fi
NETHOME=(/srv/samba/schools/default-school/students/*/"$USER")
[[ -d $NETHOME ]] || exit 0
if FOUND=$(grep -o "/srv/samba/schools/default-school/students/.*/$USER" $file); then
if [[ "$NETHOME" != "$FOUND" ]] ; then
sed -i "s|/srv/samba/schools/default-school/students/.*/$USER|$NETHOME|g" $file
fi
fi

View file

@ -0,0 +1,89 @@
#!/bin/bash
#
# patch 'Tausch' and 'Nextcloud' into dolphin's bookmarks
#
set -eu
file="${1:-$HOME/.local/share/user-places.xbel}"
[[ -e "$file" ]] || exit 0
if grep -q "Tausch\|Nextcloud" "$file" ; then
echo "Your Dolphin seems to already contain 'Tausch' and/or 'Nextcloud'." | tee "$file.lmn"
exit 0
fi
id="$(grep ID "$file" | sed -E "s|^.+ID>([[:digit:]]+)/([[:digit:]]+)</ID.+$|\1:\2|" \
| sort -n -t: -k2 | tail -1 )"
if id | grep -q teachers; then
NETHOME=/srv/samba/schools/default-school/teachers/$USER
else
NETHOME=(/srv/samba/schools/default-school/students/*/"$USER")
fi
[[ -d $NETHOME ]] || exit 0
IDENTITY="${id%%:*}"
NUM0="${id##*:}"
NUM1=$(( NUM0 + 1 ))
NUM2=$(( NUM0 + 2 ))
NUM3=$(( NUM0 + 3 ))
if [[ $HOME =~ ^/home/ ]]; then
HOMEONSERVER="+ <bookmark href=\"file://$NETHOME\">
+ <title>Home@Server</title>
+ <info>
+ <metadata owner=\"http://freedesktop.org\">
+ <bookmark:icon name=\"user-home-symbolic\"/>
+ </metadata>
+ <metadata owner=\"http://www.kde.org\">
+ <ID>$IDENTITY/${NUM1}</ID>
+ <isSystemItem>true</isSystemItem>
+ </metadata>
+ </info>
+ </bookmark>"
else
HOMEONSERVER=$'+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+\n+'
fi
patch="
--- a/$file
+++ b/$file
@@ -98,9 +98,45 @@
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
$HOMEONSERVER
+ <bookmark href=\"file:///srv/samba/schools/default-school/share\">
+ <title>Tausch</title>
+ <info>
+ <metadata owner=\"http://freedesktop.org\">
+ <bookmark:icon name=\"folder-publicshare\"/>
+ </metadata>
+ <metadata owner=\"http://www.kde.org\">
+ <ID>$IDENTITY/${NUM2}</ID>
+ <isSystemItem>true</isSystemItem>
+ </metadata>
+ </info>
+ </bookmark>
+ <bookmark href=\"file:///lmn/media/$USER/nextcloud\">
+ <title>Nextcloud</title>
+ <info>
+ <metadata owner=\"http://freedesktop.org\">
+ <bookmark:icon name=\"folder-cloud\"/>
+ </metadata>
+ <metadata owner=\"http://www.kde.org\">
+ <ID>$IDENTITY/${NUM3}</ID>
+ <isSystemItem>true</isSystemItem>
+ </metadata>
+ </info>
+ </bookmark>
<bookmark href=\"remote:/\">
<title>Network</title>
<info>
<metadata owner=\"http://freedesktop.org\">
<bookmark:icon name=\"folder-network\"/>
"
echo "$patch" | patch -z '.lmn' --fuzz=0 --backup "$file"

View file

@ -0,0 +1,222 @@
#!/bin/bash
sed -e "s|HOME|/${HOME##/srv/samba/schools/default-school/}|g" -e "s|USER|${USER}|g" > ~/.local/share/user-places.xbel <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xbel>
<xbel xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info" xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks" xmlns:kdepriv="http://www.kde.org/kdepriv">
<info>
<metadata owner="http://www.kde.org">
<kde_places_version>4</kde_places_version>
<GroupState-Places-IsHidden>false</GroupState-Places-IsHidden>
<GroupState-Remote-IsHidden>false</GroupState-Remote-IsHidden>
<GroupState-Devices-IsHidden>false</GroupState-Devices-IsHidden>
<GroupState-RemovableDevices-IsHidden>false</GroupState-RemovableDevices-IsHidden>
<GroupState-Tags-IsHidden>false</GroupState-Tags-IsHidden>
<withRecentlyUsed>true</withRecentlyUsed>
<GroupState-RecentlySaved-IsHidden>false</GroupState-RecentlySaved-IsHidden>
<withBaloo>true</withBaloo>
<GroupState-SearchFor-IsHidden>false</GroupState-SearchFor-IsHidden>
</metadata>
</info>
<bookmark href="file:///srv/samba/schools/default-schoolHOME">
<title>Home</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="user-home"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/0</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Schreibtisch">
<title>Desktop</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="user-desktop"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/1</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Dokumente">
<title>Documents</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-documents"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/2</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Downloads">
<title>Downloads</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-downloads"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/3</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Musik">
<title>Music</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-music"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/6</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Bilder">
<title>Pictures</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-pictures"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/7</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-schoolHOME/Videos">
<title>Videos</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-videos"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/8</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///srv/samba/schools/default-school/share">
<title>Tausch</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-publicshare"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/9</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="file:///lmn/media/USER/nextcloud">
<title>Nextcloud</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-cloud"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/10</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="remote:/">
<title>Network</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-network"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/4</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="trash:/">
<title>Trash</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="user-trash"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/5</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="recentlyused:/files">
<title>Recent Files</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="document-open-recent"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/9</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<bookmark href="recentlyused:/locations">
<title>Recent Locations</title>
<info>
<metadata owner="http://freedesktop.org">
<bookmark:icon name="folder-open-recent"/>
</metadata>
<metadata owner="http://www.kde.org">
<ID>1682498425/10</ID>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</bookmark>
<separator>
<info>
<metadata owner="http://www.kde.org">
<UDI>/org/kde/fstab///server/default-school/:/srv/samba/schools/default-school</UDI>
<isSystemItem>true</isSystemItem>
<IsHidden>true</IsHidden>
</metadata>
</info>
</separator>
<separator>
<info>
<metadata owner="http://www.kde.org">
<UDI>/org/kde/fstab///server/default-school/:/lmn/media/USER/home</UDI>
<isSystemItem>true</isSystemItem>
<IsHidden>true</IsHidden>
</metadata>
</info>
</separator>
<separator>
<info>
<metadata owner="http://www.kde.org">
<UDI>/org/kde/fstab///server/sysvol/:/srv/samba/USER/sysvol</UDI>
<isSystemItem>true</isSystemItem>
<IsHidden>true</IsHidden>
</metadata>
</info>
</separator>
<separator>
<info>
<metadata owner="http://www.kde.org">
<UDI>/org/kde/fstab///server/default-school/:/lmn/media/USER/share</UDI>
<isSystemItem>true</isSystemItem>
<IsHidden>true</IsHidden>
</metadata>
</info>
</separator>
<separator>
<info>
<metadata owner="http://www.kde.org">
<UDI>/org/freedesktop/UDisks2/block_devices/sda2</UDI>
<isSystemItem>true</isSystemItem>
</metadata>
</info>
</separator>
</xbel>
EOF

View file

@ -0,0 +1,20 @@
#!/usr/bin/bash
#
# Synchronize local program directory and desktop starters
#
set -eu
if ! nslookup server; then
exit 0
fi
#[[ -d /usr/local/lmn ]] || mkdir -p /usr/local/lmn
#rsync -rlptD --chown=pgmadmin:root --chmod=F755,D755 rsync://server:/local-program/ /usr/local/lmn
RSYNC_COMMAND=$(rsync -ai --delete --exclude=mimeinfo.cache \
--chown=root:root --chmod=F644,D755 "rsync://server:/desktopstarter" \
/usr/local/share/applications/ | sed '/ \.\//d')
if [[ $? -eq 0 ]] && [[ -n "${RSYNC_COMMAND}" ]]; then
echo "${RSYNC_COMMAND}"
update-desktop-database /usr/local/share/applications
fi

View file

@ -0,0 +1,6 @@
[Unit]
Description=Synchronize program data and desktop starters
[Service]
Type=simple
ExecStart=/usr/local/sbin/lmn-sync

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run lmn-sync after boot
After=network-online.target
[Timer]
OnBootSec=0min
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,98 @@
{
"policies": {
"Proxy": {
"Mode": "autoDetect"
},
"OverrideFirstRunPage": "https://www.steinbeisschule-reutlingen.de",
"Homepage": {
"URL": "https://www.steinbeisschule-reutlingen.de",
"Locked": false,
"StartPage": "previous-session"
},
"DisplayBookmarksToolbar": true,
"ManagedBookmarks": [
{
"toplevel_name": "FvS-Reutlingen"
},
{
"url": "https://www.steinbeisschule-reutlingen.de",
"name": "FvS-Homepage"
},
{
"url": "https://idam.steinbeis.schule/realms/fvs/account/#/security/signingin",
"name": "Authentifizierung/Passwort"
},
{
"url": "https://mail.steinbeis.schule",
"name": "FvS-eMail"
},
{
"url": "https://dw.steinbeis.schule",
"name": "FvS-Hilfesystem"
},
{
"url": "https://moodle.steinbeis.schule",
"name": "FvS-Moodle"
},
{
"url": "https://nc.steinbeis.schule",
"name": "FvS-Nextcloud"
},
{
"url": "https://server.pn.steinbeis.schule",
"name": "Schulkonsole"
},
{
"url": "https://peleus.webuntis.com/WebUntis/?school=Ferd.von+Steinbeis#/basic/login",
"name": "FvS-Stundenplan"
},
{
"url": "https://kreis-reutlingen.schulanmeldungen.com/anmeldeformulare/avdual",
"name": "Anmeldung AVdual"
},
{
"url": "https://steinbeisschule-reutlingen.schulanmeldungen.com/",
"name": "Anmeldung FVS"
},
{
"name": "Debian",
"children": [
{
"url": "https://www.debian.org",
"name": "Debian Homepage"
},
{
"url": "https://wiki.debian.org",
"name": "Debian Wiki"
},
{
"name": "Debian LAN/Live",
"children": [
{
"url": "https://salsa.debian.org/andi/debian-lan-ansible",
"name": "Debian LAN Ansible"
},
{
"url": "https://wiki.debian.org/DebianLive",
"name": "Debian Live"
}
]
}
]
}
],
"SearchEngines": {
"Add": [
{
"Name": "Startpage",
"URLTemplate": "https://www.startpage.com/sp/search?query={searchTerms}",
"Method": "GET",
"IconURL": "https://www.startpage.com/sp/cdn/favicons/favicon--default.ico",
"Alias": "sp",
"Description": "Startpage Search Engine"
}
],
"Default": "Startpage"
}
}
}

View file

@ -0,0 +1,7 @@
---
- name: Run update-desktop-database
ansible.builtin.command: update-desktop-database "{{ item }}"
loop:
- /usr/local/share/applications
- /usr/local/share/desktop-directories
- /etc/xdg/menus/applications-merged

View file

@ -0,0 +1,253 @@
---
- name: Preseed wireshark to allow users sniffing packets
ansible.builtin.debconf:
name: wireshark-common
question: wireshark-common/install-setuid
value: 'true'
vtype: boolean
- name: Preseed ttf-mscorefonts-installer
ansible.builtin.debconf:
name: ttf-mscorefonts-installer
question: msttcorefonts/dlurl
value: "{{ mirror_msfonts }}"
vtype: string
when: mirror_msfonts is defined and mirror_msfonts | length > 0
- name: Install desktop EDU packages and some more
ansible.builtin.apt:
name:
- atftp
- audacity
- biber
- calligraplan
- cmake ## for kdevelop
- codelite
- codelite-plugins
- curl
- elpa-color-theme-modern
- elpa-magit
- emacs
- filezilla
- freeplane
- git
- git-cola
- gitg
- gitk
- git-gui
- htop
- jq
- jupyter
- kchmviewer
- kdevelop
- kdevelop-php
- kdevelop-python
- krita
- libasound2-dev
- libdbus-glib-1-2 ## needed for zotero
- libnotify-bin ## needed for pwroff script
- libwayland-dev
- libxcursor-dev
- libxi-dev
- libxinerama-dev
- libxkbcommon-dev
- libxrandr-dev
- links2
- minder
- mosquitto-clients
- neovim
- net-tools
- netcat-openbsd
- nmap
- okular-extra-backends ## needed for CHM files
- pdf-presenter-console
- php-cli
- pipx
- planner
- pulseview
- python3-paho-mqtt
- python3-websockets
- qpdfview
- shellcheck
- sigrok
- sigrok-cli
- texlive-lang-german
- texlive-latex-recommended
- texlive-xetex
- texstudio
- tmux
- tree
- ttf-mscorefonts-installer
- twinkle
- unison-gtk
- w3m
- wireshark
- zulucrypt-gui
autoremove: true
state: latest
environment:
http_proxy: '' # this is needed to avoid ttf-mscorefonts-installer picking up aptcacher
- name: Remove update notifications from plasma-discover
ansible.builtin.apt:
name:
- plasma-discover
autoremove: true
state: absent
when: fvs_remove_discover
- name: Make sure wireshark works for all users after installation and upgrades
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/92wireshark4all
mode: '0644'
content: |
## Modify permissions after installation/upgrade to allow all
## users dumping packages on network interfaces for wireshark
DPkg::Post-Invoke {"/usr/bin/chmod o+x /usr/bin/dumpcap || true"; };
- name: Create firefox policies directory
ansible.builtin.file:
path: /etc/firefox-esr/policies
state: directory
mode: '0755'
- name: Create a symbolic link firefox to firefox-esr
ansible.builtin.file:
src: /etc/firefox-esr
dest: /etc/firefox
state: link
- name: Copy firefox policy
ansible.builtin.copy:
src: policies.json
dest: /etc/firefox-esr/policies/
mode: '0644'
- name: Create chromium policies directory
ansible.builtin.file:
path: /etc/chromium/policies/managed
state: directory
mode: '0755'
- name: Set chromium proxy-policy to auto_detect
ansible.builtin.copy:
dest: /etc/chromium/policies/managed/proxy.json
mode: '0644'
content: |
{
"ProxyMode": "auto_detect"
}
- name: Copy dolphin config scripts
ansible.builtin.copy:
src: "{{ item }}"
dest: /usr/local/bin/
mode: '0755'
loop:
- lmn-reset-dolphin.sh
- lmn-patch-dolphin.sh
- lmn-fixhome-dolphin.sh
- name: Configure KDE dolphin menu
ansible.builtin.copy:
src: lmn-dolphin.sh
dest: /etc/profile.d/
mode: '0644'
- name: Copy fvs-config.js to configure plasma
ansible.builtin.copy:
src: fvs-config.js
dest: /usr/share/plasma/shells/org.kde.plasma.desktop/contents/updates/fvs-config.js
mode: '0644'
- name: Configure some KDE aspects
ansible.builtin.blockinfile:
path: /etc/xdg/kdeglobals
create: true
mode: '0644'
block: |
[KDE]
SingleClick=false
[KDE Action Restrictions][$i]
action/start_new_session=false
#action/switch_user=false
#action/lock_screen=false
- name: Start with empty session by default
ansible.builtin.copy:
dest: /etc/xdg/ksmserverrc
mode: '0644'
content: |
[General]
loginMode=emptySession
# - name: Avoid starting kscreen (confusing autodetection)
# ansible.builtin.copy:
# dest: /etc/xdg/kded5rc
# content: |
# [Module-kscreen]
# autoload=false
#
# - name: Disable automatic lock screen and user specific modifications
# ansible.builtin.copy:
# path: /etc/xdg/kscreenlockerrc
# content: |
# [Daemon][$i]
# Autolock=false
# LockOnResume=false
#
- name: Download libdvdcss from mirror
ansible.builtin.get_url:
url: "{{ mirror_dvdcss }}/libdvdcss.so.2.2.0"
dest: /usr/lib/x86_64-linux-gnu/libdvdcss.so.2.2.0
mode: '0644'
use_proxy: false
when: mirror_dvdcss is defined and mirror_dvdcss | length > 0
- name: Link library so name
ansible.builtin.file:
src: libdvdcss.so.2.2.0
dest: /usr/lib/x86_64-linux-gnu/libdvdcss.so.2
state: link
when: mirror_dvdcss is defined and mirror_dvdcss | length > 0
- name: Patch sddm login screen to show hostname
ansible.builtin.blockinfile:
path: /usr/share/sddm/themes/debian-breeze/Main.qml
marker: // {mark} ANSIBLE MANAGED BLOCK
insertbefore: '^}$'
block: |
Text {
id: hostname
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: 10
anchors.rightMargin: 15
color: "#ffffff"
text: sddm.hostName + " | <{{ ansible_date_time['date'] }}>"
font.pointSize: config.fontSize
}
- name: Set git default-branch to main
ansible.builtin.copy:
dest: /etc/gitconfig
mode: '0644'
content: |
[init]
defaultBranch = main
- name: Adjust mmcblk-device gid to allow users to access SD-cards
ansible.builtin.copy:
dest: /etc/udev/rules.d/80-mmcblk.rules
mode: '0644'
content: |
KERNEL=="mmcblk[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", GROUP="domain users"
KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", GROUP="domain users"
- name: Include sync
ansible.builtin.include_tasks: sync.yml

View file

@ -0,0 +1,58 @@
---
- name: Create directory for local .desktop-Files
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /usr/local/share/applications
- /usr/local/share/desktop-directories
notify: Run update-desktop-database
- name: Copy fvs.directory
ansible.builtin.copy:
src: fvs.directory
dest: /usr/local/share/desktop-directories/
mode: '0644'
notify: Run update-desktop-database
- name: Copy fvs.menu
ansible.builtin.copy:
src: fvs.menu
dest: /etc/xdg/menus/applications-merged/
mode: '0644'
notify: Run update-desktop-database
- name: Copy lmn-sync script
ansible.builtin.copy:
src: lmn-sync
dest: /usr/local/sbin/
mode: '0755'
register: lmn_sync
- name: Run lmn-sync script
ansible.builtin.shell: /usr/local/sbin/lmn-sync
when: lmn_sync.changed # noqa: no-handler
- name: Deploy sudo configurations (lmn-sync for role-teacher)
ansible.builtin.copy:
dest: /etc/sudoers.d/90-lmn-sync
owner: root
group: root
mode: '0700'
content: |
%role-teacher ALL=(root) NOPASSWD: /usr/local/sbin/lmn-sync
- name: Provide lmn-sync service and timer
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
mode: '0644'
loop:
- lmn-sync.service
- lmn-sync.timer
- name: Enable lmn-sync.timer
ansible.builtin.systemd:
name: lmn-sync.timer
enabled: true

View file

@ -0,0 +1,5 @@
- name: Reload sshd
ansible.builtin.systemd:
name: sshd
state: reloaded
when: not run_in_installer|default(false)|bool

View file

@ -0,0 +1,49 @@
- name: Install kerberos packages
ansible.builtin.apt:
name: krb5-user
- name: Kerberize sshd server
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/kerberize.conf
mode: '0644'
content: |
GSSAPIAuthentication yes
notify: "Reload sshd"
- name: Kerberize ssh client, authenticate and delegate credentials
ansible.builtin.copy:
dest: /etc/ssh/ssh_config.d/kerberize.conf
mode: '0644'
content: |
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
- name: Check if firefox is available
ansible.builtin.stat:
path: /etc/firefox-esr/firefox-esr.js
register: firefox
- name: Kerberize firefox for sites in the local domain
ansible.builtin.lineinfile:
dest: /etc/firefox-esr/firefox-esr.js
line: "{{ item }}"
with_items:
- '// kerberize for sites in the local domain:'
- 'pref("network.negotiate-auth.delegation-uris", "{{ kerberize_uris | default(ansible_domain) }}");'
- 'pref("network.negotiate-auth.trusted-uris", "{{ kerberize_uris | default(ansible_domain) }}");'
when: firefox.stat.exists
- name: Ensures /etc/chromium/policies/managed dir exists
ansible.builtin.file:
path: "/etc/chromium/policies/managed"
state: directory
mode: '0755'
- name: Kerberize chromium for sites in the local domain
ansible.builtin.copy:
dest: /etc/chromium/policies/managed/idam.json
mode: '0644'
content: |
{
"AuthServerAllowlist": "{{ kerberize_uris | default(ansible_domain) }}"
}

View file

@ -0,0 +1,2 @@
---
exam_mode: true

View file

@ -0,0 +1,16 @@
#!/usr/bin/bash
# exit if not running as root. Because other user don't have privileges to start/stop firewalld.
[[ "${UID}" -eq "0" ]] || exit 0
if [[ "${PAM_USER}" =~ -exam$ ]]; then
systemctl start firewalld.service
if systemctl is-enabled --quiet libvirtd.service; then
systemctl restart libvirtd.service
fi
elif ! (users | grep -q -- "-exam"); then
systemctl stop firewalld.service
if systemctl is-enabled --quiet libvirtd.service; then
systemctl restart libvirtd.service
fi
fi

16
roles/lmn_exam/files/rmexam Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/bash
#
# rename -exam directories in /home and /lmn/media older than 12h
# remove -exam.* directories in /home and /lmn/media older than 10d
#
set -eu
for dir in /home/ /lmn/media ; do
if [[ -d "${dir}" ]]; then
find "${dir}" -maxdepth 1 -mindepth 1 -name '*-exam' -type d -cmin +720 \
-exec bash -c 'mv "$0" "$0".$( date +%Y%m%d-%H%M --reference="$0" )' {} \;
find "${dir}" -maxdepth 1 -mindepth 1 -name '*-exam.*' -type d -cmin +14400 \
-exec rm -rf {} \;
fi
done

View file

@ -0,0 +1,6 @@
[Unit]
Description=Rename/Remove -exam directories older than 12h/10d
[Service]
Type=simple
ExecStart=/usr/local/sbin/rmexam

View file

@ -0,0 +1,8 @@
[Unit]
Description=Run rmexam after boot
[Timer]
OnBootSec=0min
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,70 @@
---
# Requirement: Install firewalld after installing libvirt
- name: Install firewalld packages
ansible.builtin.apt:
name:
- firewalld
register: result
- name: Stop firewalld-service
ansible.builtin.systemd:
name: firewalld
state: stopped
when: result.changed # noqa: no-handler
- name: Disable firewalld-service
ansible.builtin.systemd:
name: firewalld
enabled: false
- name: Add virbr0 to libvirt zone
ansible.posix.firewalld:
zone: libvirt
interface: virbr0
permanent: true
state: enabled
when: vm_support is defined and vm_support
- name: Permit access to cups from libvirt
ansible.posix.firewalld:
zone: libvirt
port: 631/tcp
permanent: true
state: enabled
when: vm_support is defined and vm_support # and printing is defined and printing
- name: Permit access to usersquid from libvirt
ansible.posix.firewalld:
zone: libvirt
port: 3128/tcp
permanent: true
state: enabled
when: vm_support is defined and vm_support # and localsquid is defined and localsquid
- name: Copy some scripts
ansible.builtin.copy:
src: "{{ item }}"
dest: /usr/local/sbin/
mode: '0755'
loop:
- pam-exec.sh
- rmexam
- name: Enable login script via pam_exec.so
ansible.builtin.lineinfile:
dest: /etc/pam.d/common-session
line: 'session optional pam_exec.so /usr/local/sbin/pam-exec.sh'
- name: Provide rmexam services and timers for some scripts
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
mode: '0644'
loop:
- rmexam.service
- rmexam.timer
- name: Enable rmexam.timer
ansible.builtin.systemd:
name: rmexam.timer
enabled: true

View file

@ -0,0 +1,6 @@
extra_pkgs: []
extra_pkgs1: []
extra_pkgs2: []
extra_pkgs_bpo: []
extra_pkgs_bpo1: []
extra_pkgs_bpo2: []

View file

@ -0,0 +1,34 @@
---
- name: Install extra packages from stable
ansible.builtin.apt:
name: "{{ item }}"
loop:
- "{{ extra_pkgs }}"
- "{{ extra_pkgs1 }}"
- "{{ extra_pkgs2 }}"
- name: Add backports for {{ ansible_distribution_release }}
ansible.builtin.apt_repository:
repo: >
deb http://deb.debian.org/debian/ {{ ansible_distribution_release }}-backports
main non-free-firmware
state: present
update_cache: true
when: extra_pkgs_bpo | length > 0 or extra_pkgs_bpo1 | length > 0 or extra_pkgs_bpo2 | length > 0
- name: Install extra packages from backports
ansible.builtin.apt:
name: "{{ item }}"
state: latest # noqa package-latest
default_release: "{{ ansible_distribution_release }}-backports"
loop:
- "{{ extra_pkgs_bpo }}"
- "{{ extra_pkgs_bpo1 }}"
- "{{ extra_pkgs_bpo2 }}"
when: extra_pkgs_bpo | length > 0 or extra_pkgs_bpo1 | length > 0 or extra_pkgs_bpo2 | length > 0
- name: Timestamp successfull run and send up-to-date report
ansible.builtin.shell:
cmd: date --iso-8601=seconds >> /var/local/ansible-stamps && /usr/local/sbin/reporter
changed_when: false
tags: upgrade

View file

@ -0,0 +1,40 @@
---
kde_desktop_pkg:
- akonadi-backend-sqlite
- arduino
- bluefish
- calligra
- codeblocks
- dia
- filius
- flameshot
- freecad
- fritzing
- ghex
- gimp
- inkscape
- kde-full
- keepassxc
- kicad
- kicad-doc-de
- librecad
- openboard
- qtcreator
- spyder
- sqlite3
- sqlitebrowser
- task-german-desktop
- task-german-kde-desktop
- task-kde-desktop
- thonny
- thunderbird-l10n-de
- vlc
- vym
- webext-privacy-badger
- webext-ublock-origin-chromium
- webext-ublock-origin-firefox
- xdg-desktop-portal-kde
- xdg-desktop-portal-wlr # share screen in browser
- xournalpp
kde_desktop_pkg_bpo: [ ]

View file

@ -0,0 +1,32 @@
#!/usr/bin/bash
#
# Dolphin keeps old paths after modifications.
# Run with '--do-it' to really make the change.
#
set -eu
do="${1:-}"
bmk=".local/share/user-places.xbel"
rt="/srv/samba/schools/default-school/students"
extract() {
local grp="$1"
grp="${grp##*${rt}/}"
grp="${grp%%/*}"
echo $grp
}
for f in $(find $rt/*/*/$bmk) ; do
cor="$(extract $f)"
for l in "$(grep "$rt" "$f")" ; do
fnd="$(extract "$l")"
if [[ "$cor" != "$fnd" ]] ; then
echo "Check ${f##*${rt}/}: '$cor' != '$fnd'."
if [[ "$do" = "--do-it" ]] ; then
sed -i.lmn-fix-path "s|$rt/$fnd|$rt/$cor|g" "$f"
break
fi
fi
done
done

View file

@ -0,0 +1,68 @@
---
- name: Install desktop and educational packages
ansible.builtin.apt:
name: "{{ kde_desktop_pkg }}"
- name: Add backports {{ ansible_distribution_release }}
ansible.builtin.apt_repository:
repo: deb http://deb.debian.org/debian/ {{ ansible_distribution_release }}-backports main non-free-firmware
state: present
update_cache: true
when: kde_desktop_pkg_bpo | length > 0
- name: Install extra packages from backports
ansible.builtin.apt:
name: "{{ kde_desktop_pkg_bpo }}"
autoremove: true
default_release: "{{ ansible_distribution_release }}-backports"
when: kde_desktop_pkg_bpo | length > 0
- name: Create akonadi config dir
ansible.builtin.file:
path: /etc/xdg/akonadi/
state: directory
mode: '0755'
- name: Use sqlite in akonadi
ansible.builtin.blockinfile:
path: /etc/xdg/akonadi/akonadiserverrc
create: true
mode: '0644'
block: |
[%General]
Driver=QSQLITE3
## Akonadi complains if not set:
- name: Add home dirs to apparmor
ansible.builtin.lineinfile:
dest: /etc/apparmor.d/tunables/home.d/ubuntu
line: >-
@{HOMEDIRS}+=/srv/samba/schools/default-school/teachers/
/srv/samba/schools/default-school/students/*/
/srv/samba/schools/default-school/examusers/
- name: Tune SDDM login
ansible.builtin.blockinfile:
path: /etc/sddm.conf
create: true
mode: '0644'
block: |
[Users]
MaximumUid=999
RememberLastUser=false
RememberLastSession=false
- name: Deploy dolphin script
ansible.builtin.copy:
src: lmn-fix-dolphin.sh
dest: /usr/local/bin/
mode: '0755'
- name: Default KDE filepicker
ansible.builtin.lineinfile:
path: /etc/environment.d/90lmn-filepicker.conf
create: true
mode: '0644'
line: GTK_USE_PORTAL=1

View file

@ -0,0 +1,3 @@
---
localhome: false
localhome_logout_missing_serverhome: true

View file

@ -0,0 +1,29 @@
#!/usr/bin/bash
#
# create ~/.unison/SyncHome.prf if not exists
#
set -eu
[[ -f ~/.unison/SyncHome.prf ]] && exit 0
mkdir -p ~/.unison
if id | grep teachers; then
NETHOME="$(find /srv/samba/schools/default-school/teachers/ -maxdepth 1 -type d -name "${USER}")"
else
NETHOME="$(find /srv/samba/schools/default-school/students/ -maxdepth 2 -type d -name "${USER}")"
fi
if [[ ! -f ~/.unison/SyncHome.prf ]]; then
echo "# Unison preferences
label = Sync Home on Server
path = Bilder
path = Dokumente
path = Musik
path = Videos
root = $HOME
root = $NETHOME
dontchmod = true
perms = 0" > ~/.unison/SyncHome.prf
fi

View file

@ -0,0 +1,39 @@
---
- name: Enable pam_mkhomedir.so
ansible.builtin.lineinfile:
dest: /etc/pam.d/common-session
line: "session optional pam_mkhomedir.so umask=0077"
insertbefore: '^session\s*optional\s*pam_mount.so'
- name: Patch sddm login screen to inform about localhome
ansible.builtin.blockinfile:
path: /usr/share/sddm/themes/debian-breeze/Main.qml
marker: // {mark} ANSIBLE MANAGED BLOCK localhome
insertbefore: '^}$'
block: |
Text {
id: localhome
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.leftMargin: 15
color: "#ffffff"
text: "Lokale Anmeldung!\nHome-Verzeichnis liegt nicht im Netz!"
font.pointSize: config.fontSize
}
- name: Copy unison-createconfig scripts
ansible.builtin.copy:
src: lmn-create-unisonconfig.sh
dest: /usr/local/bin/
mode: '0755'
- name: Install auto-logout-script for first login in /etc/profile.d/
ansible.builtin.copy:
dest: /etc/profile.d/lmn-logout.sh
mode: '0755'
content: |
[[ "${UID}" -gt 10000 ]] && ! findmnt "/lmn/media/${USER}/home" > /dev/null && exit 0
{% if localhome_logout_missing_serverhome %}
[[ "${UID}" -gt 10000 ]] && ! findmnt /srv/samba/schools/default-school > /dev/null && exit 0
{% endif %}

View file

@ -0,0 +1,3 @@
---
localproxy: false
localproxy_parent: "firewall.{{ domain }}"

View file

@ -0,0 +1,11 @@
[Unit]
Description=Run squid in usermode using user kerberos ticket
[Service]
Type=simple
ExecStart=/usr/local/bin/startusersquid.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=xdg-desktop-autostart.target

View file

@ -0,0 +1,48 @@
---
- name: Install squid
ansible.builtin.apt:
name:
- squid
- name: Disable squid
ansible.builtin.systemd:
name: squid
enabled: false
state: stopped
- name: Deploy squid user mode configuration
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "/etc/squid/{{ item }}"
mode: '0644'
loop:
- squid-usermode.conf
- squid-usermode-external.conf
- name: Deploy startusersquid script
ansible.builtin.template:
src: startusersquid.sh.j2
dest: /usr/local/bin/startusersquid.sh
mode: '0755'
- name: Provide usersquid service
ansible.builtin.copy:
src: usersquid.service
dest: /etc/systemd/user/usersquid.service
mode: '0644'
- name: Enable usersquid service
ansible.builtin.systemd:
name: usersquid.service
scope: global
enabled: true
- name: Deploy http proxy config
ansible.builtin.copy:
dest: /etc/environment.d/10-lmn-proxy.conf
mode: '0644'
content: |
http_proxy="http://localhost:3128"
https_proxy="http://localhost:3128"
ftp_proxy="http://localhost:3128"
no_proxy="{{ no_proxy }}"

View file

@ -0,0 +1,14 @@
acl local-servers dstdomain .{{ domain }}
cache_peer {{ localproxy_parent }} parent 3128 0 no-query default login=NEGOTIATE auth-no-keytab
never_direct deny local-servers
always_direct allow all
#access_log stdio:/tmp/access.log squid
access_log none
cache_log /dev/null
logfile_rotate 0
pid_filename none
{% if vm_support %}
http_port 192.168.122.1:3128
{% endif %}
http_port 127.0.0.1:3128
http_access allow all

View file

@ -0,0 +1,14 @@
acl local-servers dstdomain .{{ domain }}
cache_peer {{ localproxy_parent }} parent 3128 0 no-query default login=NEGOTIATE auth-no-keytab
never_direct deny local-servers
never_direct allow all
#access_log stdio:/tmp/access.log squid
access_log none
cache_log /dev/null
logfile_rotate 0
pid_filename none
{% if vm_support %}
http_port 192.168.122.1:3128
{% endif %}
http_port 127.0.0.1:3128
http_access allow all

View file

@ -0,0 +1,8 @@
#!/usr/bin/bash
#
if nslookup firewall.{{ domain }}; then
/usr/sbin/squid --foreground -f /etc/squid/squid-usermode.conf
else
/usr/sbin/squid --foreground -f /etc/squid/squid-usermode-external.conf
fi

View file

@ -0,0 +1,4 @@
---
localuser: false
localuser_password: "Muster!"
localuser_secretsalt: "4ANAxPycC3q"

View file

@ -0,0 +1,28 @@
---
- name: Mount tmpfs on /home/{{ localuser }}
ansible.posix.mount:
name: /home/{{ localuser }}
src: tmpfs
fstype: tmpfs
opts: uid=1001,gid=1001,mode=755,size=4G
state: mounted
- name: Add local guest user
ansible.builtin.user:
name: "{{ localuser }}"
comment: "Local Guest User,,,"
shell: /bin/bash
uid: 1001
password_expire_min: 99999
createhome: false
password: "{{ localuser_password | password_hash('sha512',localuser_secretsalt) }}"
- name: Prepare generator for local guest user
ansible.builtin.copy:
dest: /etc/systemd/user-environment-generators/60-guest-user.sh
content: |
#!/usr/bin/bash
set -eu
[[ "$UID" -ne 1001 ]] && exit 0
cp -r -n /etc/skel/.* "$HOME"
mode: "0755"

View file

@ -0,0 +1,9 @@
---
misc_avoid_suspend: true
misc_pwroff: true
misc_pwroff_idle: true
misc_pxe_first: false
misc_reporter: false
misc_reporter_serv: "localhost 1234"
misc_clonescreen: false
misc_clonescreen_mode: "1920x1080@60"

View file

@ -0,0 +1,18 @@
#!/usr/bin/bash
#
# fix boot order: first PXE, then Debian
#
set -eu
cur="$(efibootmgr | grep -Ei 'BootOrder:' | \
sed -E 's/^BootOrder: ([[:xdigit:]]{4}),.+$/\1/')"
pxeip4="$(efibootmgr | grep -Ei "IP.*4" | \
sed -E 's/^Boot([[:xdigit:]]{4}).+$/\1/')"
debian="$(efibootmgr | grep -Ei "debian" | \
sed -E 's/^Boot([[:xdigit:]]{4}).+$/\1/')"
if [[ "$cur" != "$pxeip4" ]] && [[ -n "$pxeip4" ]] && [[ -n "$debian" ]] ; then
efibootmgr -o $pxeip4,$debian
else
echo "Nothing to do."
fi

48
roles/lmn_misc/files/pwroff Executable file
View file

@ -0,0 +1,48 @@
#!/bin/bash
#
# logout idle users and shutdown machine
#
set -eu
action="systemctl poweroff"
uptime=$(cat /proc/uptime | cut -f1 -d.)
maxidle=3600 ## seconds
u=($(loginctl list-users --no-legend | sort -hr | head -1))
una=${u[1]:-''}
uid=${u[0]:-''}
talk2dbus() {
local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
sudo -u $una DISPLAY=$display \
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus "$@"
}
########
## shutdown if nobody is loged in:
if [[ -z "$una" ]] || [[ $uid -lt 1000 ]] ; then
exec $action
fi
# FIXME: find idle time independent of running screensaver
if ! t=$(talk2dbus qdbus org.kde.screensaver /ScreenSaver GetActiveTime) ; then
echo "No graphical logins found."
else
idle=$(( t / 1000 ))
if [[ $idle -gt $maxidle ]] && [[ ! -d "/srv/samba/schools/default-school/teachers/" ]] ; then
talk2dbus notify-send -i system-shutdown -u critical -a 'Important System Information' \
'Please log out, the system will shut down soon!' \
'There has been no activity for too long.'
## shutdown:
#talk2dbus qdbus org.kde.ksmserver /KSMServer logout 1 2 0
## logout:
talk2dbus qdbus org.kde.ksmserver /KSMServer logout 1 0 0 || \
loginctl terminate-user $una
echo "Log-out user $una after being idle for $idle seconds."
else
echo "The user $una has been idle for $idle seconds."
fi
fi
#w -s | grep tty | sed "s/[[:space:]]\+/ /g" | cut -f4 -d ' '

View file

@ -0,0 +1,6 @@
[Unit]
Description=Run pwroff script
[Service]
Type=simple
ExecStart=/usr/local/sbin/pwroff

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run pwroff script every 15 min after 90 min uptime
[Timer]
OnBootSec=90min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.target

33
roles/lmn_misc/files/reporter Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/bash
#
# Send stdout of some commands to monitoring server.
# Collect the reports with 'nc -u -k -l 1234' on 'sendto'.
# Use /bin/nc.openbsd, /bin/nc.traditional seems not to work.
#
set -eu
sendto="collector.steinbeis.schule 1234"
n=0
cmds=(
'uname -a'
'tail -1 /var/local/ansible-stamps'
'ip route list default'
'ip link show | \
sed -nE -e "s/^[2-9]: (\S+): .+/\1/p" -e "s/.+ether ([0-9a-f:]+) .+/\1/p" | \
paste - -'
)
# 'w'
# 'uptime'
# 'ls -d --full-time /home/ansible/.ansible/tmp/'
# 'ip addr show'
# 'apt list --upgradeable -o Apt::Cmd::Disable-Script-Warning=true'
r="$HOSTNAME ------- $(date --rfc-3339=seconds) -------
$(for c in "${cmds[@]}" ; do
n=$(( n + 1 ))
echo -n "$n"
eval "$c" | sed 's/^/\t/'
done | sed "s/^/$HOSTNAME /")
## -------------------------------------------------"
echo "$r" | nc -w 1 -u $sendto

View file

@ -0,0 +1,6 @@
[Unit]
Description=Run reporting script
[Service]
Type=simple
ExecStart=/usr/local/sbin/reporter

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run reporter script every 15 min
[Timer]
OnBootSec=5min
OnUnitActiveSec=15min
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,2 @@
- name: Run update-grub
ansible.builtin.command: update-grub

View file

@ -0,0 +1,217 @@
---
- name: Enable wake-on-lan for all ethernet connections
ansible.builtin.copy:
dest: /etc/NetworkManager/conf.d/wake-on-lan.conf
mode: '0644'
content: |
[connection]
ethernet.wake-on-lan=64
- name: Prepare directory for apt-daily override
ansible.builtin.file:
path: /etc/systemd/system/apt-daily.timer.d/
mode: '0755'
state: directory
- name: Run apt update early to avoid outdated package lists
ansible.builtin.copy:
dest: /etc/systemd/system/apt-daily.timer.d/override.conf
mode: '0644'
content: |
[Timer]
RandomizedDelaySec=30m
# Avoid suspend
- name: Create directory to avoid suspend
ansible.builtin.file:
path: /etc/systemd/sleep.conf.d/
state: directory
mode: '0755'
when: misc_avoid_suspend
- name: Avoid suspending
ansible.builtin.blockinfile:
path: /etc/systemd/sleep.conf.d/nosuspend.conf
create: true
mode: '0644'
block: |
[Sleep]
AllowSuspend=no
AllowHibernation=no
AllowSuspendThenHibernate=no
AllowHybridSleep=no
when: misc_avoid_suspend
# Auto Poweroff
- name: Copy pwroff script
ansible.builtin.copy:
src: pwroff
dest: /usr/local/sbin/
mode: '0755'
- name: Provide services and timers for pwroff
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
mode: '0644'
loop:
- pwroff.service
- pwroff.timer
when: misc_pwroff
- name: Enable pwroff.timer
ansible.builtin.systemd:
name: pwroff.timer
enabled: true
when: misc_pwroff
# Shut down when idle for too long
- name: Shut down when idle for too long
ansible.builtin.copy:
dest: /etc/xdg/powermanagementprofilesrc
mode: '0644'
content: |
[AC][SuspendSession]
idleTime=7200000
suspendType=8
when: misc_pwroff_idle
# Boot splash
- name: Enable boot splash screen
ansible.builtin.replace:
dest: "/etc/default/grub"
regexp: '"quiet"$'
replace: '"quiet splash"'
notify: Run update-grub
# Grub settings
- name: Protect editing grub menu entries
ansible.builtin.blockinfile:
path: /etc/grub.d/40_custom
block: |
set superusers='root'
export superusers
password_pbkdf2 root {{ grub_pwd }}
notify: Run update-grub
when: grub_pwd | bool | default(false)
- name: Allow booting grub menu entries
ansible.builtin.lineinfile:
dest: /etc/grub.d/10_linux
line: CLASS="${CLASS} --unrestricted"
insertafter: '^CLASS=.*'
firstmatch: true
notify: Run update-grub
- name: Disable Grub submenus
ansible.builtin.lineinfile:
dest: /etc/default/grub
line: 'GRUB_DISABLE_SUBMENU=true'
insertafter: '^GRUB_TIMEOUT=.*'
notify: Run update-grub
- name: Grub timeout
ansible.builtin.lineinfile:
dest: /etc/default/grub
regexp: '^(GRUB_TIMEOUT=).*'
line: '\g<1>1'
backrefs: true
notify: Run update-grub
# PXE first boot order
- name: Copy some scripts
ansible.builtin.copy:
src: bootorder.sh
dest: /usr/local/sbin/
mode: '0755'
when: misc_pxe_first
- name: PXE first boot order
ansible.builtin.command: /usr/local/sbin/bootorder.sh
register: cmd_result
changed_when: cmd_result.stdout is not search('Nothing to do.')
when: misc_pxe_first
# Disable Caps Lock
- name: Keyboard compose key
ansible.builtin.lineinfile:
dest: /etc/default/keyboard
regexp: '^(XKBOPTIONS=).*'
line: '\1"compose:caps"'
backrefs: true
# Activate unattended upgrades
- name: Install unattended-upgrades
ansible.builtin.apt:
name:
- unattended-upgrades
- name: Update all packages unattended
ansible.builtin.replace:
path: /etc/apt/apt.conf.d/50unattended-upgrades
regexp: '^//(\s+"origin=.+-updates";)$'
replace: ' \1'
# Install reporter
- name: Copy reporter
ansible.builtin.template:
src: reporter.j2
dest: /usr/local/sbin/reporter
mode: '0755'
- name: Provide services and timers for reporter
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
mode: '0644'
loop:
- reporter.service
- reporter.timer
when: misc_reporter
- name: Enable reporter.timer
ansible.builtin.systemd:
name: reporter.timer
enabled: true
when: misc_reporter
# Prepare CloneScreen on Presenter PCs
- name: Fix primary screen for class room PCs with projector
when: misc_clonescreen
block:
- name: Set primary screen for login
ansible.builtin.blockinfile:
path: /usr/share/sddm/scripts/Xsetup
block: |
xrandr --output {{ dual_screen[0] }} --primary
when: dual_screen is defined
- name: Reset primary screen for login
ansible.builtin.blockinfile:
path: /usr/share/sddm/scripts/Xsetup
state: absent
when: dual_screen is not defined
- name: Deploy fix-screen script
ansible.builtin.template:
src: lmn-fix-screen.j2
dest: /usr/local/bin/lmn-fix-screen
mode: '0755'
- name: Deploy fix-screen autostarter
ansible.builtin.copy:
dest: /etc/xdg/autostart/lmn-fix-screen.desktop
mode: '0644'
content: |
[Desktop Entry]
Name=fix-screen
Exec=lmn-fix-screen
Type=Application
NoDisplay=true

View file

@ -0,0 +1,29 @@
#!/usr/bin/bash
#
# Set the primary screen after login
# Clone screen on all displays
# Set audio-default-sink
#
set -eu
if [[ "$XDG_SESSION_TYPE" = wayland ]] ; then
while ! kscreen-doctor -o; do
sleep 1
done
sleep 2
{% if dual_screen is defined %}
kscreen-doctor output.{{ dual_screen[1] }}.priority.1
{% endif %}
for N in $(kscreen-doctor -j | jq -r .outputs[].name) ; do
kscreen-doctor output.$N.mode.{{ misc_clonescreen_mode }} output.$N.position.0,0 output.$N.scale.$(kscreen-doctor -j | jq .outputs[].scale | sort | head -1);
done
fi
{% if audio_output is defined %}
pactl set-card-profile alsa_card.{{ audio_output[0] }} output:{{ audio_output[1] }}
pactl set-default-sink alsa_output.{{ audio_output[0] }}.{{ audio_output[1] }}
{% else %}
if pactl list cards | grep output:hdmi-stereo: | grep verfügbar:\ ja; then
pactl set-card-profile $(pactl list short cards | grep -m1 pci | head -1 | cut -f2) output:hdmi-stereo
pactl set-default-sink $(pactl list short cards | grep -m1 pci | head -1 | cut -f2 | sed s/card/output/g).output:hdmi-stereo
fi
{% endif %}

View file

@ -0,0 +1,33 @@
#!/usr/bin/bash
#
# Send stdout of some commands to monitoring server.
# Collect the reports with 'nc -u -k -l 1234' on 'sendto'.
# Use /bin/nc.openbsd, /bin/nc.traditional seems not to work.
#
set -eu
sendto="{{ misc_reporter_serv }} 1234"
n=0
cmds=(
'uname -a'
'tail -1 /var/local/ansible-stamps'
'ip route list default'
'ip link show | \
sed -nE -e "s/^[2-9]: (\S+): .+/\1/p" -e "s/.+ether ([0-9a-f:]+) .+/\1/p" | \
paste - -'
)
# 'w'
# 'uptime'
# 'ls -d --full-time /home/ansible/.ansible/tmp/'
# 'ip addr show'
# 'apt list --upgradeable -o Apt::Cmd::Disable-Script-Warning=true'
r="$HOSTNAME ------- $(date --rfc-3339=seconds) -------
$(for c in "${cmds[@]}" ; do
n=$(( n + 1 ))
echo -n "$n"
eval "$c" | sed 's/^/\t/'
done | sed "s/^/$HOSTNAME /")
## -------------------------------------------------"
echo "$r" | nc -w 1 -u $sendto

View file

@ -0,0 +1,3 @@
smb_server: "server"
smb_share: "default-school/"
nfs4: false

View file

@ -0,0 +1,4 @@
if [[ "${UID}" -gt 60000 ]]; then
[[ -L "/lmn/media/${USER}/share" ]] || ln -s .default-school/share "/lmn/media/${USER}/share"
[[ -L "/lmn/media/${USER}/home" ]] || ln -s ".default-school/${HOME##/srv/samba/schools/default-school/}" "/lmn/media/${USER}/home"
fi

View file

@ -0,0 +1,3 @@
if [[ "${UID}" -gt 60000 ]]; then
sudo /usr/local/bin/mounthome.sh &
fi

View file

@ -0,0 +1,112 @@
---
- name: Install needed packages
ansible.builtin.apt:
name:
- libpam-mount
- cifs-utils
- nfs-common
- hxtools
- davfs2
state: latest
- name: Configure pam_mount for Webdav Nextcloud
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (mount Nextcloud) -->"
block: |
<volume
fstype="davfs"
path="{{ web_dav }}"
mountpoint="/lmn/media/%(USER)/nextcloud"
options="username=%(USER),nosuid,nodev,uid=%(USER),gid=%(USERGID),grpid,file_mode=0700,dir_mode=0700,forceuid,forcegid"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
insertafter: "<!-- Volume definitions -->"
when: web_dav is defined and web_dav | length > 0
- name: Configure pam_mount for LMN homes
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (mount LMN home) -->"
block: |
<volume
fstype="cifs"
server="{{ smb_server }}"
path="{{ smb_share }}"
mountpoint="/srv/samba/schools/default-school"
options="sec=krb5i,cruid=%(USERUID),user=%(USER),gid=%(USERGID),file_mode=0700,dir_mode=0700,mfsymlinks,nobrl,actimeo=600{{ cifsopt | default(",cache=loose") }}"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
insertafter: "<!-- Volume definitions -->"
- name: Prepare mount point for homes
ansible.builtin.file:
path: /srv/samba/schools/default-school/
state: directory
mode: '0755'
- name: Prepare persistent user cache base directory
ansible.builtin.file:
path: /var/cache/user/
state: directory
mode: '1777'
- name: Create user-environment-generator directory
ansible.builtin.file:
path: /etc/systemd/user-environment-generators/
state: directory
mode: '0755'
- name: Prepare generator for persistent user cache directory
ansible.builtin.copy:
dest: /etc/systemd/user-environment-generators/50-xdg-cache-home.sh
content: |
#!/usr/bin/bash
set -eu
## local users do not need the extra cache dir:
[[ "$UID" -le 60000 ]] && exit 0
cp -r -n /etc/skel/.* "$HOME"
DIR="/var/cache/user/${UID}/"
[[ -d "$DIR" ]] || mkdir -m 0700 "$DIR"
echo XDG_CACHE_HOME="$DIR"
echo JUPYTER_ALLOW_INSECURE_WRITES=1
mode: "0755"
- name: Clean up all user processes after logout
ansible.builtin.replace:
path: /etc/security/pam_mount.conf.xml
regexp: '^(<logout wait="0" hup="no" term="no" kill="no" />)$'
replace: '<!-- \1 -->\n<logout wait="1000" hup="yes" term="yes" kill="yes" />'
- name: Kill all user processes on logout
ansible.builtin.lineinfile:
path: /etc/systemd/logind.conf
line: KillUserProcesses=yes
insertafter: '#KillUserProcesses=no'
- name: Bind mount /lmn/media with nosuid directory
ansible.posix.mount:
src: /lmn/media
path: /lmn/media
opts: nosuid,bind
state: present
fstype: none
- name: Mount NFSv4 tools directory
ansible.posix.mount:
src: "{{ nfs_server }}:tools"
path: /lmn/tools
opts: rw,_netdev,x-systemd.automount,x-systemd.idle-timeout=10s,timeo=100,soft
state: present
fstype: nfs4
when: nfs_server is defined
- name: Mount NFSv4 home directory
ansible.posix.mount:
src: server:/default-school
path: /srv/samba/schools/default-school
opts: sec=krb5p,_netdev,x-systemd.automount,x-systemd.idle-timeout=60
state: present
fstype: nfs4
when: nfs4

View file

@ -0,0 +1,23 @@
---
- name: Set aptcache
ansible.builtin.copy:
dest: /etc/apt/apt.conf
mode: '0644'
content: >
{{ apt_conf }}
when: apt_conf | bool | default(false)
- name: Set NTP server
ansible.builtin.lineinfile:
path: /etc/systemd/timesyncd.conf
insertafter: '^#NTP='
line: NTP={{ ntp_serv }}
when: ntp_serv | bool | default(false)
- name: Add proposed-updates repository
ansible.builtin.apt_repository:
repo: >
deb http://deb.debian.org/debian/ {{ ansible_distribution_release }}-proposed-updates
main non-free-firmware
state: present
when: "'R202' in group_names"

View file

@ -0,0 +1,2 @@
---
printer_admin_group: ""

View file

@ -0,0 +1,3 @@
%examusers ALL=(root) NOPASSWD: /usr/local/bin/install-printers.sh
%role-student ALL=(root) NOPASSWD: /usr/local/bin/install-printers.sh
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/install-printers.sh

View file

@ -0,0 +1,67 @@
---
- name: Install cups
ansible.builtin.apt:
name:
- cups
- name: Disable cups printer browsing
ansible.builtin.lineinfile:
dest: /etc/cups/cupsd.conf
regexp: '^(Browsing ).*'
line: '\1No'
backrefs: true
- name: Listen on all Interfaces
ansible.builtin.lineinfile:
dest: /etc/cups/cupsd.conf
line: 'Listen *:631'
regexp: '^Listen localhost'
state: present
- name: Allow access from localhost and from VM
ansible.builtin.blockinfile:
dest: /etc/cups/cupsd.conf
block: |
Allow localhost
Allow 192.168.122.0/24
insertafter: "<Location {{ item }}>"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item }}"
state: present
loop:
- "/"
- "/admin"
- name: Allow group role-teacher to manage printers
ansible.builtin.lineinfile:
dest: /etc/cups/cups-files.conf
line: "SystemGroup root lpadmin {{ printer_admin_group }}"
regexp: '^SystemGroup'
state: present
when: printer_admin_group | length > 0
- name: Disable cups-browsed
ansible.builtin.systemd:
name: cups-browsed.service
state: stopped
enabled: false
- name: Install install-printers.sh
ansible.builtin.template:
src: install-printers.sh.j2
dest: /usr/local/bin/install-printers.sh
mode: '0755'
- name: Install lmn-install-printers sudoers
ansible.builtin.copy:
src: 90-lmn-install-printers
dest: /etc/sudoers.d/
mode: '0660'
owner: root
group: root
- name: Run printer script from /etc/profile.d/
ansible.builtin.copy:
dest: /etc/profile.d/lmn-printer.sh
mode: '0644'
content: |
[[ "${UID}" -gt 10000 ]] && (sudo /usr/local/bin/install-printers.sh > /dev/null &)

View file

@ -0,0 +1,50 @@
#!/usr/bin/bash
set -eu
## Exit if first printserver is not reachable
ping -c1 -W1 {{ printservers | first }} || exit 0
printservers="{{ printservers | join(' ') }}"
hostgroup="$(id -Gn "${HOSTNAME^^}$")"
usergroup="$(id -Gn "${SUDO_USER}")"
installedprinters="$(lpstat -v | cut -f 3 -d" " | sed 's/:$//' )"
cat <<EOF
Hostgroups: ${hostgroup}
Usergroups: ${usergroup}
Local print queues:
${installedprinters}
EOF
## Remove all printers not wanted:
for p in $installedprinters ; do
printer_ip=$(lpstat -v "${p}" | sed -nE "s%.*ipp://(.+)/printers.*%\1%p")
if [[ -n $printer_ip ]] && (echo "${printservers}" | grep -w -q "${printer_ip}"); then
echo "Removing print queue '$p'."
lpadmin -x "$p"
fi
done
installedprinters="$(lpstat -v | cut -f 3 -d" " | sed 's/:$//' )"
## Add all printers needed:
for ps in $printservers ; do
echo "Checking print server '$ps' for available printers:"
printers="$(timeout 5 lpstat -h "$ps" -U "${SUDO_USER}" -v | sed -E 's/^.+ (\w+): .+$/\1/')"
echo -e "$printers\n"
for p in $printers; do
if [[ "${hostgroup}" =~ "$p" ]] || [[ "${usergroup}" =~ "$p" ]] ; then
if [[ "$installedprinters" =~ "$p" ]] ; then
echo "Print queue '$p' already available."
else
echo "Adding print queue '$p'."
timeout 10 lpadmin -p "$p" -E -v \
"ipp://$ps/printers/$p" \
-m "driverless:ipp://$ps/printers/$p" || echo "Adding queue '$p' failed."
installedprinters+=" $p"
fi
fi
done
done

View file

@ -0,0 +1,2 @@
---
security_defaultuser_login_disable: true

View file

@ -0,0 +1,11 @@
- name: Reload sshd
ansible.builtin.systemd:
name: sshd
state: reloaded
when: not run_in_installer|default(false)|bool
- name: Restart polkit
ansible.builtin.systemd:
name: polkit
state: restarted
when: not run_in_installer|default(false)|bool

View file

@ -0,0 +1,52 @@
---
- name: Deploy SSH keys
ansible.posix.authorized_key:
user: ansible
key: "{{ item }}"
loop: "{{ keys2deploy }}"
when: keys2deploy is defined
- name: Allow sudo without password for ansible
ansible.builtin.lineinfile:
path: /etc/sudoers.d/95-lmn-ansible
line: 'ansible ALL=(root) NOPASSWD: ALL'
create: true
owner: root
group: root
mode: '0700'
- name: Disable ansible user login
ansible.builtin.user:
name: ansible
password_lock: true
when: security_defaultuser_login_disable
- name: Limit SSH access to user ansible
ansible.builtin.blockinfile:
dest: /etc/ssh/sshd_config.d/local.conf
create: true
mode: '0644'
block: |
PasswordAuthentication no
AllowUsers ansible
notify: Reload sshd
- name: Deploy sudo configurations
ansible.builtin.copy:
dest: /etc/sudoers.d/90-lmn-security
owner: root
group: root
mode: '0700'
content: |
{% for user, programs in sudo_permissions.items() %}
{{ user }} ALL=(root) NOPASSWD: {% for program in programs %}{{ program }}{% if not loop.last %}, {% endif %}{% endfor %}
{% endfor %}
when: sudo_permissions is defined
- name: Deploy polkit configurations
ansible.builtin.template:
src: polkit_rules.j2
dest: /etc/polkit-1/rules.d/lmn-security.rules
mode: '0644'
notify: Restart polkit
when: polkit_rules is defined

View file

@ -0,0 +1,12 @@
// /etc/polkit-1/rules.d/lmn-security.rules
polkit.addRule(function(action, subject) {
{% for group, privlist in polkit_rules.items() %}
if (subject.isInGroup("{{ group }}")){
{% for priv in privlist %}
if (action.id == "{{ priv }}") { return polkit.Result.YES; }
{% endfor %}
}
{% endfor %}
});

View file

@ -0,0 +1,6 @@
- name: Restart sssd
ansible.builtin.service:
name: sssd
state: restarted
enabled: true
listen: "Restart sssd"

View file

@ -0,0 +1,24 @@
---
- name: Install needed packages
ansible.builtin.apt:
name:
- sssd-ad
- sssd-tools
- adcli
- name: Provide user identities from AD
ansible.builtin.template:
src: sssd.conf.j2
dest: /etc/sssd/sssd.conf
mode: '0600'
notify: Restart sssd
## Either one of the variables is defined:
- name: Join the domain
ansible.builtin.shell:
cmd: >
echo "{{ ansible_cmdline.adpw | default('') + adpw.user_input | default('') }}" |
adcli join --stdin-password -U global-admin {{ domain | upper }}
when: >
ansible_cmdline.adpw | default('') | length > 0 or
adpw.user_input | default('') | length > 0

View file

@ -0,0 +1,22 @@
[sssd]
domains = {{ domain }}
config_file_version = 2
implicit_pac_responder = False
[domain/{{ domain }}]
krb5_realm = {{ domain | upper }}
ad_domain = {{ domain }}
id_provider = ad
access_provider = ad
use_fully_qualified_names = False
cache_credentials = True
krb5_store_password_if_offline = True
default_shell = /usr/bin/bash
# default: # ldap_id_mapping = True
ad_gpo_access_control = disabled
ad_gpo_ignore_unreadable = True
ad_maximum_machine_account_password_age = 0
ignore_group_members = True
{% if localhome is defined and localhome %}
override_homedir = /home/%u
{% endif %}

View file

@ -0,0 +1,23 @@
--- /usr/lib/python3/dist-packages/spyder/plugins/editor/widgets/editor.py 2024-06-20 07:16:54.096395325 +0200
+++ /usr/lib/python3/dist-packages/spyder/plugins/editor/widgets/editor.py 2024-06-20 14:39:07.693577124 +0200
@@ -2370,15 +2370,16 @@
else:
# Else, testing if it has been modified elsewhere:
lastm = QFileInfo(finfo.filename).lastModified()
- if to_text_string(lastm.toString()) \
- != to_text_string(finfo.lastmodified.toString()):
+ dt = finfo.lastmodified.msecsTo(lastm)
+ if dt > 1000:
if finfo.editor.document().isModified():
self.msgbox = QMessageBox(
QMessageBox.Question,
self.title,
- _("<b>%s</b> has been modified outside Spyder."
+ _("It looks like <b>%s</b> has been modified "
+ "outside Spyder. The working copy is from %i milliseconds ago."
"<br>Do you want to reload it and lose all "
- "your changes?") % name,
+ "your changes?") % (name, dt),
QMessageBox.Yes | QMessageBox.No,
self)
answer = self.msgbox.exec_()

View file

@ -0,0 +1,72 @@
---
## Temporary fixes and quirks:
- name: Remove disturbing NetworkManager connection
ansible.builtin.file:
path: "/etc/NetworkManager/system-connections/Wired connection 1"
state: absent
when: ansible_interfaces | select('search', '^en[pso].+') | length > 1
- name: Fix 8086:4909 external graphics card
ansible.builtin.replace:
dest: "/etc/default/grub"
regexp: 'GRUB_CMDLINE_LINUX=""$'
replace: 'GRUB_CMDLINE_LINUX="i915.force_probe=4909"'
notify: Run update-grub
when: ansible_board_vendor == "LENOVO" and ansible_board_name == "32CB"
- name: Fix sound on 312A
ansible.builtin.replace:
dest: "/etc/default/grub"
regexp: 'GRUB_CMDLINE_LINUX="snd-intel-dspcfg.dsp_driver=1"$'
replace: 'GRUB_CMDLINE_LINUX=""'
notify: Run update-grub
when: ansible_board_vendor == "LENOVO" and ansible_board_name == "312A"
- name: Fix sound on 312A and 312D
ansible.builtin.apt:
name: firmware-sof-signed
state: latest
when: >
ansible_board_vendor == "LENOVO" and
(ansible_board_name == "312D" or ansible_board_name == "312A")
- name: Install customized CodeBlocks packages
when: "'PCroom' in group_names"
block:
- name: Check for old CodeBlocks
ansible.builtin.command:
cmd: dpkg -l codeblocks
register: codeblocks_version
changed_when: false
- name: Download codeblocks zip archive
ansible.builtin.get_url:
url: "http://livebox.pn.steinbeis.schule/codeblocks/CodeBlocks.zip"
dest: /tmp/CodeBlocks.zip
mode: '0644'
use_proxy: false
register: new_codeblocks
when: codeblocks_version.stdout is not search('svn13544')
- name: Unpack zip archive and install packages manually
ansible.builtin.shell:
cmd: unzip -d /tmp/cb/ CodeBlocks.zip && dpkg -i cb/*.deb
chdir: /tmp/
when: new_codeblocks.changed | default(false)
- name: Work around sddm hang on shutdown
ansible.builtin.lineinfile:
path: /etc/systemd/system.conf
line: DefaultTimeoutStopSec=5s
insertafter: '^#DefaultTimeoutStopSec=.*'
- name: Check if spyder is installed
ansible.builtin.stat:
path: /usr/lib/python3/dist-packages/spyder/plugins/editor/widgets/editor.py
register: spyder
- name: Patch spyder to fix 'file-has-changed' issues on CIFS
ansible.posix.patch:
src: spyder.patch
dest: /usr/lib/python3/dist-packages/spyder/plugins/editor/widgets/editor.py
when: spyder.stat.exists

View file

@ -0,0 +1,170 @@
---
# clean up stuff from obsolete/faulty tasks:
- name: Remove sddm login screen patch with deprecated marker (homeondisk)
ansible.builtin.blockinfile:
path: /usr/share/sddm/themes/debian-breeze/Main.qml
marker: // {mark} ANSIBLE MANAGED BLOCK homeondisk
state: absent
- name: Remove packages we do not need anymore
ansible.builtin.apt:
name:
- cachefilesd
- mosquitto
state: absent
purge: true
- name: Remove virtiofs service
ansible.builtin.file:
path: /etc/systemd/system/virtiofs@.service
state: absent
- name: Fix mount point permissions and owner
ansible.builtin.file:
path: "{{ item }}"
mode: '0755'
owner: root
group: root
loop:
- /srv/samba
- /srv/samba/schools
- name: Remove pam_mount sysvol mount
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (SysVol) -->"
block: |
<volume
fstype="cifs"
server="{{ smb_server }}"
path="sysvol/"
mountpoint="/srv/samba/%(USER)/sysvol"
options="sec=krb5i,cruid=%(USERUID),user=%(USER),gid=1010,file_mode=0770,dir_mode=0770,mfsymlinks"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
state: absent
- name: Remove pam_mount for VM bind mounts
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (bind mount school for VMs) -->"
state: absent
- name: Check if rmlpr.timer is installed
ansible.builtin.stat:
path: /etc/systemd/system/rmlpr.timer
register: rmlpr
- name: Disable rmlpr.timer
ansible.builtin.systemd:
name: rmlpr.timer
enabled: false
when: rmlpr.stat.exists
- name: Check if vmimage-torrent.service is installed
ansible.builtin.stat:
path: /etc/systemd/system/vmimage-torrent.service
register: vmimagetorrent
- name: Disable vmimage-torrent.service
ansible.builtin.systemd:
name: vmimage-torrent.service
enabled: false
when: vmimagetorrent.stat.exists
- name: Remove deprecated files and directories
ansible.builtin.file:
path: "{{ item }}"
state: absent
with_items:
- /etc/linuxmuster-linuxclient7
- /usr/lib/python3/dist-packages/linuxmusterLinuxclient7
- /usr/share/linuxmuster-linuxclient7
- /usr/local/bin/onLogin
- /etc/sudoers.d/90-lmn-sudotools
- /etc/systemd/system/rmlpr.service
- /etc/systemd/system/rmlpr.timer
- /usr/local/bin/sync-vm.sh
- /usr/local/bin/run-vm.sh
- /usr/local/bin/rebase-vm.sh
- /usr/local/bin/create-vm.sh
- /usr/local/bin/upload-vm.sh
- /usr/local/bin/vmimage-torrent
- /etc/systemd/system/vmimage-torrent.service
- /usr/local/bin/linbo-torrenthelper.sh
- /usr/local/bin/link-images.sh
- /usr/local/bin/start-virtiofsd.sh
- /etc/sudoers.d/90-lmn-upload-vm
- /etc/sudoers.d/90-lmn-sync-vm
- /etc/sudoers.d/90-lmn-startvirtiofsd
- /etc/sudoers.d/90-lmn-link-images
- /etc/rsync.secret
- /etc/systemd/network/30-virbr1.netdev
- /etc/systemd/network/30-virbr2.netdev
- /etc/systemd/network/40-ethernet.network
- /etc/systemd/network/40-ethernet-usb.network
- /etc/systemd/network/50-virbr1.network
- /etc/systemd/network/50-virbr2.network
- /etc/systemd/network/60-wlan0-dhcp.network
- /etc/NetworkManager/system-connections/macvlan-vm-macvtap.nmconnection
- /etc/tmpfiles.d/clean-exam.conf
- /etc/polkit-1/rules.d/lmn-networkmanager.rules
- /etc/polkit-1/rules.d/lmn-packagekit.rules
- name: Check if vm_usage_information.txt exists
ansible.builtin.stat:
path: /lmn/vm/vm_usage_information.txt
register: vm_usage_information
- name: Pre-fill vm_usage_information.txt
ansible.builtin.shell:
cmd: |
ls -tr *.qcow2 > vm_usage_information.txt || touchvm_usage_information.txt
chown lmnsynci:lmnsynci vm_usage_information.txt
chdir: /lmn/vm/
when: vm_support and not vm_usage_information.stat.exists
- name: Detect if IPP-Everywhere printers exist
ansible.builtin.shell:
cmd: grep "IPP Everywhere" /etc/cups/printers.conf
register: ipp_everywhere
failed_when: false
changed_when: false
- name: Delete old IPP-Everywhere printers
ansible.builtin.shell:
cmd: |
for p in $(lpstat -p | cut -d" " -f2); do
lpadmin -x "$p"
done
when: not ipp_everywhere.rc
- name: Remove old VM-printerlists
ansible.builtin.shell:
cmd: rm -f /lmn/media/*/.printerlist.csv
- name: Remove Listen on VMBridge
ansible.builtin.lineinfile:
dest: /etc/cups/cupsd.conf
line: 'Listen 192.168.122.1:631'
state: absent
- name: Remove NetworkManager Ansible-Block for non-laptops
ansible.builtin.blockinfile:
path: /etc/NetworkManager/NetworkManager.conf
state: absent
when: "'laptop' not in group_names"
- name: Remove pam-exec from common-auth
ansible.builtin.lineinfile:
dest: /etc/pam.d/common-auth
line: "auth optional pam_exec.so /usr/local/sbin/pam-exec.sh"
state: absent
when: exam_mode
- name: Remove pam-mkhomedir from common-session
ansible.builtin.lineinfile:
dest: /etc/pam.d/common-session
line: "session optional pam_mkhomedir.so umask=0077"
state: absent
when: localhome

View file

@ -0,0 +1,10 @@
---
- name: Remove disturbing NetworkManager connection
ansible.builtin.include_tasks:
file: "{{ taskfile }}"
loop_control:
loop_var: taskfile
loop:
- bookworm.yml
- cleanup.yml
when: ansible_distribution_release == "bookworm"

View file

@ -0,0 +1,4 @@
---
vm_support: false
vm_torrent_serv: "seedbox.{{ domain }}"
vm_uploadseed_pwd: secret = "token:topsecret"

View file

@ -0,0 +1,14 @@
#!/usr/bin/bash
#
# Synchronize desktop starters
#
set -eu
source /etc/lmn/vm.conf
RSYNC_COMMAND=$(rsync -ai --delete --exclude=mimeinfo.cache \
--chown=root:root --chmod=F644,D755 "${DESKTOPSTARTERDIR}" \
/usr/local/share/applications/ | sed '/ \.\//d')
if [[ $? -eq 0 ]] && [[ -n "${RSYNC_COMMAND}" ]]; then
echo "${RSYNC_COMMAND}"
update-desktop-database /usr/local/share/applications
fi

25
roles/lmn_vm/files/lmn-vm Normal file
View file

@ -0,0 +1,25 @@
# vm-sync: Download and synchronize VM-Images and xml-Files
%role-teacher ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
%role-student ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
%examusers ALL=(lmnsynci) NOPASSWD: /usr/local/bin/vm-sync
# vm-aria2: Start/Stop aria2 as systemd-service for VM-Images
lmnsynci ALL=(root) NOPASSWD: /usr/local/bin/vm-aria2
# vm-link-images: Link VM-Images to User-tmp-directory
%examusers ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
%role-student ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-link-images
# vm-virtiofsd: Start Virtiofsd as systemd-service
%examusers ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
%role-student ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-virtiofsd
# desktop-sync:
%examusers ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
%role-student ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
# vm-upload:
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/vm-upload

View file

@ -0,0 +1,42 @@
#!/usr/bin/bash
#
# <umount>/usr/local/sbin/pam-umount.sh %(USER) %(USERUID) %(MNTPT)</umount>'
set -eu
usr="$1"
uid="$2"
mtp="$3"
slce="system-virtiofs.slice"
slp=false
shutdownVMs(){
local VM
for VM in $(sudo -u $usr XDG_RUNTIME_DIR="/run/user/$uid" \
XDG_CONFIG_HOME="/var/tmp/vm/$uid" \
XDG_CACHE_HOME="/var/cache/user/$uid/" \
virsh list --state-running | \
sed -nE "s/.*\s+(\S+)\s+running/\1/p") ; do
sudo -u $usr XDG_RUNTIME_DIR="/run/user/$uid" \
XDG_CONFIG_HOME="/var/tmp/vm/$uid" \
XDG_CACHE_HOME="/var/cache/user/$uid/" \
virsh destroy "$VM" 2>&1 | systemd-cat || true
slp=true
done
}
######################
## This is the first mount we need to get rid of:
if [[ "$mtp" =~ "/lmn/media/$usr/share" ]] && [[ -d "/run/user/$uid" ]] ; then
shutdownVMs
[[ "$slp" = true ]] && sleep 5 # leave some time to write caches …
sudo -u ${usr} killall gvfsd | systemd-cat
sudo -u ${usr} killall dbus-daemon | systemd-cat
systemctl -q is-active "$slce" && systemctl kill "$slce"
# debug to find processes blocking umount:
# lsof >> /var/log/lsof.log
fi
## Just umount:
exec umount "$mtp"

View file

@ -0,0 +1,2 @@
[Service]
Environment=HOME=/tmp/pulse.%u

158
roles/lmn_vm/files/sync-vm.sh Executable file
View file

@ -0,0 +1,158 @@
#!/usr/bin/bash
# Push VM-Disk-Image on server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") [-d] [-a] [-t] [vmnames]"
Images from vmnames-List will be synced from server. Default by torrent.
Using flag -d VMs will be synced by rsync
Using flag -a images from images.list and xml-directory will be synced from server.
Using flag -t all torrents and xml-VM-Definitions will be synced
EOF
}
download_image() {
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.qcow2" \
/lmn/vm/
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.xml" \
/lmn/vm/
rsync -av "rsync://server:/vmimages-download/${VM_NAME}.qcow2.torrent" \
/lmn/vm/
/usr/local/bin/vmimage-torrent restart "${VM_NAME}.qcow2"
}
torrent_image() {
if [[ ! -f "/lmn/vm/${VM_NAME}.qcow2.torrent" ]]; then
echo "No torrent-File found"
exit 1
fi
lockfile="/tmp/sync-vm-${VM_NAME}.lock"
if ! flock -n "$lockfile" echo "try to acquire lock"; then
echo torrent seems to be in process.
echo waiting for completion ...
flock -w 3600 "$lockfile" echo "...completed"
sleep 5
else
(
if ! flock -n 200; then
echo "failed to acquire lock"
echo "Bitte noch einmal starten."
echo "Beliebige Taste zum Beenden."
read -n 1
exit 1
fi
torrent="${VM_NAME}.qcow2.torrent"
session="${torrent//./_}"
if vmimage-torrent status | grep -qw ^"$session"; then
vmimage-torrent stop "${VM_NAME}.qcow2"
fi
cd /lmn/vm
ctorrent -e 0 "${VM_NAME}.qcow2.torrent"
/usr/local/bin/vmimage-torrent restart "${VM_NAME}.qcow2"
if ! flock -u 200; then
echo failed to drop lock
exit 1
fi
) 200>"$lockfile"
fi
}
sync_all_images() {
rsync -av --files-from=/lmn/vm/images.list \
rsync://server:/vmimages-download/ /lmn/vm/
rsync -av rsync://server:/vmimages-download/*.xml \
/lmn/vm/
}
delete_old_qcows() {
cd /lmn/vm
for qcow2 in $(find . -maxdepth 1 -name "*.qcow2" -exec basename {} ';'); do
qcowsize=$(stat -c%s "${qcow2}")
if [[ -f "${qcow2}.size" ]] && [[ "${qcowsize}" != $(<"${qcow2}.size") ]]; then
torrent="${qcow2}.torrent"
session="${torrent//./_}"
if vmimage-torrent status | grep -qw ^"$session"; then
vmimage-torrent stop "${qcow2}"
fi
mv "${qcow2}" /tmp/
fi
done
}
sync_all_torrents() {
rsync -ai rsync://server:/vmimages-download/*.torrent /lmn/vm/
rsync -ai rsync://server:/vmimages-download/*.size /lmn/vm/
delete_old_qcows
rsync -ai rsync://server:/vmimages-download/*.xml /lmn/vm/
RSYNC_COMMAND=$(rsync -ai --delete --exclude=mimeinfo.cache rsync://server:/vmimages-download/desktop/ /usr/local/share/applications/ | sed '/ \.\//d')
if [[ $? -eq 0 ]] && [[ -n "${RSYNC_COMMAND}" ]]; then
echo "${RSYNC_COMMAND}"
update-desktop-database /usr/local/share/applications
fi
}
create_starter() {
if [[ ! -f "/usr/share/applications/VM_${VM_NAME}_starter.desktop" ]]; then
cat << EOF >"/usr/share/applications/VM_${VM_NAME}_starter.desktop"
[Desktop Entry]
Version=1.0
Type=Application
Name=VMstart: ${VM_NAME}
GenericName=VM starter ${VM_NAME}
Comment=Start VM ${VM_NAME}
#TryExec=konsole
Exec=/usr/local/bin/run-vm.sh ${VM_NAME}
Icon=clementine
Categories=VM;Engineering;
MimeType=image/vnd.dxf;
Keywords=design;VM;diagrams;graphics
Terminal=true
EOF
update-desktop-database /usr/share/applications
fi
}
if [[ "$(id -nu)" != "lmnsynci" ]]; then
echo "$(basename "$0") must be run as lmnsynci user"
show_help
exit 1
fi
while getopts ':dat' OPTION; do
case "$OPTION" in
d)
DOWNLOAD=1
;;
a)
sync_all_images
exit 0
;;
t)
sync_all_torrents
exit 0
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -lt 1 ]]; then
show_help
exit 1
fi
for VM_NAME in "$@"; do
if [[ -v "DOWNLOAD" ]]; then
echo "Downloading $VM_NAME"
download_image
else
echo "Torrenting $VM_NAME"
torrent_image
fi
done

View file

@ -0,0 +1,13 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Sync Starters
GenericName=Aktualisiert Info über vorhandene VMs
Comment=Sync VM Image information
#TryExec=konsole
Exec=if sudo /usr/local/bin/desktop-sync; then echo 'sync erfolgreich.\nFenster schließt sich in 3 Sekunden.'; sleep 3; else echo "Fehler - sollte nicht vorkommen."; read; fi
Icon=bittorrent-sync
Categories=fvs;
MimeType=image/vnd.dxf;
Keywords=design;VM;diagrams;graphics
Terminal=true

93
roles/lmn_vm/files/uploadseed Executable file
View file

@ -0,0 +1,93 @@
#!/usr/bin/python3
import os, sys
import subprocess
import xmlrpc.client as xc
import ssl
import argparse
parser = argparse.ArgumentParser(description='Upload a file to the bittorrent seeder.')
parser.add_argument('--server', required=True,
help="the server address and RPC port like 'IPaddress:port'")
parser.add_argument('--dht-port', required=True,
help='the DHT port the RPC server is listening on')
pwgrp = parser.add_mutually_exclusive_group(required=True)
pwgrp.add_argument('--passwd',
help='the RPC secret. Either this or --pwdfile needs to be ' \
'provided')
pwgrp.add_argument('--pwdfile',
help="file containing the RPC secret in the form " \
"'secret = \"token:SECRET\"'. " \
'Either this or --secret needs to be provided')
certgrp = parser.add_mutually_exclusive_group(required=True)
certgrp.add_argument('--no-cert', action='store_true',
help='do not use SSL certificate')
certgrp.add_argument('--cert', help='the certificate to use for verification')
parser.add_argument('FILE', help='the file to upload')
args = parser.parse_args()
rpcseeder = 'https://' + args.server + '/rpc'
dhtentry = args.server.split(':')[0] + ':' + args.dht_port
file2send = args.FILE
torrent = '/tmp/' + os.path.basename(file2send) + '.torrent'
if args.passwd:
secret = 'token:' + args.passwd
else:
exec(open(args.pwdfile).read())
ssl_ctx = ssl.create_default_context()
if args.no_cert:
ssl_ctx.check_hostname = False
ssl_ctx.verify_mode = ssl.CERT_NONE
print("Certificate verification disabled.")
elif args.cert is not None:
ssl_ctx.load_verify_locations(args.cert)
s = xc.ServerProxy(rpcseeder, context = ssl_ctx)
def make_torrent():
if os.path.isfile(torrent):
print("Torrent file", torrent, "exists already, please (re)move it.")
sys.exit(1)
subprocess.run(["/usr/bin/mktorrent", "-l 24", "-v", "-o", torrent, file2send], check=True)
h = subprocess.check_output(["/usr/bin/aria2c", "-S ", torrent])
for line in h.decode().splitlines():
if "Info Hash" in line:
return line.split(': ')[1]
def check_seeds(bthash):
active_seeds = s.aria2.tellActive(secret)
for seed in active_seeds:
f = seed['bittorrent']['info']['name']
gid = seed['gid']
ihash = seed['infoHash']
if f == os.path.basename(file2send):
print(file2send, "is already seeded with GID:", gid)
print("Info Hash is:", ihash)
if bthash == ihash:
print("The torrent file has not changed, exiting.")
return False
else:
print("The torrent file has changed, replacing torrent.")
s.aria2.remove(secret, gid)
return True
print("="*19, " Uploading new torrent with aria2 now. ", "="*19)
return True
def upload_torrent():
s.aria2.addTorrent(secret, xc.Binary(open(torrent, mode='rb').read()))
subprocess.run(["/usr/bin/aria2c",
"--dht-entry-point=" + dhtentry,
"--check-integrity",
"--dir=" + os.path.dirname(file2send),
torrent])
############################
if __name__ == '__main__':
infoHash = make_torrent()
if check_seeds(infoHash):
upload_torrent()
print("Upload finished.")

BIN
roles/lmn_vm/files/virtiofsd Executable file

Binary file not shown.

33
roles/lmn_vm/files/vm-aria2 Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/bash
set -eu
# if less than one arguments supplied, display usage
if [[ $# -ne 2 ]]; then
echo "This script takes as input the name of the VM " >&2
echo "Usage: $0 [start|stop] vm_name" >&2
exit 1
fi
COMMAND="$1"
VM_NAME="$2"
source /etc/lmn/vm.conf
if [[ "${COMMAND}" = "start" ]]; then
systemd-run --unit=aria2-"${VM_NAME}" \
--slice=system-aria2 \
--uid="$(id -u lmnsynci)" \
--gid="$(id -g lmnsynci)" \
--nice=19 \
--working-directory="${VM_SYSDIR}" \
--collect \
--property=Type=exec \
--property=SuccessExitStatus=1 \
aria2c --bt-hash-check-seed=true --check-integrity=true --seed-ratio=0.0 \
--dht-entry-point="${SEEDBOX_HOST}:${SEEDBOX_PORT}" \
--dht-file-path=$DHTDAT \
"${VM_SYSDIR}/${VM_NAME}.qcow2.torrent"
elif [[ "${COMMAND}" = "stop" ]] && systemctl is-active "aria2-${VM_NAME}.service"; then
systemctl stop "aria2-${VM_NAME}.service"
fi

47
roles/lmn_vm/files/vm-create Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/bash
# create 1st level-Clones
set -eu
source /etc/lmn/vm.conf
PERSISTENT=0
while getopts ':p' OPTION; do
case "$OPTION" in
p)
PERSISTENT=1
VM_DIR="${VM_DIR_PERSISTENT}"
;;
esac
done
shift "$((OPTIND -1))"
# if less than two arguments supplied, display usage
if [[ $# -ne 2 ]]; then
echo "This script takes as input the name of the VM to clone" >&2
echo "Usage: $0 vm_name_orig vm_name_clone" >&2
exit 1
fi
VM_NAME=$1
VM_CLONE=$2
# Create User-VM-Dir and link system VM-Images
[[ -d "${VM_DIR}" ]] || mkdir -p "${VM_DIR}"
if [[ "${PERSISTENT}" -eq 1 ]]; then
sudo /usr/local/bin/vm-link-images -p
else
sudo /usr/local/bin/vm-link-images
fi
# change to image-directory
cd "${VM_DIR}"
if [[ ! -f "${VM_NAME}.qcow2" ]]; then
echo "qcow2 File does not exists." >&2
exit 1
fi
qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-${VM_CLONE}.qcow2"
chmod a-w "${VM_NAME}-${VM_CLONE}.qcow2"

View file

@ -0,0 +1,28 @@
#!/usr/bin/bash
# link VM in User-Dir in /tmp or /var/vm
set -eu
source /etc/lmn/vm.conf
# change to image-directory
cd "${VM_SYSDIR}"
while getopts ':p' OPTION; do
case "$OPTION" in
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
esac
done
shift "$((OPTIND -1))"
# link system-VM-Images to User VM Directory
for i in *.qcow2; do
[[ -f "${VM_DIR}/${i}" ]] || ln "${i}" "${VM_DIR}/${i}"
done
# allow lmnsynci to remove old vm images
chgrp lmnsynci "${VM_DIR}"
chmod g+w "${VM_DIR}"

78
roles/lmn_vm/files/vm-netboot Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/bash
#
# Start a netboot VM connected to macvtap device and fraction of mem/cpus
#
set -eu
## Imporant for all virsh libvirt calls:
export XDG_CONFIG_HOME="/var/tmp/vm/${UID}"
menu=(standard "CLI Standard Debian GNU/Linux NFS"
standard-ram "CLI Standard Debian GNU/Linux RAM"
kde-desktop "KDE Plasma Desktop Debian GNU/Linux NFS"
gnome-desktop "Gnome Desktop Debian GNU/Linux NFS")
img=$(dialog --clear --backtitle "Virtual Machine Chooser" \
--title "Choose the Virtual Machine to Start" \
--menu "Start VM:" 12 70 6 "${menu[@]}" 2>&1 >/dev/tty)
## If the menu is canceled, $0 stops here because of set -e
# FIXME: Use first device found for now:
mac="$(ip link | grep -A1 -m1 "macvtap-" | \
sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
tapdev="$(ip link | grep -A1 -m1 "macvtap-" | sed -nE "s%^[1-9]:\s(\S+)@.*%\1%p")"
if [[ $# -eq 0 ]] ; then
mem=$(sed -En "s/^MemAvailable:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
arg=("--memory=$((mem/2048))" "--vcpu=$((cpu/2))")
echo Set options: "${arg[@]}"
else
arg=("$@")
fi
kernel="http://livebox/d-i/n-live/${img%-ram}/live/vmlinuz"
initrd="http://livebox/d-i/n-live/${img%-ram}/live/initrd.img"
kargs=(boot=live components splash locales=de_DE.UTF-8 keyboard-layouts=de
swap=true live-config.timezone=Europe/Berlin)
case "$img" in
standard*)
arg+=(--autoconsole=text)
kargs+=(console=ttyS0)
;;&
*-ram)
kargs+=("fetch=http://10.190.1.2/d-i/n-live/${img%-ram}/live/filesystem.squashfs")
;;
*)
kargs+=(netboot=nfs "nfsroot=10.190.1.2:/srv/nfs/debian-live/${img%-ram}")
;;
esac
type="ethernet,mac=${mac},target.dev=${tapdev},xpath1.set=./target/@managed=no"
n=0
for vm in $(virsh --connect qemu:///session list --all --name) ; do
if virsh domiflist "$vm" | grep -q "$mac" ; then
type="user"
virt-manager &
fi
if [[ "${img}$n" = "$vm" ]] ; then
n=$((n+1))
fi
done
kargs+=(hostname="${img}$n")
## FIXME: use passt, needs more settings for correct DNS/gateway
# type=user,xpath1.create=./backend,xpath2.set=./backend/@type=passt,xpath3.create=./ip,xpath4.set=./ip/@family=ipv4,xpath5.set=./ip/@address=172.16.1.1,xpath6.set=./ip/@prefix=24,xpath7.create=./portForward,xpath8.set=./portForward/@proto=tcp,xpath9.set=./portForward/range/@start=2001,xpath10.set=./portForward/range/@end=2500,xpath11.set=./portForward/range/@to=1
http_proxy='' \
exec virt-install \
--name "${img}$n" \
--osinfo debiantesting \
--nodisks --import --noreboot --transient \
--controller type=scsi,model=virtio-scsi \
--install kernel="$kernel",initrd="$initrd",kernel_args="${kargs[*]}" \
--network "type=$type" "${arg[@]}"
# --filesystem "$HOME",share
# mount -t 9p share /mnt

80
roles/lmn_vm/files/vm-rebase Executable file
View file

@ -0,0 +1,80 @@
#!/usr/bin/bash
# Rebase one level down
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") [-n newname] vmname"
This script takes as input the name of the VM to rebase one level down
-n new name of the rebased image
EOF
}
source /etc/lmn/vm.conf
while getopts ':n:p' OPTION; do
case "$OPTION" in
n)
NEWNAME=$OPTARG
;;
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less or more than one arguments supplied, display usage
if [[ $# -ne 1 ]]; then
show_help
exit 1
fi
# change to Images directory
cd "${VM_DIR}"
VM_NAME="$1"
# check if VM-Diskimage exists
if [[ ! -f "${VM_NAME}.qcow2" ]]; then
echo "File not found ${VM_NAME}.qcow2" >&2
exit 1
fi
# check if new VM-Diskimage exists
if [[ -v NEWNAME ]] && [[ -f "${NEWNAME}.qcow2" ]]; then
echo "New Base already exists: ${NEWNAME}.qcow2" >&2
exit 1
fi
NUMBASES=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep -c image)
NEWBASE=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep image | head -n 3 | tail -n 1 | cut -d' ' -f2)
CURRENTBASE=$(qemu-img info --backing-chain "${VM_NAME}.qcow2" | grep image | head -n 2 | tail -n 1 | cut -d' ' -f2)
if [[ ! "${NUMBASES}" -ge 3 ]]; then
echo "Image must have at least 2 backing-files" >&2
exit 1
fi
# check if base Diskimage exists
if [[ ! -f "${NEWBASE}" ]] || [[ ! -f "${CURRENTBASE}" ]]; then
echo "Backingfiles not found ${CURRENTBASE}, ${NEWBASE}" >&2
exit 1
fi
# rebasing disk image
qemu-img rebase -f qcow2 -b "${NEWBASE}" -F qcow2 "${VM_NAME}.qcow2"
if [[ -v NEWNAME ]]; then
NEWNAME="${NEWNAME}.qcow2"
else
rm -f "${CURRENTBASE}"
NEWNAME="${CURRENTBASE}"
fi
mv "${VM_NAME}.qcow2" "${NEWNAME}"
chmod a-w "${NEWNAME}"

375
roles/lmn_vm/files/vm-run Executable file
View file

@ -0,0 +1,375 @@
#!/usr/bin/bash
# create and run clone
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") [options] vmname"
Create a new clone, start the vm (if not yet running) and run virt-viewer.
Squid-Proxy will be started too.
options:
-n|--new new clone will be created, even if exists
-p|--persistent new clone will be created persistent, so available after reboot too
-s|--system qemu:///system instead of default qemu:///session
--no-viewer start without viewer
--heads n number of displays
--memory sizeMB memory size in MB
--cpu num number of CPUs
--os OS operating system (win10|linux|..)
--data-disk size additional data-disk
--bridge virbrX additional network interface on bridge virbrX
--uid uid set uid on guest
--gid gid set gid on guest
--macvtap additional network interface on device macvtap
--options options additional options for virt-install command
EOF
}
exit_script() {
echo "run-vm.sh ${VM_NAME} terminated by trap!" >> "/tmp/${UID}/exit-run-vm.log"
virsh --connect="${QEMU}" destroy "${VM_NAME}-clone"
trap - SIGHUP SIGINT SIGTERM # clear the trap
kill -- -$$ # Sends SIGTERM to child/sub processes
}
check_images() {
# sync vm-torrent and TPM data
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${VM_NAME}.qcow2.torrent" "${VM_NAME}.permall"
[[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${VM_NAME}.qcow2"
BACKINGARRAY=()
imgfile="${VM_SYSDIR}/${VM_NAME}.qcow2" && [[ -f "${VM_DIR}/${VM_NAME}.qcow2" ]] && imgfile="${VM_DIR}/${VM_NAME}.qcow2"
BACKINGARRAY+=("${imgfile}")
echo "Imgfile=$imgfile"
if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
if [[ ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2.torrent" ]]; then
echo "no base VM disk '${VM_NAME}.qcow2' found and/or ${VM_NAME} not found on server" >&2
exit 1
fi
# sync vm-disk image by torrent
echo "Try to sync VM ${VM_NAME} by torrent"
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${VM_NAME}"
fi
backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
while [[ -n "${backingfile}" ]]; do
echo "Backingfile required: ${backingfile}"
imgfile="${VM_SYSDIR}/${backingfile}" && [[ -f "${VM_DIR}/${backingfile}" ]] && imgfile="${VM_DIR}/${backingfile}"
BACKINGARRAY+=("${imgfile}")
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${backingfile}.torrent"
[[ -f "${VM_SYSDIR}/${backingfile}" ]] && sudo -u lmnsynci /usr/local/bin/vm-sync delete_outdated_image "${backingfile}"
if [[ ! -f "${imgfile}" ]] || ! qemu-img info -U "${imgfile}" | grep "file format: qcow2"; then
# sync vm-disk image by torrent
echo "Try to sync backingfile ${backingfile} by torrent"
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "${backingfile%.qcow2}"
fi
backingfile=$(qemu-img info -U "${imgfile}" | grep "^backing file:" | cut -d ' ' -f 3)
done
echo "VM-Image and required backingfiles available"
echo "Now, let's check the images."
# Check VM-Images in reverse order
for ((i=${#BACKINGARRAY[@]}-1; i>=0; i--))
do
echo "Checking ${BACKINGARRAY[$i]}"
if ! qemu-img check -U "${BACKINGARRAY[$i]}" 2>/dev/null; then
echo "check failed!"
echo "sync ${BACKINGARRAY[$i]} again"
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${BACKINGARRAY[$i]}.torrent"
sudo -u lmnsynci /usr/local/bin/vm-sync get_image "$(basename "${BACKINGARRAY[$i]}" .qcow2)"
fi
done
echo "VM-Image and required backingfiles available and checked"
sudo -u lmnsynci /usr/local/bin/vm-sync update_usage_information ${BACKINGARRAY[*]}
}
create_clone() {
local VM_NAME="$1"
if ! [[ -f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2" ]]; then
echo "qcow2 File does not exists." >&2
exit 1
fi
# Create User-VM-Dir and link system VM-Images
[[ -d "${VM_DIR}" ]] || mkdir -p "${VM_DIR}"
if [[ "${PERSISTENT}" -eq 1 ]]; then
sudo /usr/local/bin/vm-link-images -p
else
sudo /usr/local/bin/vm-link-images
fi
# Create backing file
cd "${VM_DIR}"
qemu-img create -f qcow2 -F qcow2 -b "${VM_NAME}.qcow2" "${VM_NAME}-clone.qcow2"
if [[ -f "${VM_SYSDIR}/${VM_NAME}.permall" ]]; then
# Copy tpm file
if [[ ! -f "${VM_NAME}.permall" ]]; then
echo "copy tpm-file"
cp "${VM_SYSDIR}/${VM_NAME}.permall" .
fi
# create tpm-clone file
echo "create tpm-clone-file"
cp "${VM_NAME}.permall" "${VM_NAME}-clone.permall"
fi
}
create_printerlist() {
## Prepare .printerlist.csv
mkdir -p "${VM_MEDIADIR}"
chgrp "$(id -g)" "${VM_MEDIADIR}"
echo "Name;IppURL" > "${VM_MEDIADIR}/.printerlist.csv"
for p in $(lpstat -v | cut -f 3 -d" " | sed 's/:$//'); do
echo "$p;ipp://192.168.122.1/printers/$p" >> "${VM_MEDIADIR}/.printerlist.csv"
done
}
create_mountlist() {
if id | grep -q teachers; then
NETHOME=/srv/samba/schools/default-school/teachers/$USER
else
NETHOME=(/srv/samba/schools/default-school/students/*/"$USER")
fi
NETHOME="${NETHOME#/srv/samba/schools}"
cat << EOF > "/lmn/media/${USER}/.mounts.csv"
Drive;Remotepath
H;\\\\10.190.1.1${NETHOME//\//\\}
T;\\\\10.190.1.1\default-school\share
EOF
echo "${USER}" > "/lmn/media/${USER}/.user"
}
start_virtiofsd() {
# BEGIN temporary fix, while linux-starter are not migrated to --uid and --gid
if [[ "$LIBVIRTOSINFO" =~ debian.* ]]; then
[[ "$GUEST_UID" == 0 ]] && GUEST_UID=1010
[[ "$GUEST_GID" == 0 ]] && GUEST_GID=1010
fi
# END temporary fix
socket="/run/user/$(id -u $USER)/virtiofs-${VM_NAME}.sock"
systemd-run --user /usr/local/bin/virtiofsd --uid-map=:${GUEST_UID}:${UID}:1: --gid-map=:${GUEST_GID}:$(id -g):1: \
--socket-path "$socket" --shared-dir "/lmn/media/${USER}" --syslog
}
ask_really_persistent() {
cat << EOF >&2
!!!!!!!!!!!!!!! Wichtig !!!!!!!!!!!!!!
Auf dem Computer existiert noch keine persistente VM mit dem Namen ${VM_NAME}.
Das Anlegen persistenter Maschinen sollte nur auf Computern geschehen,
die dem jeweiligen Benutzer zugeordnet sind.
In Klassenzimmern oder Computerräumen ist das Verwenden persistenter
Maschinen normalerweise nicht sinnvoll und sprengt die verfügbaren
Festplattenkapazität.
EOF
read -rp "Ist die Installation einer persistenten VM wirklich gewünscht? ja/nein " answer
if [[ "${answer,,}" == "ja" ]]; then
VM_DIR="${VM_DIR_PERSISTENT}"
echo "Die VM ${VM_NAME} wird persistent auf der Festplatte angelegt!"
sleep 5
else
PERSISTENT=0;
VM_DIR="${VM_DIR_CONF}"
echo "Die VM ${VM_NAME} wird nicht persistent gestartet!"
sleep 5
fi
}
QEMU='qemu:///session'
NEWCLONE=0
PERSISTENT=0
LIBVIRTOSINFO="win10"
LIBVIRTOPTS=""
NO_VIEWER=0
GUEST_UID=0
GUEST_GID=0
source /etc/lmn/vm.conf
VM_DIR_CONF="${VM_DIR}"
TEMP=$(getopt -o no:ps --long new,no-viewer,options:,persistent,system,memory:,data-disk:,heads:,cpu:,bridge:,macvtap,os:,uid:,gid:,help -n $0 -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"
while true; do
case "$1" in
-p | --persistent )
PERSISTENT=1;
VM_DIR="${VM_DIR_PERSISTENT}"
shift
;;
-n | --new )
NEWCLONE=1
shift
;;
-s | --system )
QEMU='qemu:///system'
shift
;;
-o | --options )
LIBVIRTOPTS="${LIBVIRTOPTS} $2"
shift 2
;;
--no-viewer )
NO_VIEWER=1
shift
;;
--data-disk )
LIBVIRTOPTS="${LIBVIRTOPTS} --disk ${VM_DIR}/data.qcow2,size=$2,sparse=yes"
shift 2
;;
--heads )
for i in $(seq $2)
do
LIBVIRTOPTS="${LIBVIRTOPTS} --video model.heads=$i"
done
shift 2
;;
--memory )
mem_avail=$(sed -En "s/^MemAvailable:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
mem_avail=$((mem_avail / 1024 - 2048))
if (( $2 < mem_avail )); then
LIBVIRTOPTS="${LIBVIRTOPTS} --memory $2"
else
LIBVIRTOPTS="${LIBVIRTOPTS} --memory ${mem_avail}"
fi
shift 2
;;
--cpu )
#cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
cpu=$(lscpu | grep "^CPU(s):" | sed 's/.* //g')
if (( $2 < cpu )); then
LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu $2"
else
LIBVIRTOPTS="${LIBVIRTOPTS} --vcpu ${cpu}"
fi
shift 2
;;
--bridge )
if ip link | grep $2; then
LIBVIRTOPTS="${LIBVIRTOPTS} --network=bridge=$2,model.type=virtio"
fi
shift 2
;;
--macvtap )
for interface in $(ip link | sed -En 's/.*(macvtap-.*)@.*/\1/p'); do
mac="$(ip link | grep -A1 "${interface}" | \
sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
type="ethernet,mac=${mac},target.dev=${interface},xpath1.set=./target/@managed=no,model.type=virtio"
LIBVIRTOPTS="${LIBVIRTOPTS} --network type=$type"
done
LIBVIRTOPTS="${LIBVIRTOPTS} --check mac_in_use=off"
shift
;;
--os )
LIBVIRTOSINFO=$2
shift 2
;;
--uid )
GUEST_UID=$2
shift 2
;;
--gid )
GUEST_GID=$2
shift 2
;;
--help )
show_help
exit 1
;;
-- ) shift; break ;;
* ) break ;;
esac
done
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]] ; then
show_help
exit 1
fi
VM_NAME=$1
systemctl --user restart usersquid.service &
# check, if persistent VM is really wanted
if [[ "${PERSISTENT}" == 1 ]] && [[ ! -f "${VM_DIR_PERSISTENT}/${VM_NAME}.qcow2" ]]; then
ask_really_persistent
fi
# because virsh has problems with long pathnames, using diffent configdir
export XDG_CONFIG_HOME="/var/tmp/vm/${UID}"
if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
echo "VM not yet running."
# only when school-network is reachable
if nslookup "${SEEDBOX_HOST}"; then
check_images
fi
if [[ "${NEWCLONE}" = 1 ]] || [[ ! -f "${VM_DIR}/${VM_NAME}-clone.qcow2" ]]; then
create_clone "${VM_NAME}"
fi
# delete the old vm
virsh --connect=qemu:///session undefine --nvram "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist"
#trap exit_script SIGHUP SIGINT SIGTERM
create_printerlist
create_mountlist
# start virtiofsd-service
[[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd
uuid=$(openssl rand -hex 16)
uuid="${uuid:0:8}-${uuid:8:4}-${uuid:12:4}-${uuid:16:4}-${uuid:20:12}"
if [[ -f "${VM_DIR}/${VM_NAME}-clone.permall" ]]; then
mkdir -p "/var/tmp/vm/${UID}/.config/libvirt/qemu/swtpm/${uuid}/tpm2/"
ln "${VM_DIR}/${VM_NAME}-clone.permall" "/var/tmp/vm/${UID}/.config/libvirt/qemu/swtpm/${uuid}/tpm2/tpm2-00.permall"
LIBVIRTOPTS="${LIBVIRTOPTS} --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb "
fi
# finally, create the new vm
virt-install \
--uuid="${uuid}" \
--osinfo "${LIBVIRTOSINFO}" \
--name "${VM_NAME}-clone" \
--import \
--clock hpet_present=yes,hypervclock_present=yes \
--features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
--memorybacking source.type=memfd,access.mode=shared \
--disk "${VM_DIR}/${VM_NAME}-clone.qcow2",driver.discard=unmap,target.bus=scsi,cache=writeback \
--network=bridge=virbr0,model.type=virtio \
--filesystem driver.type=virtiofs,accessmode=passthrough,target.dir=virtiofs,xpath1.set=./source/@socket="/run/user/${UID}/virtiofs-${VM_NAME}.sock" \
--controller type=scsi,model=virtio-scsi \
--check path_in_use=off \
--connect="${QEMU}" \
--noautoconsole \
--redirdev usb,type=spicevmc \
--redirdev usb,type=spicevmc \
--redirdev usb,type=spicevmc \
--redirdev usb,type=spicevmc \
--redirdev usb,type=spicevmc \
${LIBVIRTOPTS}
# --dry-run \
# --print-xml \
# > /tmp/vm.xml
# --features hyperv.synic.state=on,xpath1.set=./hyperv/vpindex/@state=on,xpath2.set=./hyperv/stimer/@state=on \
# --network type=ethernet,target.dev=vm-macvtap,xpath1.set=./target/@managed=no \
# virsh --connect="${QEMU}" start "${VM_NAME}-clone"
fi
if [[ $NO_VIEWER == 0 ]] ; then
echo "starting viewer"
trap exit_script SIGHUP SIGINT SIGTERM
virt-viewer --connect="${QEMU}" --full-screen "${VM_NAME}-clone"
fi

178
roles/lmn_vm/files/vm-sync Executable file
View file

@ -0,0 +1,178 @@
#!/usr/bin/bash
# Push/Pull VM-Disk-Image and Infos from server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") command [args]"
command:
push_file
get_file
get_image
delete_outdated_image
update_usage_information
EOF
}
get_torrent() {
if [[ ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2.torrent" ]]; then
echo "No torrent-File found"
exit 1
fi
cd "${VM_SYSDIR}"
echo Size needed: $(get_image_size "${VM_NAME}.qcow2.torrent") " Bytes (VM-Image) + 5GB (spare)"
echo Size available: $(df --block-size=1 --output=avail "${VM_SYSDIR}" | sed 1d)
while [[ $(( $(get_image_size "${VM_NAME}.qcow2.torrent") + 15000000000 )) -gt $(df --block-size=1 --output=avail "${VM_SYSDIR}" | sed 1d) ]]; do
echo "Not enough space to get ${VM_NAME}."
FILENAME="$(head -1 vm_usage_information.txt)"
if [[ -z $FILENAME ]]; then
echo "No more old VM-files to delete. Unable to get ${VM_NAME}. Please contact system administrator."
exit 1
fi
echo "Deleting $FILENAME"
sudo vm-aria2 stop "$(basename "${FILENAME}" .qcow2)"
rm -f "${FILENAME}"
[[ -f "${VM_DIR}/${FILENAME}" ]] && rm -f "${VM_DIR}/${FILENAME}"
sed -i -e 1d vm_usage_information.txt
done
lockfile="/tmp/sync-vm-${VM_NAME}.lock"
if ! flock -n "$lockfile" echo "try to acquire lock"; then
echo torrent seems to be in process.
echo waiting for completion ...
flock -w 3600 "$lockfile" echo "...completed"
sleep 5
else
(
if ! flock -n 200; then
echo "failed to acquire lock"
echo "Bitte noch einmal starten."
echo "Beliebige Taste zum Beenden."
read -n 1
exit 1
fi
# stop aria2-seeding if running
sudo vm-aria2 stop "${VM_NAME}"
cd "${VM_SYSDIR}"
# get image
aria2c --seed-time=0 --dht-file-path="$DHTDAT" \
--dht-entry-point="${SEEDBOX_HOST}:${SEEDBOX_PORT}" \
"${VM_SYSDIR}/${VM_NAME}.qcow2.torrent"
# and seed
sudo vm-aria2 start "${VM_NAME}"
if ! flock -u 200; then
echo failed to drop lock
exit 1
fi
) 200>"$lockfile"
fi
}
get_image_size() {
torrentfile=$1
length=$(aria2c -S "${torrentfile}" | grep "Total Length" | \
sed -E -e 's/.*\(([0-9,]*)\)/\1/' -e 's/,//g')
echo "$length"
}
delete_outdated_image() {
cd "${VM_SYSDIR}"
qcowsize=$(stat -c%s "${FILENAME}")
if [[ -f "${FILENAME}.torrent" ]] && [[ "${qcowsize}" != $(get_image_size "${FILENAME}.torrent") ]]; then
cat << EOF >&2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Es wurde eine neueres VM-Image auf dem Server gefunden.
Das Herunterladen des Images via WLAN benötigt viel Zeit.
EOF
read -rp "Soll das neue Image wirklich herunter geladen werden? j/n " answer
if [[ "${answer,,}" == "j" ]]; then
sudo vm-aria2 stop "${FILENAME%.qcow2}"
rm -f "${FILENAME}"
fi
fi
}
update_usage_information() {
cd "${VM_SYSDIR}"
[[ -f vm_usage_information.txt ]] || ls -tr *.qcow2 > vm_usage_information.txt || touch vm_usage_information.txt
FILENAME="$(basename $FILENAME)"
echo sed -i "/${FILENAME}/d" vm_usage_information.txt
sed -i "/${FILENAME}/d" vm_usage_information.txt
echo "${FILENAME}" >> vm_usage_information.txt
}
get_file() {
cd "${VM_SYSDIR}"
curl --fail --noproxy "${SEEDBOX_HOST}" -o "${FILENAME}" \
"http://${SEEDBOX_HOST}/aria2/${FILENAME}" || echo "File not found on seedbox"
}
push_file() {
cd "${VM_SYSDIR}"
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${FILENAME}"
}
########################
if [[ "$(id -nu)" != "lmnsynci" ]]; then
echo "$(basename "$0") must be run as lmnsynci user"
show_help
exit 1
fi
source /etc/lmn/vm.conf
while getopts ':' OPTION; do
case "$OPTION" in
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -lt 1 ]]; then
show_help
exit 1
fi
command=$1
shift
case "$command" in
push_file)
for FILENAME in "$@"; do
push_file
done
;;
get_file)
for FILENAME in "$@"; do
get_file
done
;;
get_image)
for VM_NAME in "$@"; do
get_torrent
done
;;
delete_outdated_image)
for FILENAME in "$@"; do
delete_outdated_image
done
;;
update_usage_information)
for FILENAME in "$@"; do
update_usage_information
done
;;
*)
show_help
exit 1
;;
esac

68
roles/lmn_vm/files/vm-upload Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/bash
# Push VM-Disk-Image on server
set -eu
show_help() {
cat << EOF >&2
Usage: $(basename "$0") vmname"
Create torrent and upload disk on server.
EOF
}
upload_image() {
# check if VM-Diskimage exists
if [[ ! (-f "${VM_SYSDIR}/${VM_NAME}.qcow2" || -f "${VM_DIR}/${VM_NAME}.qcow2") ]]; then
echo "File not found ${VM_NAME}.qcow2" >&2
exit 1
fi
vm-aria2 stop "${VM_NAME}" || echo "VMImage-torrent not running"
# link private VM-Diskimage to system-Dir
if [[ -f "${VM_DIR}/${VM_NAME}.qcow2" \
&& ( -f "${VM_SYSDIR}/${VM_NAME}.qcow2" && ("${VM_DIR}/${VM_NAME}.qcow2" -nt "${VM_SYSDIR}/${VM_NAME}.qcow2") \
|| ! -f "${VM_SYSDIR}/${VM_NAME}.qcow2") ]]; then
echo "copy private VM-Diskimage to system-dir"
chown lmnsynci:lmnsynci "${VM_DIR}/${VM_NAME}.qcow2"
ln -f "${VM_DIR}/${VM_NAME}.qcow2" "${VM_SYSDIR}/${VM_NAME}.qcow2"
if [[ -f "${VM_DIR}/${VM_NAME}.permall" ]]; then
cp "${VM_DIR}/${VM_NAME}.permall" "${VM_SYSDIR}/${VM_NAME}.permall"
chown lmnsynci:lmnsynci "${VM_SYSDIR}/${VM_NAME}.permall"
fi
fi
cd "${VM_SYSDIR}"
if [[ -f "/tmp/${VM_NAME}.qcow2.torrent" ]]; then
rm -f "/tmp/${VM_NAME}.qcow2.torrent"
fi
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${VM_NAME}.qcow2"
if [[ -f "${VM_SYSDIR}/${VM_NAME}.permall" ]]; then
uploadseed --server "${SEEDBOX_HOST}:${SEEDBOX_RPC_PORT}" --dht-port "${SEEDBOX_PORT}" \
--pwdfile "${SEEDBOX_PWFILE}" --no-cert "${VM_NAME}.permall"
fi
}
source /etc/lmn/vm.conf
while getopts ':p' OPTION; do
case "$OPTION" in
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]] ; then
show_help
exit 1
fi
VM_NAME=$1
upload_image

50
roles/lmn_vm/files/vm-virtiofsd Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/bash
set -eu
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]]; then
echo "This script takes as input the name of the VM " >&2
echo "Usage: $0 vm_name" >&2
exit 1
fi
VM_NAME="$1"
## Make sure VMs can read the base directory:
chgrp 1010 "/lmn/media/${SUDO_USER}"
chmod 0775 "/lmn/media/${SUDO_USER}"
socket="/run/user/$(id -u $SUDO_USER)/virtiofs-${VM_NAME}.sock"
# FIXME: This does not work. In windows, there is no virtiofs device.
# In GNU/Linux it's only readable.
#
#if ! systemctl -q is-active virtiofs-${VM_NAME}.socket ; then
# systemd-run --unit=virtiofs-${VM_NAME} \
# --slice=system-virtiofs \
# --collect \
# --socket-property=ListenStream="$socket" \
# --socket-property=Accept=no \
# --socket-property=SocketMode=0700 \
# --socket-property=SocketUser=${SUDO_USER} \
# --property=Type=exec \
# --property=StandardInput=socket \
# /usr/local/bin/virtiofsd --log-level debug --sandbox none \
# --syslog --fd=0 --shared-dir "/lmn/media/${SUDO_USER}"
#else
# systemctl restart virtiofs-${VM_NAME}.socket
#fi
if [[ ! -S "$socket" ]] ; then
systemd-run --unit=virtiofs-${VM_NAME} \
--slice=system-virtiofs \
--collect \
--property=Type=exec \
--property=SuccessExitStatus=1 \
--property="ExecStopPost=rm $socket" \
/usr/local/bin/virtiofsd --socket-path "$socket" \
--shared-dir "/lmn/media/${SUDO_USER}"
fi
sleep 1
chown "${SUDO_USER}" "$socket"

View file

@ -0,0 +1,9 @@
- name: Reload libvirtd
ansible.builtin.systemd:
name: libvirtd.service
listen: reload libvirtd
- name: Just force systemd to reread configs
ansible.builtin.systemd_service:
daemon_reload: true
listen: daemon reload

239
roles/lmn_vm/tasks/main.yml Normal file
View file

@ -0,0 +1,239 @@
---
# FIXME #691138, better: prepare interfaces ready to use, c.f. down below, macvtap.
# This task needs to be run before the last apt run to provide a ready-to-use installation.
- name: Allow users to attach to bridge
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/94qemu-bridge-suid
mode: '0644'
content: |
## Modify permissions after installation/upgrade
## to run qemu-bridge as root
DPkg::Post-Invoke {"/usr/bin/chmod 4755 /usr/lib/qemu/qemu-bridge-helper || true"; };
- name: Install libvirt packages
ansible.builtin.apt:
name:
- aria2
- mktorrent
- libvirt-daemon-system
- virt-manager
- virt-viewer
- dialog # for vm-netboot menu
# - name: allow all users to use VMs
# lineinfile:
# dest: /etc/libvirt/libvirtd.conf
# line: 'auth_unix_rw = "none"'
# insertafter: '#auth_unix_rw = "polkit"'
# notify: reload libvirtd
- name: Configure pam_mount for VM bind mounts
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (bind mounts for VMs) -->"
block: |
<!-- bind mounts for the VMs, setting gid here does not work -->
<volume
path="~"
mountpoint="/lmn/media/%(USER)/home"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
<volume
path="/srv/samba/schools/default-school/share"
mountpoint="/lmn/media/%(USER)/share"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
<volume
path="/srv/samba/schools/default-school"
mountpoint="/lmn/media/%(USER)/school"
options="bind"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user>{% if localuser %}<user>{{ localuser }}</user>{% endif %}</or></not>
</volume>
insertafter: "<!-- END ANSIBLE MANAGED BLOCK .* -->"
- name: Use umount script for proper cleanup
ansible.builtin.blockinfile:
dest: /etc/security/pam_mount.conf.xml
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK (umount script needed for bind mounts ordering) -->"
block: |
<!-- Provide special umount script here to handle bind mounts and proper ordering -->
<umount>/usr/local/sbin/pam-umount.sh %(USER) %(USERUID) %(MNTPT)</umount>
insertafter: '^<mntoptions.*'
- name: Prepare umount script
ansible.builtin.copy:
src: pam-umount.sh
dest: /usr/local/sbin/pam-umount.sh
mode: '0755'
- name: Insert domain in default-network
ansible.builtin.lineinfile:
path: /etc/libvirt/qemu/networks/default.xml
line: ' <domain name="{{ ansible_domain }}" localOnly="no"/>'
insertafter: '</ip>'
- name: Autostart default network for VMs
ansible.builtin.file:
src: /etc/libvirt/qemu/networks/default.xml
dest: /etc/libvirt/qemu/networks/autostart/default.xml
state: link
- name: Create system-user syncing VM-files and others
ansible.builtin.user:
name: lmnsynci
comment: lmn sync user
system: true
create_home: false
- name: Create /etc/lmn directory
ansible.builtin.file:
path: /etc/lmn
state: directory
mode: '0755'
- name: Create /lmn directory
ansible.builtin.file:
path: /lmn
state: directory
mode: '0755'
- name: Create /lmn/media directory
ansible.builtin.file:
path: /lmn/media
state: directory
mode: '1777'
- name: Create /var/vm directory
ansible.builtin.file:
path: /var/vm
state: directory
mode: '1777'
- name: Create vm directory
ansible.builtin.file:
path: /lmn/vm
state: directory
owner: lmnsynci
group: lmnsynci
mode: '0755'
- name: Create cleanup-vm.conf
ansible.builtin.copy:
dest: /etc/tmpfiles.d/clean-vm.conf
mode: '0644'
content: |
D /var/tmp/vm 1777 root root -
- name: Deploy sudo configurations
ansible.builtin.copy:
src: lmn-vm
dest: /etc/sudoers.d/90-lmn-vm
owner: root
group: root
mode: '0700'
- name: Deploy vmimages scripts
ansible.builtin.copy:
src: "{{ item }}"
dest: /usr/local/bin/
owner: root
group: root
mode: '0755'
loop:
- vm-create
- vm-rebase
- vm-run
- vm-upload
- vm-sync
- vm-link-images
- vm-virtiofsd
- virtiofsd
- vm-aria2
- uploadseed
- desktop-sync
- name: Deploy vm configuration file vm.conf
ansible.builtin.template:
src: vm.conf.j2
dest: /etc/lmn/vm.conf
owner: root
group: root
mode: '0644'
- name: Deploy aria2 RPC password file
ansible.builtin.copy:
dest: /etc/lmn/uploadseed.conf
owner: root
group: lmnsynci
mode: '0640'
content: |
{{ vm_uploadseed_pwd }}
- name: Prepare directory for aria2 dht.dat
ansible.builtin.file:
path: /var/cache/aria2/
state: directory
owner: lmnsynci
group: lmnsynci
mode: '0755'
- name: Prepare directory for qemu bridge config
ansible.builtin.file:
path: /etc/qemu/
state: directory
mode: '0755'
- name: Deploy bridge.conf needed for qemu session mode
ansible.builtin.copy:
dest: /etc/qemu/bridge.conf
mode: '0644'
content: |
allow virbr0
allow virbr1
allow virbr2
- name: Configure macvtap interface
ansible.builtin.copy:
dest: "/etc/NetworkManager/system-connections/macvlan-vm-macvtap-{{ item }}.nmconnection"
mode: '0600'
content: |
[connection]
id=macvlan-macvtap-{{ item[3:9] }}
type=macvlan
interface-name=macvtap-{{ item[3:9] }}
[macvlan]
mode=2
parent={{ item }}
tap=true
[ipv4]
method=disabled
[ipv6]
method=disabled
[proxy]
loop: "{{ ansible_interfaces | select('search', '^en[pso].+') }}"
- name: Adjust interface permissions for user mode VMs
ansible.builtin.copy:
dest: /etc/udev/rules.d/80-macvlan.rules
mode: '0644'
content: |
{% for interface in (ansible_interfaces | select('search', '^en[pso].+')) %}
SUBSYSTEMS=="net", KERNELS=="macvtap-{{ interface[3:9] }}", MODE="0666"
{% endfor %}
- name: Start virt-manager in session mode by default
ansible.builtin.copy:
dest: /usr/local/bin/virt-manager
content: |
#!/usr/bin/sh
exec /usr/bin/virt-manager --connect qemu:///session $@
mode: '0755'
- name: Copy vm-netboot script
ansible.builtin.copy:
src: vm-netboot
dest: /usr/local/bin/
mode: '0755'

View file

@ -0,0 +1,19 @@
# variables for LMN VM submodule
SEEDBOX_HOST="{{ vm_torrent_serv }}"
SEEDBOX_PORT=6789
SEEDBOX_RPC_PORT=6800
SEEDBOX_PWFILE="/etc/lmn/uploadseed.conf"
DHTDAT="/var/cache/aria2/dht.dat"
DESKTOPSTARTERDIR="/srv/samba/schools/default-school/share/school/AdminIT/desktop/"
VM_SYSDIR="/lmn/vm"
if [[ -v SUDO_UID ]]; then
VM_DIR="/var/tmp/vm/${SUDO_UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${SUDO_UID}"
else
VM_DIR="/var/tmp/vm/${UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${UID}"
fi
VM_MEDIADIR="/lmn/media/${USER}"

View file

@ -0,0 +1,8 @@
---
vpn: none
# Wireguad config
wg_endpoint: "203.0.113.1:51820"
wg_allowed_ips: "10.0.0.0/16;"
wg_ip_cdr: 24
wg_dns: "9.9.9.9"
wg_dns_search: "example.com"

View file

@ -0,0 +1,43 @@
#!/usr/bin/bash
#
set -eu
if [[ "$CONNECTION_ID" = "VPN-Schule" ]]; then
USERNAME=$(ps -o pid,user,args -C sddm-helper | sed -nE 's/.*user (.*)$/\1/p')
USERID=$(id -u "${USERNAME}")
GROUPID=$(id -g "${USERNAME}")
KRB5CCNAME=$(ls /tmp/krb5cc_"${USERID}"_*)
export KRB5CCNAME
printenv >&2
if [[ "$NM_DISPATCHER_ACTION" = "up" ]]; then
# Exit if server is already mounted
findmnt /srv/samba/schools/default-school > /dev/null && exit 0
if ! klist -s -c "${KRB5CCNAME}"; then
#echo "try to renew KRB5-Ticket" >&2
#sudo -u "${USERNAME}" kinit -R -c "${KRB5CCNAME}"
echo "KRB5-Ticket is expired. Sleep 3 seconds and hope it will be renewed after." >&2
sleep 3
fi
echo "prepare mountpoints" >&2
umask 0002
mkdir -p /srv/samba/schools/default-school
chmod 777 /srv/samba/schools/default-school
mkdir -p "/lmn/media/${USERNAME}/share"
mount -t cifs //server/default-school/ /srv/samba/schools/default-school \
-o "sec=krb5i,cruid=${USERID},user=${USERNAME},uid=${USERID},gid=${GROUPID},file_mode=0700,dir_mode=0700,mfsymlinks,nobrl,actimeo=600,cache=loose,echo_interval=10"
echo "after mount" >&2
mount --bind /srv/samba/schools/default-school/share "/lmn/media/${USERNAME}/share"
SUDO_USER=$USERNAME /usr/local/bin/install-printers.sh
elif [[ "$NM_DISPATCHER_ACTION" = "pre-down" ]]; then
# FIXME: Only umount server when Wireguard-Connection was the only connection to server.
# Dirty fix (works only in fvs-IP-Range)
if ! (ip r s | grep "10.190." | grep -v wg0); then
echo "Try to umount server shares"
umount "/lmn/media/${USERNAME}/share"
umount /srv/samba/schools/default-school
fi
fi
fi

View file

@ -0,0 +1,26 @@
#!/usr/bin/bash
set -eu
exit_script() {
echo "unmounting media - terminated by trap!" >> "/tmp/${SUDO_UID}-exit-mount.log"
findmnt "/lmn/media/${SUDO_USER}/share" && umount "/lmn/media/${SUDO_USER}/share"
findmnt "/srv/samba/schools/default-school" && umount "/srv/samba/schools/default-school"
trap - SIGHUP SIGINT SIGTERM # clear the trap
kill -- -$$ # Sends SIGTERM to child/sub processes
}
findmnt /srv/samba/schools/default-school > /dev/null && exit 0
umask 0002
mkdir -p /srv/samba/schools/default-school
chmod 777 /srv/samba/schools/default-school
mkdir -p "/lmn/media/${SUDO_USER}/share"
mount -t cifs //server/default-school/ /srv/samba/schools/default-school \
-o "sec=krb5i,cruid=${SUDO_UID},user=${SUDO_USER},uid=${SUDO_UID},gid=${SUDO_GID},file_mode=0700,dir_mode=0700,mfsymlinks,nobrl,actimeo=600,cache=loose,echo_interval=10"
mount --bind /srv/samba/schools/default-school/share "/lmn/media/${SUDO_USER}/share"
echo "Einbindung erfolgreich!"
echo "Dieses Fenster bitte nicht schließen!"
trap exit_script SIGHUP SIGINT SIGTERM
sleep infinity

View file

@ -0,0 +1,31 @@
---
- name: Copy mountserver script to /usr/local/bin
ansible.builtin.copy:
src: mountserver
dest: /usr/local/bin/
mode: "0755"
- name: Copy NetworkManager dispatcher-script (10-lmn-mount.sh)
ansible.builtin.copy:
src: 10-lmn-mount.sh
dest: /etc/NetworkManager/dispatcher.d/
mode: "0755"
- name: Create link to dispatcher-script (10-lmn-mount.sh)
ansible.builtin.file:
src: ../10-lmn-mount.sh
dest: /etc/NetworkManager/dispatcher.d/pre-down.d/10-lmn-mount.sh
state: link
- name: Deploy sudo configurations (apt for role-teacher)
ansible.builtin.copy:
dest: /etc/sudoers.d/90-lmn-vpn
owner: root
group: root
mode: '0700'
content: |
%role-teacher ALL=(root) NOPASSWD: /usr/local/bin/mountserver
- name: Configure Wireguard
ansible.builtin.include_tasks: wg_config.yml
when: vpn is defined and vpn == "wg"

View file

@ -0,0 +1,140 @@
---
- name: Install wireguard packages
ansible.builtin.apt:
name:
- wireguard
- name: Check if wg_server is reachable
ansible.builtin.command: echo "reachable"
delegate_to: wireguard_server
register: result
changed_when: false
- name: Inform that wg_server is unreachable
ansible.builtin.debug:
msg: |
Couldn't access wg_server. Possible reasons
* server not reachable
* no matching ssh-key
changed_when: true
when: result.stdout is not defined or result.stdout!="reachable"
- name: Configure WG Server
when:
- result.stdout is defined and result.stdout=="reachable"
- not run_in_installer|default(false)|bool
block:
- name: Set facts wg_clientname
ansible.builtin.set_fact:
wg_clientname: "{{ ansible_hostname }}"
- name: Get Wiregard-Privatekey from server
ansible.builtin.shell:
cmd: grep PrivateKey /etc/wireguard/wg0.conf | sed -En 's/.*=\s*(.+)/\1/p'
register: wg_serverprivkey
changed_when: false
delegate_to: wireguard_server
- name: Create public key (Server)
ansible.builtin.command:
cmd: "wg pubkey"
args:
stdin: "{{ wg_serverprivkey.stdout }}"
changed_when: false
register: wg_serverpubkey
- name: Set facts wg_publickey (Server)
ansible.builtin.set_fact:
wg_serverpublickey: "{{ wg_serverpubkey.stdout }}"
- name: Check if Wiregard-Config exists on server
ansible.builtin.command:
cmd: "grep -A 3 '# BEGIN ANSIBLE MANAGED BLOCK {{ wg_clientname }}' /etc/wireguard/wg0.conf"
register: wg_serverconfig
failed_when: false
changed_when: false
delegate_to: wireguard_server
- name: Set facts wg_ip
ansible.builtin.set_fact:
wg_ip: "{{ wg_serverconfig.stdout | regex_search('AllowedIPs = ([0-9.]+)/32', '\\1', multiline=True) | first }}"
when: wg_serverconfig.rc == 0 and wg_ip is not defined
- name: Check if Wireguard exists on client
ansible.builtin.stat:
path: /etc/NetworkManager/system-connections/wg0.nmconnection
register: wg_clientconfig
- name: Search IP address in NetworkManager config
ansible.builtin.command:
cmd: cat /etc/NetworkManager/system-connections/wg0.nmconnection
register: wg_address
changed_when: false
when: wg_clientconfig.stat.exists
- name: Set facts wg_ip
ansible.builtin.set_fact:
wg_ip: "{{ wg_address.stdout | regex_search('address1=([0-9.]+)/.*', '\\1', multiline=True) | first }}"
when: wg_address.rc is defined and wg_address.rc == 0 and wg_ip is not defined
- name: Set facts wg_privatekey
ansible.builtin.set_fact:
wg_privatekey: "{{ wg_address.stdout | regex_search('private-key=(.*)$', '\\1', multiline=True) | first }}"
when: wg_address.rc is defined and wg_address.rc == 0 and wg_privatekey is not defined
- name: Search maximum AllowedIP
ansible.builtin.shell:
cmd: grep AllowedIPs /etc/wireguard/wg0.conf | sed -En 's/.*=\s*([0-9.]+)\/32.*/\1/p' | sort -t . -k 3,3n -k 4,4n | tail -n 1
register: wg_ipmax
changed_when: false
delegate_to: wireguard_server
when: wg_ip is not defined
- name: Set facts wg_ip
ansible.builtin.set_fact:
wg_ip: "{{ wg_ipmax.stdout | ansible.utils.ipmath(1) }}"
when: wg_ipmax.rc is defined and wg_ipmax.rc == 0 and wg_ipmax.stdout and wg_ip is not defined
- name: Create private key
ansible.builtin.command:
cmd: "wg genkey"
register: wg_genkey
changed_when: false
when: wg_privatekey is not defined
- name: Set facts wg_privatekey
ansible.builtin.set_fact:
wg_privatekey: "{{ wg_genkey.stdout }}"
when: wg_genkey.stdout is defined
- name: Create Wireguard-Config
ansible.builtin.template:
src: wg0.nmconnection.j2
dest: /etc/NetworkManager/system-connections/wg0.nmconnection
mode: '0600'
- name: Create public key
ansible.builtin.command:
cmd: "wg pubkey"
args:
stdin: "{{ wg_privatekey }}"
register: wg_pubkey
changed_when: false
- name: Set facts wg_publickey
ansible.builtin.set_fact:
wg_publickey: "{{ wg_pubkey.stdout }}"
- name: Print WG IP
ansible.builtin.debug:
msg: "{{ wg_publickey }} -- {{ wg_pubkey.stdout }}"
- name: Set Wireguard Serverconfig
ansible.builtin.blockinfile:
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ wg_clientname }}"
path: /etc/wireguard/wg0.conf
block: |
[Peer]
PublicKey = {{ wg_publickey }}
AllowedIPs = {{ wg_ip }}/32
delegate_to: wireguard_server

View file

@ -0,0 +1,26 @@
[connection]
id=VPN-Schule
type=wireguard
autoconnect=false
interface-name=wg0
[wireguard]
listen-port=51820
private-key={{ wg_privatekey }}
mtu=1280
[wireguard-peer.{{ wg_serverpublickey }}]
endpoint={{ wg_endpoint }}
allowed-ips={{ wg_allowed_ips }}
[ipv4]
address1={{ wg_ip }}/{{ wg_ip_cdr }}
dns={{ wg_dns }}
dns-search={{ wg_dns_search }}
method=manual
[ipv6]
addr-gen-mode=stable-privacy
method=ignore
[proxy]

View file

@ -0,0 +1,15 @@
---
wlan: none
wlan_force_issue: false
wlan_ssid: "Linux-Wlan"
wlan_password: "VerySecurePassw0rd"
wlan_eap_ca:
C: "DE"
ST: "Baden-Wuerttemberg"
L: "Reutlingen"
O: "Linuxschule"
emailAddress: "admin@example.com"
CN: "Radius Certificate Authority"
password: "OtherVerySecurePassw0rd"
wlan_eap_ca_crl: "http://radius.{{ domain }}/radius-ca.crl"
wlan_enable_on_boot: true

View file

@ -0,0 +1,53 @@
---
# WPA-Enterprise (EAP-TLS) - Check if certificate needs to be re-enrolled
- name: Check if certificate is already active on client
ansible.builtin.stat:
path: "/etc/ssl/certs/{{ wlan_ssid }}.crt"
register: cert_client_active
- name: Extract serial from certificate
ansible.builtin.command: 'openssl x509 -noout -serial -in /etc/ssl/certs/{{ wlan_ssid }}.crt'
changed_when: false
register: cert_serial
when: cert_client_active.stat.exists
- name: Download crl from radius-server
ansible.builtin.get_url:
force: true
mode: "0644"
url: "{{ wlan_eap_ca_crl }}"
dest: /tmp/radius-ca.crl
when: cert_client_active.stat.exists
- name: Get radius-server ca crl
community.crypto.x509_crl_info:
path: /tmp/radius-ca.crl
list_revoked_certificates: true
register: radius_crl
when: cert_client_active.stat.exists
- name: Check if radius-server is reachable
ansible.builtin.command: echo "reachable"
delegate_to: radius_server
register: radius_reachable
changed_when: false
ignore_unreachable: true
- name: Inform that radius_server is unreachable
ansible.builtin.debug:
msg:
- "Couldn't access radius_server. Possible reasons"
- "* server not reachable"
- "* no matching ssh-key"
changed_when: true
when: radius_reachable.stdout is not defined or radius_reachable.stdout!='reachable'
- name: Issue radius certificate
ansible.builtin.include_tasks: eap-tls_issue-certificate.yaml
when:
- radius_reachable.stdout is defined and radius_reachable.stdout=="reachable"
- not run_in_installer|default(false)|bool
- |
( not cert_client_active.stat.exists ) or
(cert_serial.stdout | replace('serial=','') | int(base=16) ) in ( radius_crl.revoked_certificates | map(attribute='serial_number') | list ) or
wlan_force_issue

Some files were not shown because too many files have changed in this diff Show more