docker-volume-easyfuse 0.2.1
simple FUSE volume driver for Docker
Requires Python: >=3.6

# `easyfuse` - simple FUSE volume driver for Docker

Simple FUSE-based docker volume driver based on the local driver systax. By offloading volume mounting to a simple `mount -t fuse` subprocess call, the typical problem of https://github.com/moby/moby/issues/27103 does not apply.

The plugin uses legacy architecture (mounting as a unix socket) to expose to the user full host system capabilites of `mount`. This way, user can e.g. fully specify `uid` and `gid` volume mapping, use local system resources (e.g. SSH keys), something that is an annoying shortcoming of plugins such as `vieux/sshfs`.

Initially, the plugin was developed to allow using SSHFS in scenario, where `vieux/sshfs` simply wouldn't work, and after discovering _why_ the `local` driver would not work with fuse mount type. Hovever, in theory this plugin should work with any `fuse`-mounted filesystem (and in future, possibly even others).

### Permissions

Currently, since easyfuse uses `mount` for creating actual fuse volumes, `easyfuse` must be ran as root. This may change in future versions.

## Package dependencies

It's relatively easy to run `easyfuse` without actually installing the package, nevertheless it 
requires some non-standard third party Python libraries. To start, simply install module dependencies 
(see [requirements.txt]), either with

```
sudo python3 -m pip install -r requirements.txt
```

or (preferably) with your system package manager, e.g. for Debian/Ubuntu:

```
sudo apt install python3-aiohttp
```

When using `pip`, make sure the packages are installed globally, as `easyfuse` currently needs to be ran as root.

## Local installation in virtual environment

`easyfuse` should have no problems working out of a python virtual environment.
Simply initialize a virtual environment and install the package locally:

```
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install -e .
```

This will pull all local requirements, without interfering with the system and system's package
managers.

When running from venv with `sudo`, you may need to manually point to the venv's python as virtual
environment variables are not inherited by `sudo`-ed process, see:

```
# locally installed aiohttp in version 3.5.1, venv installed aiohttp in version 3.7.3:
(venv) $ python3 -c 'import aiohttp; print(aiohttp.__version__)'
3.7.3
(venv) $ sudo python3 -c 'import aiohttp; print(aiohttp.__version__)'
3.5.1
```

Instead, simply use `venv/bin/python` explicitly:

```
(venv) $ sudo venv/bin/python -c 'import aiohttp; print(aiohttp.__version__)'
3.7.3
```

## Running manually (without installation)

To run in a stand-alone mode (preferred for development), start in the main directory and simply run

```
sudo python3 -m easyfuse -s /run/docker/plugins/easyfuse.sock
```

see `python3 -m easyfuse -h` (no `sudo` required) for full list of available options.

## Running with `systemd` (with or without installation)

`systemd` folder contains basic systemd unit files for socket activation, either for global and local
setup.
Assuming `/etc/systemd/system` as the configuration path of choice, typical activation for a local setup would be:

```
$ SYSTEMD_UNIT_TARGET=/etc/systemd/system
$ sudo cp systemd/easyfuse.socket $SYSTEMD_UNIT_TARGET/
$ WORKDIR=$PWD envsubst '$WORKDIR' < systemd/easyfuse.service.dev | sudo tee $SYSTEMD_UNIT_TARGET/easyfuse.service
$ sudo systemctl daemon-reload
$ sudo systemctl start easyfuse.socket
```

When using virtualenv, use the appropriate config:

```
(venv) $ SYSTEMD_UNIT_TARGET=/etc/systemd/system
(venv) $ sudo cp systemd/easyfuse.socket $SYSTEMD_UNIT_TARGET/
(venv) $ envsubst '$VIRTUAL_ENV' < systemd/easyfuse.service.venv | sudo tee $SYSTEMD_UNIT_TARGET/easyfuse.service
(venv) $ sudo systemctl daemon-reload
(venv) $ sudo systemctl start easyfuse.socket
```

For a global installation (i.e. if you can call `python3 -m easyfuse -h` as root from anywhere, without virtual environment), simply use:

```
$ SYSTEMD_UNIT_TARGET=/etc/systemd/system
$ sudo cp systemd/easyfuse.socket  $SYSTEMD_UNIT_TARGET/
$ sudo cp systemd/easyfuse.service $SYSTEMD_UNIT_TARGET/
$ sudo systemctl daemon-reload
$ sudo systemctl start easyfuse.socket
```

### Automatic systemd setup

`easyfuse` provides a simple automatic `systemd` setup via `easyfuse.systemd_setup` script-module. 
See `python3 -m easyfuse.systemd_setup -h` for reference. Remember that when using `sudo` with `venv`,
you need to use the `venv` symlink instead of `python3`:

```
(venv) $ sudo python3 -m easyfuse.systemd_setup venv          # <- this will install using the global python
(venv) $ sudo venv/bin/python3 -m easyfuse.systemd_setup venv # <- this will install using the local venv
```

