tomsquest/docker-radicale

Invalid configuration: Failed to load config file

piplongrun opened this issue · 16 comments

I created a user/group with id 2999 on the host, then ran the Production-grade instruction command from the README.md. Docker shows the container has exited immediately:

pip@ubuntu18:~$ sudo docker ps -a
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                      PORTS               NAMES
354ec93509ed        tomsquest/docker-radicale   "docker-entrypoint.s…"   15 seconds ago      Exited (1) 14 seconds ago                       radicale

Digging up the log file for the container shows me this error:

{"log":"ERROR: Invalid configuration: Failed to load config file '/config/config': No such file: '/config/config'\n","stream":"stderr","time":"2019-09-15T21:00:58.133814892Z"}

And indeed /config/config isn't there. The folder for config and data have been created, but the config file is missing inside the config folder:

pip@ubuntu18:~$ ls -la ./radicale
total 16
drwxr-xr-x 4 root     root     4096 Sep 18 16:12 ./
drwxr-xr-x 6 pip      pip      4096 Sep 18 16:12 ../
drwxr-xr-x 2 root     root     4096 Sep 18 16:12 config/
drwxr-xr-x 2 radicale radicale 4096 Sep 18 16:12 data/

This is on a fresh Ubuntu 18 install with Docker version 19.03.2, build 6a30dfc.

So I made these changes to get everything working out of the box: piplongrun/docker-radicale@f98424...595b21

