Architecture

A core technology used in the upribox software is Ansible [1]: a python-based configuration management software. Our rationale behind using Ansible is twofold:

Reproducibility Every setting, installed package etc. should be documented in code. We use Ansible’s default push mode to configure the base image in order to deploy the latest upribox software and harden the base image. All changes we perform on a given base images can be reproduced (see Development).

Continuous delivery Ansible enables us to roll out bugfixes as well as new features continuously. Once the upribox software is deployed it automatically gets changes from our Github repository and deploys them using Ansible’s pull mode.

Note

Config files are overwritten periodically (see Customization).

Modules

Base setup

init

This role is responsible for basic configurations of the operating system such as

  • expanding disk space
  • configuring locale
  • user management
  • creating sudo group
  • creating remote user
  • removing default user (pi)
  • setting the hostname (upri)
  • adding authorized key

common

The common role lays the groundwork for the following more specific roles. The main parts of this role are the following:

  • installation of required software
  • building the infrastructure for logging
  • creating the logging directory (/var/tmp/log for production and /var/log/log for development mode, see Development vs. Production mode)
  • updating rsyslog config and deleting old rsyslog logfiles
  • configuring logrotate
  • settings and configurations
  • writing default settings
  • copying ansible config
  • creating directory for local facts (see Customization)
  • In order to be able to update the upribox, following task are necessary:
  • installing ansible
  • copying the update script
  • copying git deployment key
  • deploying the upribox recovery mechanism
  • restarts update at system boot if the update was interrupted

unattended_upgrades

Unattended Upgrades [2] provides the upribox automatically with the latest security updates.

upri-config

The upri-config role deploys the upri-config script, which is used for configuring the upribox. Additionally necessary cron jobs for updates and other tasks are copied. The main parts of this role are the following:

  • deployment of the upri-config script
  • create virtualenv
  • copy upri-config files
  • link managament script
  • The upribox updates every hour to the latest version on github via ansible. For this purpose the common role needs to execute among others the following tasks before updating:
  • configuring cron jobs
  • update the upribox
  • update the filter rules
  • parse user-agents which are used to fingerprint the devices connected to the upribox
  • parse log files used for statistics

Networking

arp

The upribox provides a zero-config service called Apate (see Automatic) which allows you to benefit from the ad-blocking functionality on every device in your network not just when connected to the upribox WiFi. This works with a technique called ARP spoofing. In this role the Apate daemon files are copied and configured, requirements are installed to a virtual environment and eventually the daemon is (re)started.

iptables

In order to be able to configure ad-blocking (Silent Mode) and Tor (Ninja Mode) for each device separately the upribox adds and removes iptables rules dynamically. Two lists of MAC addresses - one for devices which don’t need ad-blocking and one for devices with Tor enabled - are stored in local facts. If a user deactivates ad-blocking or activates Tor for a device in the user interface the MAC address will be added to the no_adblocking list and the tor list, respectively. The iptables rules are immediately copied to /etc/iptables/ and to take effect the service is restarted.

vpn

The upribox uses OpenVPN [3] as a VPN service to protect your communication security on the road. This can be used to protect your sensitive information when using public WiFi hotspots. The role creates the necessary certificates and keys and installs and sets up the service.

Note

For security reasons OpenVPN is not executed as root and uses SHA384 for the packet HMAC authentication and AES-256 (CBC mode) for encryption.

wlan

This role is responsible for installing and configuring all WiFi and network related services such as hostapd or isc-dhcp-server. Interface wlan0 is used as the WiFi interface for the upribox and to provide a wireless network with the default SSID upribox. Depending on the mode activated for a specific device, ads and trackers will automatically be blocked and the traffic might be routed through the Tor [5] network.

Privacy

dns

The upribox uses the dnsmasq daemon to filter DNS requests. This role set-ups dnsmasq on all interfaces and listens for requests. Filtered domains are loaded from /etc/dnsmasq.d.

dns_ninja

The DNS ninja dnsmasq daemon filters domains and in additions resolves all requests via the Tor network. The daemons listens for requests on port 5300/UDP. This setup also does not log any DNS requests it receives.

dns_unfiltered

The upribox needs another instance of the dnsmasq service which is responsible for handling DNS requests from devices on which ad-blocking is deactivated.

nginx

This role is used to install and set-up nginx for the upribox. The nginx web server is responsible for a number of tasks:

upribox blackhole

The blackhole setup returns an empty response for any request it receives, depending on the type of request this could be either an empty HTML page, or a blank image file. The server in addition attempts to reset/delete browser cookies for filtered domains: for every cookie the server receives, the server responds with the same cookie with empty values and a validity of 0. This setup ensures that tracking cookies are deleted from the user’s browser the moment the request for a domain filtered by the upribox is made.

upribox CSS filter

The upribox serves custom CSS files to remove ad-content from websites. The custom domain to serve CSS files is: filter.upri.box. CSS filters are loaded from /etc/nginx/lua/css.lua and periodically updated.

upribox web interface

