Files
opendesk/docs/existing-secrets.md
2025-12-03 21:26:07 +01:00

18 KiB

Existing Secrets

This document covers how to utilise existing secrets and special requirements. The examples documented here are mostly showing the format with the openDesk default values.

General

⚠ ATTENTION: This feature is still in early development. For now you can't simply replace plain secrets with existing secrets because some secrets are used several components where some maybe don't support existing secrets by now.

For most components when set the existing secret will supersede e.g. a password in a values.yaml file.

The file existing_secrets.yaml lists all possible references to existing secrets that are currently implemented in openDesk.

Components

This section covers information and special requirements to existing secrets that some Helm Charts expect.

Cassandra

Cassandra is pre-populated with information regarding Dovecot with a cql script. The openDesk default initDB setting is configured as follows:

  initUserData.cql: >
    CREATE KEYSPACE IF NOT EXISTS {{ .Values.databases.dovecotDictmap.name | quote }} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
    CREATE ROLE IF NOT EXISTS {{ .Values.databases.dovecotDictmap.username | quote }};
    ALTER ROLE {{ .Values.databases.dovecotDictmap.username | quote }} WITH PASSWORD = {{ regexReplaceAll "'" .Values.secrets.cassandra.dovecotDictmapUser "''" | squote }} AND LOGIN = true;
    GRANT ALL ON KEYSPACE {{ .Values.databases.dovecotDictmap.name | quote }} TO {{ .Values.databases.dovecotDictmap.username | quote }};
    CREATE KEYSPACE IF NOT EXISTS {{ .Values.databases.dovecotACL.name | quote }} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
    CREATE ROLE IF NOT EXISTS {{ .Values.databases.dovecotACL.username | quote }};
    ALTER ROLE {{ .Values.databases.dovecotACL.username | quote }} WITH PASSWORD = {{ regexReplaceAll "'" .Values.secrets.cassandra.dovecotACLUser "''" | squote }} AND LOGIN = true;
    GRANT ALL ON KEYSPACE {{ .Values.databases.dovecotACL.name | quote }} TO {{ .Values.databases.dovecotACL.username | quote }};

This has to be adapted into a secret that also holds a cql script and is named in initDBSecret.

Keycloak

Several existing secrets utilised by the Keycloak bootstrap chart are expected in a special format and/or key.

Admin credentials

stringData:
  admin.yaml: |
    username: "kcadmin"
    password: "{{ .Values.secrets.keycloak.adminPassword }}"

ox-connector

The secret openxchangeConnector.provisioningApiPassword has to provide a JSON file. The value .Values.secrets.oxConnector.provisioningApiPassword is taken from the default openDesk install without existing secrets and has to be replaced by some secret value. The following format is expected:

    stringData:
      ox-connector.json: "{ \"name\": \"ox-connector\", \"realms_topics\": [{\"realm\": \"udm\", \"topic\": \"oxmail/oxcontext\"}, {\"realm\": \"udm\", \"topic\": \"oxmail/accessprofile\"}, {\"realm\": \"udm\", \"topic\": \"users/user\"}, {\"realm\": \"udm\", \"topic\": \"oxresources/oxresources\"}, {\"realm\": \"udm\", \"topic\": \"groups/group\"}, {\"realm\": \"udm\", \"topic\": \"oxmail/functional_account\"}], \"request_prefill\": true, \"password\": \"{{ .Values.secrets.oxConnector.provisioningApiPassword }}\" }"

The secret nubus.ldapSearch.keycloak has the requirement to use password as key.

SSOFederation and Clients

Values taken from those existing secrets will supersede secret values that are already present for the client/IdP in the configuration or add them accordingly. Further the secrets for the have to provide a yaml file in a special format. Both formats rely on the same key as used in the configuration respectively. The expected format for each configuration can be seen in the table below:

Section Format
functional.authentication.clients 1.
functional.authentication.ssoFederation.idpDict 2.
keycloak.clients 1.
  1. Expected format for the clients secrets:

    opendesk-intercom:
      clientId: "opendesk-intercom"
      secret: "{{ .Values.secrets.keycloak.clientSecret.intercom }}"
    opendesk-notes:
      clientId: "opendesk-notes"
      secret: "{{ .Values.secrets.keycloak.clientSecret.notes }}"
    opendesk-dovecot:
      clientId: "opendesk-dovecot"
      secret: "{{ .Values.secrets.keycloak.clientSecret.dovecot }}"
    opendesk-oxappsuite:
      clientId: "opendesk-oxappsuite"
      secret: "{{ .Values.secrets.keycloak.clientSecret.as8oidc }}"
    opendesk-matrix:
      clientId: "opendesk-matrix"
      secret: "{{ .Values.secrets.keycloak.clientSecret.matrix }}"
    opendesk-nextcloud:
      clientId: "opendesk-nextcloud"
      secret: "{{ .Values.secrets.keycloak.clientSecret.ncoidc }}"
    opendesk-openproject:
      clientId: "opendesk-openproject"
      secret: "{{ .Values.secrets.keycloak.clientSecret.openproject }}"
    opendesk-xwiki:
      clientId: "opendesk-xwiki"
      secret: "{{ .Values.secrets.keycloak.clientSecret.xwiki }}"
    
  2. Expected format for the ssoFederation secret:

    yourIdpDictEntry:
      clientId: "yourSecretValueHere"
      clientSecret: "yourSecretValueHere"
    

