6. App configurations#

The App Center offers the possibility to add scripts at various points during the installation and configuration of the app. The scripts are described below. They can be edited in the app in the App Provider portal in the following sections:

  • Scripts → Join & unjoin script

  • Scripts → Scripts called before App Center action on apps

  • Scripts → Scripts for data store and restore (store_data, restore_data_before_setup, restore_data_after_setup)

  • Scripts → Setup script

  • Scripts → Configure scripts. (configure)

  • Scripts → Configure scripts. (configure_host)

6.1. Installation scripts#

During the installation of an app, various scripts are called. Please see the overview below about of the involved scripts and the parameters they are called with. More information on the scripts themselves can be found in the following sections.

During installation process you may need to run commands inside a container of your app, for example, to properly prepare it. For more information, refer to Command univention-app.

App workflow for installation

Fig. 6.1 App workflow for installation#

6.1.1. Script called before installation to verify that App may be installed#

The preinst script is executed on the UCS host system before the app is initialized, even before the app image is downloaded. Its purpose is to check whether installation will be successful or not. Any exit code other than 0 will result in cancellation of the installation process. This script is also executed if the app is upgraded.

The script receives the LDAP bind DN of the Administrator account and its password, the version of the app that should be installed, the locale and an error log file for log output as parameters. Error messages in the passed error log file will be passed to the UCS management system and thus to the administrator performing the installation. Proper error messages can thus be passed to the administrator.

Note that App settings with scope outside are already set by the time the script is run and can therefore be checked against.

6.1.2. Docker script restore_data_before_setup#

The lifecycle script restore_data_before_setup is executed inside the Docker container before the setup script is run. Its purpose is to restore the data which has been stored by the store_data script. The parameters are the appid, the app version and a filename for error logging.

6.1.3. Docker script setup#

The lifecycle script setup is executed inside the Docker container. It’s the heart of the initial app configuration and typically used to make environment specific settings to the container or apply certain changes that require the container environment. If the script fails (exit code != 0) the installation is aborted.

The parameters given to the script are the appid, the app version, a filename for error logging and the username and credentials for the Administrator user.

6.1.4. Docker script restore_data_after_setup#

The lifecycle script restore_data_after_setup is executed inside the Docker container after the setup script is run. Its purpose is to restore the data which has been stored by the store_data script. The parameters are the appid, the app version and a filename for error logging.

6.1.5. Settings script run on Docker host#

The settings script configure_host is executed on the Docker host after the restore_data_after_setup script is run. Its purpose is to make environment specific settings on the UCS host regarding the app. The parameters are the app action install, the app version, a filename for error logging and the locale.

6.1.6. Settings script run in Docker container#

The settings script configure is executed inside the Docker container after the configure_host script. Its purpose is to make environment specific settings in the app container. The parameters are the app action install, the appid, the app version and a filename for error logging.

6.1.7. Join script#

The joinscript inst is executed on the UCS host system after the Docker container is configured. Please refer to Domain join in the UCS Developer Reference about how to write a join script. In principle a join script is a script that runs after the installation of an app and it has write access to the LDAP directory service. If it runs successfully, the join script may save this information in a status file. If this does not happen, the user is constantly reminded to re-run the join script. So the join script does not need to run successfully. The installation will not be aborted at this point. But of course at some point it should run through successfully.

6.1.7.1. Join script helper#

Apart from the functions documented in the Developer Reference, the below listed functions are available in join scripts for dealing with apps. They require the following line in the script:

. /usr/share/univention-appcenter/joinscripthelper.sh

Furthermore, this call provides access to the following variables:

$APP

app id

$APP_VERSION

app version

$SERVICE

app name

$CONTAINER

Docker container id

6.1.7.2. Join script functions#

joinscript_add_simple_app_system_user

Adds a domain wide user to the LDAP directory that is not a real Domain User and offers an authentication account. It can be used as bind user for the app to connect to the LDAP directory. The password will be stored on the Docker Host at /etc/APP.secret. The DN will be uid=APP-systemuser,cn=users,ldap_base.

