3.8. How-to for Nextcloud#

This section provides a how-to for building a packaged integration using Nextcloud as an example. After reading this section, you know how to use the various plugins described in Packaged integrations to create a packaged integration to connect Nextcloud to Nubus for Kubernetes. It focuses on the following objectives:

  1. The Portal Service provides a tile that links to the Nextcloud instance.

  2. Nextcloud uses Nubus for Kubernetes as its identity management source. Identity administrators can use Nubus to decide which user accounts or user groups may use Nextcloud.

  3. The entire configuration is distributable as a packaged integration.

This section addresses software developers for the steps to create the packaged integration. In a second part, it addresses DevOps engineers for the steps on installing the packaged integration.

This how-to takes you along the following steps:

Software developers
  1. Validate that you meet the Requirements.

  2. Create the Packaged integration directory structure.

  3. Prepare the plugins.

  4. Bundle the plugins in a container image as packaged integration.

  5. Publish packaged integration.

  6. Communicate packaged integration.

3.8.1. Requirements#

To go through this how-to you need a computer where you can create a directory structure and files. You need the knowledge listed in Audience and required knowledge.

3.8.2. Packaged integration directory structure#

Before you begin, ensure that you meet the requirements as outlined in Requirements.

To begin with this how-to, you need to create the directory structure in Listing 3.13 on your local machine. The directory contains all the necessary files for the packaged integration.

Listing 3.13 Project directory structure for Nextcloud packaged integration#
nextcloud-packaged-integration/
├── docker
└── plugins

You can use a different top-level directory name. However, keep the directories docker and plugins as they are for consistency reasons.

You can turn the directory structure into a repository for your source code management system to track all the changes over time.

3.8.3. Prepare the plugins#

Before you continue, ensure that you have completed the steps in Packaged integration directory structure.

The packaged integration for Nextcloud uses various plugins for Nubus for Kubernetes. This section describes how you create and add these plugins to the directory structure.

  1. Add LDAP schemas.

  2. Add extended attributes for Nextcloud.

  3. Add LDAP search user so that Nextcloud can access the Directory Service.

  4. Create tile for Portal Service that points users to Nextcloud.

3.8.3.1. Add LDAP schemas#

An LDAP schema defines the data model in the Directory Service. This plugin type allows extending the data model to store additional information with an object. For more information, see LDAP schema.

Identity administrators can control which user or user group may access Nextcloud. They can also define how much disk space a user may allocate. Operators can then configure Nextcloud to evaluate these attributes. Therefore, the Nextcloud packaged integration provides an LDAP schema to add application specific attributes and object classes to achieve these use cases.