The nginx role finally prepares the setup for the upribox web interface. The nginx configuration ensures that requests to following URLs are forwarded to the upribox Django web interface.

privoxy

This role deploys the Privoxy [4] filter proxy on the upribox. The upribox uses Privoxy to: filter unwanted content in HTTP requests such as advertisement or tracker code. In addition to content filtering, Privoxy injects a custom CSS file into websites to stop (filtered) ads from showing up in websites. The filter configuration for Privoxy is stored in /etc/privoxy and updated periodically.

tor

This role setups the Tor network daemon for the upribox. The Tor daemon is configured for transparent proxying and offers its own DNS resolver to perform DNS queries through the Tor network.

User Interface

ssh

By default the upribox can be reached via SSH. This feature can be disabled in the admin page of the web interface or directly by calling the enable_ssh action of the configuration script upri-config.py (see django).

Note

In the web interface it is not possible to deactivate WiFi, SSH and Apate, since this scenario would make the upribox unreachable. Therefore one of the services should always be up and running. (see Modes)

fingerprinting

The upribox provides a service called registrar which gathers MAC address, IP address and hostname of a device and saves the information into the database. A separate script uses the user-agents provided by squid and tries to extract a model name of the device. These names are later on suggested to the user in the web interface as a way to identify his or her device in a list of other devices on the network. Furthermore the chosen name acts as a label in the device overview.

squid

In order to gather user-agents of (and subsequently fingerprint) connected devices (see fingerprinting) the upribox uses squid [7]. The squid log file is later parsed and the information saved into the database.

django

The upribox user interface (see Installation) is based on the Python Web framework Django [6]. The role is responsible for installing the requirements to a virtual environment, copying the web interface files, setting up the database and installing services like a supervisor (for the rqworker) and the application container uWSGI. By deploying this role the upribox also starts a cleanup process for the saved statistic files removing data older than 6 months.

Note

For privacy reasons the upribox does not keep the ad-blocking logfile with timestamps and URLs but tries to aggregate the information as soon as possible to store only the information that is needed for the statistics and to assure anonymity. (see Logs)

CLI Tool

All changes to the upribox configuration are perfomed via upri-config.py. This nifty command line tool can be used via SSH and also provides a secure way to perform a limited set of privileged command via the Django webinterface.

usage: upri-config.py [-h]
                      {set_ssid,set_password,restart_wlan,enable_silent,restart_silent,enable_vpn,set_vpn_connection,set_wlan_channel,restart_vpn,enable_ssh,restart_ssh,enable_apate,enable_static_ip,restart_apate,parse_logs,parse_user_agents,generate_profile,delete_profile,restart_firewall,set_ip,configure_devices,set_dns_server,set_netmask,set_gateway,restart_network,set_dhcpd,restart_dhcpd,torify_device,exclude_device,untorify_device,include_device,silent_device,check_device,filter_update,vpn_forward,vpn_unforward,backup_settings,restore_settings}
                      ...

Actions

Actions cover tasks that are able to modify the configuration of the upribox

action

Possible choices: set_ssid, set_password, restart_wlan, enable_silent, restart_silent, enable_vpn, set_vpn_connection, set_wlan_channel, restart_vpn, enable_ssh, restart_ssh, enable_apate, enable_static_ip, restart_apate, parse_logs, parse_user_agents, generate_profile, delete_profile, restart_firewall, set_ip, configure_devices, set_dns_server, set_netmask, set_gateway, restart_network, set_dhcpd, restart_dhcpd, torify_device, exclude_device, untorify_device, include_device, silent_device, check_device, filter_update, vpn_forward, vpn_unforward, backup_settings, restore_settings

This script accepts the name of an action that shall be executed

Sub-commands:

set_ssid

Sets a new SSID for the Silent WiFi by writing to the fact wlan

upri-config.py set_ssid [-h] ssid
Positional Arguments
ssid The SSID for the Silent WiFi

set_password

Sets a new password for the Silent WiFi by writing to the fact wlan

upri-config.py set_password [-h] password
Positional Arguments
password The SSID for the Silent WiFi

restart_wlan

Triggers the Ansible tasks with the tag ssid

upri-config.py restart_wlan [-h]

enable_silent

Enables/disables the Silent WiFi by writing to the fact wlan