## Using `easyfuse` with docker volume

Note: when using SSHFS, make sure the host key is accepted by the root user (or whatever user is running the mount command). This can be done by simply doing `sudo ssh user@my-ssh-host` and verifying the public key, or with `ssh-keyscan >> /root/.ssh/known_hosts`.

```
$ docker volume create -d easyfuse -o 'o=IdentityFile=$HOME/.ssh/id_ed25519,uid=1000,gid=1000,users,idmap=user,noatime,allow_other,_netdev,reconnect,rw' -o 'device=sshfs#user@my-ssh-host:/my-ssh-volume' my-volume
my-volume
```

```
$ docker run --rm -it -v my-volume:/my-volume busybox ls /my-volume -lah
total 8K
drwxrwsr-x    1 1000     1000        4.0K Nov 25 00:43 .
drwxr-xr-x    1 root     root        4.0K Nov 25 13:27 ..
```

```
$ docker volume rm my-volume
my-volume
```

## Using `easyfuse` with docker-compose

A fully-featured example with docker-compose can be found in the examples folder. See also [below](#verify-with-docker-compose).

```
volumes:
  nfs:
    driver: easyfuse
    driver_opts:
      o: IdentityFile=$PWD/my-secret.key,uid=1000,gid=1000,users,idmap=user,noatime,allow_other,_netdev,reconnect,rw
      device: "sshfs#testuser@localhost:testdir"
```

## Test with minimal local sshfs setup

Folder examples/mini-sshfs contains a minimal sshd configuration and startup script for your local user that you can use to test the plugin.

The scripts assume you have sshd available at /usr/sbin/sshd. This can be easily overriden with SSHD environment variable. To start the server simply issue:

```
examples$ mini-sshfs/run_sshd.sh
```

The script will generate user and host keys and store them locally in the mini-sshfs folder. The server runs at port 2022 (override with MINI_SSHD_PORT environment variable) and binds to localhost interfaces only (127.0.0.1 and ::1). To verify the server works you can check if ssh with the exported known_hosts and user key work:

```
examples$ ssh -o UserKnownHostsFile=mini-sshfs/known_hosts -i mini-sshfs/.keys/ssh_user_ed25519_key -p 2022 $USER@localhost
Last login: Wed Nov 25 00:00:00 2020 from xx.yy.zz.ww

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
$ exit
logout
Connection to localhost closed.
examples$
```

### Verify with a regular named volume

```
examples$ docker volume create -d easyfuse -o "o=IdentityFile=$PWD/mini-sshfs/.keys/ssh_user_ed25519_key,UserKnownHostsFile=$PWD/mini-sshfs/known_hosts,port=2022,uid=1000,gid=1000,users,idmap=user,noatime,allow_other,_netdev,reconnect,rw" -o "device=sshfs#$USER@localhost:$PWD/test" my-volume
my-volume

examples$ docker run --rm -it -v my-volume:/my-volume busybox ls /my-volume -lah
total 12K
drwxr-xr-x    1 1000     1000        4.0K Nov 25 15:16 .
drwxr-xr-x    1 root     root        4.0K Nov 25 15:45 ..
-rw-r--r--    1 1000     1000         445 Nov 25 15:17 lorem_ipsum.txt

examples$ docker volume rm my-volume
my-volume
```

### Verify with docker-compose

```
examples$ cat docker-compose.yml
version: '3.4'

services:
  mytest:
    image: alpine
    volumes:
      - 'nas:/nas:rw'
    command: [sh, -c, 'touch /nas/test; ls -lah /nas;']

volumes:
  nas:
    driver: easyfuse
    driver_opts:
      o: IdentityFile=$PWD/mini-sshfs/.keys/ssh_user_ed25519_key,UserKnownHostsFile=$PWD/mini-sshfs/known_hosts,Port=2022,uid=1000,gid=1000,users,idmap=user,noatime,allow_other,_netdev,reconnect,rw
      device: "sshfs#$USER@localhost:$PWD/test"

examples$ docker-compose up
Creating volume "examples_nas" with easyfuse driver
Starting examples_mytest_1 ... done
Attaching to examples_mytest_1
mytest_1  | total 12K
mytest_1  | drwxr-xr-x    1 1000     1000        4.0K Nov 25 16:29 .
mytest_1  | drwxr-xr-x    1 root     root        4.0K Nov 25 16:29 ..
mytest_1  | -rw-r--r--    1 1000     1000         445 Nov 25 15:17 lorem_ipsum.txt
mytest_1  | -rw-r--r--    1 1000     1000           0 Nov 25 16:29 test
examples_mytest_1 exited with code 0

examples$ docker-compose down -v
Removing examples_mytest_1 ... done
Removing network examples_default
Removing volume examples_nas
```