joinscript_add_simple_app_system_user "$@" --set mailPrimaryAddress=...
joinscript_container_is_running

Returns whether or not the Docker container is currently running.

  • 0: Yes

  • 1: No

Can be used in an if statement.

joinscript_container_is_running || die "Container is not running"
joinscript_run_in_container

Runs one command inside the container. Returns the return code of the command.

joinscript_run_in_container service myapp restart ||
die "Could not restart the service"
joinscript_container_file

Prints the absolute path for the Docker host for the filename given inside the container.

FILENAME="$(joinscript_container_file "/opt/$APP/my.cnf")"
joinscript_container_file_touch

Creates a file inside the container. Directories are created along the way. Prints the resulting filename just like “joinscript_container_file”.

joinscript_register_schema

Registers a LDAP schema file semi automatically. The schema file allows to extend LDAP objects with new attributes. The file will be copied to the Docker host’s /usr/share/univention-appcenter/apps/APPID/APPID.schema during installation. See the LDAP documentation for the syntax of a schema file.

If an official object identifier (OID) namespace is needed, Univention can provide one. It’s important to note that shipping the schema file alone is not enough. It has to be registered with the mentioned function in the join script.

The schema file content can be provided in the App Provider portal on the Identity management tab in the User rights management section, in the field for Schema extension for LDAP.

joinscript_register_schema "$@"

6.1.7.3. Join script boilerplate#

The following boilerplate can be used as a starting point for the app’s own join script.

#!/bin/bash
VERSION=1

. /usr/share/univention-appcenter/joinscripthelper.sh
joinscript_init
eval "$(univention-config-registry shell)"
ucs_addServiceToLocalhost "$SERVICE" "$@" || die

... # Place for the app's join script code

joinscript_save_current_version
exit 0

6.2. Uninstall scripts#

During the process to uninstall an app, various scripts are called. Please see the overview below about the involved scripts and the parameters they are called with. More information on the scripts themselves can be found in the following sections.

App workflow for Removal

Fig. 6.2 App workflow for Removal#

6.2.1. Script called before uninstalling to verify that App may be removed#

The prerm script is executed on the UCS host system. Its purpose is to check the prerequisites to uninstall an app or abort if they are not met. For example, the prerm may fail if other software still depends on it. Any exit code other than 0 will result in cancellation of the uninstall process. The given parameters are the LDAP bind DN of the Administrator account and its password, the version of the app that should be uninstalled/removed, the locale and an error log file for log output. Error messages in the passed error log file will be passed to the UCS management system and thus to the administrator performing the installation. Proper error messages can thus be passed to the administrator.

6.2.2. Settings script run on Docker host#

The settings script configure_host is executed on the Docker host after the prerm script is run. Its purpose is to make environment specific settings on the UCS host during the removal of the app. The parameters are the app action remove, the app version, a filename for error logging and the locale.

6.2.3. Settings script run in Docker container#

The settings script configure is executed inside the Docker container after the configure_host script. Its purpose is to make environment specific settings in the app container before it’s removed. The parameters are the app action remove, the appid, the app version and a filename for error logging.

6.2.4. Docker script store_data#

The lifecycle script store_data is required if data exists in the container which should not be removed when the container is replaced with a new container or if the app is uninstalled. The script is not required if all the data is stored outside of the container for example in a database or a mapped volume. It’s executed inside the Docker container and it should copy the relevant data to /var/lib/univention-appcenter/apps/APPID/data/. Afterwards, the data can be restored by one of the restore_data* scripts. The parameters are the appid, the app version and a filename for error logging.

6.2.5. Unjoin script#

The unjoin script uinst is executed on the UCS host system after the Docker container is removed. See Writing unjoin scripts in UCS Developer Reference for how to write an unjoin script. It should revert most (if not all) changes done in the join script. With the notable exception of schema registration. An LDAP schema extension should never be removed once it was registered.

6.3. Upgrade scripts#