upri-config.py enable_silent [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not Silent WiFi is enabled (“yes” or “no”)

restart_silent

Triggers the Ansible tasks with the tag toggle_silent

upri-config.py restart_silent [-h]

enable_vpn

Enables/disables the VPN by writing to the fact vpn

upri-config.py enable_vpn [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not VPN is enabled (“yes” or “no”)

set_vpn_connection

Sets a custom port and protocol for the upribox OpenVPN server by writing to the fact vpn

upri-config.py set_vpn_connection [-h] port_protocol
Positional Arguments
port_protocol The port and protocol used for the OpenVPN server (usage: “1194/udp”)

set_wlan_channel

Sets a new WiFi channel for the Silent WiFi by writing to the fact wlan

upri-config.py set_wlan_channel [-h] channel
Positional Arguments
channel The channel for the Silent WiFi

restart_vpn

Triggers the Ansible tasks with the tag toggle_vpn

upri-config.py restart_vpn [-h]

enable_ssh

Enables/disables the ssh by writing to the fact ssh

upri-config.py enable_ssh [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not SSH is enabled (“yes” or “no”)

restart_ssh

Triggers the Ansible tasks with the tag toggle_ssh

upri-config.py restart_ssh [-h]

enable_apate

Enables/disables the Apate (see arp) by writing to the fact apate

upri-config.py enable_apate [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not Apate is enabled (“yes” or “no”)

enable_static_ip

Sets the upribox to DHCP or static IP mode by writing to the fact interfaces

upri-config.py enable_static_ip [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not a static IP is enabled (“yes” or “no”)

restart_apate

Triggers the Ansible tasks with the tag toggle_apate

upri-config.py restart_apate [-h]

parse_logs

Parses the log files of the services and aggregates the statistics data

upri-config.py parse_logs [-h]

parse_user_agents

Parses the log file of the service squid containing MAC addresses, IP addresses and user-agents and saves the gathered information into the database

upri-config.py parse_user_agents [-h]

generate_profile

Generates openvpn client certificates and saves the generated openvpn client configuration into the database

upri-config.py generate_profile [-h] profile_id
Positional Arguments
profile_id The profile ID of a profile that was created in the web interface

delete_profile

Revokes previously generated openvpn client certificates

upri-config.py delete_profile [-h] profile_id
Positional Arguments
profile_id The profile ID of a profile that was created in the web interface

restart_firewall

Triggers the Ansible tasks with the tag iptables

upri-config.py restart_firewall [-h]

set_ip

Sets a static IP by writing to the fact interfaces

upri-config.py set_ip [-h] ip
Positional Arguments
ip The static IP address for the upribox

configure_devices

Triggers the Ansible tasks with the tag configure_devices

upri-config.py configure_devices [-h]

set_dns_server

Sets the DNS server by writing to the fact interfaces

upri-config.py set_dns_server [-h] dns
Positional Arguments
dns The DNS server for the upribox

set_netmask

Sets subnetmask by writing to the fact interfaces

upri-config.py set_netmask [-h] netmask
Positional Arguments
netmask The subnetmask for the upribox

set_gateway

Sets the gateway by writing to the fact interfaces

upri-config.py set_gateway [-h] gateway
Positional Arguments
gateway The gateway for the upribox

restart_network

Triggers the Ansible tasks with the tag network_config

upri-config.py restart_network [-h]

set_dhcpd

Enables/disables the DHCP server by writing to the fact dhcpd

upri-config.py set_dhcpd [-h] boolean
Positional Arguments
boolean

Possible choices: yes, no

Whether or not the upribox acts as a DHCP server (“yes” or “no”)

restart_dhcpd

Triggers the Ansible tasks with the tag dhcp_server

upri-config.py restart_dhcpd [-h]

torify_device

Adds iptables rule to torify a specific device

upri-config.py torify_device [-h] mac
Positional Arguments
mac The MAC address of the device whose traffic shall be routed over the tor network

exclude_device

Adds iptables rule to disable ad-blocking for a specific device

upri-config.py exclude_device [-h] mac
Positional Arguments
mac The MAC address of the device whose traffic shall not be ad-blocked

untorify_device

Removes iptables rule to untorify a specific device

upri-config.py untorify_device [-h] mac
Positional Arguments
mac The MAC address of the device whose traffic shall not be routed over the tor network

include_device

Removes iptables rule to enable ad-blocking for a specific device

upri-config.py include_device [-h] mac
Positional Arguments
mac The MAC address of the device whose traffic shall be ad-blocked

silent_device

Shortcut for calling of include_device and untorify_device

upri-config.py silent_device [-h] mac
Positional Arguments
mac The MAC address of the device whose mode shall be set to silent

check_device

Checks if device with given ip address is online

upri-config.py check_device [-h] ip
Positional Arguments
ip The IP address of the device to check

filter_update

updates the filter files

upri-config.py filter_update [-h]

vpn_forward

enables vpn port forwarding if possible

upri-config.py vpn_forward [-h] [--debug]
Named Arguments
--debug

Print additional debug messages

Default: False

vpn_unforward

removes vpn port forwarding

upri-config.py vpn_unforward [-h] [--debug]
Named Arguments
--debug

Print additional debug messages

Default: False

backup_settings

Saves settings and logs to a backup archive

upri-config.py backup_settings [-h] [path]
Positional Arguments
path

The path of the backup archive

Default: “/home/upri”

restore_settings

Restore settings from backup archive

upri-config.py restore_settings [-h] path
Positional Arguments
path The path of the backup archive

Footnotes

[1]https://www.ansible.com
[2]https://wiki.debian.org/UnattendedUpgrades
[3]https://openvpn.net/
[4]https://www.privoxy.org/
[5]https://www.torproject.org/
[6]https://www.djangoproject.com
[7]http://www.squid-cache.org/