Table of contents

This document was written on february and march 2017.

Konnectors

:warning: Note: this documentation is outdated. It is kept for historical reasons and shows the design and trade-offs we have made initially. But a lot of things have changed since, so don’t expect the things to be still the same.

What we want ?

Konnectors is an application for Cozy v2 that fetch data from different web sites and services, and save them into a Cozy. The 50+ connectors represent a lot of work from the community. So, we want to port it to Cozy v3. There will be 2 parts:

Security

The risks

Konnectors is not just a random application. It’s a very good target for attacks on Cozy because of these specificities:

Access to couchdb

The stack has the admin credentials of couchdb. If a rogue code can read its configuration file or intercept connexions between the stack and couchdb, it will have access to couchdb with the admin credentials, and can do anything on couchdb.

Access to the stack

An attacker can try to profit of konnectors for accessing the stack. It can target the port 6060, used by the stack to manage the cozy instances. Or, it can use its privileged position for timing attacks on passwords.

Spying other connectors

A rogue connector may try to spy other connectors to pick the credentials for external web sites. It can be done by reading the environment variables or ptracing them.

DoS

A connector can use a lot of CPU, Ram, or generate a lot of disk I/O to make a deny of service on the server. The connector can remove files on the server to make konnectors stop working.

Exploiting the CPU or the bandwidth

The resources of the server can be seen as valuable: the CPU can be used for bitcoins mining. The bandwidth can be used for DDoS of an external target.

Sending spam

Profit of the configured SMTP server to send spams.

Be root

Row hammer can be a way to gain root access on a server.

Possible measures

Permissions

We can forbid the konnectors to speak directly with couchdb, and pass by the stack for that. And use the permissions to restrict what each konnectors can do with the cozy-stack.

ignore-scripts for npm/yarn

Npm and yarn can execute scripts defined in package.json when installing nodejs dependencies. We can use the ignore-scripts option to disable this behaviour.

Forbid addons in nodejs

Nodejs can require addons, ie C/C++ compiled libraries. I’ve found no flag to disable the install of such modules for npm/yarn, and no flag for nodejs to prevent loading them. We can try to detect and remove such modules just after the installation of node modules. They should have a .node extension.

Note: not having a compiler on the server is not enough. Npm can install precompiled modules.

vm/sandbox for Nodejs

vm2 is a sandbox that can run untrusted code with whitelisted Node’s built-in modules.

Mock net

We can mock the net module of nodejs to add some restrictions on what it can do. For example, we can check that it does only http/https, and blacklist connection to localhost:6060. It is only effective if the konnector has no way to start a new node processus.

Timeout

If a konnector takes too long, after a timeout, it should be killed. It implies that the cozy-stacks supervises the konnectors.

Chroot

Chroot is a UNIX syscall that makes an application see only a part of the file-system. In particular, we can remove access to /proc and /sys by not mounting them, and limit access to /dev to just /dev/null, /dev/zero, and /dev/random by symlinks them.

Executing as another user

We can create UNIX users that will just serve to execute the konnectors, and nothing else. It’s a nice way to give more isolation, but it means that we have to find a way to execute the konnectors: either run the cozy-stack as root, or have a daemon that launches the konnectors.

Ulimit & Prlimit

ulimit provides control over the resources available to the shell and to processes started by it, on systems that allow such control. It can be used to linit the number of processes (protection against fork bombs), or the memory that can be used.

prlimit can do the same for just one command (technically, for a new session, not the current one).

Linux namespaces

One feature Linux provides here is namespaces. There are a bunch of different kinds:

It turns out that making namespaces is totally easy! You can just run a program called unshare.

Source: What even is a container, by Julia Evans

Cgroups

cgroups (abbreviated from control groups) is a Linux kernel feature that limits, accounts for, and isolates the resource usage (CPU, memory, disk I/O, network, etc.) of a collection of processes.

Seccomp BPF

Seccomp BPF is an extension to seccomp that allows filtering of system calls using a configurable policy implemented using Berkeley Packet Filter rules.

Isolation in a docker