It may be necessary to move data from the old container to the new container when the app container is replaced during an upgrade or when the app is uninstalled. The upgrade scripts can be used for this purpose. Please see an overview of the involved scripts and the parameters they are called with in the figure below. More information on the scripts themselves can be found in the following sections.

During upgrade process you may need to run commands inside a container of your app, for example, to run a data migration. For more information, refer to Command univention-app.

App workflow for upgrade

Fig. 6.3 App workflow for upgrade#

6.3.1. Script called before upgrade to verify that App may be upgraded#

The preinst script is executed on the UCS host system before the app upgrade is initialized, even before the Docker image is downloaded. Its purpose is to check whether the requirements for the upgrade are fulfilled. Any exit code other than 0 will result in cancellation of the upgrade process.

The script receives the LDAP bind DN of the Administrator account and its password, the old version of the app and the new version, the locale and an error log file for log output as parameters. Error messages in the passed error log file will be passed to the UCS management system and thus to the administrator performing the installation. Proper error messages can thus be passed to the administrator.

6.3.2. Docker script store_data#

The lifecycle script store_data is required if data exists in the container which should not be removed when it’s replaced with a new container or if the app is uninstalled. It isn’t required if all the data is stored outside the container for example in a database or a mapped volume. The script is executed inside the Docker container and it should copy the relevant data to /var/lib/univention-appcenter/apps/APPID/data/. Afterwards, the data can be restored by one of the restore_data* scripts when they are executed in the new container.

6.3.3. Docker script restore_data_before_setup#

The lifecycle script restore_data_before_setup is executed inside the Docker container before the setup script is run. Its purpose is to restore the data which has been stored by the store_data script.

6.3.4. Docker script setup#

The lifecycle script setup is executed inside the Docker container. ‘t is used to make environment specific settings to the new container or apply certain changes that require the container environment. If the script fails (exit code != 0) the upgrade is aborted.

The parameters given to the script are the appid, the app version, a filename for error logging and the username and credentials for the Administrator user.

6.3.5. Docker script restore_data_after_setup#

The lifecycle script restore_data_after_setup is executed inside the Docker container after the setup script is run. Its purpose is to restore the data which has been stored by the store_data script in the old container.

6.3.6. Settings script run on Docker host#

The settings script configure_host is executed on the Docker host after the restore_data_after_setup script is run. Its purpose is to make environment specific settings on the UCS host regarding the app during the upgrade. The parameters are the app action upgrade, the app version, a filename for error logging and the locale.

6.3.7. Settings script run in Docker container#

The settings script configure is executed inside the Docker container after the configure_host script is run. Its purpose is to make environment specific settings in the app container during the upgrade. The parameters are the app action upgrade, the appid, the app version and a filename for error logging.

6.3.8. Join Script#

Finally, the join script inst is called to end the upgrade. With an updated join script changes can be made to the environment that require the necessary execution permissions or access to the UCS directory service. When a join script should run during the upgrade, please keep in mind to increment the VERSION counter. For more information on the join script in general see Join script.

6.4. App settings#

The App settings allow the user to configure the app during its runtime. The App Provider Portal can be used to define which settings are displayed to the user. The app can react accordingly to the changes.

If App settings are defined for an app, the user can reach these settings in the app configuration, see App settings button).

App settings button

Fig. 6.4 App settings button#

An example for an App settings dialog is in App settings example).

App settings example

Fig. 6.5 App settings example#

The App settings can be defined on the tab Advanced in the section App settings in the App Provider Portal.

6.4.1. React on App settings#

The settings are saved inside the Docker container in the file /etc/univention/base.conf in the format key: value. After the settings are changed, two scripts are executed. First, the script configure_host. This script is run on the Docker host. Second, the script configure is executed. It’s executed inside the Docker container. In the App Provider Portal, the path of the script can be given (Configure scripts) or the script code can be uploaded (Path to script inside the container (absolute)).

6.4.2. App settings configuration#

The App settings are defined in the ini format. The definition can be done in the field Settings that can be used to configure the app ini file format. One ini file can contain several settings.

