Merge branch 'fvs'

This commit is contained in:
Andreas B. Mundt 2024-03-12 19:45:10 +01:00
commit 92bacc4dd3
71 changed files with 4036 additions and 0 deletions

152
inventory.yml Normal file
View file

@ -0,0 +1,152 @@
$ANSIBLE_VAULT;1.1;AES256
63633264306266356564663836343363656536353265653630623231313232316232373364396234
3236316538656536366265323139396261613363653261620a336663633534646530613832646330
37636232373730363966333835333935346534373737373366333137393336343333386638373236
3761336362363136320a313937373361636337613464383266643763356436623163353164373133
66653337346637633231663433653465353963656561633037633131643931363439323737303636
37313630326337313333633632343232373562363662396161343866393031383564383239626461
35316334333464393064643534626166333264663932316533303963343038653162656232656165
33346162393633633736623532386333653464646335626236356466343439633130653836623462
34613337623034313834663231343830623836313037313435306633356131326235346364643234
65646361336234343937653139303034333438626238366531656230653639373636346565643035
65306130386331643037633165613635393433653537333238393365343736623932363432663166
36376266313936393931366333303863353033643962303861393536383966343932373233356234
64663834663965373038333265616532313161656337613035393736653864393562656630326561
66343634383139386133326463633333313164303638313636663361373036396235636332323563
31383337636535313139636661306430396335323237653364333838616434363931333861656262
35366431646161396234666135343663343266626538363833356666613932303766373238363030
64353666363662633836383135313738376235653565323039323962326665323334303731393535
35303161636133376631386437626432363463383536343530396533316330393537643036346438
34373939373938356466363261643936323964313232313331623237643564326239353861666636
31363462336137373365333366383635303639643263303633386637303839363531336133353035
66643039313764353935346133396233373735626163383734333736383062663038643464323137
30613239393864333365663331313634373338616238303735646532386135336664623635373934
34633366303736316438613733316230343039373434653565313631623931646363333139363332
66353934396632613330313061336336626531656334633237633536613138393938353364623137
34636231643565316435356334663736326463643131393135636366373338326233636564326165
31646632326430353036363163303839336638393962373665346237373831393639376231396362
38366438656236646436653634356235386238366233666433656161316636643035353165373837
30653036346236306162626536393937653235306236313036633135653139656261666361316336
66333835343735333937396362353330383139663635386236373531653837383231356535326330
64323735383833306463636461373061376133366261356166626466656365663730383133353062
35356136623464656662613438313535316466323538313438313963383532616232336564346534
64333736316238393232623763306539373034366566306561336161336630613133633762636131
34653565353031373436666363613164343964626164366537646438363965396366623765383333
39656630383564623735666663643836363963356563303137343064373434343336663536353562
30636165623032343136363534373761626638346432323661653730633264323837356163656132
66323533316130356364353536303536613365663536346431306634623938613531373465323738
33336132663431613338653432613863323665393532346130313332316366633439323664363361
61323233303732393637303366336336626638353931663365326266386632666235343164623336
65653861353133353661646463383133613733653561333136656665333338636331616262346661
66383862323664626632613865663933313338633839313864303366306631613565633734353564
38333137633933323538313632393233373836653365333531356336616265396163353365356162
66636637646436343834313165363830346339636532343362333133666437323862636439323438
39363934623766666464623838396465323539366433653334636365653665356237373235646661
63316135626430363637383637363366313563326165393263393539353738313936373365346364
62393931353138656633343933313337306266623834653038383437313238643530333130623666
37653532333966666564343131316463383836373831393964333664653538616662383733623462
31393131666662323236623838343630316434303961323663346262656538313464393564323132
39623037323334613632666536366363306139646431646465376535393331333366333537343561
66333862353036393930623632313737333062363464353437663966313438636261306165353032
32306533363463316661653334306261336236323366353037353461613632633934333734373038
37356230613336353938633562343632633031616635343461316333373062646137396163653235
65653661386637376330313438323630353432353064383263626339333262363566366633613531
66636237363265313266323038376163356437376532656362626436366235393335646165323061
61663162313463613431393762633165653035343061333035333439616165646237356633323631
65333861663236633064646563346233653231656430623564666265363834633635336163313864
39646538613164393161306137393065623935643937393935313438363434623538373963663461
39636530373739306333346132636533633861363530613437663632303963336634376639366134
31323032393135336136636338366634393236303334633664343865613839663966373339373239
61643765393539626337303264373133366265643438333935666633343738653761626437323861
38633030653931313762333764316134393566306435303338623832653035393766663539663164
64643136656338343334396234363139363337323033333163323261356431356362636435363838
64333866313031333937353034376337346631343338626364343034616635633138313039613464
33336439356438616234306666646132623230646161373938656239323134653435663734653235
64646338343161363364316639336538646439313939326337346339376466666136333165636161
64393732313538373334363431363836626364643834333265653135653035636330656464613731
36316266316430336166393564306462363232336335626335616165313734633466636534353035
37626637633534656235363530633866633062626537383935313763373562633737313032643462
61353561366639306437626332643633626463313663366561646331373735393032616332326562
36626266316236363930616662636134623865356632303465316266616663353039616261396663
34373037623763393263316166343131643531663134616165386636653562336135346333326461
39623964656137323439316166666132616561666235633439326464383937653738336435646134
64383661363962333838376135323864633234386662643864336132343231366266363632313666
31356339633938316636616537633335333564396631336238383063383164393238656438646131
31373164313062623930636438346437623736353830396533343730366162343164393661396263
63383837633335366534343137663634646631366234656365383235353532626163393537376537
37643661666464303133313362366230313132363738643765613735613462333861633736643664
38303537303338626139323566306536616236353563613862323435636165373434316432663864
39373033326134626530663932623230396531636433346432653262666430653866656330613861
64646265363562623663353532616663383830383633366462323235383966383263396535363536
30633266386339656230326365303330376138313432653132323138333863663961613538623566
62636565613130633166656639653264376461663737356162336331313834613662353430656431
35313237633837623562396539623336313562313765383035373036333233356461366336383838
65616164623762303033633130636364666630633465633435616666313031343863386261373033
39623638313761393434333431646638363062303437373765666435386531343339323862666132
38666635363431613530313766386165633032643836663861653434656231383333666633643266
32333336383334303763323237653934313032643261326233376335616230386234346161353431
63643733643634313133336332663933656363393861633831346261353334376464633562326534
31663066656130363965656361626464613463323764383036623063386166653839366637376135
34646466313836353234383762376165356439626532646430313730326166313839356263633966
39643731613363613564356337653931626666363337356138333335393463393865666636393532
33353366633465303765373936316334633331323930653235313231343039616238663433613365
31373866666466363965346637656435383433626563313330316132376364366532316331343534
32353462646536343538346130383139663334313331323935396537313835366161613539653164
38666265306664623666396530383464653661356562613037323362303361323537326239323661
66383230386437656132653966333062343536623861373461326333646266366430613563306436
32623132303966323638643639343537643135396566323630313038636465353830636439343465
34333136613435653037303437646439623035643539623663633561616137656138373063653662
35643830613235663931386634626463333331313864666662313534626464663635616437313061
66373039396333336639623637353839656333386635363733636636306132333130643131656438
64333561323464333631356465393665613635356265343764346531613538633137366231343032
30323832346266653238633432383334393661333737383761303238613734623534646232633562
63643066663363306432366662643362663665313431616665653239386666623864633639383431
37393630636363303438383665326663323561653335396238353138616533323965353031336465
39383132666564333661383831323539313334393632653865353662373439663438656534653133
63353562373637323436316530666639613637353866633931343536306230323765643335376563
33626661366339663161653334666232626464636134623537336237663662343833613564306366
64613132313038663761626562323832343937316136363666393438656165653666326463656565
39663032656537313563346138323731626562396566646535633830636638643463323334356365
65396564633530386237626664353734366336363339336162373139373036666339313531316265
63313266316238383363333262373038653062653561656466643837343863633562633530366433
61333663366361306233353463646636386335343131396461613562653264306432326663313436
37656538356534393065386263373264623664333738306363373465323663313831323665323862
37613366313361376137363663393334623237326663383766663736333133353931356264663163
65653439356463393938626331323835383232386638323531326661613433343833343130626531
61656461333565666633353739626266653138316163326565336461333934376235376639623338
66396662323338663966653731386633623331626339653664383366316466653661366630333966
63343930363565343464373731386237396363353562633634663935373733373663356537333134
38623362633936616533646535626533376366663535626364646536333333313664356532356130
33366164303836643730656439386537633761653032333061306261646364373961323334623831
35393832323830336633303030653963393532376466353966333039313661373263303136636534
65623830646662396236626263313763623833346236343361373465633434366131326362663034
64323161383832663765636566363932613434353031653031633262363233653630343736653436
35333435663562303135646531663033373538376366323638333031653962306563383733393235
64333763663163306537346638633763363932336337633039633865633931656136356537366466
66373639303037623066383239383335643664666362303364396465633361396536383133653939
31336438646531376364303731313430396464373432646234356637663966333665636136653761
39396138343439353565343139643165313864643132386633643836366564636130623666303933
36623563326434656439333962336132343662663936653866316163633339336361376633396530
66356639366330613335353633613831303535633538633233306666383761633864386538666266
61383861393863626434613333633430396636666433306533623432356464633764663433373931
38636538313830366438313964666430353237656639633763386332613636636462653634343831
62626638633863353165366438656465376466633966356133366263636666343632313761653766
65336461346531356631376462623737316538373363353230333436303532333835396435646634
39306438663436636532373063643138366531313936306135666466333765646630336532313238
64623530626334303638626364343864613164353732386262323439343735383164333232363434
34666165353366343566656163373738663065643132303734623533326634346238343630643838
64393833646132613761356334306539303233323466336533363938313633343834356239613034
64653935373761396331323936616334303561613762623733613637653031623564366538373630
61313733333863303630633036353535393532326134636139343532396163346537383665663838
61386637353566306134393430366633636437616565343465393333306138376462633131623332
65343035653066396533336238393365663432393537323431366532353232333036353662386363
35313162646432626632633032636162343764343463366236386438323131623766363937336436
31333332373731643164626137643162393964393334643237393133366237323562623839356534
31343163656366616638663265323939663765666563333239306563653265343765613432633430
63613231326333313437313166326236346538656236373933313262616334656264376430626263
65396261386439646562343235326630353265356362666131316664343430343534353062613232
64313234323430396139373331356434363663646339653566303638623839383330623630613662
33653863333534656665313362326338346338303434363932323635666463323536343865386235
31366134643263303630653534306364616466323538626335366231333762643961656165316534
34626532393730323034663434386234313939336262333266656134613938613433396337376531
343631376663336639636437653165333163