(See also docker/docs#2979)

Hi,

Thanks for reporting this.

I am unable to reproduce given a valid /config volume. (following the documentation at https://github.com/tomsquest/docker-radicale#custom-configuration)

For example, here is a custom config folder where the config file has been copied. Before that I create the /opt/radicale/data and /opt/radicale/config folder.

$ tree /opt/radicale       
/opt/radicale
├── config
│   └── config
└── data

Then the production command can be run successfully given the volume are adapted:

docker run -d --name radicale \
    -p 127.0.0.1:5232:5232 \
    --read-only \
    --init \
    --security-opt="no-new-privileges:true" \
    --cap-drop ALL \
    --cap-add CHOWN \
    --cap-add SETUID \
    --cap-add SETGID \
    --cap-add KILL \
    --pids-limit 50 \
    --memory 256M \
    --health-cmd="curl --fail http://localhost:5232 || exit 1" \
    --health-interval=30s \
    --health-retries=3 \
    -v /opt/radicale/data:/data \
    -v /opt/radicale/config:/config:ro \
    tomsquest/docker-radicale

By the way, I saw your PR and having the config volume mounted readonly is a good think from a security point of view.

I am able to reproduce @piplongrun's problem using the docker-compose.yml provided. The strange thing is that this should not happen, right? Since the provided config file is copied into /config before /config is declared a volume, and according to the Dockerfile documentation:

The docker run command initializes the newly created volume with any data that exists at the specified location within the base image.

I am confused why the generated config volume is empty.

@jeslinmx

  • are you able to reproduce without the capabilities (--cap-drop, --cap-add)?
  • are you able to reproduce without the volumes? (and without the capabilities drop)

FYI, regarding capabilities, I had some strange permission issues (why I did no undestand, but seems that the root on the container was not able to touch the files owned by root on the host :/).

Just to dispell any misunderstanding: any volume mounted on a container will shadow the target directory.

ie. given container have a /container-folder, when running -v /host-directory:/container-folder then the container will only see the content of /host-directory in its /container-folder.

Hence, related to this issue, the container's /config is replaced by the host config volume, which means that the host must have a config file in mounted config directory.

Trying that now.

Without capabilities:

  radicale:
    image: tomsquest/docker-radicale:amd64
    build:
      context: https://github.com/tomsquest/docker-radicale.git
      args:
        - UID=1000
        - GID=1000
    restart: unless-stopped
    healthcheck: 
      interval: 5m
    read_only: true
    security_opt:
      - no-new-privileges:true
    # cap_drop:
    #   - ALL
    # cap_add:
    #   - SETUID
    #   - SETGID
    #   - CHOWN
    #   - KILL
    init: true
    volumes:
      - ./testradicale/data:/data
      - ./testradicale/config:/config:ro
      - ./testradicale/htpasswd:/htpasswd:ro
jeslinmx@localhost:~/selfhosting$ ls testradicale
ls: cannot access 'testradicale': No such file or directory
jeslinmx@localhost:~/selfhosting$ dc up -d radicale
Starting selfhosting_radicale_1 ... done
jeslinmx@localhost:~/selfhosting$ docker logs -f 363b237445ba15733963890d254d9d1db59cdbc316bf2ab3b70543c450cbc610
ERROR: Invalid configuration: Failed to load config file '/config/config': No such file: '/config/config'

Without capabilities and volumes:

  radicale:
    image: tomsquest/docker-radicale:amd64
    build:
      context: https://github.com/tomsquest/docker-radicale.git
      args:
        - UID=1000
        - GID=1000
    restart: unless-stopped
    healthcheck: 
      interval: 5m
    read_only: true
    security_opt:
      - no-new-privileges:true
    # cap_drop:
    #   - ALL
    # cap_add:
    #   - SETUID
    #   - SETGID
    #   - CHOWN
    #   - KILL
    init: true
    # volumes:
    #   - ./testradicale/data:/data
    #   - ./testradicale/config:/config:ro
    #   - ./testradicale/htpasswd:/htpasswd:ro

everything runs fine.

Just to dispell any misunderstanding: any volume mounted on a container will shadow the target directory.

ie. given container have a /container-folder, when running -v /host-directory:/container-folder then the container will only see the content of /host-directory in its /container-folder.

Hence, related to this issue, the container's /config is replaced by the host config volume, which means that the host must have a config file in mounted config directory.

But this only applies if /host-directory exists, right? Otherwise as per the documentation, docker should create /host-directory and populate it with the contents of /container-folder, and only then does /host-directory shadow /container-folder?

By the way, @tomsquest what is your docker -v? I suspect this is being caused by a problem in docker not behaving according to documentation - I am having the same problem with the netdata image.

Hum, @jeslinmx , I did not know about Docker pulling data from the container when the host dir does not exist.

Here is my docker version:

$ docker -v
Docker version 19.03.2, build 6a30dfca03

Tested on Ubuntu LTS 18.04 and 19.04.

Hum, @jeslinmx , I did not know about Docker pulling data from the container when the host dir does not exist.

I was under the impression that that's what the line

39 COPY config /config/config

in the Dockerfile was supposed to achieve - copying a basic config into the volume so it would be dumped into a host directory upon mounting.

Here is my docker version:

$ docker -v
Docker version 19.03.2, build 6a30dfca03

Tested on Ubuntu LTS 18.04 and 19.04.

Same as both @piplongrun and I. How strange.

COPY config /config/config is in the Dockerfile, which means it is already done when docker run is executed. docker run will run the entrypoint script and the CMD command (which could be override).
So without mounting volume, the /config/config file exists.

Closing this issue as it seems that the missing config file is due to the behaviour of Docker when mounting a missing directory in the container.

Hi, I am running into this issue and it seems don't have an obvious solution. Can anyone shed some lights?

Startup command I use:

docker run -d --name radicale \
    -p 127.0.0.1:5232:5232 \
    -e UID=1000 \
    -e GID=1000 \
    --init \
    --security-opt="no-new-privileges:true" \
    --cap-drop ALL \
    --cap-add CHOWN \
    --cap-add SETUID \
    --cap-add SETGID \
    --cap-add KILL \
    --pids-limit 50 \
    --memory 256M \
    --health-cmd="curl --fail http://localhost:5232 || exit 1" \
    --health-interval=30s \
    --health-retries=3 \
    -v /opt/radicale/data:/data \
    -v /opt/radicale/config:/config \
    tomsquest/docker-radicale

@lifehome did you put the config file under your /opt/radicale/config, ie. the complete file path should be : /opt/radicale/config/config.

@tomsquest I have successfully started using my command above after copying and create the config file.
Tho is it possible to automate the creation of the config file?

In fact, you only need to mount the config volume if you changed something in the config.
Else -v /opt/radicale/config:/config \ is useless.

Indeed, the config file is IN the container, mounting a volume "on it", just replace/shadow the config directory, then radicale cannot find is config file.