The name of a setting is the name of the section in the ini file, for example

[myapp/mysetting]

It’s recommended to use the app ID as a prefix to prevent collisions.

The type of the attribute is defined with the keyword Type. The following types are supported:

String

A standard input field with no restrictions. This is used by default.

Int

A number field which is validated accordingly.

Bool

A checkbox. The value true or false is set.

List

A widget that lets the user choose from a predefined set of values.

Password

A password input.

Note

The content will be stored as clear text value inside the Docker container.

File

An upload widget. The content is stored directly in a file according to the definition of the setting.

PasswordFile

As a File, but shown as a password input.

Status

A read-only settings that is actually meant as a feedback channel for the user. This does not render a widget, but instead just writes a text with whatever was written into this variable. Writing to it’s up to the App Provider (e.g., by using the configure script).

The attribute Description is used to define the description of the setting. It’s shown next to the widget so that the user knows what to do with this form. It can be localized by also defining Description[de] and so on.

The attribute Group can be used to group settings. All settings sharing one group will be put under that label. The default group is Settings. It’s also possible to localize it for example Group[de].

The attribute Show can be used to define when the setting should be shown. By default the setting attribute is shown when the app is up and running. It’s also possible to show the setting attribute during the installation. The following values are possible Install, Upgrade, Remove and Settings. It’s possible to specify more than one value which must be separated by comma.

The attribute ShowReadOnly can be used in the same way as Show. The difference is that the value is not changeable.

The attribute InitialValue can be used during the installation. If no value for this attribute was given during the installation, the defined value is set.

The attribute Required can be used to define if this setting has to be set or not.

The attribute Scope is used to specify if the value is set inside the Docker container (inside), on the Docker host (outside) or on both (inside, outside). The default is inside. Values in the scope inside can be referenced in the docker-compose.yml for multi container apps just like Univention Configuration Registry Variables. For an example see Post processing of Docker Compose file.

The attributes Labels and Values are used if a type List is defined. The attribute Labels defines the values shown to the user and the attribute Values defines the values which are stored. The lists are comma separated and should have the same size. If a comma is necessary inside a label or value, it can be escaped with a \\.

The attribute Filename can be used to define the absolute path where the file should be stored. This attribute is needed in case the types File or PasswordFile are used.

6.4.3. App settings examples#

This is a minimal settings definition:

[myapp/mysetting]
Type = String
Description = This is the description of the setting
Description[de] = Das ist die Beschreibung der Einstellung

These are two more advanced settings

[myapp/myfile]
Type = File
Filename = /opt/myapp/license
Description = License for the App
Description[de] = Lizenz der App
Show = Install, Settings
Group = License and List
Group[de] = Lizenz und Liste
[myapp/list]
Type = List
Description = List of values
Show = Install
ShowReadOnly = Settings
Values = value1, value2, value3
Labels = Label 1, Label 2, Label 3
InitialValue = value2
Scope = inside, outside
Group = License and List
Group[de] = Lizenz und Liste

The first of these two settings will upload a file to /opt/myapp/license inside the container. The second will save myapp/list: value2 (or another value) inside the container and on the Docker host. Both settings will be shown before the installation. On the App settings page, the list setting will be read-only.

6.5. Certificates#

UCS provides a certificate infrastructure for secure communication protocols. See SSL certificate management in the UCS manual.

Apps may need access to the UCS certificate infrastructure or need to be aware of changes to the certificates. The Univention App Center provides a simple way to manage certificates inside an app. The script update-certificates is executed on the UCS host automatically during the installation and upgrade of apps (but can also be executed manually) and provides apps a simple way to gain access to certificates and to react to changes to certificates.

# update all apps
univention-app update-certificates

# update app "my-app"
univention-app update-certificates my-app