Isode is a 3 years old project that aims to isolate nodejs apps in docker containers. A possibility would be to follow this path and isolate the konnectors inside docker.

It’s a real burden for administrators. And its command line options often changes from one version to another, making difficult to deploy something reliable for self-hosted users. So we will try to avoid it.

Isolation in docker contains is mostly a combination of Linux Namespaces, Cgroups, and Seccomp BPF. There are other options with those (see below).

Rkt

Rkt is a security-minded, standard-based container engine. It is similar to Docker, but Docker needs running a daemon whereas rkt can be launched from command-line with no daemon.

NsJail / FireJail

NsJail and FireJail are two tools that use Linux Namespaces and Seccomp BPF to reduce the risks to run untrusted applications on Linux. FireJail seems to be more suited for graphical apps, and NsJail for networking services.

NaCl / ZeroVM

ZeroVM is an open source virtualization technology that is based on the Chromium Native Client (NaCl) project. ZeroVM creates a secure and isolated execution environment which can run a single thread or application. But NaCl is no longer maintained and ZeroVM has some severe limitations, so, it won’t be used.

Konnector isolation study

The short list of tools which will be tested to isolate connectors is Rkt and NsJail which on paper better fullfill our needs.

NsJail

NsJail is a lightweight process isolation tool, making use of Linux namespaces and seccomp-bpf syscall filters. It is not a container tool like docker. Its features are quite extensive regarding isolation. The README gives the full list of available options. Although available in the google github, it is not an official google tool.

NsJail is:

Rkt

Rkt is very similar to docker. It can even directly run docker images from the docker registry, which gives us a lot of existing images to use, even if we want to be able to use other languages than node. For example, we could have also a container dedicated to weboob, another container could use phantomjs or casper and without forcing self-hosted users to do complicated installation procedures.

Rkt is :

I managed to run a nodejs container with just the following commands :

rkt run --insecure-options=image --interactive docker://node:slim --name nodeslim -- -v
rkt list   # to get the container uuid
rkt export --app=nodeslim <uuid> nodeslim.aci
rkt run --insecure-options=image --interactive nodeslim.aci -- -v  # to run node -v in the new container

Note: the –insecure-options param is to avoid the check of the image signature to ease the demonstration

Choice

The best choice would be Rkt for it’s ease of use (which is good for contribution) and wide range of isolation features + access to the big docker ecosystem without beeing a burden for the host administrator. Note : the limitation of NsJail I saw might be due to my lack of knowledge regarding system administration.

Proposed use of rkt regarding connectors

Installation

As stated before, rkt is easy to install. It may also be possible to make it available in the cozy-stack docker image but I did not test it (TODO)

Image creation

To create an ACI file image, you just need to run a docker image one time :

rkt run  --uuid-file-save=$PWD/uuid --insecure-options=image --interactive docker://node:slim --name nodeslim -- -v
rkt export --app=nodeslim `cat uuid` nodeslim.aci && rm uuid

The node:slim image weights 84M at this time. The node:alpine image also exists and is way lighter (19M) but I had problems with DNS with this, and alpine can cause some nasty bugs that are difficult to track.

Running a connector

A path dedicated to run the konnectors with a predefined list of node packages available (the net module could be mocked with special limitations to blacklist some urls)

A script will run the node container giving as option the script to launch. The path is mounted inside the container. The following script does just that

1
2
3
4
5
6
7
8
9
#!/usr/bin/env bash
rm -rf ./container_dir
cp -r ./container_dir_template ./container_dir
rkt run --net=host --environment=CREDENTIAL=value;COZY_URL=url --uuid-file-save=$PWD/uuid --volume data,kind=host,source=$PWD/container_dir --insecure-options=image nodeslim.aci --cpu=100m --memory=128M --name rktnode --mount volume=data,target=/usr/src/app --exec node -- /usr/src/app/$1 $2 &
# the container will handle itself the communication with the stack
sleep 60
rkt stop --force --uuid-file=uuid
rkt rm --uuid-file=uuid
rm -rf ./container_dir

