2025-06-26-13-33-49: Cronjob
This commit is contained in:
commit
29049c8da1
127 changed files with 7089 additions and 0 deletions
2
roles/custom/fvs/defaults/main.yml
Normal file
2
roles/custom/fvs/defaults/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
fvs_remove_discover: true # Remove discover to disable update_notify
|
||||
113
roles/custom/fvs/files/fvs-config.js
Normal file
113
roles/custom/fvs/files/fvs-config.js
Normal 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
|
||||
5
roles/custom/fvs/files/fvs.directory
Normal file
5
roles/custom/fvs/files/fvs.directory
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[Desktop Entry]
|
||||
Type=Directory
|
||||
Name=FvS
|
||||
Icon=face-smile-big
|
||||
#X-KDE-BaseGroup=info
|
||||
12
roles/custom/fvs/files/fvs.menu
Normal file
12
roles/custom/fvs/files/fvs.menu
Normal 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>
|
||||
6
roles/custom/fvs/files/lmn-dolphin.sh
Normal file
6
roles/custom/fvs/files/lmn-dolphin.sh
Normal 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
|
||||
20
roles/custom/fvs/files/lmn-fixhome-dolphin.sh
Normal file
20
roles/custom/fvs/files/lmn-fixhome-dolphin.sh
Normal 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
|
||||
89
roles/custom/fvs/files/lmn-patch-dolphin.sh
Executable file
89
roles/custom/fvs/files/lmn-patch-dolphin.sh
Executable 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"
|
||||
222
roles/custom/fvs/files/lmn-reset-dolphin.sh
Executable file
222
roles/custom/fvs/files/lmn-reset-dolphin.sh
Executable 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
|
||||
20
roles/custom/fvs/files/lmn-sync
Normal file
20
roles/custom/fvs/files/lmn-sync
Normal 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
|
||||
6
roles/custom/fvs/files/lmn-sync.service
Normal file
6
roles/custom/fvs/files/lmn-sync.service
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description=Synchronize program data and desktop starters
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/lmn-sync
|
||||
9
roles/custom/fvs/files/lmn-sync.timer
Normal file
9
roles/custom/fvs/files/lmn-sync.timer
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=Run lmn-sync after boot
|
||||
After=network-online.target
|
||||
|
||||
[Timer]
|
||||
OnBootSec=0min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
98
roles/custom/fvs/files/policies.json
Normal file
98
roles/custom/fvs/files/policies.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
roles/custom/fvs/handlers/main.yml
Normal file
7
roles/custom/fvs/handlers/main.yml
Normal 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
|
||||
253
roles/custom/fvs/tasks/main.yml
Normal file
253
roles/custom/fvs/tasks/main.yml
Normal 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
|
||||
58
roles/custom/fvs/tasks/sync.yml
Normal file
58
roles/custom/fvs/tasks/sync.yml
Normal 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
|
||||
5
roles/kerberize/handlers/main.yml
Normal file
5
roles/kerberize/handlers/main.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
- name: Reload sshd
|
||||
ansible.builtin.systemd:
|
||||
name: sshd
|
||||
state: reloaded
|
||||
when: not run_in_installer|default(false)|bool
|
||||
49
roles/kerberize/tasks/main.yml
Normal file
49
roles/kerberize/tasks/main.yml
Normal 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) }}"
|
||||
}
|
||||
2
roles/lmn_exam/defaults/main.yml
Normal file
2
roles/lmn_exam/defaults/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
exam_mode: true
|
||||
16
roles/lmn_exam/files/pam-exec.sh
Normal file
16
roles/lmn_exam/files/pam-exec.sh
Normal 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
16
roles/lmn_exam/files/rmexam
Executable 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
|
||||
6
roles/lmn_exam/files/rmexam.service
Normal file
6
roles/lmn_exam/files/rmexam.service
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description=Rename/Remove -exam directories older than 12h/10d
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/rmexam
|
||||
8
roles/lmn_exam/files/rmexam.timer
Normal file
8
roles/lmn_exam/files/rmexam.timer
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description=Run rmexam after boot
|
||||
|
||||
[Timer]
|
||||
OnBootSec=0min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
70
roles/lmn_exam/tasks/main.yml
Normal file
70
roles/lmn_exam/tasks/main.yml
Normal 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
|
||||
6
roles/lmn_finish/defaults/main.yml
Normal file
6
roles/lmn_finish/defaults/main.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extra_pkgs: []
|
||||
extra_pkgs1: []
|
||||
extra_pkgs2: []
|
||||
extra_pkgs_bpo: []
|
||||
extra_pkgs_bpo1: []
|
||||
extra_pkgs_bpo2: []
|
||||
34
roles/lmn_finish/tasks/main.yaml
Normal file
34
roles/lmn_finish/tasks/main.yaml
Normal 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
|
||||
40
roles/lmn_kde/defaults/main.yml
Normal file
40
roles/lmn_kde/defaults/main.yml
Normal 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: [ ]
|
||||
32
roles/lmn_kde/files/lmn-fix-dolphin.sh
Executable file
32
roles/lmn_kde/files/lmn-fix-dolphin.sh
Executable 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
|
||||
68
roles/lmn_kde/tasks/main.yml
Normal file
68
roles/lmn_kde/tasks/main.yml
Normal 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
|
||||
3
roles/lmn_localhome/defaults/main.yml
Normal file
3
roles/lmn_localhome/defaults/main.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
localhome: false
|
||||
localhome_logout_missing_serverhome: true
|
||||
29
roles/lmn_localhome/files/lmn-create-unisonconfig.sh
Normal file
29
roles/lmn_localhome/files/lmn-create-unisonconfig.sh
Normal 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
|
||||
|
||||
39
roles/lmn_localhome/tasks/main.yml
Normal file
39
roles/lmn_localhome/tasks/main.yml
Normal 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 %}
|
||||
3
roles/lmn_localproxy/defaults/main.yml
Normal file
3
roles/lmn_localproxy/defaults/main.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
localproxy: false
|
||||
localproxy_parent: "firewall.{{ domain }}"
|
||||
11
roles/lmn_localproxy/files/usersquid.service
Normal file
11
roles/lmn_localproxy/files/usersquid.service
Normal 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
|
||||
48
roles/lmn_localproxy/tasks/main.yml
Normal file
48
roles/lmn_localproxy/tasks/main.yml
Normal 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 }}"
|
||||
|
|
@ -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
|
||||
14
roles/lmn_localproxy/templates/squid-usermode.conf.j2
Normal file
14
roles/lmn_localproxy/templates/squid-usermode.conf.j2
Normal 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
|
||||
8
roles/lmn_localproxy/templates/startusersquid.sh.j2
Executable file
8
roles/lmn_localproxy/templates/startusersquid.sh.j2
Executable 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
|
||||
4
roles/lmn_localuser/defaults/main.yml
Normal file
4
roles/lmn_localuser/defaults/main.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
localuser: false
|
||||
localuser_password: "Muster!"
|
||||
localuser_secretsalt: "4ANAxPycC3q"
|
||||
28
roles/lmn_localuser/tasks/main.yml
Normal file
28
roles/lmn_localuser/tasks/main.yml
Normal 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"
|
||||
9
roles/lmn_misc/defaults/main.yml
Normal file
9
roles/lmn_misc/defaults/main.yml
Normal 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"
|
||||
18
roles/lmn_misc/files/bootorder.sh
Normal file
18
roles/lmn_misc/files/bootorder.sh
Normal 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
48
roles/lmn_misc/files/pwroff
Executable 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 ' '
|
||||
6
roles/lmn_misc/files/pwroff.service
Normal file
6
roles/lmn_misc/files/pwroff.service
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description=Run pwroff script
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/pwroff
|
||||
9
roles/lmn_misc/files/pwroff.timer
Normal file
9
roles/lmn_misc/files/pwroff.timer
Normal 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
33
roles/lmn_misc/files/reporter
Executable 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
|
||||
6
roles/lmn_misc/files/reporter.service
Normal file
6
roles/lmn_misc/files/reporter.service
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description=Run reporting script
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/reporter
|
||||
9
roles/lmn_misc/files/reporter.timer
Normal file
9
roles/lmn_misc/files/reporter.timer
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=Run reporter script every 15 min
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5min
|
||||
OnUnitActiveSec=15min
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
2
roles/lmn_misc/handlers/main.yml
Normal file
2
roles/lmn_misc/handlers/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- name: Run update-grub
|
||||
ansible.builtin.command: update-grub
|
||||
217
roles/lmn_misc/tasks/main.yml
Normal file
217
roles/lmn_misc/tasks/main.yml
Normal 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
|
||||
29
roles/lmn_misc/templates/lmn-fix-screen.j2
Normal file
29
roles/lmn_misc/templates/lmn-fix-screen.j2
Normal 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 %}
|
||||
33
roles/lmn_misc/templates/reporter.j2
Executable file
33
roles/lmn_misc/templates/reporter.j2
Executable 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
|
||||
3
roles/lmn_mount/defaults/main.yml
Normal file
3
roles/lmn_mount/defaults/main.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
smb_server: "server"
|
||||
smb_share: "default-school/"
|
||||
nfs4: false
|
||||
4
roles/lmn_mount/files/lmn-linkhome.sh
Normal file
4
roles/lmn_mount/files/lmn-linkhome.sh
Normal 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
|
||||
3
roles/lmn_mount/files/lmn-mounthome.sh
Normal file
3
roles/lmn_mount/files/lmn-mounthome.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if [[ "${UID}" -gt 60000 ]]; then
|
||||
sudo /usr/local/bin/mounthome.sh &
|
||||
fi
|
||||
112
roles/lmn_mount/tasks/main.yml
Normal file
112
roles/lmn_mount/tasks/main.yml
Normal 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
|
||||
23
roles/lmn_network/tasks/main.yml
Normal file
23
roles/lmn_network/tasks/main.yml
Normal 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"
|
||||
2
roles/lmn_printer/defaults/main.yml
Normal file
2
roles/lmn_printer/defaults/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
printer_admin_group: ""
|
||||
3
roles/lmn_printer/files/90-lmn-install-printers
Normal file
3
roles/lmn_printer/files/90-lmn-install-printers
Normal 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
|
||||
67
roles/lmn_printer/tasks/main.yml
Normal file
67
roles/lmn_printer/tasks/main.yml
Normal 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 &)
|
||||
50
roles/lmn_printer/templates/install-printers.sh.j2
Normal file
50
roles/lmn_printer/templates/install-printers.sh.j2
Normal 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
|
||||
2
roles/lmn_security/defaults/main.yml
Normal file
2
roles/lmn_security/defaults/main.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
security_defaultuser_login_disable: true
|
||||
11
roles/lmn_security/handlers/main.yml
Normal file
11
roles/lmn_security/handlers/main.yml
Normal 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
|
||||
52
roles/lmn_security/tasks/main.yml
Normal file
52
roles/lmn_security/tasks/main.yml
Normal 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
|
||||
12
roles/lmn_security/templates/polkit_rules.j2
Normal file
12
roles/lmn_security/templates/polkit_rules.j2
Normal 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 %}
|
||||
});
|
||||
|
||||
6
roles/lmn_sssd/handlers/main.yml
Normal file
6
roles/lmn_sssd/handlers/main.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
- name: Restart sssd
|
||||
ansible.builtin.service:
|
||||
name: sssd
|
||||
state: restarted
|
||||
enabled: true
|
||||
listen: "Restart sssd"
|
||||
24
roles/lmn_sssd/tasks/main.yml
Normal file
24
roles/lmn_sssd/tasks/main.yml
Normal 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
|
||||
22
roles/lmn_sssd/templates/sssd.conf.j2
Normal file
22
roles/lmn_sssd/templates/sssd.conf.j2
Normal 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 %}
|
||||
23
roles/lmn_tmpfixes/files/spyder.patch
Normal file
23
roles/lmn_tmpfixes/files/spyder.patch
Normal 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_()
|
||||
72
roles/lmn_tmpfixes/tasks/bookworm.yml
Normal file
72
roles/lmn_tmpfixes/tasks/bookworm.yml
Normal 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
|
||||
170
roles/lmn_tmpfixes/tasks/cleanup.yml
Normal file
170
roles/lmn_tmpfixes/tasks/cleanup.yml
Normal 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
|
||||
10
roles/lmn_tmpfixes/tasks/main.yml
Normal file
10
roles/lmn_tmpfixes/tasks/main.yml
Normal 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"
|
||||
4
roles/lmn_vm/defaults/main.yml
Normal file
4
roles/lmn_vm/defaults/main.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
vm_support: false
|
||||
vm_torrent_serv: "seedbox.{{ domain }}"
|
||||
vm_uploadseed_pwd: secret = "token:topsecret"
|
||||
14
roles/lmn_vm/files/desktop-sync
Normal file
14
roles/lmn_vm/files/desktop-sync
Normal 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
25
roles/lmn_vm/files/lmn-vm
Normal 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
|
||||
42
roles/lmn_vm/files/pam-umount.sh
Normal file
42
roles/lmn_vm/files/pam-umount.sh
Normal 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"
|
||||
2
roles/lmn_vm/files/pulseaudio-override.conf
Normal file
2
roles/lmn_vm/files/pulseaudio-override.conf
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[Service]
|
||||
Environment=HOME=/tmp/pulse.%u
|
||||
158
roles/lmn_vm/files/sync-vm.sh
Executable file
158
roles/lmn_vm/files/sync-vm.sh
Executable 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
|
||||
13
roles/lmn_vm/files/sync.desktop
Normal file
13
roles/lmn_vm/files/sync.desktop
Normal 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
93
roles/lmn_vm/files/uploadseed
Executable 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
BIN
roles/lmn_vm/files/virtiofsd
Executable file
Binary file not shown.
33
roles/lmn_vm/files/vm-aria2
Executable file
33
roles/lmn_vm/files/vm-aria2
Executable 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
47
roles/lmn_vm/files/vm-create
Executable 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"
|
||||
28
roles/lmn_vm/files/vm-link-images
Executable file
28
roles/lmn_vm/files/vm-link-images
Executable 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
78
roles/lmn_vm/files/vm-netboot
Executable 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
80
roles/lmn_vm/files/vm-rebase
Executable 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
375
roles/lmn_vm/files/vm-run
Executable 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
178
roles/lmn_vm/files/vm-sync
Executable 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
68
roles/lmn_vm/files/vm-upload
Executable 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
50
roles/lmn_vm/files/vm-virtiofsd
Executable 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"
|
||||
9
roles/lmn_vm/handlers/main.yml
Normal file
9
roles/lmn_vm/handlers/main.yml
Normal 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
239
roles/lmn_vm/tasks/main.yml
Normal 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'
|
||||
19
roles/lmn_vm/templates/vm.conf.j2
Normal file
19
roles/lmn_vm/templates/vm.conf.j2
Normal 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}"
|
||||
8
roles/lmn_vpn/defaults/main.yml
Normal file
8
roles/lmn_vpn/defaults/main.yml
Normal 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"
|
||||
43
roles/lmn_vpn/files/10-lmn-mount.sh
Executable file
43
roles/lmn_vpn/files/10-lmn-mount.sh
Executable 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
|
||||
26
roles/lmn_vpn/files/mountserver
Normal file
26
roles/lmn_vpn/files/mountserver
Normal 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
|
||||
31
roles/lmn_vpn/tasks/main.yml
Normal file
31
roles/lmn_vpn/tasks/main.yml
Normal 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"
|
||||
140
roles/lmn_vpn/tasks/wg_config.yml
Normal file
140
roles/lmn_vpn/tasks/wg_config.yml
Normal 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
|
||||
26
roles/lmn_vpn/templates/wg0.nmconnection.j2
Normal file
26
roles/lmn_vpn/templates/wg0.nmconnection.j2
Normal 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]
|
||||
15
roles/lmn_wlan/defaults/main.yaml
Normal file
15
roles/lmn_wlan/defaults/main.yaml
Normal 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
|
||||
53
roles/lmn_wlan/tasks/eap-tls_check-certificate.yaml
Normal file
53
roles/lmn_wlan/tasks/eap-tls_check-certificate.yaml
Normal 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
Loading…
Add table
Add a link
Reference in a new issue