Dead letter box for your private network. Access home devices without dynamic DNS. No open ports required.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
application
cmd
config
crypto #31 client can validate Drop fingerprint Jan 3, 2018
daemon
drop
it
model
request renamed request processor id Nov 4, 2017
rest
worker
.gitignore
.travis.yml
LICENSE
README.md README.md: GoDoc link Jan 3, 2018

README.md

Deadbox

Access data and devices in your private network without Dynamic DNS, port opening etc.

A program in your private network connects to an internet-accessible server, ready to receive encrypted and authorized requests.

Build Status codecov 0pdd.com GoDoc

I'm very happy to hear your feedback and ideas. Simply file an issue!

Idea

Problem

When a user wants to access his private data from the internet, he currently has two possibilities:

  • Either he uses a public cloud, like Google Drive, Microsoft OneDrive etc. This forces the user to always store his data on foreign servers. He is not able to control who may access these data, as no popular (free of charge) cloud provider has support for encryption.
  • Or he runs a private cloud (ownCloud, Nextcloud) on NAS devices. This forces the user to open a port to his private network behind a public DNS name, inducing a severe security risk. Security holes in the router, operating system or application software allow an attacker to enter the users private network and data. The risk of such a security hole is high, as Dynamic DNS and private clouds rely on a broad interface and many different technologies.

Having a way of remotely accessing private data, without neither storing them on foreign servers nor opening up one's private network using large interfaces, would significantly improve security of all parts of the private network.

Approach

Deadbox is an application that combines the concepts of peer to peer and message bus to establish communication between public and private networks. It consists of two parts: workers and drops.

  • The worker runs on devices in your private network and connects to the drop, which is located on a machine somewhere publicly available.
  • The user accesses the drop through a web-based UI, sending usecase based requests like "search for a file by name" or "upload a file". The drop stores the requests in a queue.
  • The worker requests all items in his queue from the drop, processes them and sends the answers back to the drop. From there, they can be requested by the user's UI.
  • All requests and respones are end-to-end encrypted. The drop stores the worker's certified public keys, while the user only needs short-lived key pairs.

Advantages

The user's experience is similar to that of a typical cloud application. He uploads and downloads files or accesses other functionality through a comfortable web-based UI.

Still, the system provides more security, due to the following reasons.

  • The private network provides no interface vulnerable to probing techniques like port scanning.
  • The private network exposes a very small interface, protected through well-known authentication techniques. It is additionally protected through the server, who won't process unauthenticated requests.
  • The publicly available drop does not store unencrypted private data at all. The data stored by the drop are deleted after request completion or within a configurable timeout. Therefore, attacking the servers data storage will not allow to access private information.
  • Communication with the private network may only happen via the drop, which therefore acts as a shield against DoS or bruteforce attacks. The availability of the drop might be in risk, but the private network is secured.

By combining worker and drop in one single binary and adding some basic routing, a peer to peer network emerges, allowing to combine workers from different private networks using tree-like or even rather random structures. One deadbox instance could serve as worker and drop at the same time, loading requests from foreign drops, providing them to other workers.

This offers a high flexibility in retrieving data and sending commands from/to private devices.

Disadvantages

Compared to a centralized cloud-based storage solution, the deadbox suffers under limited availability and increased configuration effort.

  • The service is only available when the devices in the private network are on and connected. However, this could be circumvented by letting the drop not only store the encrypted requests, but also the encrypted responses for some limited time. Of course, this is not feasible in all use cases and, in any case, leads to possibly high latency.
  • The user needs to install, configure and maintain instances on at least two different devices, from which one must be in the public internet. This could be circumvented by providing a central cloud service, maybe on a subscription base.

Challenges

  • While the worker needs to open the connection to the drop, the drop must be able to push requests to the worker, so that the user won't experience a high latency. Similarly, the worker must be able to push his responses back to the drop and thus to the user.
  • When thinking about authentication, it is clear that the worker should not need to trust the drop. Otherwise, in terms of security, the drop would be a single point of failure. Instead, it would be better if both could rely on a common third party for establishing authentication, as it is possible with e.g. OpenID.

Use cases and examples

The deadbox could be used in following scenarios.

  • A user wants to access his home NAS from the internet, while retaining a high level of security.
  • A user wants to orchestrate smart home devices.
  • A company wants to access and control multiple devices in different networks from a single interface.

Possible use cases are as follows.

  • The user finds a file on one of his devices by the files name, location or contents.
  • The user downloads a file from one of his devices.
  • The user downloads a folder from one of his devices.
  • The user uploads a file to one of his devices.
  • The user establishes a regular synchronization of folders between a portable device and a device inside his private network.
  • The user turns on a device using wake on lan.
  • The user runs preconfigured scripts and programs on one of his devices.
  • The user controls a smart home device, like setting the room temperature.
  • The user retrieves data from a smart home device, like a webcam or a thermostat.

Related Work

The following projects are similar to deadbox.

  • Syncthing is an open source, multi platform synchronization solution. If you want to synchronize the same set of directories between the same set of devices for some time, use Syncthing! However, if you'd rather have some publicly reachable endpoint for your private devices and use it to perform actions (of any kind) on your device, use deadbox.
  • ngrok is a proprietary tunnel to your device. If you want to access any kind of webapp or TCP service (like Nextcloud), running on your device, itself running behind a firewall, from the public internet, use ngrok. However, if you'd rather have a fully open source solution, without the need to run multiple server components, or if you want to support non-interactive scenarios, where you want to enqueue actions (like sending files) while the target device is offline, use deadbox.

Specification

This spec serves as a guideline for the current implementation. However, it is subject to change, and some points might be of quite low priority.

