Compare commits

...
Sign in to create a new pull request.

22 commits
fvs ... main

Author SHA1 Message Date
Raphael Dannecker
6f1827ff00 Fix for https://github.com/V4bel/dirtyfrag/ 2026-05-13 09:05:38 +02:00
Raphael Dannecker
588192f5f1 Fix for CVE-2026-31431 https://copy.fail/ 2026-05-13 09:05:15 +02:00
Raphael Dannecker
69b04a36e3 Packages requested by some teachers 2026-05-13 09:04:52 +02:00
Raphael Dannecker
8431377eff Allow teachers to shrink VM disk images 2026-05-13 09:04:22 +02:00
Raphael Dannecker
0f1809c788 Update regex pattern to handle Trixie's partial English output 2026-05-13 09:03:36 +02:00
Raphael Dannecker
dbec89d2e1 Fix naming of audio sinks 2026-05-13 09:03:05 +02:00
Raphael Dannecker
c92966c0e2 FVS Fix VM-image permissions 2026-05-13 09:02:35 +02:00
Raphael Dannecker
11182e891e Only VM media directory needs restricted access 2026-05-13 09:00:46 +02:00
Raphael Dannecker
09ac1a48c1 Share student home directories with teachers in VMs 2026-05-13 09:00:22 +02:00
Raphael Dannecker
97f5f5e326 Replace custom virtiofsd package by trixie repository package and remove deprecated wrapper scripts 2026-05-13 08:59:49 +02:00
Raphael Dannecker
b2ce1d3234 Deny access to sensitive data for other users 2026-05-13 08:59:18 +02:00
Raphael Dannecker
159ba43e64 Reduce NFS share mount timeout to prevent login delays on offline laptops 2026-05-13 08:58:49 +02:00
Raphael Dannecker
978c4b7250 Add link to ticket system to Firefox bookmarks 2026-05-13 08:58:29 +02:00
Raphael Dannecker
d7b9d096d0 Prevent unintended local execution of delegated tasks in ansible-pull 2026-05-13 08:55:04 +02:00
Daniel Werz
fb98d53fad Fix conditional check of string variable 2026-03-04 11:22:39 +01:00
Raphael Dannecker
c90e626e3d Add comment line to prevent empty content 2026-03-04 11:22:39 +01:00
Raphael Dannecker
cf5e11d7f1 Report failed services (e.g. lmn-updater) 2026-03-04 11:22:39 +01:00
Raphael Dannecker
3deccfb88f Remove reporter file because reporter template is used 2026-03-04 11:22:39 +01:00
Raphael Dannecker
6558f376f5 Add inventory vault password for updater service 2026-03-04 11:22:39 +01:00
Raphael Dannecker
51135966d3 Fix start-condition in lmn-updater.timer 2026-03-04 11:22:39 +01:00
Raphael Dannecker
8ee5517612 Restricting the Ansible pull operation (lmn-updater) on changes in the repository 2026-03-04 11:22:39 +01:00
Raphael Dannecker
c4dbcffebd Add automatic updater using ansible-pull 2026-03-04 11:22:39 +01:00
20 changed files with 102 additions and 102 deletions

View file