To add the LDAP schema, use the following steps:

  1. Create the directory ldap-schema/ in your plugins/ directory.

  2. Add the file nextcloud.schema to the ldap-schema/ directory with the content in Listing 3.14. The schema adds the following LDAP elements:

    Attributes
    • univentionNextcloudEnabled

    • univentionNextcloudQuota

    Object classes
    • univentionNextcloudUser

    • univentionNextcloudGroup

    Listing 3.14 LDAP schema for the Nextcloud packaged integration#
    # Attribute Types
    #-----------------
    
    attributetype ( 1.3.6.1.4.1.10176.99999.00424.10 NAME 'univentionNextcloudEnabled'
            DESC 'whether user or group should be available in Nextcloud'
            EQUALITY caseIgnoreMatch
            SUBSTR caseIgnoreSubstringsMatch
            SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
    
    attributetype ( 1.3.6.1.4.1.10176.99999.00424.11 NAME 'univentionNextcloudQuota'
            DESC 'defines how much disk space is available for the user'
            EQUALITY caseIgnoreMatch
            SUBSTR caseIgnoreSubstringsMatch
            SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE)
    
    # Object Classes
    #---------------
    
    objectclass ( 1.3.6.1.4.1.10176.99999.00424.12 NAME 'univentionNextcloudUser'
            DESC 'A Nextcloud user'
            SUP top AUXILIARY
            MUST ( cn  )
            MAY ( univentionNextcloudEnabled $ univentionNextcloudQuota )
            )
    
    objectclass ( 1.3.6.1.4.1.10176.99999.00424.13 NAME 'univentionNextcloudGroup'
            DESC 'A Nextcloud group'
            SUP top AUXILIARY
            MUST ( cn  )
            MAY ( univentionNextcloudEnabled )
            )
    

Your directory structure looks like Listing 3.15.

Listing 3.15 Directory structure with LDAP schemas#
nextcloud-packaged-integration/
├── docker
└── plugins
    └── ldap-schema
        └── nextcloud.schema

3.8.3.2. Add extended attributes#

Extended attributes are a feature of UDM which provides a mapping between an input widget and a UDM attribute. The UDM HTTP REST API configures extended attributes. The UDM data loader plugin automates the configuration process of extended attributes for the Nextcloud packaged integration. For more information about the UDM data loader, see UDM data loader.

To add an extended attribute, use the following steps:

  1. Create the directory udm-data-loader/ in your plugins/ directory.

  2. Add the 86_Nextcloud.yaml file to the udm-data-loader/ directory with the content in Listing 3.16.

    Take a look at the first extended attribute with the name univentionNextcloudEnabled. The attribute has the property default. It’s emphasized in the listing. In this case, default: "TRUE" means that UDM automatically allows newly created users to access Nextcloud, if univentionNextcloudEnabled wasn’t set explicitly when the user was created.

    Note

    The 86_Nextcloud.yaml file that you can download here, includes more data than shown in Listing 3.16. Section 3.8.3.3 to Section 3.8.3.4 explain the remaining content of the file in detail.

    Listing 3.16 Definition for extended attributes in YAML format for the UDM data loader#
    ---
    # Create container for Nextcloud custom attributes
    action: create
    module: container/cn
    position: cn=custom attributes,cn=univention,{{ ldapBaseDn }}
    properties:
      name: "nextcloud"
    ---
    # Create extended attribute Nextcloud for user
    action: create
    module: settings/extended_attribute
    position: cn=nextcloud,cn=custom attributes,cn=univention,{{ ldapBaseDn }}
    properties:
      name: "univentionNextcloudEnabled"
      CLIName: nextcloudEnabled
      ldapMapping: univentionNextcloudEnabled
      module: ["users/user"]
      shortDescription: Access to Nextcloud
      overwriteTab: False
      valueRequired: False
      tabName: NextCloud Hub
      syntax: "boolean"
      multivalue: False
      tabPosition: 1
      tabAdvanced: False
      overwritePosition: "0"
      doNotSearch: False
      hook: 'None'
      mayChange: True
      deleteObjectClass: False
      default: "TRUE"
      objectClass: univentionNextcloudUser
    ---
    # Create extended attribute Nextcloud Quota for user
    action: create
    module: settings/extended_attribute
    position: cn=nextcloud,cn=custom attributes,cn=univention,{{ ldapBaseDn }}
    properties:
      name: "univentionNextcloudUserQuota"
      CLIName: nextcloudQuota
      ldapMapping: univentionNextcloudQuota
      module: ["users/user"]
      shortDescription: "Nextcloud Quota"
      longDescription: "Amount of storage available to the user (ex: 512 MB or 12 GB)"
      tabName: NextCloud Hub
      overwriteTab: False
      valueRequired: False
      syntax: 'string'
      default: ""
      tabAdvanced: False
      mayChange: True
      multivalue: False
      deleteObjectClass: False
      tabPosition: 1
      overwritePosition: "0"
      doNotSearch: False
      hook: 'None'
      objectClass: univentionNextcloudUser
    ---
    # Create extended attribute Nextcloud for group
    action: create
    module: settings/extended_attribute
    position: cn=nextcloud,cn=custom attributes,cn=univention,{{ ldapBaseDn }}
    properties:
      name: "univentionNextcloudGroupEnabled"
      CLIName: nextcloudEnabled
      ldapMapping: univentionNextcloudEnabled
      module: ["groups/group"]
      shortDescription: "Available in Nextcloud"
      longDescription: "The group is available in Nextcloud"
      tabName: NextCloud Hub
      overwriteTab: False
      valueRequired: False
      syntax: 'boolean'
      default: "0"
      tabAdvanced: False
      mayChange: True
      multivalue: False
      deleteObjectClass: False
      tabPosition: 1
      overwritePosition: "0"
      doNotSearch: False
      hook: 'None'
      objectClass: univentionNextcloudGroup
    

3.8.3.3. Add LDAP search user#

Applications like Nextcloud read the list of users from the Directory Service. OpenLDAP is the Directory Service in Nubus. To access the Directory Service and read directory objects, it’s best practice to use a separate user account for each application. Each application uses its own user account to search for users in the Directory Service through LDAP. Therefore, the documentation usually refers to this user account with the term LDAP search user.

You need to use the UDM data loader plugin to add the LDAP search user to Nubus. The UDM data loader uses the UDM HTTP REST API to create the directory objects through UDM. For more information about the UDM data loader, see UDM data loader.

To add the LDAP search user, open the 86_Nextcloud.yaml file in the plugins/udm-data-loader/ directory and add the content in Listing 3.17. The UDM data loader creates the LDAP search user in the users/ldap UDM module. Unlike the users/user UDM module which addresses identity administrators to manage user accounts that represent people, the users/ldap UDM module hides the LDAP search user from the user account and user group management in the Management UI.

Note the template variable nextcloudUserPassword.

Caution

Don’t hard code the user password for the LDAP search user. The UDM data loader provides a template mechanism and uses Jinja2. Using a template variable allows the operator to choose their own password when installing the packaged integration.

Tip

You can use the template mechanism to also make other values configurable. For more information, see Template variables in the data loader.

Listing 3.17 Definition for LDAP search user for Nextcloud in YAML format for the UDM data loader#
---
# Create Nextcloud service user
action: create
module: users/ldap
position: cn=users,{{ ldapBaseDn }}
properties:
  username: "nextcloudUser"
  lastname: "LDAP-system-User"
  password: {{ nextcloudUserPassword }}
  overridePWHistory: true
  overridePWLength: true

See also

Identity Store and Directory Service

in Univention Nubus for Kubernetes - Architecture Manual [1] for information about the architecture of the Identity Store and Directory Service in Nubus for Kubernetes.

3.8.3.4. Create tile for Portal Service#

A portal tile adds a link to Nextcloud on the Portal Service so that end users have a direct navigation to Nextcloud. Adding a portal tile comprises the following actions:

  1. Create a portal tile, define its appearance, behavior, and visibility.

  2. Add the portal tile to a portal category where the Portal Service shows it.

Important

The Portal Service only shows portal tiles that have a reference entry in the portal category.

A portal tile is also a directory object. To add the portal tile, you need to use the UDM data loader plugin. Open the 86_Nextcloud.yaml file in the plugins/udm-data-loader/ directory and add the content in Listing 3.18.

Note the template variables portalNextcloudLinkBase and ldapBaseDn. Similar to the password for the LDAP Search user in Add LDAP search user, the operator must set the value for portalNextcloudLinkBase upon the installation of the packaged integration. Nubus for Kubernetes provides the value for ldapBaseDn by default so that the operator doesn’t have to set the value for it. For more information about the template mechanism, see Template variables in the data loader.

properties.icon contains the Base64-encoded data of the Nextcloud icon for the portal tile.

Listing 3.18 Definition for the portal tile for Nextcloud in YAML format for the UDM data loader#
---
# Create Nextcloud portal tile
action: create_or_modify
module: portals/entry
position: cn=entry,cn=portals,cn=univention,{{ ldapBaseDn }}
properties:
  name: "nextcloud"
  icon: "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGlkPSJMYXllcl8xIiB3aWR0aD0iMTQxLjQ4NSIgaGVpZ2h0PSI5OS42MDMiIHg9IjAiIHk9IjAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE5Ni42IDcyIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMzIuNjQyIDkzLjM3NyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhIGlkPSJtZXRhZGF0YTIwIi8+PGRlZnMgaWQ9ImRlZnMxOCI+PGNsaXBQYXRoIGlkPSJjbGlwUGF0aDg4MTIiIGNsaXBQYXRoVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48Y2lyY2xlIGlkPSJjaXJjbGU4ODE0IiBjeD0iOTUuNjY5IiBjeT0iOTUuNjY5IiByPSI3OS43MjQiIHN0eWxlPSJmaWxsOiMwMDA4MGQ7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlLXdpZHRoOjEiLz48L2NsaXBQYXRoPjwvZGVmcz48cGF0aCBpZD0icGF0aDEwNTIiIGQ9Im0gNjYuNDA3ODk2LDkuMzc1IGMgLTExLjgwNTI3MSwwIC0yMS44MTEyMTcsOC4wMDMxOTYgLTI0LjkxMjM5MiwxOC44NDY2MjEgLTIuNjk1MjQ1LC01Ljc1MTUxNyAtOC41MzU5MzQsLTkuNzgwOTM4IC0xNS4yNjMzOTQsLTkuNzgwOTM4IC05LjI1MTg1LDAgLTE2Ljg1NzExLDcuNjA1MjYzIC0xNi44NTcxMSwxNi44NTcxMDggMCw5LjI1MTgzMyA3LjYwNTI2LDE2Ljg2MDU2NyAxNi44NTcxMSwxNi44NjA1NjcgNi43Mjc0NiwwIDEyLjU2ODE0OSwtNC4wMzE4ODUgMTUuMjYzMzk1LC05Ljc4NDQxMiAzLjEwMTE3NSwxMC44NDQyNSAxMy4xMDcxMiwxOC44NTAxMDYgMjQuOTEyMzkxLDE4Ljg1MDEwNiAxMS43MTc5NjQsMCAyMS42NzI4OSwtNy44ODUxMTEgMjQuODUzMzgyLC0xOC42MDcwNDggMi43NDUwMzYsNS42MjE5MzQgOC41MTM0MzYsOS41NDEzNTQgMTUuMTQ1MzQyLDkuNTQxMzU0IDkuMjUxODUsMCAxNi44NjA1NywtNy42MDg3MzQgMTYuODYwNTcsLTE2Ljg2MDU2NyAwLC05LjI1MTg0NSAtNy42MDg3MiwtMTYuODU3MTA4IC0xNi44NjA1NywtMTYuODU3MTA4IC02LjYzMTkwNiwwIC0xMi40MDAzMDYsMy45MTY5NjUgLTE1LjE0NTM0Miw5LjUzNzg5MSBDIDg4LjA4MDc4NiwxNy4yNTc0NzUgNzguMTI1ODYsOS4zNzUgNjYuNDA3ODk2LDkuMzc1IFogbSAwLDkuODk1NTE4IGMgOC45MTE2NDgsMCAxNi4wMzA3NDgsNy4xMTU2NTMgMTYuMDMwNzQ4LDE2LjAyNzI3MyAwLDguOTExNjA1IC03LjExOTEsMTYuMDMwNzM3IC0xNi4wMzA3NDgsMTYuMDMwNzM3IC04LjkxMTU5MywwIC0xNi4wMjcyNDcsLTcuMTE5MTMyIC0xNi4wMjcyNDcsLTE2LjAzMDczNyAwLC04LjkxMTYyIDcuMTE1NjUzLC0xNi4wMjcyNzEgMTYuMDI3MjQ3LC0xNi4wMjcyNzMgeiBNIDI2LjIzMjExLDI4LjMzNjIwMiBjIDMuOTA0MzgsMCA2Ljk2NTA1LDMuMDU3MTg4IDYuOTY1MDUsNi45NjE1ODkgMCwzLjkwNDM4NiAtMy4wNjA2Nyw2Ljk2NTA0OSAtNi45NjUwNSw2Ljk2NTA0OSAtMy45MDQzOSwwIC02Ljk2MTYxLC0zLjA2MDY2MyAtNi45NjE2MSwtNi45NjUwNDkgMCwtMy45MDQ0MDEgMy4wNTcyMiwtNi45NjE1ODkgNi45NjE2MSwtNi45NjE1ODkgeiBtIDgwLjE3NDUxLDAgYyAzLjkwNDQyLDAgNi45NjUwNiwzLjA1NzE4OCA2Ljk2NTA2LDYuOTYxNTg5IDAsMy45MDQzODYgLTMuMDYwNjYsNi45NjUwNDkgLTYuOTY1MDYsNi45NjUwNDkgLTMuOTA0MzYsMCAtNi45NjE1NzYsLTMuMDYwNjYzIC02Ljk2MTU3NiwtNi45NjUwNDkgMCwtMy45MDQ0MDEgMy4wNTcyMjYsLTYuOTYxNTg5IDYuOTYxNTc2LC02Ljk2MTU4OSB6IiBzdHlsZT0iY29sb3I6IzAwMDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0OjQwMDtmb250LXN0cmV0Y2g6bm9ybWFsO2ZvbnQtc2l6ZTptZWRpdW07bGluZS1oZWlnaHQ6bm9ybWFsO2ZvbnQtZmFtaWx5OnNhbnMtc2VyaWY7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO3RleHQtZGVjb3JhdGlvbi1saW5lOm5vbmU7dGV4dC1kZWNvcmF0aW9uLXN0eWxlOnNvbGlkO3RleHQtZGVjb3JhdGlvbi1jb2xvcjojMDAwO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7d3JpdGluZy1tb2RlOmxyLXRiO2RpcmVjdGlvbjpsdHI7YmFzZWxpbmUtc2hpZnQ6YmFzZWxpbmU7dGV4dC1hbmNob3I6c3RhcnQ7d2hpdGUtc3BhY2U6bm9ybWFsO2NsaXAtcnVsZTpub256ZXJvO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmlzaWJpbGl0eTp2aXNpYmxlO29wYWNpdHk6MTtpc29sYXRpb246YXV0bzttaXgtYmxlbmQtbW9kZTpub3JtYWw7Y29sb3ItaW50ZXJwb2xhdGlvbjpzUkdCO2NvbG9yLWludGVycG9sYXRpb24tZmlsdGVyczpsaW5lYXJSR0I7c29saWQtY29sb3I6IzAwMDtzb2xpZC1vcGFjaXR5OjE7ZmlsbDojMDA4MmM5O2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDo1LjU2NTkwMDMzO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO3N0cm9rZS1vcGFjaXR5OjE7Y29sb3ItcmVuZGVyaW5nOmF1dG87aW1hZ2UtcmVuZGVyaW5nOmF1dG87c2hhcGUtcmVuZGVyaW5nOmF1dG87dGV4dC1yZW5kZXJpbmc6YXV0bztlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIi8+PHBhdGggc3R5bGU9ImZpbGw6IzAwODJjOTtmaWxsLW9wYWNpdHk6MTtzdHJva2Utd2lkdGg6LjQ3MDM4NTIyIiBpZD0icGF0aDExNzQiIGQ9Im0gMjEuMjM1NjkzLDY5LjA0Mzc1NiBjIC0wLjMyOTI2LDAgLTAuNDcxNDcsMC4xODc5MzYgLTAuNDcxNDcsMC41MTcyNDIgViA4My4yMDM2OCBjIDAsMC4zMjkyNyAwLjE0MjIxLDAuNTE0OTUgMC40NzE0NywwLjUxNDk1IGggMC4zNzc2MyBjIDAuMzI5MjcsMCAwLjUxNDk0LC0wLjE4NTY4IDAuNTE0OTQsLTAuNTE0OTUgViA3MS44NzQ4MzMgbCA3LjQ0NzMsMTEuNTU3NzI3IGMgMC4wMzI0LDAuMDUwNSAwLjA2NzcsMC4wODQyIDAuMTAyOTksMC4xMjEyMyAwLjAxMDYsMC4wMTI1IDAuMDE3OSwwLjAyNTYgMC4wMjk4LDAuMDM2NiAwLjAzMTcsMC4wMjg5IDAuMDY2NSwwLjA0NCAwLjEwMDY1LDAuMDYxOCAwLjAxOSwwLjAxIDAuMDMzOCwwLjAyNDcgMC4wNTUsMC4wMzIgMC4wMTQ4LDAuMDA1IDAuMDMwNCwwLjAwMiAwLjA0NTgsMC4wMDYgMC4wNTI1LDAuMDEzNSAwLjEwNjE4LDAuMDI3NSAwLjE2OTM2LDAuMDI3NSBoIDAuMzc1MzQgYyAwLjMyOTI2LDAgMC40NzE0NiwtMC4xODU2NyAwLjQ3MTQ2LC0wLjUxNDk1IFYgNjkuNTYwNzY4IGMgMCwtMC4zMjkzMDUgLTAuMTQyMiwtMC41MTcyNDEgLTAuNDcxNDYsLTAuNTE3MjQxIGggLTAuMzc1MzQgYyAtMC4zMjkyOSwwIC0wLjUxNzI0LDAuMTg3OTM2IC0wLjUxNzI0LDAuNTE3MjQxIFYgODAuODkwMTEgbCAtNy40NDczLC0xMS41NTc3MTYgYyAtMC4wMjU0LC0wLjAzOTM5IC0wLjA1NjEsLTAuMDYzMzkgLTAuMDg0NywtMC4wOTM5IC0wLjA4NiwtMC4xMjE2MTEgLTAuMjIyMiwtMC4xOTQ1NDUgLTAuNDE2NTQsLTAuMTk0NTQ1IHogbSA4OS40MjAxNTcsMC4xODc2NzYgYyAtMC4zMjkyNiwwIC0wLjE4NzY3LDAuMTg3OTU2IC0wLjE4NzY3LDAuNTE3MjQxIHYgNC42NTc0MTcgYyAwLDAuNDcwMzcgMC4wNDU2LDAuNzk4NzIgMC4wNDU2LDAuNzk4NzIgaCAtMC4wNDU2IGMgMCwwIC0wLjg5NDE5LC0yLjA2ODkzIC0zLjM4NzIyLC0yLjA2ODkzIC0yLjcyODIxLDAgLTQuNjU3NzEsMi4xNjM3MiAtNC41NjM1Nyw1LjM2MjMxIDAsMy4xOTg2MiAxLjc0MDI0LDUuNDEwNDEgNC41MTU1MSw1LjQxMDQxIDIuNjgxMTgsMCAzLjU3NDksLTIuMTY1MDggMy41NzQ5LC0yLjE2NTA4IGggMC4wNDggYyAwLDAgLTAuMDkzOSwwLjI4MjgzIC0wLjA5MzksMC42NTkxMyB2IDAuNzk4NzQgYyAwLDAuMzI5MjYgMC4xODc5NiwwLjQ3MTQ4IDAuNTE3MjYsMC40NzE0OCBoIDAuMzI5NTUgYyAwLjMyOTI3LDAgMC40NjkxNywtMC4xODc5OCAwLjQ2OTE3LC0wLjUxNzI0IFYgNjkuNzQ4NjczIGMgMCwtMC4zMjkyODUgLTAuNTE3NTQsLTAuNTE3MjQxIC0wLjg0NjgxLC0wLjUxNzI0MSB6IG0gLTM2LjU0OTg1OSwwLjA0ODEgYyAtMC4zMjkyNzYsMCAtMC4xMzk2MSwwLjE4Nzk3NiAtMC4xMzk2MSwwLjUxNzI0MSBWIDgxLjMyMDE3IGMgMCwyLjI1NzgzIDEuNTAzNzY2LDIuNTQwMzkgMi4zNTA0NjMsMi41NDAzOSAwLjM3NjMwMSwwIDAuNTE3MjE3LC0wLjE4Nzk2IDAuNTE3MjE3LC0wLjUxNzIxIHYgLTAuMzI5NTggYyAwLC0wLjMyOTI2IC0wLjE4ODIxMiwtMC40NjkxOCAtMC40MjMzOTQsLTAuNDY5MTggLTAuNDcwNDA1LC0wLjA0NyAtMS4wODAyNDksLTAuMTg4ODcgLTEuMDgwMjQ5LC0xLjUwNTk0IFYgNjkuNzk2NzcgYyAwLC0wLjMyOTI2NiAtMC41MTc1MzEsLTAuNTE3MjQxIC0wLjg0NjgwNywtMC41MTcyNDEgeiBNIDU3LjIyMDI2Niw3MC41MDE2OSBjIC0wLjMyOTI3LDAgLTAuNTE3MjM4LDAuMTg3OTc1IC0wLjUxNzIzOCwwLjUxNzI0IHYgMi40NDY1OSAxLjE3NjM4IDUuMzE0MjMgYyAwLDIuNDQ1OTkgMS4zNjUxMDUsMy44MTA2NCAzLjYyMjk0NiwzLjgxMDY0IDAuNDIzMzQsMCAwLjU2MzAxMSwtMC4xMzk5MyAwLjU2MzAxMSwtMC40NjkxOCB2IC0wLjI4MzggYyAwLC0wLjM3NjI5IC0wLjEzOTY3MSwtMC40NzAyNCAtMC41NjMwMTEsLTAuNTE3MjQgLTAuNzk5NjUyLC0wLjA0NyAtMi4yNTg5MDUsLTAuMzI5MTIgLTIuMjU4OTA1LC0yLjcyODA5IHYgLTUuMTc0NjQgaCAyLjExNzAwOSBjIDAuMzI5MjY4LDAgMC41MTcyMzgsLTAuMTM5OSAwLjUxNzIzOCwtMC40NjkxOCB2IC0wLjE0MTkgYyAwLC0wLjMyOTI2IC0wLjE4Nzk3LC0wLjUxNzIyIC0wLjUxNzIzOCwtMC41MTcyMiBoIC0yLjExNzAwOSB2IC0yLjQ0NjU5IGMgMCwtMC4zMjkyNjUgLTAuMTM5OTA5LC0wLjUxNzI0IC0wLjQ2OTE3NywtMC41MTcyNCB6IG0gLTE4LjczNDk2MywyLjYzNDI3IGMgLTIuODIyMjksMCAtNS4wODE5MiwyLjAyMzU5IC01LjEyODg4LDUuNDEwMzcgMCwzLjE5ODU5IDIuMzUyODksNS40MDgwOSA1LjQxMDM5LDUuNDA4MDkgMS42NDYzMjgsMCAyLjg2ODUyLC0wLjcwNDk1IDMuNDMyOTg2LC0xLjEyODMxIDAuMjM1MjYsLTAuMTg4MTQgMC4yODMwMTQsLTAuNDIzOTIgMC4xNDE4OTYsLTAuNjU5MTIgbCAtMC4xNDE4OTYsLTAuMjMzNDYgYyAtMC4xNDExMTUsLTAuMjgyMjMgLTAuMzc0NjEyLC0wLjMzMDA1IC0wLjY1Njg0NiwtMC4xNDE4OCAtMC40NzAzODMsMC4zNzYyOSAtMS40MTMyOTUsMC45NDA2NCAtMi43MzAzNzEsMC45NDA2NCAtMi4xMTY3MDksMCAtMy45NTEzMTksLTEuNTA2MDQgLTMuOTk4Mjc5LC00LjE0MDE5IGggNy40NzkzMzEgYyAwLjI4MjI0NywwIDAuNTE3MjM4LC0wLjIzNTAxIDAuNTE3MjM4LC0wLjUxNzI1IDAsLTIuOTYzNCAtMS41NTAyOTEsLTQuOTM4ODkgLTQuMzI1NTY5LC00LjkzODg5IHogbSAyOS4yMjM4ODMsMCBjIC0zLjA1NzQ4MiwwIC01LjQwOTIwMywyLjI1NzU1IC01LjQ1NjE2MSw1LjQ1NjE0IDAsMy4xOTg2IDIuMzUyODk2LDUuNDEwMzkgNS40MTAzODcsNS40MTAzOSAxLjg4MTU0MSwwIDMuMTUxMzA3LC0wLjg5NDkzIDMuNjY4NzE4LC0xLjMxODI4IDAuMjM1MjYyLC0wLjIzNTIgMC4yODA3MjksLTAuNDIyNjUgMC4xMzk2MTksLTAuNzA0OSBMIDcxLjMzMjEzLDgxLjc5MTY1IGMgLTAuMTg4MTM2LC0wLjI4MjI1IC0wLjM3NjkwMiwtMC4zMzAwNSAtMC42NTkxMzEsLTAuMTQxOTEgLTAuNDcwMzgzLDAuNDIzMzQgLTEuNDU3NTUyLDEuMDgyNTUgLTIuOTE1NzUxLDEuMDgyNTUgLTIuMjU3ODI2LDAgLTQuMDQ2MzQ4LC0xLjY5NDE5IC00LjA0NjM0OCwtNC4xNDAxOSAwLC0yLjQ5MzAyIDEuNzg4NTIyLC00LjE4NTk2IDQuMDQ2MzQ4LC00LjE4NTk2IDEuMjIzMDA4LDAgMi4xMTU4MTYsMC42MTEzNyAyLjU4NjE4LDAuOTQwNjUgMC4yODIyNDEsMC4xODgwNyAwLjUxNjc0OCwwLjE4ODM4IDAuNzA0OTEzLC0wLjA5MzggbCAwLjE0MTksLTAuMjM1NzIgYyAwLjIzNTI3MSwtMC4yODIyNCAwLjE4NzEyNSwtMC41MTY3NyAtMC4wNDgxLC0wLjcwNDkgLTAuNTE3NDIyLC0wLjQyMzM3IC0xLjY0NTUxNSwtMS4xNzYzOCAtMy40MzI5ODYsLTEuMTc2MzggeiBtIDE1Ljg5OTMwMSwwIGMgLTMuMDEwNDUxLDAgLTUuNDU2MTU2LDIuMzA0ODIgLTUuNDU2MTU2LDUuMzYyMzEgMCwzLjEwNDUxIDIuNDQ1NzA1LDUuNDU2MTUgNS40NTYxNTYsNS40NTYxNSAzLjAxMDQ3OCwwIDUuNDU2MTY4LC0yLjM1MTY0IDUuNDU2MTY4LC01LjQ1NjE1IDAsLTMuMDU3NDkgLTIuNDQ1NjksLTUuMzYyMzEgLTUuNDU2MTY4LC01LjM2MjMxIHogbSAtMzAuNDI5OTkxLDAuMTU3OTMgYyAtMC4xMTUxOCwwLjAxODQgLTAuMjI2MDM3LDAuMDk1OSAtMC4zMzE4NTcsMC4yMjE5NyBsIC0xLjkwNDE2NCwyLjI2ODA1IC0xLjQyMzU0NiwxLjY5ODE4IC0yLjE1ODIwNSwtMi41NzAxNSAtMS4xNjk1MDUsLTEuMzk2MDggYyAtMC4xMDU4NzYsLTAuMTI2MTEgLTAuMjI1Nzk1LC0wLjE5NTI1IC0wLjM1MDE2MywtMC4yMDU5NyAtMC4xMjQzNTQsLTAuMDEgLTAuMjUzNzk2LDAuMDM2MSAtMC4zNzk5MTksMC4xNDE4OSBsIC0wLjI4ODM3MSwwLjI0MjU4IGMgLTAuMjUyMjIzLDAuMjExNjcgLTAuMjM5MDEsMC40NDU4MyAtMC4wMjc0NSwwLjY5ODA3IGwgMS45MDQxNjYsMi4yNjgwMyAxLjU3OTE3MiwxLjg4MzU3IC0yLjMxMTU0MywyLjc1MzI2IGMgLTAuMDAyNCwwLjAwMiAtMC4wMDM1LDAuMDA1IC0wLjAwNDYsMC4wMDYgbCAtMS4xNjcyMTUsMS4zODkyMyBjIC0wLjIxMTY1MywwLjI1MjIzIC0wLjE4ODEzMiwwLjUxODQyIDAuMDY0MDgsMC43MzAwOSBsIDAuMjg4MzY4LDAuMjQwMyBjIDAuMjUyMjM5LDAuMjExNjQgMC40ODE4MTMsMC4xNTg0MSAwLjY5MzQ2NSwtMC4wOTM5IGwgMS45MDE4NzYsLTIuMjY4MDYgMS40MjU4MzQsLTEuNjk4MTggMi4xNTgyMDQsMi41NzI0NCBjIDEwZS00LDAuMDAyIDAuMDAzNSwwLjAwNCAwLjAwNDYsMC4wMDUgbCAxLjE2NDkyOCwxLjM5MTUgYyAwLjIxMTY1MiwwLjI1MjIzIDAuNDc3ODM0LDAuMjczMzcgMC43MzAwODEsMC4wNjE3IGwgMC4yODgzNzEsLTAuMjQwMyBjIDAuMjUyMjM3LC0wLjIxMTY1IDAuMjM5MTM0LC0wLjQ0NTgxIDAuMDI3NDYsLTAuNjk4MDUgbCAtMS45MDQxNjEsLTIuMjcwMzQgLTEuNTc5MTc3LC0xLjg4MTI5IDIuMzExNTQ2LC0yLjc1NTU0IGMgMC4wMDI0LC0wLjAwMiAwLjAwMzUsLTAuMDA0IDAuMDA0NiwtMC4wMDYgbCAxLjE2NzIxNCwtMS4zODkyMSBjIDAuMjExNjUxLC0wLjI1MjI0IDAuMTg4MTMyLC0wLjUxODQ0IC0wLjA2NDA4LC0wLjczMDA5IGwgLTAuMjg4MzcxLC0wLjI0MDMgYyAtMC4xMjYxMTIsLTAuMTA1ODcgLTAuMjQ2NDA4LC0wLjE0NjU1IC0wLjM2MTYwNywtMC4xMjgxNSB6IG0gMzguNjYyMzA4LDAuMDc3OSBjIC0wLjMyOTI4LDAgLTAuNDcxNDgsMC4xODc5NiAtMC40NzE0OCwwLjUxNzIzIHYgNi4wNjcyMiBjIDAsMi42ODEyIDEuOTc1NywzLjk5ODI5IDQuNDIxNjksMy45OTgyOSAyLjQ0NiwwIDQuNDIxNjk2LC0xLjMxNzA5IDQuNDIxNjk2LC0zLjk5ODI5IHYgLTYuMDY3MjMgYyAwLjA0NywtMC4zMjkyNiAtMC4xMzk5MSwtMC41MTcyMyAtMC40NjkxNzYsLTAuNTE3MjMgaCAtMC4zNzc2MyBjIC0wLjMyOTI3LDAgLTAuNTE3MjQsMC4xODc5NyAtMC41MTcyNCwwLjUxNzIzIHYgNS42OTE4OSBjIDAsMS41OTkzMSAtMS4wMzUsMy4wNTc2NiAtMy4wNTc2NSwzLjA1NzY2IC0xLjk3NTYsMCAtMy4wNTc2MywtMS40NTgzNSAtMy4wNTc2MywtMy4wNTc2NiB2IC01LjY5MTg5IGMgMCwtMC4zMjkyNiAtMC4xODc5NywtMC41MTcyMyAtMC41MTcyNiwtMC41MTcyMyB6IG0gLTUzLjQwMzU2MSwwLjk0MDYzIGMgMS41MDUyMjYsMCAyLjgyMTYxLDEuMDgxNTUgMi45MTU3NTMsMy4yNDUzMSBoIC02LjQ5MDYzMyBjIDAuMzI5MjcsLTIuMTE2NzQgMS44MzQ0NywtMy4yNDUzMSAzLjU3NDg4LC0zLjI0NTMxIHogbSA0NS4xNzEyNDQsMC4wOTM5IGMgMi4yMTA4MDksMCAzLjk5ODMwMywxLjc0MDIzIDMuOTk4MzAzLDQuMDkyMTQgMCwyLjQ0NTk4IC0xLjc4NzQ5NCw0LjIzNDAxIC0zLjk5ODMwMyw0LjIzNDAxIC0yLjIxMDc4MSwwIC0zLjk5OTM4NSwtMS44MzUwNSAtNC4wNDYzMzIsLTQuMjM0MDEgMCwtMi4zMDQ4OCAxLjgzNTU1MSwtNC4wOTIxNCA0LjA0NjMzMiwtNC4wOTIxNCB6IG0gMjMuNTY2MzAzLDAgYyAyLjIxMDgyLDAgMy4yOTMzOSwyLjAyMzQ2IDMuMjkzMzksNC4xNDAyIDAsMi45NjM0IC0xLjYwMTAyLDQuMTg1OTUgLTMuMzQxNDQsNC4xODU5NSAtMS45Mjg1NiwwIC0zLjI0NDEzLC0xLjY0NTkgLTMuMjkxMDgsLTQuMTg1OTUgMCwtMi42MzQxNSAxLjUwNDY1LC00LjE0MDIgMy4zMzkxMywtNC4xNDAyIHoiLz48L3N2Zz4="
  link:
    - - en_US
      - {{ portalNextcloudLinkBase }}/apps/files
  allowedGroups:
    - 'cn=Domain Users,cn=groups,{{ ldapBaseDn }}'
    - 'cn=Domain Admin,cn=groups,{{ ldapBaseDn }}'
  linkTarget: newwindow
  description:
    de_DE: Dateien verwalten, bearbeiten und teilen
    en_US: Manage, edit and share files
  displayName:
    de_DE: Nextcloud
    en_US: Nextcloud
---
action: ensure_list_contains
module: portals/category
position: cn=domain-service,cn=category,cn=portals,cn=univention,{{ ldapBaseDn }}
properties:
  entries:
  - cn=nextcloud,cn=entry,cn=portals,cn=univention,{{ ldapBaseDn }}
...

Your directory structure looks like Listing 3.19.

Listing 3.19 Directory structure with plugins for Nextcloud packaged integration#
nextcloud-packaged-integration/
├── docker
└── plugins
    ├── ldap-schema
    │   └── nextcloud.schema
    └── udm-data-loader
        └── 86_Nextcloud.yaml

3.8.4. Build container image#

After you prepared the plugins for the packaged integration, it’s time to bundle them in a container image. This section follows the steps outlined in Bundle packaged integrations and applies them to the Nextcloud packaged integration.

To bundle the packaged integration, use the following steps:

  1. Create the Dockerfile in the docker/ directory with the content in Listing 3.20.

    Listing 3.20 Dockerfile for creating the container image for the packaged integration#
    # SPDX-License-Identifier: AGPL-3.0-only
    # SPDX-FileCopyrightText: 2024 - 2025 Univention GmbH
    
    ARG BASE_IMAGE_TAG=3.20
    ARG BASE_IMAGE=docker.io/alpine
    FROM ${BASE_IMAGE}:${BASE_IMAGE_TAG}
    
    # Create system group and system user
    RUN addgroup -S appgroup && adduser -S appuser -G appgroup
    USER appuser
    
    WORKDIR /
    
    # Copy plugins of the packaged integration to the Docker image
    COPY plugins /plugins
    # Copy the plugin loader to the Docker image
    COPY docker/loader.sh /bin/loader
    
    CMD ["/bin/loader"]
    
  2. Create the loader.sh loader script in the docker/ directory with the content in Listing 3.21. For information about the loader, see Packaged integration loader.

    Listing 3.21 Loader script for copying the plugins to the target directory during installation#
    #!/bin/sh
    # SPDX-License-Identifier: AGPL-3.0-only
    # SPDX-FileCopyrightText: 2024 - 2025 Univention GmbH
    
    set -eu
    
    echo "Copying the Nubus plugins into the /target volume"
    for source in /plugins/*; do
      plugin_type=$(basename "${source}")
      target="/target/${plugin_type}"
      if [ -d "${target}" ]; then
        echo "COPY - Plugin type ${plugin_type} in /target, copying files."
        cp -av "${source}" /target
      else
        echo "SKIP - Plugin type ${plugin_type} not in /target, skipping."
      fi
    done
    

    Finally, your directory structure looks like Listing 3.22.

    Listing 3.22 Directory structure with Dockerfile, loader, and plugins#
    nextcloud-packaged-integration/
    ├── docker
    │   ├── Dockerfile
    │   └── loader.sh
    └── plugins
        ├── ldap-schema
        │   └── nextcloud.schema
        └── udm-data-loader
            └── 86_Nextcloud.yaml
    
  3. To build the container image, run the commands in Listing 3.23.

    Listing 3.23 Example for building the container image for the packaged integration#
    $ export IMAGE_NAME="nextcloud"
    
    $ cd nextcloud-packaged-integration
    $ docker build -t "$IMAGE_NAME" -f docker/Dockerfile .
    

3.8.5. Publish packaged integration#

To make the packaged integration available for Nubus for Kubernetes, you need to publish it to a container registry where your intended audience has access to.

To publish your container image, use the commands from the example in Listing 3.24.

Listing 3.24 Example for publishing container image with bundled packaged integration#
$ export REGISTRY="artificts.software-univention.de"
$ export REPOSITORY="nubus/images/nextcloud-packaged-integration"
$ export IMAGE_NAME="nextcloud"
$ export IMAGE_TAG="0.1.0"

$ docker image tag "$IMAGE_NAME" "$REGISTRY"/"$REPOSITORY"/"$IMAGE_NAME":"$IMAGE_TAG"
$ docker image push "$REGISTRY"/"$REPOSITORY"/"$IMAGE_NAME":"$IMAGE_TAG"

The environment variables used in Listing 3.24 need to reflect the host and folder structure of your container registry. An operator later uses them in their Helm values when referring to the container image of the packaged integration. Table 3.2 shows the mapping.

Table 3.2 Term mapping and description for used environment variables#

Environment variable

Description

Helm Chart key

REGISTRY

FQDN and optional port of your container registry

registry

REPOSITORY

PATH to the image on the registry without trailing slash /

repository

IMAGE_NAME

The name of the container image

name

IMAGE_TAG

Your version tag or latest

tag

3.8.6. Communicate packaged integration#

You need to tell operators that want to install your packaged integration, where they can find the container image, what each template variable is for, and which values each variable accepts. Provide an example. This how-to defines the following template variables that you need to communicate:

nextcloudUserPassword

The operator must define a dedicated password for the LDAP search user. They must use the same password in the Nextcloud LDAP configuration for the search user.

portalNextcloudLinkBase

The operator must set the base URL to their Nextcloud instance, for example https://nextcloud.example.com.

Tip

You don’t need to communicate the ldapBaseDn template variable, because Nubus for Kubernetes automatically fills in the value during installation.