Linux

The security risk of running docker mariadb/mysql with MYSQL_ALLOW_EMPTY_PASSWORD=yes

This is part of a common docker-compose.yml which is frequently seen on the internet

version: '3'
services:
  mariadb:
    image: 'mariadb:latest'
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
      - MYSQL_DATABASE=redmine
    volumes:
      - './mariadb_data:/var/lib/mysql'
 [...]

Simple and secure, right? A no-root-password MariaDB instance that’s running in a separate container and does not have its port 3306 exposed – so only services from the same docker-compose.yml can reach it since docker-compose puts all those services in a separate network.

Wrong.

While the MariaDB instance is not reachable from the internet since no, it can be reached by any process via its internal IP address.

In order to comprehend what’s happening, we shall take a look at docker’s networks. In this case, my docker-compose config is called redmine.

$ docker network ls | grep redmine
ea7ed38f469b        redmine_default           bridge              local

This is the network that docker-compose creates without any explicit network configuration. Let’s inspect the network to show the hosts:

[
    // [...]
        "Containers": {
            "2578fc65b4dab9f204d0a252e421dd4ddd9f41c35642d48350f4e59370581757": {
                "Name": "redmine_mariadb_1",
                "EndpointID": "1e6d81acc096a12fc740173f4e107090333c42e8a86680ac5c9886c148d578e7",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "7867f71d2a36265c34c133b70aea487b90ea68fcf30ecb42d6e7e9a376cf8e07": {
                "Name": "redmine_redmine_1",
                "EndpointID": "f5ac7b3325aa9bde12f0c625c4881f9a6fc9957da4965767563ec9a3b76c19c3",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
    // [...]
]

We can see that the IP address of the redmine_mariadb_1 container is 172.18.0.2.

Using the internal IP 172.18.0.2, you can access the MySQL server.

Any process on the host (even from unprivileged users) can connect to the container without any password, e.g.

$ mysqldump -uroot -h172.18.0.2 --all-databases
// This will show the dump of the entire MariaDB database

How to mitigate this security risk?

Mitigation is quite easy since we only need to set a root password for the MariaDB instance.

My recommended best practice is to avoid duplicate passwords. In order to do this, create a .env file in the directory where docker-compose.yml is located.

MARIADB_ROOT_PASSWORD=aiPaipei6ookaemue4voo0NooC0AeH

Remember to replace the password by a random password or use this shell script to automatically create it:

echo MARIADB_ROOT_PASSWORD=$(pwgen 30) > .env

Now we can use ${MARIADB_ROOT_PASSWORD} in docker-compose.yml whereever the MariaDB root password is required, for example:

version: '3'
services:
  mariadb:
    image: 'mariadb:latest'
    environment:
      - MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
      - MYSQL_DATABASE=redmine
    volumes:
      - './mariadb_data:/var/lib/mysql'
  redmine:
    image: 'redmine:latest'
    environment:
      - REDMINE_USERNAME=admin
      - REDMINE_PASSWORD=redmineadmin
      - REDMINE_EMAIL=ukoehler@techoverflow.net
      - REDMINE_DB_MYSQL=mariadb
      - REDMINE_DB_USERNAME=root
      - REDMINE_DB_PASSWORD=${MARIADB_ROOT_PASSWORD}
    ports:
      - '3718:3000'
    volumes:
      - './redmine_data/conf:/usr/src/redmine/conf'
      - './redmine_data/files:/usr/src/redmine/files'
      - './redmine_themes:/usr/src/redmine/public/themes'
    depends_on:
      - mariadb

Note that the mariadb docker image will not change the root password if the database directory already exists (mariadb_data in this example).

My recommended best practice for changing the root password is to use mysqldump --all-databases to export the entire database to a SQL file, then backup and delete the data directory, then re-start the container so the new root password will be set. After that, re-import the dump from the SQL file.

Posted by Uli Köhler in Databases, Docker, Linux

Best practice for installing & autostarting OpenVPN client/server configurations on Ubuntu

This post details my systemd-based setup for installing and activating OpenVPN client or server configs on Ubuntu. It might also work for other Linux distributions that are based on systemd..

First, place the OpenVPN config (usually a .ovpn file, but it can also be a .conf file) in /etc/openvpnYou need to change the filename extension to .conf.ovpn won’t work. Furthermore, ensure that there are no spaces in the filename.

In this example, our original OpenVPN config will be called techoverflow.ovpn, hence it needs to be copied to /etc/openvpn/techoverflow.conf!

Now we can enable (i.e. autostart on boot – but not start immediately) the config using

sudo systemctl enable openvpn@techoverflow

For techoverflow.conf you need to systemctl enableopenvpn@techoverflow whereas for a hypothetical foo.conf you would need to systemctl enable openvpn@foo.

Now we can start the VPN config – i.e. run it immediately using

sudo systemctl start openvpn@techoverflow

Now your VPN client or server is running – or is it? We shall check the logs using

journalctl -xfu openvpn@techoverflow

In order to manually restart the VPN client or server use

sudo systemctl restart openvpn@techoverflow

and similarly run this to stop the VPN client or server:

sudo systemctl stop openvpn@techoverflow

In order to show if the instance is running – i.e. show its status, use

sudo systemctl status openvpn@techoverflow

Example output for an OpenVPN client:

● openvpn@techoverflow.service - OpenVPN connection to techoverflow
     Loaded: loaded (/lib/systemd/system/openvpn@.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2020-11-29 03:37:52 CET; 953ms ago
       Docs: man:openvpn(8)
             https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
             https://community.openvpn.net/openvpn/wiki/HOWTO
   Main PID: 4123809 (openvpn)
     Status: "Pre-connection initialization successful"
      Tasks: 1 (limit: 18689)
     Memory: 1.3M
     CGroup: /system.slice/system-openvpn.slice/openvpn@techoverflow.service
             └─4123809 /usr/sbin/openvpn --daemon ovpn-techoverflow --status /run/openvpn/techoverflow.status 10 --cd /etc/openvpn --script-security 2 --config /etc/ope>

Nov 29 03:37:52 localgrid systemd[1]: Starting OpenVPN connection to techoverflow...
Nov 29 03:37:52 localgrid ovpn-techoverflow[4123809]: OpenVPN 2.4.7 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Sep >
Nov 29 03:37:52 localgrid ovpn-techoverflow[4123809]: library versions: OpenSSL 1.1.1f  31 Mar 2020, LZO 2.10
Nov 29 03:37:52 localgrid systemd[1]: Started OpenVPN connection to techoverflow.
Nov 29 03:37:52 localgrid ovpn-techoverflow[4123809]: TCP/UDP: Preserving recently used remote address: [AF_INET]83.135.163.227:19011
Nov 29 03:37:52 localgrid ovpn-techoverflow[4123809]: UDPv4 link local (bound): [AF_INET][undef]:1194
Nov 29 03:37:52 localgrid ovpn-techoverflow[4123809]: UDPv4 link remote: [AF_INET]83.135.163.22:19011
Nov 29 03:37:53 localgrid ovpn-techoverflow[4123809]: [nas-vpn.haar.techoverflow.net] Peer Connection Initiated with [AF_INET]83.135.163.227:19011

 

Posted by Uli Köhler in Linux, VPN

Simple self-hosted WebWormhole.io using docker-compose

WebWormhole.io is a new service similar to and inspired by magic-wormhole that allows easily sharing files between browsers without the need to install a software. Internally, it uses WebRTC, allowing direct transfer of files between computers even through firewalls.

While there is no official Docker image published on Docker Hub, the WebWormhole GitHub project provides an official Dockerfile. Based on this, I have published ulikoehler/webwormhole which has been built using

git clone https://github.com/saljam/webwormhole.git
cd webwormhole
docker build -t ulikoehler/webwormhole:latest .
docker push ulikoehler/webwormhole:latest

This is the docker-compose.yml that you can use to run WebWormhole behind a reverse proxy:

version: '3'
services:
  webwormhole:
    image: 'ulikoehler/webwormhole:latest'
    entrypoint: ["/bin/ww", "server", "-http=localhost:52618", "-https="]
    network_mode: host

and this is my nginx config:

server {
    server_name  webwormhole.mydomain.com;

    access_log off;
    error_log /var/log/nginx/webwormhole.mydomain.com.error.log;

    location / {
        proxy_pass http://localhost:52618/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_redirect default;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/webwormhole.mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/webwormhole.mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    #ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    if ($host = webwormhole.mydomain.com) {
        return 301 https://$host$request_uri;
    }

    server_name webwormhole.mydomain.com;

    listen 80;
    return 404; # managed by Certbot
}

I store docker-compose.yml in /var/lib/webwormhole.mydomain.com and I used the script from our previous post Create a systemd service for your docker-compose project in 10 seconds in order to create this systemd config file in /etc/systemd/system/webwormhole.mydomain.com.service:

[Unit]
Description=webwormhole.mydomain.com
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
WorkingDirectory=/var/lib/webwormhole.mydomain.com
# Shutdown container (if running) when unit is started
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down
# Start container when unit is started
ExecStart=/usr/local/bin/docker-compose -f docker-compose.yml up
# Stop container when unit is stopped
ExecStop=/usr/local/bin/docker-compose -f docker-compose.yml down

[Install]
WantedBy=multi-user.target

which you can start and enable using

sudo systemctl enable webwormhole.mydomain.com
sudo systemctl start webwormhole.mydomain.com

 

Posted by Uli Köhler in Docker, Linux

Create a systemd service for your docker-compose project in 10 seconds

Run this in the directory where docker-compose.yml is located:

wget -qO- https://techoverflow.net/scripts/create-docker-compose-service.sh | bash /dev/stdin

This script will automatically create  a systemd service that starts docker-compose up and shuts down using docker-compose down. Our script will also systemctl enable the script (i.e. start automatically on boot) and systemctl start it (start it immediately).

How it works

The command above will download the script from TechOverflow and run it in bash:

#!/bin/bash
# Create a systemd service that autostarts & manages a docker-compose instance in the current directory
# by Uli Köhler - https://techoverflow.net
# Licensed as CC0 1.0 Universal
SERVICENAME=$(basename $(pwd))

echo "Creating systemd service... /etc/systemd/system/${SERVICENAME}.service"
# Create systemd service file
sudo cat >/etc/systemd/system/$SERVICENAME.service <<EOF
[Unit]
Description=$SERVICENAME
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
WorkingDirectory=$(pwd)
# Shutdown container (if running) when unit is started
ExecStartPre=$(which docker-compose) -f docker-compose.yml down
# Start container when unit is started
ExecStart=$(which docker-compose) -f docker-compose.yml up
# Stop container when unit is stopped
ExecStop=$(which docker-compose) -f docker-compose.yml down

[Install]
WantedBy=multi-user.target
EOF

echo "Enabling & starting $SERVICENAME"
# Autostart systemd service
sudo systemctl enable $SERVICENAME.service
# Start systemd service now
sudo systemctl start $SERVICENAME.service

The service name is the directory name:

SERVICENAME=$(basename $(pwd))

Now we will create the service file in /etc/systemd/system/${SERVICENAME}.service using the template embedded in the script

The script will automatically determine the location of docker-composeusing $(which docker-compose) and finally enable and start the systemd service:

# Autostart systemd service
sudo systemctl enable $SERVICENAME.service
# Start systemd service now
sudo systemctl start $SERVICENAME.service

 

Posted by Uli Köhler in Docker, Linux

Running Portainer using docker-compose and systemd

In this post we’ll show how to run Portainer Community Edition on a computer using docker-compose and systemd. In case you haven’t installed docker or docker-compose, see How to install docker and docker-compose on Ubuntu in 30 seconds.

If you already have a Portainer instance and want to run a Portainer Edge Agent on a remote computer, see Running Portainer Edge Agent using docker-compose and systemd!

First, create the directory where the docker-compose.yml will live and edit it:

sudo mkdir -p /var/lib/portainer
sudo nano /var/lib/portainer/docker-compose.yml

Now paste this config file:

version: '2'

services:
  portainer:
    image: portainer/portainer
    command: -H unix:///var/run/docker.sock
    restart: always
    ports:
      - 9192:9000
      - 8000:8000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data

volumes:
  portainer_data:

In this case, we’re exposing the Web UI on port 9192 since we’re using a reverse proxy setup in order to access the web UI. Using Portainer over HTTP without a HTTPS frontend is a security risk!

This is my nginx config that is used to reverse proxy my Portainer instance. Note that I generate the HTTPS config using certbot --nginx, hence it’s not shown here:

server {
    server_name  portainer.mydomain.com;

    location / {
        proxy_pass http://localhost:9192/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_redirect default;
    }

    listen 80;
}

Now we can create the systemd service that will automatically start Portainer:

sudo nano /etc/systemd/system/portainer.service
[Unit]
Description=Portainer
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
WorkingDirectory=/var/lib/portainer
# Shutdown container (if running) when unit is stopped
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down
# Start container when unit is started
ExecStart=/usr/local/bin/docker-compose -f docker-compose.yml up
# Stop container when unit is stopped
ExecStop=/usr/local/bin/docker-compose -f docker-compose.yml down

[Install]
WantedBy=multi-user.target

Now we can can enable autostart on boot and start Portainer:

sudo systemctl enable portainer.service
sudo systemctl start portainer.service

 

Posted by Uli Köhler in Container, Docker, Linux, Portainer

Running Portainer Edge Agent using docker-compose and systemd

In this post we’ll show how to run the Portainer Edge Agent on a computer using docker-compose and systemd. In case you haven’t installed docker or docker-compose, see How to install docker and docker-compose on Ubuntu in 30 seconds.

If you don’t have a Portainer instance running to which the Edge Agent can connect, see Running Portainer using docker-compose and systemd!

First, create the directory where the docker-compose.yml will live and edit it:

sudo mkdir -p /var/lib/portainer-edge-agent
sudo nano /var/lib/portainer-edge-agent/docker-compose.yml

Now paste this config file:

version: "3"

services:
  portainer_edge_agent:
    image: portainer/agent
    command: -H unix:///var/run/docker.sock
    restart: always
    volumes:
      - /:/host
      - /var/lib/docker/volumes:/var/lib/docker/volumes
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_agent_data:/data
    environment:
      - CAP_HOST_MANAGEMENT=1
      - EDGE=1
      - EDGE_ID=[YOUR EDGE ID]
      - EDGE_KEY=[YOUR EDGE KEY]

volumes:
  portainer_agent_data:

Don’t forget to fill in [YOUR EDGE ID] and [YOUR EDGE KEY]. You can find those by creating a new endpoint in your Portainer instance.

Now we can create the systemd service that will automatically start the Edge Agent:

sudo nano /etc/systemd/system/PortainerEdgeAgent.service
[Unit]
Description=PortainerEdgeAgent
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
WorkingDirectory=/var/lib/portainer-edge-agent
# Shutdown container (if running) when unit is stopped
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down
# Start container when unit is started
ExecStart=/usr/local/bin/docker-compose -f docker-compose.yml up
# Stop container when unit is stopped
ExecStop=/usr/local/bin/docker-compose -f docker-compose.yml down

[Install]
WantedBy=multi-user.target

Now we can can enable and start the agent:

sudo systemctl enable PortainerEdgeAgent.service
sudo systemctl start PortainerEdgeAgent.service

 

Posted by Uli Köhler in Container, Docker, Linux, Portainer

How to cleanup large gitlab prometheus/data in Omnibus/Docker setting

In many of my dockerized Gitlab instances, the prometheus/data folder was eating up multiple Gigabytes of hard drive space even though I was not using Prometheus at all.

In order to fix this, I first disabled Prometheus in the docker-compose.yml config using

prometheus_monitoring['enable'] = false

Also see How I reduced gitlab memory consumption in my docker-based setup for a detailed explanantion.

After that, you need to restart gitlab in order for the settings change to take effect.

Now you can just delete the Prometheus data folder. Make a backup of the entire gitlab data folder before this step.

Run this command from within your gitlab data folder:

rm -rf prometheus/data

 

Posted by Uli Köhler in Allgemein, Linux

How to fix apt update error NO_PUBKEY 78BD65473CB3BD13

If you encounter this error message during APT update:

The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 78BD65473CB3BD13

you need to import the key using

sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net:80 --recv-keys 78BD65473CB3BD13

 

Posted by Uli Köhler in Linux

How to delete all .DS_Store files recursively on the command line

Use this command to recursively delete all .DS_Store files present in the directory mydir and all its subdirectories:

find mydir -type f -name .DS_Store -print0 | xargs -0 rm

Warning: Deleting the files can’t be undone.

Posted by Uli Köhler in Linux

How to fix SSH not accepting public key in authorized_keys

Problem:

You have added your SSH public key to a remote server manually or using ssh-copy-id but still you can’t login using that public key

Solution:

Typically this is caused by bad permissions of your ~/.ssh directory and/or your authorized_keys file. Fix that using:

chmod -R 700 ~/.ssh

then try to login again

Posted by Uli Köhler in Linux

How to run systemd timer twice daily

In order to run a systemd timer twice daily at fixed times, use this syntax in the .timer file:

OnCalendar=*-*-* 00,12:00:00

This line means: Run the service on each and every day (*-*-*) at 00:00:00 and 12:00:00 (00,12:00:00)

Posted by Uli Köhler in Linux

Find and remove all empty directories using the Linux command line

In order to find and remove all empty subdirectories in the current directory (.) recursively, use this command:

find . -depth -type d -print0 | xargs -0 rmdir

This command will only remove empty directories! Any file or non-empty directory won’t be modified.

Explanation of what the parts mean:

  • find: Use the find command to find directories recursively
  • .: Start recursing from the current directory. In case you want to start from a different directory, use that directory name here.
  • -type d: Only find directories – ignore files
  • -depth: Before printing a directory name, print all its sub-directory names. This avoids having to run this command repeatedly because the parent directory can’t be removed since its empty sub-directories need to be removed first
  • -print0 When printing all the directories that have been found, print a NUL character between directories. This is required in order to handle spaces in the directory names correctly
  • | xargs: Pipe the directories to xargs, a program that runs
  • -0: Split the input by NUL characters instead of newlines. This corresponds with the -print0 option to find and is required to handle spaces in directory names correctly.
  • rmdir: For each directory found, run rmdir i.e. try to remove the directory if it’s empty.
Posted by Uli Köhler in Linux

How to fix Raspberry Pi OpenVPN error “ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such device (errno=19)”

Problem:

You want to setup OpenVPN on your Raspberry Pi but you see an error message like

Fri Jun 26 18:12:35 2020 ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such device (errno=19)
Fri Jun 26 18:12:35 2020 Exiting due to fatal error

Solution:

This error occurs if you’ve installed OpenVPN using sudo apt install -y openvpn but if you didn’t reboot after installing it. In order to fix the issue, reboot using

sudo reboot

 

Posted by Uli Köhler in Linux, Raspberry Pi

How to fix raspi-config “The splash screen is not installed so cannot be activated”

Problem:

You want to enable the boot splash screen on your Raspberry Pi using raspi-config, but you see this error message:

The splash screen is not installed so cannot be activated

followed by There was an error running option B3 Splash Screen

Solution:

As you can find out from reading the raspi-config source code, it checks for the existence of /usr/share/plymouth/themes/pix/pix.script. In order to install this file, install the rpd-plym-splash package:

sudo apt -y install rpd-plym-splash
Posted by Uli Köhler in Embedded, Linux, Raspberry Pi

How to manually reload Chromium Kiosk

Problem:

You are running a Chromium Kiosk e.g. on a Raspberry Pi using a command like

chromium-browser --noerrdialogs --disable-infobars --disk-cache-dir=/dev/null --disk-cache-size=1 --kiosk http://localhost

e.g. in /etc/xdg/openbox/autostart, but you don’t know how to manuy reload the Kiosk e.g. after you have changed the underlying website

Solution:

In /etc/xdg/openbox/autostart or wherever your chromium-browser command is, enclose it in

while true ; do [CHROMIUM COMMAND] ; sleep 1 ; done

The complete command would look like this, for example:

while true ; do chromium-browser  --noerrdialogs --disable-infobars --disk-cache-dir=/dev/null --disk-cache-size=1 --kiosk http://localhost ; sleep 1 ; done

Now, to manually reload Chromium, all you have to do is to kill the process using

killall /usr/lib/chromium-browser/chromium-browser-v7

This will kill the Chromium process and the while loop will automatically restart it after one second.

In case you see an error message like

/usr/lib/chromium-browser/chromium-browser-v7: No such file or directory

you need to find out which executable is used for Chromium in order to pass that to killall. To find out the name of the executable, use

ps a | grep -i chromium

and look for a string similar to /usr/lib/chromium-browser/chromium-browser-v7.

Posted by Uli Köhler in Embedded, Linux

How to fix Chromium Kiosk still displaying old page after reboot

Problem:

You are running a Chromium Kiosk application on an embedded computer (like a Raspberry Pi) using a command like

chromium-browser --noerrdialogs --disable-infobars --kiosk http://localhost

but when you update the webpage, Chromium still displays the old page even after a reboot.

Solution:

Disable Chromium’s cache by adding

--disk-cache-dir=/dev/null --disk-cache-size=1

to the command (which is typically found in /etc/xdg/openbox/autostart). The full command will look like this:

chromium-browser --noerrdialogs --disable-infobars --disk-cache-dir=/dev/null --disk-cache-size=1 --kiosk http://localhost
Posted by Uli Köhler in Embedded, Linux

How to fix wifi blocked on boot on Raspberry Pi 4

After migrating a fresh Raspbian install using the official 2020-04 Raspbian light image from my Raspberry Pi Model 2 to my new Raspberry Model 4, the Wifi was disabled at boot.

I tried configuring the Wifi using raspi-config but that didn’t change anything.

First, try rfkill unblock all and then reboot in order to check if the Wifi adapter is still unblocked after the reboot. In my case, this fixed the issue permanently and wifi worked immediately.

If that doesn’t help, check if country=... is set in /etc/wpa_supplicant/wpa_supplicant.conf. You need to set it to your correct country code to comply with regulatory limits. For example, use country=DE to set the regulatory domain to Germany.

Posted by Uli Köhler in Linux, Raspberry Pi

How to setup OnlyOffice using docker-compose & nginx

Prerequisite: Install docker and docker-compose

For example, follow our guide How to install docker and docker-compose on Ubuntu in 30 seconds

Step 1: Create docker-compose.yml

Create the directory where we’ll install OnlyOffice using

sudo mkdir /var/lib/onlyoffice

and then edit the docker-compose configuration using e.g.

sudo nano /var/lib/onlyoffice/docker-compose.yml

and copy and paste this content

version: '3'
services:
  onlyoffice-documentserver:
    image: onlyoffice/documentserver:latest
    restart: always
    environment:
      - JWT_ENABLED=true
      - JWT_SECRET=ahSaTh4waeKe4zoocohngaihaub5pu
    ports:
      - 2291:80
    volumes:
      - ./onlyoffice/data:/var/www/onlyoffice/Data
      - ./onlyoffice/lib:/var/lib/onlyoffice
      - ./onlyoffice/logs:/var/log/onlyoffice
      - ./onlyoffice/db:/var/lib/postgresql

Now add your custom password in JWT_SECRET=... ! Don’t forget this step, or anyone can use your OnlyOffice server ! I’m using pwgen 30 to generate a new random password (install using sudo apt -y install pwgen).

Step 2: Setup systemd service

Create the service using sudo nano /etc/systemd/system/onlyoffice.service:

[Unit]
Description=OnlyOffice server
Requires=docker.service
After=docker.service

[Service]
Restart=always
User=root
Group=docker
# Shutdown container (if running) when unit is stopped
ExecStartPre=/usr/local/bin/docker-compose -f /var/lib/onlyoffice/docker-compose.yml down -v
# Start container when unit is started
ExecStart=/usr/local/bin/docker-compose -f /var/lib/onlyoffice/docker-compose.yml up
# Stop container when unit is stopped
ExecStop=/usr/local/bin/docker-compose -f /var/lib/onlyoffice/docker-compose.yml down -v

[Install]
WantedBy=multi-user.target

Now enable & start the service using

sudo systemctl enable onlyoffice
sudo systemctl start onlyoffice

Step 3:  Create nginx reverse proxy configuration

Note that we mapped OnlyOffice’s port 80 to port 2291. In case you’re not using nginx as reverse proxy, you need to manually configure your reverse proxy to pass requests to port 2291.

server {
    server_name onlyoffice.mydomain.org;

    access_log /var/log/nginx/onlyoffice.access_log;
    error_log /var/log/nginx/onlyoffice.error_log info;

    location / {
        proxy_pass http://127.0.0.1:2291;
        proxy_http_version 1.1;
        proxy_read_timeout 3600s;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host            $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header X-Frontend-Host $host;
        # Uncomment this line and reload once you have setup TLS for that domain !
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    }

    listen 80;
}

Now test if your nginx config works using nginx -t and reload using service nginx reload.

Now I recommend to setup Let’s Encrypt for your domain so that your OnlyOffice instance will only be accessed using an encrypted connecting (sudo certbot --nginx, see other guides if you don’t know how to do that).

Once certbot asks you whether to redirect, choose option 2 – Redirect to HTTPS.

Step 4: Test OnlyOffice

If your installation worked, you should see a screen like this:

If not, try checking the logs using

sudo journalctl -xu onlyoffice

(Optional) Step 5: Configure NextCloud to use OnlyOffice

If you are running NextCloud, go to Settings => ONLYOFFICE and enter your domain and the JWT_SECRET you created before:

Ensure that Connect to demo ONLYOFFICE Document Server is unchecked and click Save.

Nextcloud will tell you at the top right if it has been able to connect to your OnlyOffice instance successfully:

  • Settings successfully updated means that NextCloud is now connected to OnlyOffice
  • Invalid token means that your password / secret key does not match
  • Other messages typically mean that your OnlyOffice is not running or that you haven’t entered the correct domain or protocol. I recommend to only use https:// – use http:// for testing only and don’t forget to revert back to https:// once you have found the issue.
Posted by Uli Köhler in Container, Docker, Linux, nginx