What happens with update-certificates?

  • The UCS root CA certificate is copied to /usr/local/share/ca-certificates/ucs.crt inside the container.

  • update-ca-certificates is executed in the Docker container, if it exists, to update the CA certificate list.

  • The UCS root CA certificate is copied to /etc/univention/ssl/ucsCA/CAcert.pem inside the container.

  • The Docker host UCS certificate is copied to /etc/univention/ssl/docker-host-certificate/{\cert.perm,private.key} and /etc/univention/ssl/FQDN_DOCKER_HOST/{cert.perm,private.key}.

Every app can define a update_certificates script. In the app provider portal it can be added on the tab Advanced in the section Certificates.

Example:

#!/bin/sh
# cat the UCS root CA to the app's root CA chain
cat /etc/univention/ssl/ucsCA/CAcert.pem >> /opt/my-app/ca-bundle.crt
service my-app-daemon restart

The script has to be uploaded via the upload API (section App Provider Portal upload interface). The script should be written locally and then uploaded with the following command:

./univention-appcenter-control upload --username "$your_username" 5.0/myapp=1.0 update_certificates

6.6. Mail integration#

Univention Corporate Server (UCS) provides a complete mail stack with the Mailstack app in the App Center. It includes Postfix as MTA for SMTP and Dovecot for IMAP. If the app relies on an existing mail infrastructure, it’s one option to use the mail stack app and require its installation in the UCS domain. This can be configured for the app in the App Provider portal on the Version tab in the section Required apps by adding the Mailserver app and setting Installed in domain. With this configuration the App Center on the system administrator’s UCS system will check, if the Mailserver app is installed somewhere in the domain and asks the administrator to install it accordingly.

Next the app needs to be configured to use the UCS SMTP and IMAP servers. This is done in the Join Script, see Join script. The following snippet gives an example what should be included in the Join Script:

...
eval "$(univention-config-registry shell)"
...
# use the first IMAP server as smtp and imap server
mailserver="$(univention-ldapsearch -LLL '(univentionService=IMA)' cn |
sed -ne 's/^cn: "//p;T;q')"
if [ -n "$mailserver" ]; then
  mailserver="$mailserver.$domainname"

  # for Docker Apps the helper script joinscript_run_in_container
  # can be used to run commands in the container
  . /usr/share/univention-appcenter/joinscripthelper.sh
  joinscript_run_in_container my-app-setup --config imap="$mailserver"
  joinscript_run_in_container my-app-setup --config smtp="$mailserver"
  joinscript_run_in_container my-app-setup --config sieve="$mailserver"
fi
...

The snipped searches the UCS LDAP directory for the host with the service IMAP and sets the FQDN of this host as IMAP, SMTP and SIEVE server for the app. This is a good default and may not be correct for some setups.

The best practice mail settings when the UCS mail stack is used, are the following.

IMAP:

  • TLS

  • Port 143

  • Authentication is possible for domain users with a primary mail address.

  • The user’s uid or the primary mail address are both valid for authentication.

SMTP:

  • TLS

  • Port 587 (submission) for authentication

  • Mechanism Login or Mechanism Plain

6.6.1. Provide mail with Docker Apps#

For the intended app it may be necessary to provide SMTP and IMAP with a custom setup for the app. To provide SMTP and/or IMAP services in a Docker app, these services have to be stopped on the Docker host. This can be done in the app’s preinst Docker script, see Script called before installation to verify that App may be installed. Example:

#!/bin/sh

# stop imap/smtp on docker host
systemctl stop postfix dovecot
ucr set postfix/autostart=no dovecot/autostart=no

To map SMTP and/or IMAP ports from the container to the host to be able to use the Docker host as IMAP/SMTP server exclusive ports for the container have to be set to the relevant ports (e.g. 110, 143, 993, 995, 587, 25, 465, 4190 for POP3(S), IMAP(S), SMTP(S), submission and sieve). See Ports on how to set an exclusive port.

Firewall exceptions for these ports are create automatically.

Best practice is to at least map the IMAP data store to the Docker host to provide a separation of data and container (important for migration to Docker and Docker image updates). See Persistent data with volumes.

6.6.2. Use local mail on Docker host#