This script can be run like this :

./rkt.sh mynewkonnector.js

If the mynewkonnector.js file is available in the container_dir_template directory.

Cons : must forbid access to port 5984 and 6060 + SMTP server

The limitation of time, CPU and memory will avoid most DOS attacks (to my knowledge). For memory use, I still don’t see a way to prevent the excessive use of swap from the container. To prevent the connectors from listening to each other, they should be run in containers with different uid, avoiding them to listen to each other.

Solution to limit access of the container to 5984 and 6060 ports + SMTP

The container must be started in bridged mode. With that, the container still has access to localhost but through a specific IP address visible with ifconfig. That way, the host can have iptable rules to forbid access to specified ports to the bridge.

To connect a container in bridge mode :

On the host create the file /etc/rkt/net.d/10-containers.conf

{
    "name": "bridge",
    "type": "bridge",
    "bridge": "rkt-bridge-nat",
    "ipMasq": true,
    "isGateway": true,
    "ipam": {
        "type": "host-local",
        "subnet": "10.2.0.0/24",
        "routes": [
               { "dst": "0.0.0.0/0" }
        ]
    }
}

and run your container with the “–net=bridge” option. That way, a new interface is available in the container and gives you access to the host.

Konnector install and run details

Install

The konnectors will be installed in the .cozy_konnectors directory which is in the VFS using git clone (like the apps at the moment).

The konnectors installation may be triggered when the user says he wants to use it. The resulting repository is then kept for each run of the konnector. It may then be given to the user the possibility to upgrade the konnector to the latest version if any.

To update a given konnector, a git pull command is run on the konnector.

Details about running a konnector

To run a given konnector, the stack will copy this connector in a “run” directory, which is not in the VFS. This directory will be given to the rocket container as the current working directory with full read and write access on it. This is where the container will put its logs and any temp file needed. There will be also cozy-client.js and the shared libraries in a lib directory inside this directory. The lib directory will be the content of the actual server lib directory.

The konnector will be run with the following environment variables :

In the end of the konnector execution (or timeout), the logs are read in the log.txt file and added to the konnector own log file (in VFS) and the run directory is then destroyed.

Multi-account handling

This section is devoted to allow the user to use one account for multiple konnectors. It will follow the following constraints in mind:

New doctype : io.cozy.accounts

A new doctype will have to be created to allow to keep konnector accounts independently from each konnector. The one once used by the email application seems to be a good candidate : io.cozy.accounts

Here is an example document with this doctype :

{
    _id: "ojpiojpoij",
    name: "user decided name for the account",
    accountType: "google",
    login: "mylogin",
    password: "123456"
}

Any attribute needed for the account may be added : email, etc…

Updates needed in existing application and konnectors

CRUD manipulation of io.cozy.accounts and linking them with konnectors will be handled by the “my accounts” client application.

Each konnector need also to declare a new field in the “fields” attribute which will be the type of account, related to the accountType field in the new account docType.

Ex:

module.exports = baseKonnector.createNew({
  name: 'Trainline',
  vendorLink: 'www.captaintrain.com',
  category: 'transport',
  color: {
    hex: '#48D5B5',
    css: '#48D5B5'
  },
  fields: {
    login: {
      type: 'text'
    },
    password: {
      type: 'password'
    },
    folderPath: {
      type: 'folder',
      advanced: true
    },
    accountType: "trainline"
  },
  dataType: ['bill'],
  models: [Bill],
  fetchOperations: [
    ...
  ]
})

With this new field, which will appear also in the io.cozy.konnectors docType, the “my account” client appliction will be able to propose existing accounts of the good type for activating a new konnector.

Migration path

For the migration of existing, activated konnectors in V2, the type of account for each konnector will have to be indicated in a V2 “my account” application update. After that, it will be possible to create the accounts associated to each activated konnectors an link the konnectors to these accounts in a migration script.

Study on konnectors installation on VFS

The VFS is slow and installing npm packages on it will cause some performance problem. We are trying to find solution to handle that.

We found 3 possible solutions :

TODO