From e97f7674db775821b328b8e0cde7817ed3a1bd3a Mon Sep 17 00:00:00 2001 From: "Franz Heinzmann (Frando)" Date: Mon, 17 Oct 2022 12:27:27 +0200 Subject: [PATCH] try to support collabora --- .env.sample | 16 +++ .gitignore | 1 + abra.sh | 9 +- compose.office.yml | 177 ++++++++++++++++++++++++++++++++ compose.yml | 5 +- office/app-registry.yaml | 58 +++++++++++ office/collabora-entrypoint.sh | 4 + office/onlyoffice-entrypoint.sh | 19 ++++ office/wopiserver-entrypoint.sh | 15 +++ office/wopiserver.conf.dist | 128 +++++++++++++++++++++++ 10 files changed, 426 insertions(+), 6 deletions(-) create mode 100644 compose.office.yml create mode 100644 office/app-registry.yaml create mode 100644 office/collabora-entrypoint.sh create mode 100644 office/onlyoffice-entrypoint.sh create mode 100644 office/wopiserver-entrypoint.sh create mode 100644 office/wopiserver.conf.dist diff --git a/.env.sample b/.env.sample index 18e1d07..87f9675 100644 --- a/.env.sample +++ b/.env.sample @@ -3,6 +3,9 @@ TYPE=ocis DOMAIN={{ .Domain }} LETS_ENCRYPT_ENV=production +# This is here so later lines can extend it; you likely don't wanna edit +COMPOSE_FILE="compose.yml" + # Make true if using OIDC and you want accounts to be created automatically PROXY_AUTOPROVISION_ACCOUNTS=false @@ -23,3 +26,16 @@ PROXY_TLS=false # Set to true if using self signed certs OCIS_INSECURE=false + +# ----- +# Onlyoffice, Collabora, WOPI server + +#COMPOSE_FILE="$COMPOSE_FILE:compose.office.yml" +#SECRET_WOPI_SECRET_VERSION=v1 +#SECRET_COLLABORA_ADMIN_PASSWORD_VERSION=v1 +#COLLABORA_DOMAIN= +#ONLYOFFICE_DOMAIN= +#WOPISERVER_DOMAIN= +#COLLABORA_ADMIN_USER=admin +# Valid values are: Debug, Info, Warning, Error. +# WOPISERVER_LOGLEVEL=Error diff --git a/.gitignore b/.gitignore index 7a6353d..c2d4b18 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .envrc +SANDBOX diff --git a/abra.sh b/abra.sh index b640190..737eef2 100644 --- a/abra.sh +++ b/abra.sh @@ -1,9 +1,10 @@ export APP_ENTRYPOINT_VERSION=v2 +export WOPISERVER_ENTRYPOINT_VERSION=v4 +export WOPISERVER_CONF_VERSION=v4 +export COLLABORA_ENTRYPOINT_VERSION=v2 +export ONLYOFFICE_ENTRYPOINT_VERSION=v2 +export APP_REGISTRY_VERSION=v2 ocis() { - echo "1: $1" - echo "2: $2" - echo "all: $@" - /usr/bin/ocis "$@" } diff --git a/compose.office.yml b/compose.office.yml new file mode 100644 index 0000000..53d92aa --- /dev/null +++ b/compose.office.yml @@ -0,0 +1,177 @@ +--- +version: "3.8" + +services: + app: + configs: + - source: app_registry + target: /etc/ocis/app-registry.yaml + mode: 0666 + + driver-collabora: + image: "owncloud/ocis:2.0.0-beta.8" + networks: + - internal + command: app-provider server + environment: + - COLLABORA_DOMAIN + - WOPISERVER_DOMAIN + - DOMAIN + - REVA_GATEWAY=${STACK_NAME}_app:9142 + - APP_PROVIDER_GRPC_ADDR=0.0.0.0:9164 + - APP_PROVIDER_EXTERNAL_ADDR=${STACK_NAME}_driver-collabora:9164 + - APP_PROVIDER_DRIVER=wopi + - APP_PROVIDER_WOPI_APP_NAME=Collabora + - APP_PROVIDER_WOPI_APP_ICON_URI=https://${COLLABORA_DOMAIN}/favicon.ico + - APP_PROVIDER_WOPI_APP_URL=https://${COLLABORA_DOMAIN} + - APP_PROVIDER_WOPI_INSECURE=${INSECURE:-false} + - APP_PROVIDER_WOPI_DRIVER_WOPI_URL=https://${WOPISERVER_DOMAIN} + - APP_PROVIDER_WOPI_FOLDER_URL_BASE_URL=https://${DOMAIN} + volumes: + - config:/etc/ocis + + # driver-onlyoffice: + # image: "owncloud/ocis:2.0.0-beta.8" + # networks: + # - internal + # user: "0" # needed for apk add in entrypoint script + # entrypoint: + # - /bin/sh + # - /entrypoint-override.sh + # #command: app-provider server + # environment: + # - ONLYOFFICE_DOMAIN + # - WOPISERVER_DOMAIN + # # first exapmle second docs + # - REVA_GATEWAY=${STACK_NAME}_app:9142 + # - STORAGE_GATEWAY_ENDPOINT=${STACK_NAME}_app:9142 + # - APP_PROVIDER_GRPC_ADDR=0.0.0.0:9164 + # - APP_PROVIDER_EXTERNAL_ADDR=${STACK_NAME}_driver-onlyoffice:9164 + # - APP_PROVIDER_DRIVER=wopi + # - APP_PROVIDER_WOPI_APP_NAME=OnlyOffice + # - APP_PROVIDER_WOPI_APP_ICON_URI=https://${ONLYOFFICE_DOMAIN}/web-apps/apps/documenteditor/main/resources/img/favicon.ico + # - APP_PROVIDER_WOPI_APP_URL=https://${ONLYOFFICE_DOMAIN} + # - APP_PROVIDER_WOPI_INSECURE="${INSECURE:-false}" + # # first exapmle second docs + # - APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL=https://${WOPISERVER_DOMAIN} + # - APP_PROVIDER_WOPI_DRIVER_WOPI_URL=https://${WOPISERVER_DOMAIN} + # # TODO: Secret + # - APP_PROVIDER_WOPI_WOPI_SERVER_IOP_SECRET=${WOPI_IOP_SECRET:-LoremIpsum123} + # volumes: + # - config:/etc/ocis + # configs: + # - source: onlyoffice_entrypoint + # target: /entrypoint-override.sh + # mode: 0555 + + wopiserver: + image: cs3org/wopiserver:v9.1.1 + networks: + - internal + - proxy + entrypoint: + - /bin/sh + - /entrypoint-override.sh + secrets: + - wopi_secret + configs: + - source: wopiserver_entrypoint + target: /entrypoint-override.sh + mode: 0555 + - source: wopiserver_conf + target: /etc/wopi/wopiserver.conf.dist + mode: 0444 + environment: + - WOPISERVER_DOMAIN + - WOPISERVER_INSECURE=${INSECURE:-false} + - WOPISERVER_LOGLEVEL=${WOPISERVER_LOGLEVEL:-Error} # Valid values are: Debug, Info, Warning, Error. + # TODO: Secrets + - WOPISECRET=${WOPI_JWT_SECRET:-LoremIpsum567} + - REVA_GATEWAY=${STACK_NAME}_app:9142 + volumes: + - wopi_data:/var/wopi_local_storage + - wopi_recovery:/var/spool/wopirecovery + # - wopi_logs:/var/log/wopi + deploy: + labels: + - "traefik.enable=true" + - "traefik.http.services.${STACK_NAME}-wopiserver.loadbalancer.server.port=8880" + - "traefik.http.routers.${STACK_NAME}-wopiserver.rule=Host(`${WOPISERVER_DOMAIN}`)" + - "traefik.http.routers.${STACK_NAME}-wopiserver.entrypoints=web-secure" + - "traefik.http.routers.${STACK_NAME}-wopiserver.tls.certresolver=${LETS_ENCRYPT_ENV}" + + collabora: + # image: collabora/code:22.05.0.1@sha256:49dd6e729c61390b0e75b9a6a77138a21fc2103a7e232bb7b3e7f5151580a9d5 + image: collabora/code:22.05.6.3.1 + networks: + - internal + - proxy + secrets: + - collabora_admin_password + environment: + - COLLABORA_DOMAIN + - COLLABORA_ADMIN_USER=${COLLABORA_ADMIN_USER:-admin} + - aliasgroup1=https://${WOPISERVER_DOMAIN}:443 + - DONT_GEN_SSL_CERT="YES" + # - extra_params="--o:ssl.enable=false --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=${DOMAIN}" + - extra_params=--o:ssl.enable=false --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=${DOMAIN} + - username=${COLLABORA_ADMIN_USER} + - password=${COLLABORA_ADMIN_PASSWORD:-admin} + cap_add: + - MKNOD + deploy: + labels: + - "traefik.enable=true" + - "traefik.http.services.${STACK_NAME}-collabora.loadbalancer.server.port=9980" + - "traefik.http.routers.${STACK_NAME}-collabora.rule=Host(`${COLLABORA_DOMAIN}`)" + - "traefik.http.routers.${STACK_NAME}-collabora.entrypoints=web-secure" + - "traefik.http.routers.${STACK_NAME}-collabora.tls.certresolver=${LETS_ENCRYPT_ENV}" + + # onlyoffice: + # image: onlyoffice/documentserver:7.1.1 + # networks: + # - internal + # - proxy + # environment: + # - ONLYOFFICE_DOMAIN + # - WOPI_ENABLED=true + # - USE_UNAUTHORIZED_STORAGE=${INSECURE:-false} # self signed certificates + # deploy: + # labels: + # - "traefik.enable=true" + # - "traefik.http.services.${STACK_NAME}-onlyoffice.loadbalancer.server.port=80" + # - "traefik.http.routers.${STACK_NAME}-onlyoffice.rule=Host(`${ONLYOFFICE_DOMAIN}`)" + # - "traefik.http.routers.${STACK_NAME}-onlyoffice.entrypoints=web-secure" + # - "traefik.http.routers.${STACK_NAME}-onlyoffice.middlewares=${STACK_NAME}-onlyoffice" + # - "traefik.http.routers.${STACK_NAME}-onlyoffice.tls.certresolver=${LETS_ENCRYPT_ENV}" + # - "traefik.http.middlewares.${STACK_NAME}-onlyoffice.headers.customrequestheaders.X-Forwarded-Proto=https" + +configs: + onlyoffice_entrypoint: + name: ${STACK_NAME}_onlyoffice_entrypoint_${APP_ENTRYPOINT_VERSION} + file: office/onlyoffice-entrypoint.sh + wopiserver_entrypoint: + name: ${STACK_NAME}_wopiserver_entrypoint_${WOPISERVER_ENTRYPOINT_VERSION} + file: office/wopiserver-entrypoint.sh + collabora_entrypoint: + name: ${STACK_NAME}_collabora_entrypoint_${COLLABORA_ENTRYPOINT_VERSION} + file: office/collabora-entrypoint.sh + wopiserver_conf: + name: ${STACK_NAME}_wopiserver_conf_${WOPISERVER_CONF_VERSION} + file: office/wopiserver.conf.dist + app_registry: + name: ${STACK_NAME}_app_registry_${APP_REGISTRY_VERSION} + file: office/app-registry.yaml + +secrets: + wopi_secret: + name: ${STACK_NAME}_wopi_secret_${SECRET_WOPI_SECRET_VERSION} + external: true + collabora_admin_password: + name: ${STACK_NAME}_collabora_admin_password_${SECRET_COLLABORA_ADMIN_PASSWORD_VERSION} + external: true + +volumes: + wopi_data: + wopi_recovery: + # wopi_logs: diff --git a/compose.yml b/compose.yml index 9202c9f..31131cf 100644 --- a/compose.yml +++ b/compose.yml @@ -3,7 +3,7 @@ version: "3.8" services: app: - image: "owncloud/ocis:2.0.0-beta.5" + image: "owncloud/ocis:2.0.0-beta.8" configs: - source: app_entrypoint target: /docker-entrypoint.sh @@ -14,8 +14,9 @@ services: - DEMO_USERS=false - IDM_CREATE_DEMO_USERS=false - PROXY_AUTOPROVISION_ACCOUNTS=true + - PROXY_OIDC_REWRITE_WELLKNOWN=true - OCIS_URL=https://${DOMAIN} - - OCIS_LOG_LEVEL=error + - OCIS_LOG_LEVEL=${OCIS_LOG_LEVEL:-error} - OCIS_LOG_COLOR=false - OCIS_OIDC_ISSUER= - WEB_OIDC_CLIENT_ID= diff --git a/office/app-registry.yaml b/office/app-registry.yaml new file mode 100644 index 0000000..4cf4262 --- /dev/null +++ b/office/app-registry.yaml @@ -0,0 +1,58 @@ +app_registry: + mimetypes: + - mime_type: application/pdf + extension: pdf + name: PDF + description: PDF document + icon: '' + default_app: '' + allow_creation: false + - mime_type: application/vnd.oasis.opendocument.text + extension: odt + name: OpenDocument + description: OpenDocument text document + icon: '' + default_app: Collabora + allow_creation: true + - mime_type: application/vnd.oasis.opendocument.spreadsheet + extension: ods + name: OpenSpreadsheet + description: OpenDocument spreadsheet document + icon: '' + default_app: Collabora + allow_creation: true + - mime_type: application/vnd.oasis.opendocument.presentation + extension: odp + name: OpenPresentation + description: OpenDocument presentation document + icon: '' + default_app: Collabora + allow_creation: true + - mime_type: application/vnd.openxmlformats-officedocument.wordprocessingml.document + extension: docx + name: Microsoft Word + description: Microsoft Word document + icon: '' + default_app: OnlyOffice + allow_creation: true + - mime_type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + extension: xlsx + name: Microsoft Excel + description: Microsoft Excel document + icon: '' + default_app: OnlyOffice + allow_creation: true + - mime_type: application/vnd.openxmlformats-officedocument.presentationml.presentation + extension: pptx + name: Microsoft PowerPoint + description: Microsoft PowerPoint document + icon: '' + default_app: OnlyOffice + allow_creation: true + - mime_type: application/vnd.jupyter + extension: ipynb + name: Jupyter Notebook + description: Jupyter Notebook + icon: '' + default_app: '' + allow_creation: true diff --git a/office/collabora-entrypoint.sh b/office/collabora-entrypoint.sh new file mode 100644 index 0000000..c9c587f --- /dev/null +++ b/office/collabora-entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +export COLLABORA_ADMIN_PASSWORD=$(cat /run/secrets/collabora_admin_password) +source /start-collabora-online.sh diff --git a/office/onlyoffice-entrypoint.sh b/office/onlyoffice-entrypoint.sh new file mode 100644 index 0000000..8df12d7 --- /dev/null +++ b/office/onlyoffice-entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +apk add curl + +#TODO: app driver itself should try again until OnlyOffice is up... + +retries=10 +while [[ $retries -gt 0 ]]; do + if curl --silent --show-error --fail http://onlyoffice/hosting/discovery > /dev/null; then + ocis app-provider server + else + echo "OnlyOffice is not yet available, trying again in 10 seconds" + sleep 10 + retries=$((retries - 1)) + fi +done +echo 'OnlyOffice was not available after 100 seconds' +exit 1 diff --git a/office/wopiserver-entrypoint.sh b/office/wopiserver-entrypoint.sh new file mode 100644 index 0000000..3b12f85 --- /dev/null +++ b/office/wopiserver-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +echo "${WOPISECRET}" > /etc/wopi/wopisecret + +cp /etc/wopi/wopiserver.conf.dist /etc/wopi/wopiserver.conf +sed -i 's/wopiserver.owncloud.test/'${WOPISERVER_DOMAIN}'/g' /etc/wopi/wopiserver.conf +sed -i 's/ocis:9142/'${REVA_GATEWAY}'/g' /etc/wopi/wopiserver.conf +sed -i 's/loglevel = Error/loglevel = '${WOPISERVER_LOGLEVEL}'/g' /etc/wopi/wopiserver.conf + +if [ "$WOPISERVER_INSECURE" == "true" ]; then + sed -i 's/sslverify\s=\sTrue/sslverify = False/g' /etc/wopi/wopiserver.conf +fi + +/app/wopiserver.py diff --git a/office/wopiserver.conf.dist b/office/wopiserver.conf.dist new file mode 100644 index 0000000..07c37e6 --- /dev/null +++ b/office/wopiserver.conf.dist @@ -0,0 +1,128 @@ +# +# This config is based on https://github.com/cs3org/wopiserver/blob/master/wopiserver.conf +# +# wopiserver.conf +# +# Default configuration file for the WOPI server for oCIS +# +############################################################## + +[general] +# Storage access layer to be loaded in order to operate this WOPI server +# only "cs3" is supported with oCIS +storagetype = cs3 + +# Port where to listen for WOPI requests +port = 8880 + +# Logging level. Debug enables the Flask debug mode as well. +# Valid values are: Debug, Info, Warning, Error. +loglevel = Error +loghandler = stream +logdest = stdout + +# URL of your WOPI server or your HA proxy in front of it +wopiurl = https://wopiserver.owncloud.test + +# URL for direct download of files. The complete URL that is sent +# to clients will include the access_token argument +downloadurl = https://wopiserver.owncloud.test/wopi/cbox/download + +# The internal server engine to use (defaults to flask). +# Set to waitress for production installations. +internalserver = waitress + +# List of file extensions deemed incompatible with LibreOffice: +# interoperable locking will be disabled for such files +nonofficetypes = .md .zmd .txt .epd + +# List of file extensions to be supported by Collabora (deprecated) +codeofficetypes = .odt .ott .ods .ots .odp .otp .odg .otg .doc .dot .xls .xlt .xlm .ppt .pot .pps .vsd .dxf .wmf .cdr .pages .number .key + +# WOPI access token expiration time [seconds] +tokenvalidity = 86400 + +# WOPI lock expiration time [seconds] +wopilockexpiration = 3600 + +# WOPI lock strict check: if True, WOPI locks will be compared according to specs, +# that is their representation must match. False (default) allows for a more relaxed +# comparison, which compensates incorrect lock requests from Microsoft Office Online +# on-premise setups. +wopilockstrictcheck = False + +# Enable support of rename operations from WOPI apps. This is currently +# disabled by default as it has been observed that both MS Office and Collabora +# Online do not play well with this feature. +# Not supported with oCIS, must always be set to "False" +enablerename = False + +# Detection of external Microsoft Office or LibreOffice locks. By default, lock files +# compatible with Office for Desktop applications are detected, assuming that the +# underlying storage can be mounted as a remote filesystem: in this case, WOPI GetLock +# and SetLock operations return such locks and prevent online apps from entering edit mode. +# This feature can be disabled in order to operate a pure WOPI server for online apps. +# Not supported with oCIS, must always be set to "False" +detectexternallocks = False + +# Location of the webconflict files. By default, such files are stored in the same path +# as the original file. If that fails (e.g. because of missing permissions), +# an attempt is made to store such files in this path if specified, otherwise +# the system falls back to the recovery space (cf. io|recoverypath). +# The keywords and are replaced with the actual username's +# initial letter and the actual username, respectively, so you can use e.g. +# /your_storage/home/user_initial/username +#conflictpath = / + +# ownCloud's WOPI proxy configuration. Disabled by default. +#wopiproxy = https://external-wopi-proxy.com +#wopiproxysecretfile = /path/to/your/shared-key-file +#proxiedappname = Name of your proxied app + +[security] +# Location of the secret files. Requires a restart of the +# WOPI server when either the files or their content change. +wopisecretfile = /etc/wopi/wopisecret +# iop secret is not used for cs3 storage type +#iopsecretfile = /etc/wopi/iopsecret + +# Use https as opposed to http (requires certificate) +usehttps = no + +# Certificate and key for https. Requires a restart +# to apply a change. +wopicert = /etc/grid-security/host.crt +wopikey = /etc/grid-security/host.key + +[bridge] +# SSL certificate check for the connected apps +sslverify = True + +# Minimal time interval between two consecutive save operations [seconds] +#saveinterval = 200 + +# Minimal time interval before a closed file is WOPI-unlocked [seconds] +#unlockinterval = 90 + +# CodiMD: disable creating zipped bundles when files contain pictures +#disablezip = False + +[io] +# Size used for buffered reads [bytes] +chunksize = 4194304 + +# Path to a recovery space in case of I/O errors when reaching to the remote storage. +# This is expected to be a local path, and it is provided in order to ease user support. +# Defaults to the indicated spool folder. +recoverypath = /var/spool/wopirecovery + +[cs3] +# Host and port of the Reva(-like) CS3-compliant GRPC gateway endpoint +revagateway = ocis:9142 + +# Reva/gRPC authentication token expiration time [seconds] +# The default value matches Reva's default +authtokenvalidity = 3600 + +# SSL certificate check for Reva +sslverify = True