@ -30,6 +30,10 @@
"url": "https://info.steinbeis.schule", "url": "https://info.steinbeis.schule",
"name": "FvS-Hilfesystem" "name": "FvS-Hilfesystem"
}, },
{
"url": "https://ticket.steinbeis.schule",
"name": "FvS-IT-Support"
},
{ {
"url": "https://moodle.steinbeis.schule", "url": "https://moodle.steinbeis.schule",
"name": "FvS-Moodle" "name": "FvS-Moodle"

View file

@ -59,6 +59,7 @@
- net-tools - net-tools
- netcat-openbsd - netcat-openbsd
- nmap - nmap
- octave
- okular-extra-backends ## needed for CHM files - okular-extra-backends ## needed for CHM files
- pdf-presenter-console - pdf-presenter-console
- php-cli - php-cli

View file

@ -33,6 +33,7 @@
dest: /etc/profile.d/lmn-logout.sh dest: /etc/profile.d/lmn-logout.sh
mode: '0755' mode: '0755'
content: | content: |
# logout script (may be empty)
{% if localhome_logout_missing_serverhome %} {% if localhome_logout_missing_serverhome %}
[[ "${UID}" -gt 10000 ]] && ! findmnt /srv/samba/schools/default-school > /dev/null && exit 0 [[ "${UID}" -gt 10000 ]] && ! findmnt /srv/samba/schools/default-school > /dev/null && exit 0
{% endif %} {% endif %}

View file

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

View file

@ -98,7 +98,7 @@
export superusers export superusers
password_pbkdf2 root {{ grub_pwd }} password_pbkdf2 root {{ grub_pwd }}
notify: Run update-grub notify: Run update-grub
when: grub_pwd | bool | default(false) when: grub_pwd is defined and grub_pwd is truthy
- name: Allow booting grub menu entries - name: Allow booting grub menu entries
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
@ -190,6 +190,36 @@
tags: tags:
- baseinstall - baseinstall
# Updater
- name: Provide services and timers for updater
ansible.builtin.template:
src: "{{ item }}.j2"
dest: "/etc/systemd/system/{{ item }}"
mode: '0644'
loop:
- lmn-updater.service
- lmn-updater.timer
when: misc_updater_repository | default(false) is truthy
- name: Enable updater.timer
ansible.builtin.systemd:
name: lmn-updater.timer
enabled: true
when:
- misc_updater_repository | default(false) is truthy
- misc_updater_autostart | default(false) is truthy
- name: Deploy inventory password file
ansible.builtin.copy:
dest: /root/.inventory-pw
owner: root
mode: '0640'
content: "{{ misc_updater_inventory_password }}"
when:
- misc_updater_repository | default(false) is truthy
- misc_updater_inventory_password | default(false) is truthy
# Prepare CloneScreen on Presenter PCs # Prepare CloneScreen on Presenter PCs
- name: Fix primary screen for class room PCs with projector - name: Fix primary screen for class room PCs with projector

View file

@ -22,8 +22,8 @@ fi
pactl set-card-profile alsa_card.{{ audio_output[0] }} output:{{ audio_output[1] }} 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] }} pactl set-default-sink alsa_output.{{ audio_output[0] }}.{{ audio_output[1] }}
{% else %} {% else %}
if pactl list cards | grep output:hdmi-stereo: | grep verfügbar:\ ja; then if pactl list cards | grep output:hdmi-stereo: | grep -E "verfügbar: ja|available: yes"; then
pactl set-card-profile $(pactl list short cards | grep -m1 pci | head -1 | cut -f2) output:hdmi-stereo 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 pactl set-default-sink $(pactl list short cards | grep -m1 pci | head -1 | cut -f2 | sed s/card/output/g).hdmi-stereo
fi fi
{% endif %} {% endif %}

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run LMN Client updates via ansible-pull
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/ansible-pull --only-if-changed --verbose --vault-password-file /root/.inventory-pw -l %H -d /root/lmn-client \
--skip-tags no_ansible_pull -i {{ misc_updater_inventory }} --url={{ misc_updater_repository }} -C {{ misc_updater_branch }} lmn-client.yml

View file

@ -0,0 +1,9 @@
[Unit]
Description=Run LMN Updater every day
After=network-online.target
[Timer]
OnBootSec=5min
[Install]
WantedBy=timers.target

View file

@ -16,6 +16,7 @@ cmds=(
'ip link show | \ 'ip link show | \
sed -nE -e "s/^[2-9]: (\S+): .+/\1/p" -e "s/.+ether ([0-9a-f:]+) .+/\1/p" | \ sed -nE -e "s/^[2-9]: (\S+): .+/\1/p" -e "s/.+ether ([0-9a-f:]+) .+/\1/p" | \
paste - -' paste - -'
'systemctl --failed | grep -v "^$"'
) )
# 'w' # 'w'
# 'uptime' # 'uptime'

View file

@ -97,7 +97,7 @@
ansible.posix.mount: ansible.posix.mount:
src: "{{ nfs_server }}:tools" src: "{{ nfs_server }}:tools"
path: /lmn/tools path: /lmn/tools
opts: rw,_netdev,x-systemd.automount,x-systemd.idle-timeout=10s,timeo=100,soft opts: rw,_netdev,x-systemd.automount,x-systemd.idle-timeout=10s,x-systemd.mount-timeout=10,timeo=100,soft
state: present state: present
fstype: nfs4 fstype: nfs4
when: nfs_server is defined when: nfs_server is defined

