Schulangebot powered by UCS@school hosted by Hostern

In den letzten Tagen erreichen uns viele Anfragen aus dem Hosting-Umfeld, wie man schnell und einfach für Schulen ein Angebot anbieten kann, damit diese arbeiten können.
Dieser Artikel beschreibt, wie man dies umsetzen kann. Er dient als Diskussionsgrundlage und wird laufend erweitert.

Ziel
Ein Angebot zu erstellen, das es Schulen erlaubt “Online-Lehre quick & dirty” anzubieten. Vorbild ist unteranderem beispielsweise der Artikel “Wie geht Online-Lehre quick & dirty?”.

Schulangebot

  • UCS@school-Appliance mit weiteren wichtigen Diensten
  • Prio 1:
    • Benutzerverwaltung (Benutzer, Klassen, Arbeitsgruppen, CSV-Import)
    • Dateispeicher & Messenger & Etherpad
    • möglichst DSGVO-konform
    • Erweiterbar um weitere Dienste
  • Prio 2: Video-Conferencing, Mail, Lern-Management-System

Appliance zum selber hosted by Hostern

Konzept

  • Um horizontal zu skalieren, setzen wir eine VM auf, die später geclont und individualisiert wird.
  • Ein MVP befindet sich hier: https://mvp.ucs-schule.de/univention/portal/
  • Für spätere Aktualisierungen wird zusätzlich ein Ansible-Server genutzt.
  • Es gibt eine Domain mit Subdomains pro Schule.
  • Nach Bestellung bekommt jede Schule ihre Appliance vorprovisioniert angelegt.
    • Inklusive eines Links auf das Portal
    • Zugangsdaten für Schuladministrator + Standardpasswort (muss beim ersten Login zurückgesetzt werden)
    • Link zur Benutzer-Verwaltung
    • Link zum CSV-Import mit Beispiel-Benutzer-Import-Datei

Dokumentation

Automatic Setup of UCS instances

Idea:

  • UCS Appliance image
  • Start instance from this image
  • Run setup commands via SSH
  • Ready togo UCS system

Requirments:

The UCS Appliance is a kind of template. It is totally possible to change the appliance image
(install additional software) and snapshot this state. The individual (appliance) instances can than be created from this snapshot. After the provisioning (setup) can be started.

The root password of the UCS Appliance image is “univention” by default, ssh login enabled.

Initial Provisioning/Setup:
http://docs.software-univention.de/installation-4.4.html#appliance:use:auto:profile

Create a file /var/cache/univention-system-setup/profile on the instance

hostname="ms"
domainname="schule.intranet"
windows/domain="SCHULE"
ldap/base="dc=schule,dc=intranet"
root_password="univention"
locale/default="de_DE.UTF-8:UTF-8"
locale="en_US.UTF-8:UTF-8 de_DE.UTF-8:UTF-8"
server/role="domaincontroller_master"
interfaces/eth0/type="dhcp"

and run the following command:

/usr/lib/univention-system-setup/scripts/setup-join.sh`

The system is now a fully provisioned UCS Master system, the password for the Administrator/root user
is the “root_password” from the profile.

Upgrade to latest minor version:

univention-upgrade --ignoressh --ignoreterm \
  --updateto=$(ucr get version/version)-99 --noninteractive

Install license:

univention-license-import /tmp/ucs.license && univention-license-check

Force HTTPS:

ucr set force_https=yes

Install an UCS AppCenter App:

printf '%s' "$admin_password" >/tmp/univention
univention-app install myapp --username Administrator --noninteractive \
  --pwdfile /tmp/univention

Install UCS@School:

printf '%s' "$admin_password" >/tmp/univention
univention-app install --noninteractive --username Administrator \
  --pwdfile /tmp/univention ucsschool

The installation of ucs@school requires an additional setup setup, which is currently only availbale via an extra python script, make sure to change all variables according to your environment.

from univention.lib.umc import Client, ConnectionError, HTTPError
import sys
import time

username = 'Administrator'
password = 'univention'
master = 'ms.schule.intranet'
host = 'ms.schule.intranet'
setup = 'singlemaster'
samba = '4'
schoolOU = 'schule'

params = {
	'setup': setup,
	'username': username,
	'password': password,
	'master': master,
	'samba': samba,
	'schoolOU': schoolOU,
}

client = Client(host, username, password, language='en-US')

result = client.umc_command('schoolinstaller/install', params).result
if result and not result.get('success', True):
	print('ERROR: Failed to run installer! {}'.format(result))
	sys.exit(1)

print('=== INSTALLATION STARTED ===')
status = {'finished': False}
failcount = 0
last_message = None
while not status['finished']:
	if failcount >= 1200:
		print('ERROR: {} failed attempts - committing suicide'.format(failcount))
		sys.exit(1)
	try:
		status = client.umc_command('schoolinstaller/progress').result
		failcount = 0
	except (HTTPError, ConnectionError) as exc:
		failcount += 1
		print('TRACEBACK {} in client.umc_command("schoolinstaller/progress"):\n{}'.format(failcount, exc))
		time.sleep(1)
	message = '%(component)s - %(info)s' % status
	if last_message != message:
		last_message = message
		print(message)

if len(status['errors']) > 0:
	print('ERROR: installation failed - {}'.format(status))
	sys.exit(1)

sys.exit(0)

And run this script on your instance:

python /opt/school-setup.py
# restart of UMC
service univention-management-console-server restart
service univention-management-console-web-server restart

Create school admin with rights to create/delete school users

udm users/user create  \
      --position cn=admins,cn=users,ou=schule,dc=ucs-schule,dc=intranet \
      --set username=schul-admin \
      --set lastname="Admin" \
      --set password=25oJZ3Gouw5QU \
      --set description="Admin for schule" \
      --set displayName="Schul-Admin" \
      --append groups="cn=admins-schule,cn=ouadmins,cn=groups,dc=ucs-schule,dc=intranet" \
      --append groups="cn=lehrer-schule,cn=groups,ou=schule,dc=ucs-schule,dc=intranet" \
      --append groups="cn=Domain Users schule,cn=groups,ou=schule,dc=ucs-schule,dc=intranet" \
      --set primaryGroup="cn=Domain Users schule,cn=groups,ou=schule,dc=ucs-schule,dc=intranet" \
      --set school=schule \
      --set ucsschoolRole=teacher:school:schule 
      --set ucsschoolRole=school_admin:school:schule

** Configure Import **

ucr set ucsschool/import/http_api/ALLOWED_HOSTS=mvp.ucs-schule.de
ucr set ucsschool/import/http_api/client/ssl_verify=false

univention-directory-manager groups/group modify \
        --dn "cn=schule-import-all,cn=groups,ou=schule,dc=ucs-schule,dc=intranet" \
        --append users="uid=schul-admin,cn=admins,cn=users,ou=schule,dc=ucs-schule,dc=intranet"
 
mv user_import_http-api.json /var/lib/ucs-school-import/configs/user_import_http-api.json

Install Self Service:

univention-install -y univention-self-service-master \
  univention-self-service univention-self-service-passwordreset-umc
# optional, add a group to the list of "self service" users
ucr set umc/self-service/passwordreset/whitelist/groups=‘Domain Users,Domain Users schule’

Install/Configure Let’s encrypt:

 printf '%s' "$admin_password" >/tmp/univention
 univention-app install letsencrypt --username Administrator --noninteractive \
  --pwdfile /tmp/univention

 # configure with external FQDN
 FQDN="schule.ucs-schule.de"
 ucr set letsencrypt/domains="$FQDN"
 univention-app configure letsencrypt
 service apache2 restart

Install RocketChat:

 printf '%s' "$admin_password" >/tmp/univention
 univention-app install rocketchat --username Administrator --noninteractive --pwdfile /tmp/univention
 # if new users should be "rocketchat" users by default
 ldap_base="$(ucr get ldap/base)
 udm settings/extended_attribute modify \
  --dn "cn=rocketchatActivated,cn=rocketchat,cn=custom attributes,cn=univention,$ldap_base" \
  --set default=TRUE

the last backslash is too much. needs to be removed.