MariaDB

When initialising databases, users and credentials the Chart expects .sql files inside the secret to mount and feed them to the database client.

The expected format for the databases is as follows:

stringData:
  init-db-open-xchange.sql: |
    CREATE DATABASE IF NOT EXISTS openxchange_dummy;
    GRANT ALL PRIVILEGES ON openxchange_dummy.* TO "openxchange_user"@"%";
    FLUSH PRIVILEGES;
  init-db-nextcloud.sql: |
    CREATE DATABASE IF NOT EXISTS nextcloud;
    GRANT ALL PRIVILEGES ON nextcloud.* TO "nextcloud_user"@"%";
    FLUSH PRIVILEGES;
  init-db-xwiki.sql: |
    CREATE DATABASE IF NOT EXISTS xwiki;
    GRANT ALL PRIVILEGES ON xwiki.* TO "xwiki_user"@"%";
    FLUSH PRIVILEGES;

For the user and credentials the following format is expected:

stringData:
  init-user-open-xchange.sql: |
    CREATE USER IF NOT EXISTS "openxchange_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.openxchangeUser | quote }};
    ALTER USER "openxchange_user"@"%" WITH MAX_USER_CONNECTIONS 100;
    ALTER USER "openxchange_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.openxchangeUser | quote }};
  init-user-nextcloud.sql: |
    CREATE USER IF NOT EXISTS "nextcloud_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.nextcloudUser | quote }};
    ALTER USER "nextcloud_user"@"%" WITH MAX_USER_CONNECTIONS 100;
    ALTER USER "nextcloud_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.nextcloudUser | quote }};
  init-user-xwiki.sql: |
    CREATE USER IF NOT EXISTS "xwiki_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.xwikiUser | quote }};
    ALTER USER "xwiki_user"@"%" WITH MAX_USER_CONNECTIONS 100;
    ALTER USER "xwiki_user"@"%" IDENTIFIED BY {{ .Values.secrets.mariadb.xwikiUser | quote }};

MinIO

Like described in the upstream values.yaml credentials and information about a user in existing secrets listed in usersExistingSecrets have to be formatted as follows:

stringData:
  username1: |
    username=test-username
    password=test-password
    disabled=false
    policies=readwrite,consoleAdmin,diagnostics
    setPolicies=false

Further we need the credentials introduced at MinIO in various other components that didn't implement the special format from MinIO. Hence we have to create key-value-pairs of the passwords for them.

Notes

There are some values that consist of more than just one secret part.

backend:
  configuration:
    django:
      superuserEmail:
        value: {{ printf "default.admin@%s" .Values.global.domain | quote }}
    redisUrl:
      value: "redis://default:{{ .Values.cache.notes.password | default .Values.secrets.redis.password }}@{{ .Values.cache.notes.host }}:{{ .Values.cache.notes.port }}/7"

OpenProject

Here we need a custom secret to inject confidential data into environment variables as expected by OpenProject.

stringData:
  OPENPROJECT_SEED__ENTERPRISE__TOKEN: {{ .Values.enterpriseKeys.openproject.token | quote }}
  OPENPROJECT_SEED_LDAP_OPENDESK_BINDPASSWORD: {{ .Values.secrets.nubus.ldapSearch.openproject | quote }}
  OPENPROJECT_AUTHENTICATION_GLOBAL__BASIC__AUTH_USER: {{ .Values.secrets.openproject.apiAdminUsername | quote }}
  OPENPROJECT_AUTHENTICATION_GLOBAL__BASIC__AUTH_PASSWORD: {{ .Values.secrets.openproject.apiAdminPassword | quote }}
  OPENPROJECT_SOUVAP__NAVIGATION__SECRET: {{ .Values.secrets.centralnavigation.apiKey | quote }}
  OPENPROJECT_SMTP__PASSWORD: {{ .Values.secrets.postfix.opendeskSystemPassword | quote }}

PostgreSQL

In order to initialise PostgreSQL with databases, users and credentials existing secrets are expected to contain .sql files.

The expected format for the databases is as follows:

stringData:
  init-db-keycloak.sql: |
    SELECT 'CREATE DATABASE keycloak' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'keycloak')\gexec
    ALTER DATABASE keycloak OWNER TO keycloak_user;
    GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak_user;
  init-db-keycloakExtension.sql: |
    SELECT 'CREATE DATABASE keycloak_extensions' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'keycloak_extensions')\gexec
    ALTER DATABASE keycloak_extensions OWNER TO keycloak_extensions_user;
    GRANT ALL PRIVILEGES ON DATABASE keycloak_extensions TO keycloak_extensions_user;
  init-db-notes.sql.sql: |
    SELECT 'CREATE DATABASE notes' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'notes')\gexec
    ALTER DATABASE notes OWNER TO notes_user;
    GRANT ALL PRIVILEGES ON DATABASE notes TO notes_user;
  init-db-openproject.sql: |
    SELECT 'CREATE DATABASE openproject' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'openproject')\gexec
    ALTER DATABASE openproject OWNER TO openproject_user;
    GRANT ALL PRIVILEGES ON DATABASE openproject TO openproject_user;
  init-db-synapse.sql: |
    SELECT 'CREATE DATABASE matrix ENCODING ''UTF8'' LC_COLLATE=''C'' LC_CTYPE=''C'' template=template0' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'matrix')\gexec
    ALTER DATABASE matrix OWNER TO matrix_user;
    GRANT ALL PRIVILEGES ON DATABASE matrix TO matrix_user;
  init-db-umsGuardianManagementApi.sql: |
    SELECT 'CREATE DATABASE guardianmanagementapi' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'guardianmanagementapi')\gexec
    ALTER DATABASE guardianmanagementapi OWNER TO guardianmanagementapi_user;
    GRANT ALL PRIVILEGES ON DATABASE guardianmanagementapi TO guardianmanagementapi_user;
  init-db-umsNotificationsApi.sql: |
    SELECT 'CREATE DATABASE notificationsapi' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'notificationsapi')\gexec
    ALTER DATABASE notificationsapi OWNER TO notificationsapi_user;
    GRANT ALL PRIVILEGES ON DATABASE notificationsapi TO notificationsapi_user;
  init-db-umsSelfservice.sql: |
    SELECT 'CREATE DATABASE selfservice' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'selfservice')\gexec
    ALTER DATABASE selfservice OWNER TO selfservice_user;
    GRANT ALL PRIVILEGES ON DATABASE selfservice TO selfservice_user;
  init-db-nextcloud.sql: |
    SELECT 'CREATE DATABASE nextcloud' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'nextcloud')\gexec
    ALTER DATABASE nextcloud OWNER TO nextcloud_user;
    GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud_user;
  init-db-xwiki.sql: |
    SELECT 'CREATE DATABASE xwiki ENCODING ''UNICODE'' template=template0' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'xwiki')\gexec
    ALTER DATABASE xwiki OWNER TO xwiki_user;
    GRANT ALL PRIVILEGES ON DATABASE xwiki TO xwiki_user;

For the user and credentials the following format is expected:

stringData:
  init-user-keycloak.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'keycloak_user') THEN
      ALTER ROLE "keycloak_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.keycloakUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "keycloak_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.keycloakUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-notes.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'notes_user') THEN
      ALTER ROLE "notes_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.notesUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "notes_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.notesUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-openproject.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'openproject_user') THEN
      ALTER ROLE "openproject_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.openprojectUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "openproject_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.openprojectUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-keycloakExtension.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'keycloak_extensions_user') THEN
      ALTER ROLE "keycloak_extensions_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.keycloakExtensionUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "keycloak_extensions_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.keycloakExtensionUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-synapse.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'matrix_user') THEN
      ALTER ROLE "matrix_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.matrixUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "matrix_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.matrixUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-umsNotificationsApi.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'notificationsapi_user') THEN
      ALTER ROLE "notificationsapi_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsNotificationsApiUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "notificationsapi_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsNotificationsApiUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-umsGuardianManagementApi.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'guardianmanagementapi_user') THEN
      ALTER ROLE "guardianmanagementapi_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsGuardianManagementApiUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "guardianmanagementapi_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsGuardianManagementApiUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-umsSelfservice.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'selfservice_user') THEN
      ALTER ROLE "selfservice_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsSelfserviceUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "selfservice_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.umsSelfserviceUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-nextcloud.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'nextcloud_user') THEN
      ALTER ROLE "nextcloud_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.nextcloudUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "nextcloud_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.nextcloudUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$
  init-user-xwiki.sql: |
    DO $$BEGIN
    IF EXISTS (SELECT FROM pg_user WHERE usename = 'xwiki_user') THEN
      ALTER ROLE "xwiki_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.xwikiUser | squote }} CONNECTION LIMIT 100;
    ELSE
      CREATE ROLE "xwiki_user" WITH LOGIN ENCRYPTED PASSWORD {{ .Values.secrets.postgresql.xwikiUser | squote }} CONNECTION LIMIT 100;
    END IF;
    END$$

XWiki

Properties listed in the file of the existing secret will overwrite plain values.

Licenses can also be given via properties and require the format licenses=<EnterpriseLicense>,<Applicationslicense>.

Like described in the upstream values.yaml credentials and information about a user in existing secrets listed in propertiesSecret have to be formatted as follows:

stringData:
  propertiesFile: |
    propertie1=propertie1Value
    propertie2=propertie2Value
    propertie3=propertie3Value