View file

@ -5,14 +5,14 @@
mode: '0644' mode: '0644'
content: > content: >
{{ apt_conf }} {{ apt_conf }}
when: apt_conf | bool | default(false) when: apt_conf is defined and apt_conf is truthy
- name: Set NTP server - name: Set NTP server
ansible.builtin.lineinfile: ansible.builtin.lineinfile:
path: /etc/systemd/timesyncd.conf path: /etc/systemd/timesyncd.conf
insertafter: '^#NTP=' insertafter: '^#NTP='
line: NTP={{ ntp_serv }} line: NTP={{ ntp_serv }}
when: ntp_serv | bool | default(false) when: ntp_serv is defined and ntp_serv is truthy
- name: Add proposed-updates repository - name: Add proposed-updates repository
ansible.builtin.apt_repository: ansible.builtin.apt_repository:

View file

@ -32,3 +32,29 @@
state: absent state: absent
purge: true purge: true
autoremove: true autoremove: true
# CVE-2026-31431 https://copy.fail/#mitigation
- name: Create modprobe config to disable algif_aead
ansible.builtin.lineinfile:
path: /etc/modprobe.d/disable-algif.conf
line: "install algif_aead /bin/false"
create: true
mode: '0644'
- name: Remove algif_aead module if loaded
community.general.modprobe:
name: algif_aead
state: absent
# Dirty.Frag
- name: Create modprobe config to disable modules needed for dirty.frag
ansible.builtin.copy:
dest: /etc/modprobe.d/dirtyfrag.conf
content: |
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
mode: '0644'
- name: Set VM permissions
ansible.builtin.command: chmod -R o+r /lmn/vm

View file

@ -16,11 +16,6 @@ lmnsynci ALL=(root) NOPASSWD: /usr/local/bin/vm-aria2
%role-student 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 %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: # desktop-sync:
%examusers ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync %examusers ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync
%role-student ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync %role-student ALL=(root) NOPASSWD: /usr/local/bin/desktop-sync

Binary file not shown.

View file