With a stopped Postfix on the Docker host, mail can no longer be delivered locally. If that is a problem, the following setup can help.

Install the extremely simple MTA ssmtp and configure this MTA to use the localhost (our Docker container is listening on localhost:25).

univention-install --yes ssmtp
# add mailhub=localhost:25 in to /etc/ssmtp/ssmtp.conf

Now configure Postfix in the Docker container to deliver mails from the Docker host locally by adding the FQDN of the Docker host to mydestination:

ucr set mail/postfix/mydestination="\$myhostname, localhost.\$mydomain, localhost, $DOCKER_HOST_NAME"

6.7. Subdomains / dedicated FQDN for an App#

There may be reasons why an App needs to have its own FQDN within the UCS domain. Some Apps may not be able to configure a web interface that integrates well into the default Apache sites of UCS, see Web interface.

To avoid naming collisions, the App’s FQDN should reference the Docker Host’s FQDN, e.g, myapp.ucs-primary.domain.tld. UCS can do the following to allow this scenario to work as smooth as possible:

  • Add a dedicated FQDN for the App and make it known to the internal DNS. That means that the new FQDN is an alias for the actual FQDN of the Docker host.

  • Generate a certificate for this FQDN. Technically, a wildcard certificate is created.

  • Generate a virtual host for Apache with that new FQDN. Thus, requests to that FQDN will be handled by the VHost. The skeleton configuration can be easily extended by writing a configuration file that is then included in the VHost entry.

For this to work, this snippet can be used in the join script (Join script):

univention-add-vhost \
    "myapp.$(ucr get hostname).$(ucr get domainname)" 443 \
    --conffile /var/lib/univention-appcenter/apps/myapp/data/apache.conf \
    "$@"  # "$@" is used to pass credentials
# write the apache.conf, maybe by using the App Settings
systemctl reload apache2
nscd -i hosts  # only needed if the new fqdn should be used immediately by the system
systemctl reload bind9  # same here

This will create the following entry in /etc/apache2/sites-available/univention-vhosts.conf

# Virtual Host for myapp.ucs-primary.domain.tld/443
<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName myapp.ucs-primary.domain.tld
    IncludeOptional /var/lib/univention-appcenter/apps/myapp/data/apache.con[f]
    SSLEngine on
    SSLProxyEngine on
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off
    SSLProxyCheckPeerExpire off

    SSLCertificateFile /etc/univention/ssl/*.ucs-primary.domain.tld/cert.pem
    SSLCertificateKeyFile /etc/univention/ssl/*.ucs-primary.domain.tld/private.key
    SSLCACertificateFile /etc/univention/ssl/ucsCA/CAcert.pem
</VirtualHost>
</IfModule>

Note

Although this seems convenient for some Apps, this feature creates an internal name. It may still be inconvenient for testers that run UCS in a virtual environment where their browser is not part of UCS’ DNS.

Warning

This method may not work in the “AD member mode”. There, a Windows Domaincontroller is the leading system and provides the DNS. The DNS alias has to be added by the Admin manually there as our script cannot add it for them.

6.8. Firewall#

This section describes how the local Univention Firewall based on iptables is changed by apps and how it can be customized. Docker containers have access to the Docker host. And the Docker containers can be made available for external clients with Ports redirection settings, see Ports.

If MariaDB or PostgreSQL are used as database, those ports will be opened automatically for the Docker container (section Database).

Every app can provide additional custom rules to open required ports. This can be done in the join script (section Join script). In the example the port 6644 is opened for TCP and UDP:

$ univention-config-registry set \
  "security/packetfilter/package/$APP/tcp/6644/all=ACCEPT" \
  "security/packetfilter/package/$APP/tcp/6644/all/en=$APP" \
  "security/packetfilter/package/$APP/udp/6644/all=ACCEPT" \
  "security/packetfilter/package/$APP/udp/6644/all/en=$APP"

$ systemctl try-restart univention-firewall

Please also add corresponding ucr unset commands in the unjoin script so that the firewall rules will be removed when the app is removed from the system (section Unjoin script).