313
lmn-client.yml Normal file
View file

@ -0,0 +1,313 @@
## This playbook deploys a client for LinuxMuster.
#
# Use the following in the installer's preseed file:
#
# d-i preseed/late_command string \
# mkdir -p /target/home/ansible/.ssh && \
# echo "ssh-ed25519 A...YOUR.KEY...Z" >> /target/home/ansible/.ssh/authorized_keys ; \
# in-target chown -R ansible:ansible /home/ansible/.ssh/ ; \
# in-target chmod -R og= /home/ansible/.ssh/ ; \
# if [ -n "$playbook" ] ; then \
# mkdir -v /target/dev/shm ; \
# in-target mount -v -t tmpfs tmpfs /dev/shm ; \
# echo "$vaultpw" > /target/dev/shm/vaultpw ; \
# in-target ansible-pull --verbose --purge --extra-vars="run_in_installer=true" \
# --vault-password-file /dev/shm/vaultpw \
# -i localhost, --url=git://ansible.example.org/.git -C YOUR_BRANCH $playbook ; \
# fi
#
---
- name: Apply common configuration to the machines
hosts: all # desktop:laptop
remote_user: ansible
become: yes
pre_tasks:
- pause:
prompt: "Enter global-admin AD password. Leave empty to skip domain join"
echo: false
register: adpw
no_log: true
when: "ansible_cmdline.adpw is not defined"
- name: Preseed apparmor
debconf:
name: apparmor
question: apparmor/homedirs
value: >-
/srv/samba/schools/default-school/teachers/
/srv/samba/schools/default-school/students/*/
/srv/samba/schools/default-school/examusers/
vtype: string
vars_files: lmn-vault
vars:
domain: "{{ ansible_domain }}"
kerberize_uris: "{{ vault_kerberize_uris }}" ## example.org
apt_conf: "{{ vault_apt_conf }}" ## Acquire::http::Proxy "http://aptcache.example.org:3142/";
ntp_serv: "{{ vault_ntp_serv }}" ## ntp.example.org
proxy: "{{ vault_proxy }}" ## http://firewall.example.org:3128
no_proxy: "{{ vault_no_proxy }}" ## firewall.example.org,server.example.org,idam.example.org,dw.example.org
printservers: "{{ vault_printservers }}" ## ['10.0.0.1', '10.0.0.15']
## PAM mount nextcloud, remove or leave empty to skip:
web_dav: "{{ vault_web_dav }}" ## https://nc.example.org/remote.php/dav/files/%(USER)
## Local mirror for mscorefonts. Remove or leave empty to use no mirror:
mirror_msfonts: "{{ vault_mirror_msfonts }}" ## http://livebox.example.org/mscorefonts/
## Local mirror for libdvdcss. Remove or leave empty to use no mirror:
mirror_dvdcss: "{{ vault_mirror_dvdcss }}" ## http://livebox.example.org/libdvdcss/
uploadseed_pwd: "{{ vault_uploadseed_pwd }}"
rsyncsecret: "{{ vault_rsyncsecret }}"
keys2deploy: "{{ vault_keys2deploy }}" ## ['ssh-ed25519 AAAAC…uYlnS0', 'ssh-ed25519 AAAA…KTM']
localuser: "{{ vault_localuser }}" ## needed here for the (universal) pam-mount configuration
## Use grub-mkpasswd-pbkdf2 to calculate the password hash:
grub_pwd: "{{ vault_grub_pwd }}"
nfs4: false
extra_pkgs:
- vim
- mc
- tmux
- krb5-user
- debconf-utils
extra_pkgs_bpo: [] # [ linux-image-amd64 ]
roles:
- lmn_network
- role: up2date_debian
tags: upgrade
- lmn_sssd
- lmn_mount
- lmn_kde
- lmn_fvs ## school specific customization
- lmn_vm
- lmn_printer
- kerberize
- lmn_security
tasks:
## Temporary fixes and quirks:
- name: Fix 8086:4909 external graphics card
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
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
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
block:
- name: Check for old CodeBlocks
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
use_proxy: False
when: codeblocks_version.stdout is not search('svn13456')
- name: Unpack zip archive and install packages manually
shell:
cmd: unzip -d /tmp/cb/ CodeBlocks.zip && dpkg -i cb/*.deb
chdir: /tmp/
when: codeblocks_version.stdout is not search('svn13456')
when: groups.PCroom is defined and inventory_hostname in groups.PCroom
## Temporarily fix boot order
- name: Check for the buggy kernel
stat:
path: /boot/vmlinuz-6.1.0-17-amd64
register: bug
- name: Check for the fixed kernel
stat:
path: /boot/vmlinuz-6.1.0-18-amd64
register: fix
- name: Work around kernel with CIFS regression
block:
- name: Make sure kernel package -16 is available
ansible.builtin.apt:
name: linux-image-6.1.0-16-amd64
state: present
- name: Set 6.1.0-16 as default kernel in grub
lineinfile:
dest: /etc/default/grub
regexp: '^(GRUB_DEFAULT=).*'
line: '\g<1>"Debian GNU/Linux, with Linux 6.1.0-16-amd64"'
backrefs: yes
notify: Run update-grub
when: bug.stat.exists and not fix.stat.exists
- name: Set latest kernel in grub
lineinfile:
dest: /etc/default/grub
regexp: '^(GRUB_DEFAULT=).*'
line: '\g<1>0'
backrefs: yes
when: fix.stat.exists or not bug.stat.exists
notify: Run update-grub
## Clean up stuff from obsolete/faulty tasks:
- name: Remove packages we do not need anymore
ansible.builtin.apt:
name:
- unattended-upgrades
- cachefilesd
state: absent
purge: True
- name: Remove virtiofs service
file:
path: /etc/systemd/system/virtiofs@.service
state: absent
- name: Fix mount point permissions and owner
file:
path: "{{ item }}"
mode: '0755'
owner: root
group: root
loop:
- /srv/samba
- /srv/samba/schools
- name: Remove pam_mount sysvol mount
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><user>{{ localuser }}</user></or></not>
</volume>
state: absent
- name: check if rmlpr.timer is installed
stat: path=/etc/systemd/system/rmlpr.timer
register: rmlpr
- name: disable rmlpr.timer
systemd:
name: rmlpr.timer
enabled: false
when: rmlpr.stat.exists
- name: check if vmimage-torrent.service is installed
stat: path=/etc/systemd/system/vmimage-torrent.service
register: vmimagetorrent
- name: disable vmimage-torrent.service
systemd:
name: vmimage-torrent.service
enabled: false
when: vmimagetorrent.stat.exists
- name: Remove deprecated files and directories
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
- name: check if vm_usage_information.txt exists
stat: path=/lmn/vm/vm_usage_information.txt
register: vm_usage_information
- name: pre-fill vm_usage_information.txt
shell:
cmd: |
ls -tr *.qcow2 > vm_usage_information.txt || touchvm_usage_information.txt
chown lmnsynci:lmnsynci vm_usage_information.txt
chdir: /lmn/vm/
when: not vm_usage_information.stat.exists
## bookworm fixes/hacks:
- name: Work around sddm hang on shutdown
ansible.builtin.lineinfile:
path: /etc/systemd/system.conf
line: DefaultTimeoutStopSec=5s
insertafter: '^#DefaultTimeoutStopSec=.*'
#################
- name: Timestamp successfull run and send up-to-date report
ansible.builtin.shell:
cmd: date --iso-8601=seconds >> /root/.ansible/stamps && /usr/local/sbin/reporter
changed_when: False
tags: upgrade
#################
- name: Apply additional laptop configuration
hosts: laptop
remote_user: ansible
become: yes
vars_files: lmn-vault
vars:
ssid: "{{ vault_ssid }}"
wifipasswd: "{{ vault_wifipasswd }}"
localuser: "{{ vault_localuser }}"
localuser_pwd: "{{ vault_localuser_pwd }}"
roles:
- role: lmn_wlan_iwd
when: ansible_interfaces | select('search', 'wl.+') | first is defined
- lmn_networkd
- lmn_localuser
tasks:
- name: Remove deprecated files and directories (laptop-class)
file:
path: "{{ item }}"
state: absent
with_items:
- /etc/systemd/network/80-wlan-dhcp.network
- /etc/systemd/network/wlan-dhcp.network
- /etc/systemd/network/virbr1.netdev
- /etc/systemd/network/virbr1.network
- /etc/systemd/network/wlan-dhcp.network

112
lmn-vault Normal file
View file

@ -0,0 +1,112 @@
$ANSIBLE_VAULT;1.1;AES256
65646637366132333430346461646331313431363233363736306264633633396665626332623934
6439363764316132383635626137313764633162636362340a613832323934646431663632396361
36323539663238363738393131363034333561343233383238396234613434633334323235626637
6266326166333334650a353637383131313136613635333237616361353732663734613833306538
35643831653332346634616139363032656433623032383832363837653231306465353766343961
65313134303434333635306634633731313462396535383662616134653762343732366431373032
65346564663335633936636662626162636134636339343463376166666333346133616136626665
66373064303562323564363864366363663336383862336632373436666330373465636135623762
36656632346664326463646666313663343662363865343166376363313866663536623234383561
61616637653630316230626337653135396134323636303538346435663639643662646133383363
39393234333934656166366633356663326162396431383362306339623534646162613339383836
39666464666163633033653434306365393933383232653364363062356133356239626538633338
32303030626538373637323533303964643838383331366365326465666530623965613731663261
33626563386262353531353635356430333633633665393230613561633836653636636639313663
37373736346234313134313232376164633332386563383863343266626231373237643063663533
39343939393331653665633335653264376531383364376565646239616231343531336134626531
38396630363865376161313835376261646637383438323537626433323232353632386439393933
32306238643436653666333561643764633831343962643165356232663932633763396437383634
35313763323633643439376333643836613637613339343731636633313064386231613135623832
32643934376233663865326534313735633535316635393932646263313135373633636333333737
33386365363830336139323763303734383966616165356462333734333666343830356234616662
30616434623237653138623538643331373432663137323333376632343065316431313734333965
64333237396236666664613466623039626634343238363136363438663730626132646562646536
61653562666636613164373464663931356565623862306332653230396230326636363638393862
62663765373436303831613435383866323138316633336532336632383065343537326332653235
30383764396361396565323035346531396365623130636538396238613037353438346365363331
64383636376566306136653033613638323865653266383136373231353063303434636332363166
37373462353530326663386139333536616138386431373763383838336365336634366339636637
32356263653964343461393162396539303534343562653032643461626235373339666363646637
37633934393036356331363563303330316234393535376338646235663235383966396634383166
37656633373562643530663037333735396638363963323837653831396233653962356536616236
37323139306131323561303061633136303234316335363361633766623530303762663465353163
33313733386265346333663065366536616533326364613231313330373137616130373539313131
37316637653934653035373965636633626262626561313338646261313530356163356364663834
64663037666133626261386266666633336666323362373237626639373535333937336331353039
30313833353766626332376432326531616464643364313232386633613361623234653536313830
61626666333037393564303738646333643534623138366264353339326331386433343733623837
63646431646533383331356334656466316465623735613537646536623364636632323566626233
31663263623539343562383836366134366236346539386532373735633237373363636438366632
39613330336365626137363765313930623262666263393835626532333262343265313761356333
34623663633636373734326662363865396635613933393464356436393161303132663564366437
35323166643762333862656561306239343034643562316534316236636362363162306131633961
31653736613732643930666338333131373634333166633466656663636163396266306538626666
65326163646137373236633363663063383162393435356163366665653033666161333037303035
35396334663135373863646664613137666565356161353865316164633939323037323664346331
63313230333565336232663166616465363038326663633066623531363338623430313332333138
34363532643036343831353463356665646363363239643835396661356665393035303561653337
62663335636533366334643636653366303231353630633166343832313133303663393836623036
33616563636266356130356635663538343236356632376461626532626436616334386330356430
62323864323534333032643737656164343633636365623664336236626239633138626230383536
39306534383933326638666130383139316334373530373335633238316238353038643136366533
38396661626661373964363630633963373732343161663065386539306637313633303534663466
66396361373163313865373131636239613930333963663462306636626431363934343136616330
30643763343838316338643463323833666639616437336361303363393361356431356266353233
63366366336231386530313961356538613136366530343338353063343332333165323763613566
34653133633532613332376634616234383237666261363038613437646366356332636530623534
30613735663666636232623230633161663064666436333161633334356336383038386535336133
39663963333031383961643232636539313137346132336462336165313862653366303135353730
63303834313462646633333232646661623731613439633434663266303834376635346438356438
39303066663633656234633131366330356363636535373034613037363837326562306562663538
61316666643230626662663266373330643865393938313232306130376333306536393930363037
66636562396339633763656431653036646361313632313932636231626333303337366266623238
39616336656537363439373231643132363264306135346437386465326265666137353032336261
61323234643662653233353737346661373630376630343635383834313038373162643135343434
37613634333330326132623437623834363539343037643764303631613463343863643065643063
33326537376130346365323361343266663331343038663037623438666362656236613065366235
65363130356133353739623733376531636438643535633731646431653837343531313531373436
31356139393363646262623664663261613931636330663436336466633038643763336337336330
33393433346332623538653262303462636363363338346538376463373838363036343634363131
31636332343931643436393464656165616631373339336537623130343630346164383830313165
36316364653739646330663762356332393262653931613933643963626433633532353766663632
65616262666433383763363636303131373064636261616661613139373766336639376336393962
65336332303164353763636332323031363363653262386331313038646564393131303366653834
33303464303566656363343464336164363264626436306465633261386464663764636431353037
31363034323331333235346137653139323835326135653337323339346239383038363861313638
31363136353037396634326239306665303230616131363965653439656361356538623135613238
66366639333331306337323562343934313532323633613034353863623839636135393465383832
34323031326262306161613439323836646538363136336537313266343662383935373762666138
64336132356662366436326664653234303034623066313736353439396334653630643136336431
31386330326636303334313535363564383964623538656666376136366365633538386139333862
65336662653965343035306534393962616438636366646664383231316365366435663763643663
64383034336565663561626262636263616336303066396164633464313830363338303932356638
32373162313330303935316137366435373532346363386461303933643237383830623335626639
33383335653436353831303163656530613962303439383563376534663738383035346433303834
31663863343864656463643433383938393464613865356134346261663333616537663066333965
32366466373165633936323232333237383638313434366437376237653837363532393564323035
64623234376538666237653938346634346532656135333165353864383739353737643965636539
63626134376330346538656539333362633765363735656161323635323164323038633139653663
64616466353137623937333237633163646266326437663833393437336662356465336566353832
30653063666261613534393439663664326336353338393439336137386662316137666236636337
32326336396430633136333064383164373033366230333832333564616364663931653233333233
62353264343865663865323461643032633465336564646161303039356266303738306435353131
30376431616631613463313666383664343962306265613361376365353361303162653834623631
33643762306232636134666366373637353234353265303437306261383861333235383530383638
62353338323535333438376335636339386161326564623037323861343134396637366362646335
66633865386339666265396438353362333463376361306666313331353063313331636539343835
66326661386532343865653365356531663365663865666439653039643333363363653838616436
38333037353333373866316333613538373263386334626665363239353162376335373238613737
31616465326138663934356530353263653133636232396134316163343131316664633964643437
37353937653665326638663631383733646563336162643361643366633564663439396639373966
66356163343731353430626537326466393538363939313134343464643666323037356639323538
35626137303439316233313664326535396234326432396132646361663936636362626232383530
37366334333035656638383161663732393864333562373761303031353262303666626436373065
66396233393864373463363065373461353538626135663937656330326632663863353438643838
63663438356663313039616135393833623838366530353735333161663739366431393139623737
63306464353039323065623765363665663266393934653962303761383362646364373239313062
38353737663434646138303562303835373439653137656234653333313234366436623963386636
66313837393636373537663030393331613633306531306339306261636366343362333736363465
32316662666664636437393736383130663235373266393263623131643339323266633633336334
30643737396364303462363262653332346637643466323339633435323436366430626339393537
633439343732646238343833663731646631

43
lmn-www-server.yml Normal file
View file

@ -0,0 +1,43 @@
## This playbook deploys a FvS web server machine.
---
- name: apply configuration to the web server
hosts: all
remote_user: ansible
become: yes
pre_tasks:
- pause:
prompt: "Enter global-admin AD password. Leave empty to skip domain join"
echo: false
register: adpw
no_log: true
when: "ansible_cmdline.adpw is not defined"
vars:
domain: "pn.steinbeis.schule"
extra_pkgs:
- vim
- apache2
- python3-flask
extra_pkgs_bpo: [ ] # [ libreoffice ]
roles:
- up2date_debian
- lmn_sssd
- kerberize
tasks:
- name: Override home dir location
lineinfile:
dest: /etc/sssd/sssd.conf
line: override_homedir = /home/%u
- name: enable pam_mkhomedir.so
lineinfile:
dest: /etc/pam.d/common-session
line: "session optional pam_mkhomedir.so umask=0026"
insertbefore: "session optional pam_mount.so"
- name: enable apache mod userdir
apache2_module:
state: present
name: userdir

View file

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

View file

@ -0,0 +1,45 @@
- name: Install kerberos packages
apt:
name: krb5-user
state: latest
- name: Kerberize sshd server
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/kerberize.conf
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
content: |
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
- name: Check if firefox is available
stat: path=/etc/firefox-esr/firefox-esr.js
register: firefox
- name: Kerberize firefox for sites in the local domain
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
file:
path: "/etc/chromium/policies/managed"
state: directory
- name: Kerberize chromium for sites in the local domain
copy:
dest: /etc/chromium/policies/managed/idam.json
content: |
{
"AuthServerAllowlist": "idam.steinbeis.schule"
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,63 @@
#!/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 )"
IDENTITY="${id%%:*}"
NUM0="${id##*:}"
NUM1=$(( NUM0 + 1 ))
NUM2=$(( NUM0 + 2 ))
patch="
--- a/$file
+++ b/$file
@@ -98,9 +98,33 @@
<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>$IDENTITY/${NUM1}</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/${NUM2}</ID>
+ <isSystemItem>true</isSystemItem>
+ </metadata>
+ </info>
+ </bookmark>
<bookmark href=\"remote:/\">
<title>Network</title>
<info>
<metadata owner=\"http://freedesktop.org\">
<bookmark:icon name=\"folder-network\"/>
"
echo "$patch" | patch -z '.lmn' --fuzz=0 --backup "$file"

View file

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

View file

@ -0,0 +1,82 @@
{
"policies": {
"Proxy": {
"Mode": "system"
},
"OverrideFirstRunPage": "https://www.steinbeisschule-reutlingen.de",
"Homepage": {
"URL": "https://www.debian.org",
"Locked": false,
"StartPage": "previous-session"
},
"DisplayBookmarksToolbar": true,
"ManagedBookmarks": [
{
"toplevel_name": "FvS-Reutlingen"
},
{
"url": "https://www.steinbeisschule-reutlingen.de",
"name": "FvS-Homepage"
},
{
"url": "https://server.pn.steinbeis.schule",
"name": "Schulkonsole/Passwort ändern"
},
{
"url": "https://dw.steinbeis.schule",
"name": "FvS-Hilfesystem"
},
{
"url": "https://mail.steinbeis.schule",
"name": "FvS-eMail"
},
{
"url": "https://nc.steinbeis.schule",
"name": "FvS-Nextcloud"
},
{
"url": "https://moodle.steinbeis.schule",
"name": "FvS-Moodle"
},
{
"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"
}
}
}

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

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

@ -0,0 +1,258 @@
---
- 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
apt:
name:
- atftp
- calligraplan
- cmake ## for kdevelop
- codelite
- codelite-plugins
- curl
- elpa-color-theme-modern
- elpa-magit
- emacs
- filezilla
- freeplane
- git
- gitg
- gitk
- htop
- jupyter
- kdevelop
- kdevelop-php
- kdevelop-python
- krita
- libnotify-bin ## needed for pwroff script
- links2
- minder
- neovim
- net-tools
- netcat-openbsd
- nmap
- pdf-presenter-console
- php-cli
- pipx
- planner
- pulseview
- python3-websockets
- qpdfview
- shellcheck
- sigrok
- sigrok-cli
- texlive-latex-recommended
- 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
apt:
name:
- plasma-discover
autoremove: true
state: absent
- name: Make sure wireshark works for all users after installation and upgrades
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/92wireshark4all
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/
- name: Copy some scripts
copy:
src: "{{ item }}"
dest: /usr/local/sbin/
mode: 0755
loop:
- pwroff
- bootorder.sh
- reporter
- name: Provide services and timers for some scripts
copy:
src: "{{ item }}"
dest: "/etc/systemd/system/{{ item }}"
mode: 0644
loop:
- pwroff.service
- pwroff.timer
- reporter.service
- reporter.timer
- name: Enable pwroff.timer
systemd:
name: "{{ item }}"
enabled: true
loop:
- pwroff.timer
- reporter.timer
- name: PXE first boot order
command: /usr/local/sbin/bootorder.sh
register: cmd_result
changed_when: cmd_result.stdout is not search('Nothing to do.')
when: groups.PCroom is defined and inventory_hostname in groups.PCroom
- 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
- name: Configure KDE dolphin menu
ansible.builtin.copy:
src: lmn-dolphin.sh
dest: /etc/profile.d/
- 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
blockinfile:
path: /etc/xdg/kdeglobals
create: true
block: |
[KDE]
SingleClick=false
[KDE Action Restrictions][$i]
action/start_new_session=false
#action/switch_user=false
#action/lock_screen=false
- name: Shut down when idle for too long
ansible.builtin.copy:
dest: /etc/xdg/powermanagementprofilesrc
content: |
[AC][SuspendSession][$i]
idleTime=7200000
suspendType=8
- name: Start with empty session by default
ansible.builtin.copy:
dest: /etc/xdg/ksmserverrc
content: |
[General]
loginMode=emptySession
- name: Fix primary screen for class room PCs with projector
block:
- name: Switch projector off for login
lineinfile:
dest: /usr/share/sddm/scripts/Xsetup
line: 'xrandr --output {{ dual_screen[0] }} --off'
- 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
content: |
[Desktop Entry]
Name=fix-screen
Exec=lmn-fix-screen
Type=Application
NoDisplay=true
when: dual_screen is defined
#- 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
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
blockinfile:
path: /usr/share/sddm/themes/debian-breeze/Main.qml
marker: // {mark} ANSIBLE MANAGED BLOCK
insertbefore: '\s+//Footer'
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
}

View file

@ -0,0 +1,10 @@
#!/usr/bin/bash
#
# Set the primary screen after login
#
set -eu
sleep 5
if [[ "$XDG_SESSION_TYPE" = wayland ]] ; then
kscreen-doctor output.{{ dual_screen[1] }}.priority.1
fi

View file

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

View file

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

View file

@ -0,0 +1,170 @@
---
- name: Install desktop and educational packages
apt:
name:
- akonadi-backend-sqlite
- arduino
- bluefish
- calligra
- codeblocks
- dia
- flameshot
- freecad
- fritzing
- ghex
- gimp
- inkscape
- kde-full
- keepassxc
- librecad
- mu-editor
- 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
autoremove: true
state: latest
- name: Add {{ ansible_distribution_release }}-backports
apt_repository:
repo: deb http://deb.debian.org/debian/ {{ ansible_distribution_release }}-backports main non-free-firmware
state: present
update_cache: true
- name: Install extra packages from backports
apt:
name:
- filius
- kicad
- kicad-doc-de
- libreoffice
- libreoffice-l10n-de
state: latest # noqa package-latest
autoremove: true
default_release: "{{ ansible_distribution_release }}-backports"
- name: Create akonadi config dir
ansible.builtin.file:
path: /etc/xdg/akonadi/
state: directory
mode: '0755'
- name: Use sqlite in akonadi
blockinfile:
path: /etc/xdg/akonadi/akonadiserverrc
create: true
block: |
[%General]
Driver=QSQLITE3
## Akonadi complains if not set:
- name: Add home dirs to apparmor
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
blockinfile:
path: /etc/sddm.conf
create: true
block: |
[Users]
MaximumUid=999
RememberLastUser=false
RememberLastSession=false
- name: Enable wake-on-lan for all ethernet connections
ansible.builtin.copy:
dest: /etc/NetworkManager/conf.d/wake-on-lan.conf
content: |
[connection]
ethernet.wake-on-lan=64
- name: Create directory to avoid suspend
ansible.builtin.file:
path: /etc/systemd/sleep.conf.d/
state: directory
mode: '0755'
- name: Avoid suspending
blockinfile:
path: /etc/systemd/sleep.conf.d/nosuspend.conf
create: true
block: |
[Sleep]
AllowSuspend=no
AllowHibernation=no
AllowSuspendThenHibernate=no
AllowHybridSleep=no
- name: Deploy dolphin script
copy:
src: lmn-fix-dolphin.sh
dest: /usr/local/bin/
mode: '0755'
################# general settings ##################
- name: Enable boot splash screen
replace:
dest: "/etc/default/grub"
regexp: '"quiet"$'
replace: '"quiet splash"'
notify: Run update-grub
- name: Protect editing grub menu entries
blockinfile:
path: /etc/grub.d/40_custom
block: |
set superusers='root'
export superusers
password_pbkdf2 root {{ grub_pwd }}
notify: Run update-grub
- name: Allow booting grub menu entries
lineinfile:
dest: /etc/grub.d/10_linux
line: CLASS="${CLASS} --unrestricted"
insertafter: '^CLASS=.*'
firstmatch: true
notify: Run update-grub
- name: Disable Grub submenus
lineinfile:
dest: /etc/default/grub
line: 'GRUB_DISABLE_SUBMENU=true'
insertafter: '^GRUB_TIMEOUT=.*'
notify: Run update-grub
- name: Grub timeout
lineinfile:
dest: /etc/default/grub
regexp: '^(GRUB_TIMEOUT=).*'
line: '\g<1>1'
backrefs: yes
notify: Run update-grub
- name: Keyboard compose key
lineinfile:
dest: /etc/default/keyboard
regexp: '^(XKBOPTIONS=).*'
line: '\1"compose:caps"'
backrefs: yes

View file

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

View file

@ -0,0 +1,2 @@
smb_server: "server"
smb_share: "default-school/"

View file

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

View file

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

View file

@ -0,0 +1,102 @@
---
- name: Install needed packages
apt:
name:
- libpam-mount
- cifs-utils
- nfs-common
- hxtools
- davfs2
state: latest
- name: Configure pam_mount for Webdav Nextcloud
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=1010,grpid,file_mode=0770,dir_mode=0770,forceuid,forcegid"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user><user>{{ localuser }}</user></or></not>
</volume>
insertafter: "<!-- Volume definitions -->"
when: web_dav is defined and web_dav | length > 0
- name: Configure pam_mount for LMN homes
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=1010,file_mode=0770,dir_mode=0770,mfsymlinks,nobrl,actimeo=600{{ cifsopt | default(",cache=loose") }}"
><not><or><user>root</user><user>ansible</user><user>Debian-gdm</user><user>sddm</user><user>{{ localuser }}</user></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
- 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 home directory
ansible.posix.mount:
src: server:/default-school
path: /srv/samba/schools/default-school
opts: sec=krb5p,_netdev,x-systemd.automount,x-systemd.idle-timeout=60
state: present
fstype: nfs4
when: nfs4

View file

@ -0,0 +1,29 @@
---
- name: Deploy http proxy config
copy:
dest: /etc/environment.d/10-lmn-proxy.conf
content: |
http_proxy="{{ proxy }}"
https_proxy="{{ proxy }}"
ftp_proxy="{{ proxy }}"
no_proxy="{{ no_proxy }}"
- name: Set aptcache
ansible.builtin.copy:
dest: /etc/apt/apt.conf
content: >
{{ apt_conf }}
- name: Set NTP server
ansible.builtin.lineinfile:
path: /etc/systemd/timesyncd.conf
insertafter: '^#NTP='
line: NTP={{ ntp_serv }}
- name: Add proposed-updates repository
apt_repository:
repo: >
deb http://deb.debian.org/debian/ {{ ansible_distribution_release }}-proposed-updates
main non-free-firmware
state: present
when: groups.R202 is defined and inventory_hostname in groups.R202

View file

@ -0,0 +1,90 @@
---
# temporary disable network manager
- name: Use iwd but ignore interfaces managed by systemd-networkd (wlan0,en*)
blockinfile:
dest: /etc/NetworkManager/NetworkManager.conf
block: |
[device]
match-device=interface-name:wlx*
wifi.backend=iwd
[connection]
match-device=interface-name:wlx*
ipv4.route-metric=2048
[keyfile]
unmanaged-devices=interface-name:wlan0;interface-name:en*;interface-name:vm*
- name: Enable Networkmanager
ansible.builtin.systemd:
name: NetworkManager.service
#state: started
enabled: true
- name: Configure systemd-networkd virbr1.netdev
ansible.builtin.copy:
dest: "/etc/systemd/network/30-{{ item }}.netdev"
content: |
[NetDev]
Name={{ item }}
Kind=bridge
loop:
- virbr1
- virbr2
- name: Set MAC-Address of virtio1 to ethernet nic
ansible.builtin.lineinfile:
path: /etc/systemd/network/30-virbr1.netdev
line: "MACAddress={{ ansible_facts[ansible_interfaces | select('search', '^en.*') | first].macaddress }}"
when: ansible_interfaces | select('search', '^en.*')
- name: Configure systemd-networkd ethernet.network
ansible.builtin.copy:
dest: /etc/systemd/network/40-ethernet.network
content: |
[Match]
Name=enp*
[Network]
Bridge=virbr1
- name: Configure systemd-networkd ethernet-usb.network
ansible.builtin.copy:
dest: /etc/systemd/network/40-ethernet-usb.network
content: |
[Match]
Name=enx*
[Network]
Bridge=virbr2
- name: Configure systemd-networkd virbr1.network
ansible.builtin.copy:
dest: /etc/systemd/network/50-virbr1.network
content: |
[Match]
Name=virbr1
[Network]
DHCP=yes
[DHCPv4]
UseDomains=true
RouteMetric=512
- name: Configure systemd-networkd virbr2.network
ansible.builtin.copy:
dest: /etc/systemd/network/50-virbr2.network
content: |
[Match]
Name=virbr2
[Network]
DHCP=yes
[DHCPv4]
UseDomains=false
RouteMetric=2048
- name: Configure systemd-networkd wlan.network
ansible.builtin.copy:
dest: /etc/systemd/network/60-wlan0-dhcp.network
content: |
[Match]
Name=wlan0
[Network]
DHCP=yes
[DHCPv4]
UseDomains=true

View file

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

View file

@ -0,0 +1,59 @@
---
- name: Install cups
apt:
name:
- cups
state: latest
- name: Disable cups printer browsing
lineinfile:
dest: /etc/cups/cupsd.conf
regexp: '^(Browsing ).*'
line: '\1No'
backrefs: yes
- name: Listen on VMBridge
lineinfile:
dest: /etc/cups/cupsd.conf
line: 'Listen 192.168.122.1:631'
insertafter: 'Listen localhost:631'
state: present
- name: Allow access from localhost and from VM
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: Disable cups-browsed
ansible.builtin.systemd:
name: cups-browsed.service
state: stopped
enabled: no
- name: Install install-printers.sh
template:
src: install-printers.sh.j2
dest: /usr/local/bin/install-printers.sh
mode: 0755
- name: Install lmn-install-printers sudoers
copy:
src: 90-lmn-install-printers
dest: /etc/sudoers.d/
mode: 0660
owner: root
group: root
- name: Run printer script from /etc/profile.d/
copy:
dest: /etc/profile.d/lmn-printer.sh
content: |
[[ "${UID}" -gt 10000 ]] && (sudo /usr/local/bin/install-printers.sh > /dev/null &)

View file

@ -0,0 +1,48 @@
#!/usr/bin/bash
set -eu
printservers="{{ printservers | join(' ') }}"
hostgroup="$(id -Gn "${HOSTNAME^^}$")"
usergroup="$(id -Gn "${SUDO_USER}")"
installedprinters="$(lpstat -p | cut -f 2 -d" ")"
cat <<EOF
Hostgroups: ${hostgroup}
Usergroups: ${usergroup}
Local print queues:
${installedprinters}
EOF
## Remove all printers not wanted:
for p in $installedprinters ; do
if [[ ! "${hostgroup}" =~ "$p" ]] && [[ ! "${usergroup}" =~ "$p" ]] ; then
echo "Removing print queue '$p'."
lpadmin -x "$p"
fi
done
## Prepare .printerlist.csv
mkdir -p "/lmn/media/${SUDO_USER}"
echo "Name;IppURL" > "/lmn/media/${SUDO_USER}/.printerlist.csv"
## Add all printers needed:
for ps in $printservers ; do
echo "Checking print server '$ps' for available printers:"
printers="$(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."
fi
echo "$p;ipp://192.168.122.1/printers/$p" >> "/lmn/media/${SUDO_USER}/.printerlist.csv"
fi
done
done

View file

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

View file

@ -0,0 +1,29 @@
---
- name: Deploy SSH keys
ansible.posix.authorized_key:
user: ansible
key: "{{ item }}"
loop: "{{ keys2deploy }}"
- 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
- name: Limit SSH access to user ansible
ansible.builtin.blockinfile:
dest: /etc/ssh/sshd_config.d/local.conf
create: true
block: |
PasswordAuthentication no
AllowUsers ansible
notify: Reload sshd

View file

@ -0,0 +1,3 @@
- name: restart sssd
service: name=sssd state=restarted enabled=yes
listen: "restart sssd"

View file

@ -0,0 +1,25 @@
---
- name: Install needed packages
apt:
name:
- sssd-ad
- sssd-tools
- adcli
state: latest
- name: Provide user identities from AD
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
shell:
cmd: >
echo "{{ ansible_cmdline.adpw | default('') + adpw.user_input | default('') }}" |
adcli join --stdin-password -U global-admin {{ domain | upper }}
when: >
ansible_cmdline.adpw | default('') | length > 0 or
adpw.user_input | default('') | length > 0

View file

@ -0,0 +1,19 @@
[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

View file

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

View file

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

View file

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

View file

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

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

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

66
roles/lmn_vm/files/mounthome.sh Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/bash
set -eu
home="$(getent passwd "$SUDO_UID" | cut -d : -f 6 | sed 's|/srv/samba/schools/default-school/||')"
exit_script() {
echo "unmounting media - terminated by trap!" >> "/tmp/${SUDO_UID}-exit-mount.log"
findmnt "/lmn/media/${SUDO_USER}/oldhome" && umount "/lmn/media/${SUDO_USER}/oldhome" && rmdir "/lmn/media/${SUDO_USER}/oldhome"
findmnt "/lmn/media/${SUDO_USER}/oldprojects" && umount "/lmn/media/${SUDO_USER}/oldprojects" && rmdir "/lmn/media/${SUDO_USER}/oldprojects"
findmnt "/lmn/media/${SUDO_USER}/linuxhome" && umount "/lmn/media/${SUDO_USER}/linuxhome" && rmdir "/lmn/media/${SUDO_USER}/linuxhome"
trap - SIGHUP SIGINT SIGTERM # clear the trap
kill -- -$$ # Sends SIGTERM to child/sub processes
}
exit_script_home() {
echo "unmounting media - terminated by trap!" >> "/tmp/${SUDO_UID}-exit-mount.log"
umount "/lmn/media/${SUDO_USER}/home"
trap - SIGHUP SIGINT SIGTERM # clear the trap
kill -- -$$ # Sends SIGTERM to child/sub processes
}
##########################
if [[ "$#" -gt 0 ]] && [[ "$1" = '-u' ]]; then
findmnt "/lmn/media/${SUDO_USER}/home" && umount "/lmn/media/${SUDO_USER}/home" && rmdir "/lmn/media/${SUDO_USER}/home"
#findmnt "/lmn/media/${SUDO_USER}/share" && umount "/lmn/media/${SUDO_USER}/share" && rmdir "/lmn/media/${SUDO_USER}/share"
findmnt "/lmn/media/${SUDO_USER}/oldhome" && umount "/lmn/media/${SUDO_USER}/oldhome" && rmdir "/lmn/media/${SUDO_USER}/oldhome"
findmnt "/lmn/media/${SUDO_USER}/oldprojects" && umount "/lmn/media/${SUDO_USER}/oldprojects" && rmdir "/lmn/media/${SUDO_USER}/oldprojects"
findmnt "/lmn/media/${SUDO_USER}/linuxhome" && umount "/lmn/media/${SUDO_USER}/linuxhome" && rmdir "/lmn/media/${SUDO_USER}/linuxhome"
elif [ "$#" -gt 0 ] && [ "$1" = '-o' ]; then
echo "Einbinden der Daten des alten/bisherigen Systems (PaedML Novell)."
echo "Bitte den Username und Passwort aus dem ALTEN System eingeben."
read -rp "Username: " username
read -srp "Passwort: " PASSWD
export PASSWD
echo
mkdir -p "/lmn/media/${SUDO_USER}/oldhome"
mkdir -p "/lmn/media/${SUDO_USER}/oldprojects"
#errcode=$(mount -t cifs -o "username=${username},uid=${SUDO_UID},gid=1010,file_mode=0770,dir_mode=0770,forceuid,forcegid" \
# "//192.168.1.2/DOCS/fvs" "/lmn/media/${SUDO_USER}/oldhome")
#if [[ ! "${errcode}" ]]; then
mount -t cifs -o "username=${username},uid=${SUDO_UID},gid=1010,file_mode=0770,dir_mode=0770,forceuid,forcegid,nobrl,mfsymlinks" \
"//192.168.1.2/DOCS/fvs" "/lmn/media/${SUDO_USER}/oldhome"
mount -t cifs -o "username=${username},uid=${SUDO_UID},gid=1010,file_mode=0770,dir_mode=0770,forceuid,forcegid,nobrl,mfsymlinks" \
"//192.168.1.2/DATA/fvs/projekte" "/lmn/media/${SUDO_USER}/oldprojects"
#echo "Mounting successfull!"
echo "Einbindung erfolgreich!"
echo "Dieses Fenster bitte nicht schließen!"
#echo "Um weiter zu arbeiten: <Strg> + <Z>"
trap exit_script SIGHUP SIGINT SIGTERM
sleep infinity
elif [ "$#" -gt 0 ] && [ "$1" = '-l' ]; then
echo "Einbinden des Netboot-Home-Verzeichnises. Daten des alten/bisherigen Systems (PaedML Novell)."
echo "Bitte den Username und Passwort aus dem ALTEN System (PaedML Novell) eingeben."
echo "Bitte auch Groß- und Kleinschreibung achten."
read -rp "Username: " username
mkdir -p "/lmn/media/${SUDO_USER}/linuxhome"
mount -t fuse -o "allow_other,uid=${SUDO_UID},gid=1010,reconnect" \
"sshfs#${username}@home.steinbeisschule-reutlingen.de:" "/lmn/media/${SUDO_USER}/linuxhome"
#echo "Mounting successfull!"
echo "Einbindung erfolgreich!"
echo "Dieses Fenster bitte nicht schließen!"
#echo "Um weiter zu arbeiten: <Strg> + <Z>"
trap exit_script SIGHUP SIGINT SIGTERM
sleep infinity
fi

View file

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

View file

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

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

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

View file

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

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

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

BIN
roles/lmn_vm/files/virtiofsd Executable file

Binary file not shown.

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

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

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

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

View file

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

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

@ -0,0 +1,72 @@
#!/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="/tmp/${UID}/.config"
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
mac="$(ip link | grep -A1 "vm-macvtap" | \
sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\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=vm-macvtap,xpath1.set=./target/@managed=no"
for vm in $(virsh --connect qemu:///session list --all --name) ; do
if virsh domiflist "$vm" | grep -q "$mac" ; then
type="user"
virt-manager &
break
fi
done
## 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" \
--osinfo debiantesting \
--nodisks --import --noreboot --transient \
--controller type=scsi,model=virtio-scsi \
--install kernel="$kernel",initrd="$initrd",kernel_args="${kargs[*]}" \
--network "type=$type" "${arg[@]}"
# --filesystem "$HOME",share
# mount -t 9p share /mnt

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

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

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

@ -0,0 +1,256 @@
#!/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
--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-torrents and machine definition file
sudo -u lmnsynci /usr/local/bin/vm-sync get_file "${VM_NAME}.qcow2.torrent"
[[ -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"
}
QEMU='qemu:///session'
NEWCLONE=0
PERSISTENT=0
LIBVIRTOSINFO="win10"
LIBVIRTOPTS=""
NO_VIEWER=0
source /etc/lmn/vm.conf
TEMP=$(getopt -o no:ps --long new,no-viewer,options:,persistent,system,memory:,data-disk:,heads:,cpu:,bridge:,os:,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=$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
;;
--os )
LIBVIRTOSINFO=$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
# check, if we have to start squid
if ! killall -s 0 squid; then
echo "starting squid."
/usr/sbin/squid -f /etc/squid/squid-usermode.conf
fi
# because virsh has problems with long pathnames, using diffent configdir
export XDG_CONFIG_HOME="/tmp/${UID}/.config"
if ! virsh --connect="${QEMU}" list | grep "${VM_NAME}-clone"; then
echo "VM not yet running."
sudo /usr/local/bin/desktop-sync
check_images
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 "${VM_NAME}-clone" || echo "${VM_NAME}-clone did not exist"
#trap exit_script SIGHUP SIGINT SIGTERM
# start virtiofsd-service
[[ "${QEMU}" = 'qemu:///session' ]] && sudo /usr/local/bin/vm-virtiofsd "${VM_NAME}"
# finally, create the new vm
virt-install \
--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 \
${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

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

@ -0,0 +1,168 @@
#!/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")
echo Size available: $(df --block-size=1 --output=avail "${VM_SYSDIR}" | sed 1d)
while [[ $(get_image_size "${VM_NAME}.qcow2.torrent") -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
sudo vm-aria2 stop "${FILENAME%.qcow2}"
rm -f "${FILENAME}"
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

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

@ -0,0 +1,60 @@
#!/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"
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"
}
source /etc/lmn/vm.conf
while getopts ':p' OPTION; do
case "$OPTION" in
p)
VM_DIR="${VM_DIR_PERSISTENT}"
;;
?)
show_help
exit 1
;;
esac
done
shift "$((OPTIND -1))"
# if less than one arguments supplied, display usage
if [[ $# -ne 1 ]] ; then
show_help
exit 1
fi
VM_NAME=$1
upload_image

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

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

View file

@ -0,0 +1,17 @@
# variables for LMN VM submodule
SEEDBOX_HOST="seedbox.pn.steinbeis.schule"
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="/tmp/${SUDO_UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${SUDO_UID}"
else
VM_DIR="/tmp/${UID}/vm"
VM_DIR_PERSISTENT="/var/vm/${UID}"
fi

View file

@ -0,0 +1,11 @@
- name: Reload libvirtd
systemd:
name: libvirtd.service
listen: reload libvirtd
- name: Run update-desktop-database
command: update-desktop-database "{{ item }}"
loop:
- /usr/local/share/applications
- /usr/local/share/desktop-directories
- /etc/xdg/menus/applications-merged

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

@ -0,0 +1,277 @@
---
# 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
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
apt:
name:
- aria2
- mktorrent
- libvirt-daemon-system
- virt-manager
- dialog # for vm-netboot menu
state: latest
autoremove: true
#- 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
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><user>{{ localuser }}</user></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><user>{{ localuser }}</user></or></not>
</volume>
insertafter: "<!-- END ANSIBLE MANAGED BLOCK .* -->"
- name: Use umount script for proper cleanup
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
lineinfile:
path: /etc/libvirt/qemu/networks/default.xml
line: ' <domain name="{{ ansible_domain }}" localOnly="no"/>'
insertafter: '</ip>'
- name: Autostart default network for VMs
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
file:
path: /etc/lmn
state: directory
- name: Create /lmn directory
file:
path: /lmn
state: directory
- name: Create /lmn/media directory
file:
path: /lmn/media
state: directory
mode: '1777'
- name: Create /var/vm directory
file:
path: /var/vm
state: directory
mode: '1777'
- name: Create vm directory
file:
path: /lmn/vm
state: directory
owner: lmnsynci
group: lmnsynci
mode: 0755
- name: Install squid
apt:
name:
- squid
state: latest
autoremove: true
- name: Disable squid
systemd:
name: squid
enabled: false
state: stopped
- name: Deploy squid user mode configuration
template:
src: squid-usermode.conf.j2
dest: /etc/squid/squid-usermode.conf
mode: '0644'
- name: Deploy sudo configurations
copy:
src: "{{ item }}"
dest: "/etc/sudoers.d/90-{{ item }}"
owner: root
group: root
mode: '0700'
loop:
- lmn-mounthome
- lmn-vm
- name: Deploy vmimages scripts
copy:
src: "{{ item }}"
dest: /usr/local/bin/
owner: root
group: root
mode: '0755'
loop:
- mounthome.sh
- 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.copy:
src: vm.conf
dest: /etc/lmn/vm.conf
owner: root
group: root
- name: Deploy aria2 RPC password file
ansible.builtin.copy:
dest: /etc/lmn/uploadseed.conf
owner: root
group: lmnsynci
mode: '0640'
content: |
{{ uploadseed_pwd }}
- name: Prepare directory for aria2 dht.dat
ansible.builtin.file:
path: /var/cache/aria2/
state: directory
owner: lmnsynci
group: lmnsynci
- name: Prepare directory for qemu bridge config
ansible.builtin.file:
path: /etc/qemu/
state: directory
- name: Deploy bridge.conf needed for qemu session mode
ansible.builtin.copy:
dest: /etc/qemu/bridge.conf
content: |
allow virbr0
allow virbr1
allow virbr2
- name: Configure macvtap interface
ansible.builtin.copy:
dest: /etc/NetworkManager/system-connections/macvlan-vm-macvtap.nmconnection
mode: '0600'
content: |
[connection]
id=macvlan-vm-macvtap
type=macvlan
interface-name=vm-macvtap
[macvlan]
mode=2
parent={{ ansible_default_ipv4['interface'] }}
tap=true
[ipv4]
method=disabled
[ipv6]
method=disabled
[proxy]
- name: Adjust interface permissions for user mode VMs
ansible.builtin.copy:
dest: /etc/udev/rules.d/80-macvlan.rules
content: |
SUBSYSTEMS=="net", KERNELS=="vm-macvtap", MODE="0666"
- 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/
notify: Run update-desktop-database
- name: Copy fvs.menu
ansible.builtin.copy:
src: fvs.menu
dest: /etc/xdg/menus/applications-merged/
notify: Run update-desktop-database
- name: check if sync.desktop is installed
stat: path=/usr/local/share/applications/sync.desktop
register: syncdesktop
- name: remove deprecated desktop-files
ansible.builtin.shell: rm -f /usr/local/share/applications/*.desktop
when: not syncdesktop.stat.exists
notify: Run update-desktop-database
- name: Copy initial sync starter
ansible.builtin.copy:
src: sync.desktop
dest: /usr/local/share/applications/
notify: Run update-desktop-database
- name: Start virt-manager in session mode by default
ansible.builtin.copy:
dest: /usr/local/bin/virt-manager
content: |
#!/usr/bin/sh
exec /usr/bin/virt-manager --connect qemu:///session $@
mode: '0755'
- name: Copy vm-netboot script
ansible.builtin.copy:
src: vm-netboot
dest: /usr/local/bin/
mode: '0755'

View file

@ -0,0 +1,11 @@
acl local-servers dstdomain .{{ domain }}
cache_peer firewall.{{ domain }} 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
http_port 192.168.122.1:3128
http_access allow all

View file

@ -0,0 +1,60 @@
## Make sure to use an initrd providing firmware:
## wget https://cdimage.debian.org/cdimage/firmware/testing/current/firmware.cpio.gz
## cat initrd.gz firmware.cpio.gz > initrd-fw.gz
---
- name: Install packages related to iwd and wifi
ansible.builtin.apt:
name:
- iwd
- systemd-resolved
- firmware-realtek # for our wifi sticks
- rfkill
state: latest
- name: Disable wpa-supplicant
ansible.builtin.systemd:
name: wpa_supplicant.service
enabled: False
- name: Enable iwd
ansible.builtin.systemd:
name: iwd.service
enabled: True
- name: Prepare directory for iwd
file:
path: /var/lib/iwd/
state: directory
- name: Configure iwd for wifi device
ansible.builtin.copy:
dest: /var/lib/iwd/{{ ssid }}.psk
content: |
[Security]
Passphrase={{ wifipasswd }}
- name: Enable systemd-networkd
ansible.builtin.systemd:
name: systemd-networkd.service
enabled: True
- name: Provide service to enable WiFi on boot
ansible.builtin.copy:
dest: /etc/systemd/system/enable-wifi.service
content: |
[Unit]
Description=Switch WiFi on
[Service]
Type=oneshot
ExecStart=/usr/sbin/rfkill enable wifi
[Install]
WantedBy=multi-user.target
- name: Enable the enable-wifi service
ansible.builtin.systemd:
name: enable-wifi.service
enabled: True
daemon_reload: True

View file

@ -0,0 +1,24 @@
---
- name: Configure WLAN for devices
community.general.nmcli:
conn_name: "{{ ssid }}"
type: wifi
ssid: "{{ ssid }}"
ifname: "{{ ansible_interfaces | select('search', 'wl.+') | first }}"
wifi_sec:
key-mgmt: wpa-psk
psk: "{{ wifipasswd }}"
autoconnect: true
state: present
when: |
not run_in_installer|default(false)|bool and
ansible_interfaces | select('search', 'wl.+') | first is defined
- name: Provide WLAN config during installation
template:
src: ssid.nmconnection.j2
dest: "/etc/NetworkManager/system-connections/{{ ssid }}.nmconnection"
mode: '0600'
when: |
run_in_installer|default(false)|bool and
ansible_interfaces | select('search', 'wl.+') | first is defined

View file

@ -0,0 +1,21 @@
[connection]
id=FVS-devices
type=wifi
interface-name={{ ansible_interfaces | select('search', 'wl.+') | first }}
[wifi]
mode=infrastructure
ssid=FVS-devices
[wifi-security]
key-mgmt=wpa-psk
psk={{ wifipasswd }}
[ipv4]
method=auto
[ipv6]
addr-gen-mode=default
method=auto
[proxy]

View file

@ -0,0 +1,2 @@
extra_pkgs: ""
extra_pkgs_bpo: ""

View file

@ -0,0 +1,49 @@
# Update lists and upgrade packages.
- name: update apt package lists
apt:
update_cache: true
cache_valid_time: 86400
- block:
- name: upgrade packages
apt:
upgrade: dist
autoremove: true
autoclean: true
rescue:
- name: Looks like dpkg was interrupted, configure manually
command:
cmd: dpkg --configure -a
- name: Try again to upgrade packages
apt:
upgrade: dist
autoremove: true
autoclean: true
- name: install etckeeper
apt:
name: etckeeper
state: latest # noqa package-latest
- name: install extra packages from stable
apt:
name: "{{ extra_pkgs }}"
state: latest # noqa package-latest
when: extra_pkgs|length
- name: add {{ ansible_distribution_release }}-backports
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
- name: install extra packages from backports
apt:
name: "{{ extra_pkgs_bpo }}"
state: latest # noqa package-latest
default_release: "{{ ansible_distribution_release }}-backports"
when: extra_pkgs_bpo|length

24
tools/collector Executable file
View file

@ -0,0 +1,24 @@
#!/usr/bin/bash
#
# collect messages from reporter and drop them into log files
#
set -eu
port=1234
#logdir="/var/log/collector"
logdir="/tmp/collector"
mkdir -vp "$logdir"
nc -k -l -u -p "$port" | while read line ; do
sndr="${line%% *}"
msg="${line#* }"
if [[ "$sndr" =~ [a-z0-9]+ ]] ; then
if [[ "$msg" =~ ^-------\ .+\ -------$ ]] ; then
echo "$(date --rfc-3339=seconds) → Report from '$sndr' received."
echo "$msg" > "$logdir/$sndr"
else
echo "$msg" >> "$logdir/$sndr"
fi
fi
done

93
tools/emitter Executable file
View file

@ -0,0 +1,93 @@
#!/usr/bin/bash
#
# Run ansible on all hosts older than the latest git commit.
# Use argument "$(date)" to update all machines independent
# of the last ansible run.
#
set -eu
## maximal age of file in minutes:
age="15"
pbook="lmn-client"
logdir="/tmp/collector"
debug=false
## date of latest git commit in ansible repository:
git_date="$(date --iso-8601=seconds --date="$(git log --date=iso-strict | \
head -3 | sed -nE "s/^Date:\s+(.+)$/\1/p")")"
echo "Latest commit in git at: $git_date."
if [[ $# = 0 ]] ; then
timestamp="$git_date"
else
timestamp="$(date --iso-8601=seconds --date="$1")"
fi
echo "Time stamp at: $timestamp."
#dir="$(mktemp -d)"
dir="/tmp/emitter"
mkdir -vp "$dir"
hlist=""
n=0
running=0
ansible_arg=""
find_outdated(){
hlist=""
n=0
running=0
ansible_arg="--tags=upgrade"
while IFS= read -r -d '' file ; do
running=$(( running + 1 ))
$debug && echo -n "Processing host '$file' with IP address "
d="$(sed -nE "s/^2\s+(\S.+)$/\1/p" "$file")"
if [[ -z "$d" ]] || \
[[ $(date --date="$d" +%s) -lt $(date --date="$timestamp" +%s) ]] ; then
r='([0-9]{1,3}\.){3}[0-9]{1,3}'
ipa="$(sed -nE "s/^3\s+default via.+ src ($r) metric.+/\1/p" "$file")"
if [[ -z "$ipa" ]] ; then
# FIXME: Outdated report format, try fallback:
ipa="$(sed -nE "s|^.+default via.+ src ($r) metric.+|\1|p" "$file" | head -1)"
echo -ne "\n Outdated '$ipa': $file"
fi
hlist="$hlist,$ipa"
n=$(( n + 1 ))
if [[ $(date --date="$d" +%s) -lt $(date --date="$git_date" +%s) ]] ; then
## ansible run needed at least on one machine, run it on all:
echo -n "✗"
ansible_arg=""
else
echo -n "U"
fi
else
echo -n '✓'
fi
done < <(find "$logdir" -maxdepth 1 -type f -mmin -$age -print0)
hlist="${hlist#,}"
echo -n " $n/$running "
}
run_ansible(){
local hsts="$1"
if [[ -n "$hsts" ]] ; then
if ! echo | eval ANSIBLE_RETRY_FILES_ENABLED=1 ANSIBLE_RETRY_FILES_SAVE_PATH="$dir" \
ansible-playbook --vault-password-file ~/.vaultpwd \
-bi inventory.yml "$pbook.yml" "$ansible_arg" -l "$hsts" ; then
while IFS= read -r ipa ; do
echo "Ansible for IP address '$ipa' failed."
done < "$dir/$pbook.retry"
fi
fi
}
#################
while true ; do
date +%H:%M | tr '\n' ' '
find_outdated
run_ansible "$hlist"
t=$(( 600/(n*n+1) ))
echo "sleeping ${t}s "
sleep $t
done

17
tools/wol-generator.sh Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/bash
#
# Pipe the '--list-hosts' output of ansible into this program to wake up all corresponding hosts:
#
# ansible-playbook [...] -i inventory/inventory.yml -l R317 --list-hosts | ./wol-generator.sh
#
set -eu
tmpf="$(mktemp)"
devs='devices.csv'
while read -r line ; do
sed -nE -e "s%.*(..:..:..:..:..:..);(${line//./\\.});.*%\1 \2%p" "$devs" >> "$tmpf"
done < <(cat - | grep -E "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
wakeonlan -f "$tmpf"
rm "$tmpf"