@ -125,8 +125,6 @@ create_clone() {
create_printerlist() { create_printerlist() {
## Prepare .printerlist.csv ## Prepare .printerlist.csv
mkdir -p "${VM_MEDIADIR}"
chgrp "$(id -g)" "${VM_MEDIADIR}"
echo "Name;IppURL" > "${VM_MEDIADIR}/.printerlist.csv" echo "Name;IppURL" > "${VM_MEDIADIR}/.printerlist.csv"
for p in $(lpstat -v | cut -f 3 -d" " | sed 's/:$//'); do 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" echo "$p;ipp://192.168.122.1/printers/$p" >> "${VM_MEDIADIR}/.printerlist.csv"
@ -149,7 +147,7 @@ start_virtiofs_service() {
local drive_letter=$3 local drive_letter=$3
local socket="/run/user/${UID}/virtiofs-${VM_NAME}-${target_name,,}.sock" local socket="/run/user/${UID}/virtiofs-${VM_NAME}-${target_name,,}.sock"
systemd-run --user /usr/local/bin/virtiofsd --uid-map=":${GUEST_UID}:${UID}:1:" --gid-map=":${GUEST_GID}:$(id -g):1:" \ systemd-run --user /usr/lib/qemu/virtiofsd --uid-map=":${GUEST_UID}:${UID}:1:" --gid-map=":${GUEST_GID}:$(id -g):1:" \
--socket-path "${socket}" --shared-dir "${shared_dir}" --syslog --socket-path "${socket}" --shared-dir "${shared_dir}" --syslog
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
@ -208,6 +206,7 @@ EOF
QEMU='qemu:///session' QEMU='qemu:///session'
NEWCLONE=0 NEWCLONE=0
PERSISTENT=0 PERSISTENT=0
LIBVIRTOSINFO="win10" LIBVIRTOSINFO="win10"
@ -361,6 +360,8 @@ if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
else else
VMINFO_DIR="/lmn/media/${USER}" VMINFO_DIR="/lmn/media/${USER}"
fi fi
mkdir -p "${VM_MEDIADIR}" -m 700
chgrp "$(id -g)" "${VM_MEDIADIR}"
create_printerlist create_printerlist
create_mountlist create_mountlist
@ -368,7 +369,7 @@ if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
[[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd [[ "${QEMU}" = 'qemu:///session' ]] && start_virtiofsd
# Create VMInfo Json file # Create VMInfo Json file
#( umask 027; ./vm-create-vminfo > "${VMINFO_DIR}/.vminfo.json" ) #( umask 077; ./vm-create-vminfo > "${VMINFO_DIR}/.vminfo.json" )
# Start vminfo.timer # Start vminfo.timer
systemctl --user restart vminfo.timer systemctl --user restart vminfo.timer

View file

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

View file

@ -57,7 +57,9 @@ def get_krb5 ():
def get_mounts(): def get_mounts():
mounts = [] mounts = []
mounts.append({ 'Drive': 'H', 'RemotePath': '\\\\server.pn.steinbeis.schule' + nethome.replace('/srv/samba/schools','').replace('/','\\'), 'Name': 'Home_Server' }) mounts.append({ 'Drive': 'H', 'RemotePath': '\\\\server.pn.steinbeis.schule' + nethome.replace('/srv/samba/schools','').replace('/','\\'), 'Name': 'Home_Server' })
mounts.append({ 'Drive': 'T', 'RemotePath': '\\\\server.pn.steinbeis.schule\default-school\share', 'Name': 'Tausch' }) mounts.append({ 'Drive': 'T', 'RemotePath': '\\\\server.pn.steinbeis.schule\\default-school\\share', 'Name': 'Tausch' })
if "role-teacher" in vminfo['Groups']:
mounts.append({ 'Drive': 'S', 'RemotePath': '\\\\server.pn.steinbeis.schule\\default-school\\students', 'Name': 'SuS' })
return mounts return mounts
def get_user_folders(): def get_user_folders():

View file

@ -16,11 +16,13 @@
name: name:
- aria2 - aria2
- mktorrent - mktorrent
- guestfs-tools
- libvirt-daemon-system - libvirt-daemon-system
- virt-manager - virt-manager
- virt-viewer - virt-viewer
- dialog # for vm-netboot menu - dialog # for vm-netboot menu
- python3-impacket - python3-impacket
- virtiofsd
# - name: allow all users to use VMs # - name: allow all users to use VMs
# lineinfile: # lineinfile:
@ -125,9 +127,7 @@
- vm-upload - vm-upload
- vm-sync - vm-sync
- vm-link-images - vm-link-images
- vm-virtiofsd
- vm-vminfo - vm-vminfo
- virtiofsd
- vm-aria2 - vm-aria2
- uploadseed - uploadseed
- desktop-sync - desktop-sync
@ -222,7 +222,7 @@
Description=Create .vminfo.json for VMs Description=Create .vminfo.json for VMs
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/bash -c 'umask 027; /usr/local/bin/vm-vminfo > "{% if localhome %}/home{% else %}/lmn/media{% endif %}/${USER}/.vminfo.json"' ExecStart=/usr/bin/bash -c 'umask 077; /usr/local/bin/vm-vminfo > "{% if localhome %}/home{% else %}/lmn/media{% endif %}/${USER}/.vminfo.json"'
dest: /etc/systemd/user/vminfo.service dest: /etc/systemd/user/vminfo.service
mode: '0644' mode: '0644'

View file

@ -29,3 +29,5 @@
- name: Configure Wireguard - name: Configure Wireguard
ansible.builtin.include_tasks: wg_config.yml ansible.builtin.include_tasks: wg_config.yml
when: vpn is defined and vpn == "wg" when: vpn is defined and vpn == "wg"
tags:
- no_ansible_pull

View file

@ -39,3 +39,5 @@
- name: Configure WPA-Enterprise (EAP-TLS) - name: Configure WPA-Enterprise (EAP-TLS)
ansible.builtin.include_tasks: eap-tls_check-certificate.yaml ansible.builtin.include_tasks: eap-tls_check-certificate.yaml
when: wlan == 'eap-tls' when: wlan == 'eap-tls'
tags:
- no_ansible_pull