Security

  • Drop secures its REST interface using TLS. If the worker cannot establish a trust chain for the drops certificate, Public Key Pinning might be an option.
  • When we want the Worker to authentificate against the drop, it could use client certificates while connecting with TLS.
  • The Worker provides a public key to the Drop. The key pair could be generated on first startup.
  • When the User wants to send a request to a Worker, he identifies the Worker using a unique identifier.
    • The identifier must be pre-known to the user, the Drop should not provide a list of known workers, which makes it harder to find vulnerable endpoints.
    • The identifier equals the public key fingerprint. This makes identiy spoofing harder and allows the user to trust that the Worker he connects to is the one he actually expects.
  • User retrieves the Worker's public key from the Drop and encrypts requests to the Worker therewith. The key is authenticated against the Worker's id (see above).
  • User includes its own public key in its requests to the Worker, which in turn encrypts responses therewith.

Command Line Interface

The main program is deadbox, providing at least the following arguments.

  • -worker /path/to/worker.yml makes the pocess act as an worker, connecting to a drop and waiting for commands.
  • -drop /path/to/drop.yml makes the process act as a drop, opening a port providing a REST interface for both users and workers.
  • -auth /path/to/auth.yml configures authentication for both workers and drops.

worker.yml

The file worker.yml contains the configuration for an worker. It must configure at least the following aspects.

  • A textual identifier of the worker instance. Should be unique amongst all workers on the same drop.
  • URL of one (or even multiple) drops.
  • A reference to a private (and possibly public) key file, used to decrypt received messages and to identify and authentificate the Worker against users and the drop. The keys will be generated automatically if not existant.
  • Enabled receivers, with the appropriate configuration for each.
  • A file system receiver must be configured with the file system location, include/exclude patterns and allowed/disallowed operations.
  • An executing receiver must be configured with the command to execute when invoked.
  • Per receiver, it must be possible to enable/disable it for individual authenticated users.

drop.yml

The file drop.yml contains the configuration for a drop. It must configure at least the following aspects.

  • Hostname and port to bind on.
  • A whitelist of allowed workers, identified by their unique identifiers.

auth.yml

The file auth.yml configures the authentication method to be used by both workers and drops. It must contain at least the following information.

  • Authentication method to be used, e.g. htpasswd file or OAuth 2.0 server.
  • Parameters for the authentication method, e.g. location of the passwd file or URL and access data to the OAuth 2.0 server.
  • Note, that drop and worker might need different parameters. While the drop must be able to establish user authentication, the worker must simply be able to consume a request.

REST interface

The drop offers a REST interface to be consumed by users and workers. It will provide at least the following endpoints.

  • POST /worker/ lets a Worker register with the Drop or update his information. This endpoint is idempotent.
  • GET /worker/{workerId} allows users to retrieve data about a Worker by means of a pre-known workerId.
  • GET /worker/{workerId}/request lets a Worker retrieve all pending requests targeted to him.
  • POST /worker/{workerId}/request allows a User to file a new request targeted to a Worker. This endpoint is idempotent.
  • POST /worker/{workerId}/response/{requestId} is called by a Worker to transport the response to a request. This endpoint is idempotent.
  • GET /worker/{workerId}/response/{requestId} allows users to retrieve the response to a previously filed request, if already available.

As for a worker entry, at least the following information need to be contained.

  • The Workers unique identification.
  • A timestamp, after which the Worker may be regarded as outdated and removed. The timestamp must be in the future. The Drop might apply a smaller timeout than requested.
  • The request types supported by the Worker, given as URN.
  • The Workers public key, used to encrypt the requests, given in ASN1 format.

As for a queue entry, which is a request, at least the following information need to be contained.

  • The requests unique identification.
  • A timestamp, after which the request may be regarded as outdated and removed. The Drop might apply a smaller timeout than requested.
  • The request type, given as URN. The request type should be contained in the list of supported request types as provided by the Worker.
  • The request payload.
  • If we want to apply routing over multiple hops, we might also need a unique identifier of the originating drop, and possibly an ordered list of all drops that transported the request.

As for a response, at least the following information need to be contained.

  • The original requests unique identification.
  • A timestamp, after which the response may be regarded as outdated and removed. The Drop might apply a smaller timeout than requested.
  • The response payload.
  • If we want to apply routing over multiple hops, we might also need a unique identifier of the originating drop, and possibly an ordered list of all drops that are still to address.

Public Key Fingerprinting

The task of creating a public key fingerprint, and therefore a Worker Id, is defined by the following algorithm. The algorithm can be configured using the parameters

  • HashFunction. The cryptographic hash function to be used, i.e. SHA-256.
  • Encoding. The function for mapping the hash sum to its string representation, i.e. Base32. Base32 is chosen as default, because it performs good when it has to be read by human users.
  • ChallengeLevel. At a value of 0, it is very cheap to calculate the fingerprint, but it's also quite cheap to calculate a hash collision.
  • FingerprintLength. When equal to the number of bytes the HashFunction produces, the fingerprint consists of the full hashsum, providing maximum protection against hash collision search, but also making the fingerprint difficult to use.

The algorithm consists of the following steps.

  • Bring the public key into a binary representation,
  • Add other information which must be protected against tampering:
    • the ChallengeLevel itself,
  • Add a challengeSolution, which is initially 0,
  • Calculate the hashsum of these data using HashFunction,
  • Verify that the leftmost ChallengeLevel bits of the hashsum are zero,
    • If not so, increase the challengeSolution by one and recalculate the hash,
  • Encode the hashsum without the first (ChallengeLevel+7)/8 bytes using the Encoding,
  • Group the encoded hashsum in pairs of two characters, shorten it to the first FingerprintLength groups and separate the pairs by colons :.

The params ChallengeLevel and FingerprintLength should be chosen in a way that low FingerprintLength is compensated with a high CalculationCost and vice versa.