Eigener Server für Kalender und Kontakte

Kalender und Kontakte sollten auf allen Geräten, sei es mobil oder stationär, abrufbar und synchron sein. Das spricht dafür, dass sie über Netzwerk oder sogar das Internet abgeglichen werden. Müssen diese sensiblen Daten aber gleich in der Cloud eines großen Anbieters wie Google und Co landen?

Wenn man dies nicht will, bieten sich Lösungen auf CalDav– und CardDav-Technologie an. Beide Protokolle setzen auf WebDAV nach RFC 4918 auf. CalDAV synchronisiert Kalenderdaten nach RFC 4791, CardDav hingegen Kontakte nach RFC 6352.

Bei der Auswahl des Servers für beide Protokolle hat man viel Auswahl. Die Wikipedia bietet eine Aufstellung von Möglichkeiten.

Server einrichten

Für den Server habe ich ich mich für Radicale entschieden. Zusätzlich soll als Frontend InfCloud verwendet werden um auch mit dem Browser von beliebigen Endgeräten auf die Kalender und Adressen zugreifen zu können.

Thomas Queste hat mit dem Docker Image tomsquest/docker-radicale eine einfache Möglichkeit geschaffen, diese Software zu verwenden.

Zunächst legt man Verzeichnisse an:

  • /data/cal/config – für die Konfigurationsdatei und die Datei mit den Nutzern
  • /data/cal/data – für die eigentlichen Kalender- und Adress-Daten

An Konfigurationsdateien benötigt man die Datei /data/cal/config/config mit folgendem Inhalt:

[server]
hosts = 0.0.0.0:5232

[encoding]

[auth]
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt

[rights]

[storage]
filesystem_folder = /data/collections

[web]
type = radicale_infcloud

[logging]

[headers]

Die Datei /data/cal/config/users wird mit den üblichen Tools zur Verwaltung von .htaccess-Dateien modifiziert: htpasswd. Wichtig ist mit der obigen Konfiguration der Parameter -B zur Aktivierung der bcrypt-Verschlüsselung:

htpasswd -B /data/cal/config/users $username

Anschließend muss der Docker-Container angelegt und ausgeführt werden:

docker run \
    -d \
    --name radicale \
    -p 5323:5232 \
    -v ~/tmp/cal_data:/data \
    -v ~/tmp/cal_config:/config:ro \
    tomsquest/docker-radicale

Natürlich sollte man im produktiven Einsatz eine TLS-gesicherte Verbindung auf Port 443 verwenden!

Wenn alles funktioniert sollte man unter http://localhost:5232/.web auf die Weboberfläche von Radicale zugreifen können. Als Logins dienen die Accounts, die in der Datei users angelegt wurden. Dort kann man nun alle benötigten Collections, wie Adressbücher, Kalender und Aufgabenlisten anlegen.

Clients

Clients wurden für eine möglichst große Anzahl von Plattformen untersucht.

Browser

InfCloud (in Version 0.13.1) läuft laut Hersteller in allen HTML5 fähigen Browsern wie Safari/Mobile Safari, allen Webkit-basierten, iCab, Firefox, Opera (15+) und Chrome. Zugegriffen werden kann auf InfCloud unter http://localhost:5232/.web/infcloud/.

iOS

Funktioniert direkt ohne weitere Software, was mich positiv überraschte.

Unter EinstellungenAccounts & Passwörter → Account hinzufügen → Andere → CalDAV-Account hinzufügen konfiguriert man den Zugriff auf die Kalender:

Server ist hier der Hostname, unter der Radicale zu erreichen ist. Benutzername und Passwort sind wie in der Users-Datei angegeben und die Beschreibung ist ein beliebiger Text.

Analog verfährt man mit dem Einbinden der Adressbücher. Unter EinstellungenAccounts & Passwörter → Account hinzufügen → Andere → CardDAV-Account hinzufügen konfiguriert man den Zugriff auf die Adressbücher. Die Werte sind analog zu denen bei den Kalendern.

Android

Google macht es uns bei Android etwas schwerer, man will wohl die eigene Cloud mal wieder pushen.

Aber mit CalDAV Sync Free Beta erhält man eine Open-Source Software, die weiterhilft. Ist die Software installiert, so kann man unter EinstellungenKonten → Konto hinzufügen mit der Option CalDav Sync Adapter den Zugang zu den Kalendern einrichten.

Der Benutzer und das Passwort sind die Werte, die auch für das Login in InfCloud verwendet werden. Die URL findet man in der Web-UI von Radicale und sollte einfach per Mail auf das Android Gerät kopiert werden. Anschließend findet man unter EinstellungenKonten → CalDav Sync Adapter den neu eingerichteten Kalender und muss bei diesen noch die Synchronisierung aktivieren:

Jetzt sollte der eingerichtete Kalender in der Kalender App eingebunden sein.

Kontaktdaten via CardDav können ebenso nur mit Zusatzsoftware eingebunden werden. Hier CardDAV-Sync free eine kostenlose Option, die leider nicht frei (wie in freier Software) ist. Auch hier muss man wieder die komplette URL aus der Radicale Web-UI verwenden (analog zur Lösung für CalDAV-Daten). Anschließend können Kontakte zwischen Radicale synchronisiert werden und auch auf dem Android Gerät wie gewohnt erstellt werden.

Desktop

macOS

Unter macOS gibt es, wie unter iOS auch, die Möglichkeit direkt mit Bordmitteln auf Adress- und Kalenderdaten zuzugreifen. In den Einstellungen gibt es Optionen sowohl für CalDAV als auch für CardDAV.

Die Einrichtung findet analog zu iOS statt (hier sieht man inwieweit iOS und macOS sich gleichen).

Linux/Windows

Hier kann Thunderbird mit den beiden Add-ons Lightning (getestet in Version 6.2) und CardBook (getestet in Version 31.2) alle notwendigen Funktionen bereitstellen.

Lightning konfigurieren

Hier wählt man beim Anlegen eines neuen Kalenders die Option Im Netzwerk und als Format CalDAV. Auch hier muss man die URL des Kalenders aus der Web-UI von Radicale entnehmen und einfügen. Anschließend definiert man einen lokalen Namen und legt eine Farbe fest. Im Authentifizierungsdialog trägt man Nutzernamen und Passwort ein. Die Authentifizierung muss pro Server nur einmal vorgenommen werden.

CardBook konfigurieren

Beim Anlegen eines neuen Adressbuches wiederum die Option Im Netzwerk und als Format CardDAV. Dann kopiert man erneut die URL des Adressbuchs und authentifiziert sich. Wichtig das vCard-Format muss auf 3.0 geändert werden! Ansonsten können die erstellten vCards nicht auf allen Clients (z.B. InfCloud) bearbeitet werden.

Windows 10

Unter Windows 10 kann man sich mit folgendem Trick behelfen: https://www.ctrl.blog/entry/how-to-win10-webdav-syncengine (Allerdings ohne Garantie für zukünftige Updates).

 

Docker und IPv6

Zu meinem Root-Server gab es ein /64-IPv6 Netz (z.B. 2a03:4000:15:3b7/64)  und somit mehr als genug Adressen. Doch wie kann ich Docker-Container unter über diese Adressen ansprechen?

Zunächst fügt man eine IPv6-Adresse aus dem Netz der Netzwerkkarte hinzu:

ip -6 a a 2a03:4000:15:3b7::3/64 dev eth0

Dann startet man einen Container und ruft die IPv4 Adresse aus dem Docker-internen Netz ab:

docker run --name testnginx -d nginx
docker inspect --format '{{ .NetworkSettings.IPAddress }}'\
    testnginx

Anschließend leitet man mit socat die IP-Pakete von der IPv6-Adresse an die IPv4-Adresse des Containers weiter:

socat TCP6-LISTEN:80,reuseaddr,fork,bind=[2a03:4000:15:3b::3]\
    TCP4:172.17.0.15:80

Nun kann man direkt auf den Port des Containers über IPv6 zugreifen.

Influxdb und Grafana

Aus meinem beruflichen Umfeld ist mir Grafana als Lösung zur grafischen Aufbereitung gut bekannt. Daten beziehen wir klassisch aus Graphite.

Für ein Projekt habe ich nun die Daten über öffentliches Netz (also das Internet) in die Datensenke einliefern müssen. Zunächst auch hier an Graphite gedacht, jedoch ergab sich schnell die Überlegung, das der Graphite Port 2003 (hier lauscht der Carbon Daemon) jedwede Daten ohne Authentifizierung annimmt. In einem  öffentlichen Netz sicher kein akzeptabler Zustand, da hier jeder mit Kenntnis von Port und IP beliebige Stördaten einliefern kann. Also auf die Suche nach Alternativen.

InfluxDB bietet laut db-engines.com  als Berechtigungskonzept mit einer “einfache[n] Rechteverwaltung mit Benutzeraccounts” mehr als Graphite. Auch verspricht die Implementierung in Go mehr Performance. Grund genug, sich InfluxDB näher anzusehen.

InfluxDB

Dank des offiziellen Docker Images ist InfluxDB schnell probeweise gestartet. Hinweise liefert die Seite des Docker-Hub. Zunächst wird die Standardkonfiguration extrahiert:

docker run --rm influxdb influxd config > influxdb.conf

In dieser Datei muss die Option auth-enabled nun aktiviert werden:

...
[http] 
...
auth-enabled = true
...

In Kombination mit meinen beiden Projekten docker_nginx_auto_proxy und docker_ssl_endpoint kann man nun einen Docker Container starten, der Daten über TLS abgesichert entgegen nimmt:

docker run --name=influxdb_1 -d \
 -e PROXY_DATA=server_names:$INFLUXDOMAIN,port:8086 \
 -v /data/influxdb/data:/var/lib/influxdb \
 -v /data/influxdb/conf:/etc/influxdb/influxdb.conf:ro \
 influxdb -config /etc/influxdb/influxdb.conf

Nun müssen wir mindestens einen Admin-Nutzer anlegen. Zunächst verbinden wir uns auf die InfluxDB-CLI:

docker run --rm --link=influxdb_1 -it influxdb influx -host influxdb_1

Anschließend legt folgendes Statement den Admin-Nutzer bofh an:

CREATE USER bofh WITH PASSWORD 'rrzs42' 
  WITH ALL PRIVILEGES

Anschließend die CLI beenden und beim Neuverbinden zusätzlich die Parameter username und password zur Authentifizierung verwenden. Weitere Informationen zur Nutzerverwaltung von InfluxDB findet man in der offiziellen Dokumentation.

Eine Datenbank sollte in der CLI ebenfalls erstellt werden:

CREATE DATABASE datensenke

Testweise können nun bereits Daten eingeliefert werden:

curl -X POST -u bofh:rrzs42 \
  --data-binary 'wert,key=value wert=42.23'\
  'https://$INFLUXDOMAIN/write?db=datensenke'

Die genaue Beschreibung über das Schreiben von Daten findet man in der offiziellen Dokumentation.

Grafana

Für Grafana gibt es ebenfalls ein offizielles Docker-Image. Der erste Schritt ist das Extrahieren der Konfiguration:

docker run --rm --name grafana grafana/grafana
docker cp grafana:/etc/grafana/grafana.ini .

Die wichtigste Anpassung der Konfiguration bezieht sich auf das Abschalten des selbständigen Anmeldens neuer Nutzer (allow_sign_up = false in der Kategorie users). Somit ist nur noch der Administrator (Login: admin) in der Lage, neue Nutzer anzulegen.

Gestartet wird der Grafana-Container mit folgendem Kommando:

docker run --name=grafana -d \
    -e PROXY_DATA=server_names:$GRAFANADOMAIN,port:3000 \
    -v /data/grafana/lib:/var/lib/grafana/ \
    -v /data/grafana/conf:/etc/grafana/grafana.ini \ 
    -e "GF_SERVER_ROOT_URL=https://$GRAFANADOMAIN" \
    -e "GF_SECURITY_ADMIN_PASSWORD=s3cr3t" \
    grafana/grafana

Nun kann die Grafana-Instanz unter  https://$GRAFANADOMAIN aufgerufen werden und der Login funktioniert mit den Credentials admin:s3cr3t.

Der nächste Schritt sollte das Ändern des Passworts des Administrators sein und das Anlegen neuer Nutzer sein. Dies geschieht unter Grafana-MenuAdminGlobal Users.

Um wie die Daten aus InfluxDB in Grafana nutzen zu können, legt man eine Datenquelle unter Grafana-MenuData Sources an. Die Einstellungen für InfluxDB sind in unserem Fall:

  • Type: InfluxDB
  • URL: https://$INFLUXDOMAIN
  • Access: Proxy
  • Http Auth: With Credentials
  • InfluxDB Details:
    • Database: datensenke
    • User: bofh
    • Password: rrzs42

Jetzt sind wir in der Lage, über Dashboards wie gewohnt Graphen an zu legen. Beispielsweise:Et voilà, flexible und ansehnliche graphische Aufbereitung von Zeitseriendaten.

Icinga2 mit Docker

Aus diversen Gründen hatte ich Bedarf nach einen Icinga2 Setup inklusive icingaweb2.

Einleitung

Wichtige Anforderungskriterien waren:

  • Alarmierung über Mails ist möglich
  • Neuste Versionen zum Testen
  • Docker-typische “Separation of Concerns” und nicht “alles in einen Container”

Images wie icinga/icinga2 oder jordan/icinga2 konnten entweder nicht alle Features oder waren Monolithisch angelegt. Muss man einen monolithischen Container, der seine Datenbank enthält, durch eine neue Version ersetzen, sind die Daten (ohne zusätzlichen Aufwand) verloren.

Also doch ein eigenes Image, oder besser gleich 2:

docker_icinga2_core

Das docker_icinga2_core-Image bietet folgende Features:

  • Ausführen des icinga2 Daemons und der Plugins aus monitoring-plugins-standard
  • Volume zum Ablegen der Icinga2-Konfiguration außerhalb des Containers
  • Versand von Alarm-Benachrichtigungen über einen Smarthost (z.B. gmail, siehe hierzu GmailAndExim4)
  • Speicherung der aktuellen Konfiguration und Statusinformationen in einer MySQL (IDO)
  • Integrierte Kommandozeilenoption zum einmaligen Einrichten der IDO-Datenbank mit passendem Schema
  • (Optionale) Anbindung von Graphite zur Speicherung der Messdaten und Aufbereitung als Graphen
  • (Optionale) Bereitstellung der Icinga2-API für andere Container auf Port 5665

docker_icinga2_web

Das docker_icinga2_web-Image bietet folgende Features:

  • Zugriff auf icingaweb2 über Container-Port 80 (eventuell empfiehlt sich der Einsatz von docker_nginx_auto_proxy)
  • Steuerung des icinga2_core über die Icinga2-API
  • Die aktuellen Statusinformationen werden der IDO Datenbank entnommen
  • Festlegung der berechtigten Nutzer finden über eine Auth Datenbank statt

Deployment

Darstellung der Zusammenhänge der einzelnen Komponenten

Weitere Informationen zum Starten der einzelnen Container finden sich in den Git-Repositories zu docker_icinga2_core und docker_icinga2_web.

Fazit

Das Container-Duo läuft nun seit einigen Tagen auf meinem NAS und überwacht diverse interne Geräte sowie einige externe Dienste und meldet mir deren Ausfall zeitnah per Mail. Graphite und/oder Grafana ist noch nicht aktiv angebunden ist aber während der Testphase verprobt worden.

Update 29. November 2017

Auf Wunsch eines meines Lesers Patrick finden sich die Quellen für die beiden Container unter:

Warnung: ist nicht explizit erneut geprüft und kann Fehler enthalten. Benutzen auf eigenes Risiko ;)

Debian Pakete bauen im Docker-Container

Leider gibt es wohl für Debian 8 Jessie kein (aktuelles) Netatalk gibt, hier eine kleine Beschreibung wie man sich mit Docker und checkinstall ein entsprechendes Paket selber erstellen kann, ohne seinen Host mit den zur Paketerstellung notwendigen Abhängigkeiten zu belasten. Als Einstieg diente mir das Wiki von Netatalk und das Blog von Daniel Lange.

Zunächst das Dockerfile:

# Wir wollen das Paket später auf Debian 8 einsetzen. Andere 
# Distributionen sind analog möglich
FROM debian:8

# Update der Paketbasis und Installation der benötigten Abhängigkeiten
RUN apt-get update -y; \
    apt-get install -y build-essential \
        devscripts debhelper cdbs autotools-dev dh-buildinfo \
        libdb-dev libwrap0-dev libpam0g-dev libcups2-dev libkrb5-dev \
        libltdl3-dev libgcrypt11-dev libcrack2-dev \
        libavahi-client-dev libldap2-dev libacl1-dev \
        libevent-dev d-shlibs dh-systemd wget libtdb-dev checkinstall

# Quellcode beziehen und extrahieren
RUN wget http://prdownloads.sourceforge.net/netatalk/netatalk-3.1.10.tar.bz2; \
    tar xvf netatalk-3.1.10.tar.bz2

Anschließend bauen wir das Docker-Image:

docker build --tag=docker_build_netatalk .

Auführen mit:

docker run -v /tmp:/target --rm -ti docker_build_netatalk

Im Container dann folgendes durchführen:

$ cd netatalk-3.1.10
$ ./configure \
    --with-init-style=debian-systemd \
    --without-libevent \
    --without-tdb \
    --with-cracklib \
    --enable-krbV-uam \
    --with-pam-confdir=/etc/pam.d \
    --with-dbus-daemon=/usr/bin/dbus-daemon \
    --with-dbus-sysconf-dir=/etc/dbus-1/system.d \
    --with-tracker-pkgconfig-version=1.0
$ make
# Legt einige Verzeichnisse an, wird ja eh weggeworfen
$ make install 
$ checkinstall \
    --default \
    --install=no \
    --requires="libtdb1,libcrack2"
# Benutze cp oder scp, um das erzeugte Paket zu sichern
$ cp /netatalk-3.1.10/netatalk_3.1.10-1_amd64.deb /target
$ exit

Nach dem Ende löscht sich der Container dank –rm selbst und wir haben mit netatalk_3.1.10-1_amd64.deb ein schönes Debian-Paket, welches auch sauber wieder entfernt werden kann.

Docker.io in der Praxis – OpenVPN Server

Nach dem ersten Artikel zu Docker nun ein weiterer Post zum Thema.

Diesmal direkt mit dem Erstellen eines eigenen Images. Es geht um OpenVPN, ein open-source VPN, welches eine Vielzahl von Plattformen (Windows, OS X, Linux, Android und iOS) unterstützt.

Veröffentlicht habe ich das ganze unter github.Die zentrale Datei ist die Datei Dockerfile, welche den Build des Images steuert.

FROM debian

MAINTAINER Joachim Lusiardi

RUN apt-get update; \
    apt-get -y install openvpn ;

ADD start.sh /start.sh

RUN chmod +x /start.sh

VOLUME ["/etc/openvpn"]
VOLUME ["/var/log/openvpn"]

EXPOSE 1194/udp
EXPOSE 1194/tcp

ENTRYPOINT /start.sh

FROM legt ein Basis-Image fest, hier die neuste Version von Debian.
MAINTAINER gibt den Betreuer des Images an.
RUN führt Befehle innerhalb des Images aus. Hier das Updaten der Packet-Quellen, Installieren von OpenVPN und dem Ausführbarmachen des start-Skripts.
ADD fügt eine Datei dem Image hinzu.
VOLUME gibt an, das ein Verzeichnis des Images beim Ausführen auf ein Verzeichnis des Hosts gelegt werden kann.
EXPOSE definiert welche Ports von ausserhalb des Images erreichbar sein sollen. Diese sind nicht automatisch beim Starten des Images auf eine öffentliche IP gebunden.
ENTRYPOINT gibt an, welches Kommando ausgeführt werden soll.

Weitere Detail auf github und auch unter https://registry.hub.docker.com/u/shing19m/docker-openvpn-server/.

Docker.io in der Praxis – Installation

Nachdem Docker immer mehr gehyped wird (LinuxMagazin 09/14, c’t 17/2014), soll das ganze hier mal für einen Root-Server inklusive folgender Dienste getestet:

  • WordPress (inklusive MySQL Datenbank)
  • OpenVPN Server
  • getrennter SSH Server für IRC mit irssi in screen oder tmux

Zusätzlich soll als Option das schnelle und einfache Bereitstellen von weiteren Diensten auf Basis von Technologien wie Node.js und NoSQL Datenbanken möglich sein.

Installation von Docker auf dem Root-Servers

Zunächst muss Docker installiert werden. Das soll nun hier besprochen werden.

Basisinstallation

Der Hoster bietet hier Debian-76-wheezy-64-minimal an.

Nach der Installation muss die Liste der Paket-Quellen angepasst werden (Debian Testing wird benötigt). Dabei hilft der Debian Sources List Generator:

deb http://ftp.debian.org/debian testing main contrib 
deb-src http://ftp.debian.org/debian testing main contrib 

deb http://ftp.debian.org/debian/ jessie-updates main contrib 
deb-src http://ftp.debian.org/debian/ jessie-updates main contrib 

deb http://security.debian.org/ jessie/updates main contrib 
deb-src http://security.debian.org/ jessie/updates main contrib

Anschließend mit apt-get update und apt-get dist-upgrade auf die Debian-Version jessie (bzw. testing) updaten.

Nun für den richtigen Kernel sorgen:

echo "deb http://ftp.us.debian.org/debian wheezy-backports main" > \
    /etc/apt/sources.list.d/wheezy-backports.list
apt-get update
apt-get -t wheezy-backports install linux-image-amd64

Docker installieren

Für Docker gibt es ein Ubuntu Repository, welches auch für Debian verwendet werden kann:

apt-key adv --keyserver keyserver.ubuntu.com \
    --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
echo "deb http://get.docker.io/ubuntu docker main" > \
    /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install lxc-docker

Die beiden letzen Schritte nach dieser Anleitung: http://doduck.com/docker-install-on-debian-7/

IPv6 für die Container

IPv6 ermöglicht es, für jeden Container eine eigene IP zu reservieren.

Leider wird IPv6 von Docker zum aktuellen Stand (23. August 2014, Docker Version  1.2.0) noch nicht unterstützt, kann aber über lxc direkt an den Container durchgereicht werden. Wenn IPv6 nicht verwendet werden soll, so kann dieser Abschnitt übersprungen werden.

Für IPv6 muss in der Datei /etc/sysctl.conf folgende Zeile eingefügt oder wieder eingkommentiert werden:

net.ipv6.conf.all.forwarding=1

Und der Docker-Daemon muss auf den lxc Treiber umgestellt werden. Dazu unter Debian in der Datei /etc/default/docker folgendes eintragen:

DOCKER_OPTS="--exec-driver=lxc"

Zusätzlich muss das lxc Paket installiert werden:

apt-get install lxc

Nun muss der von Docker angelegten Bridge noch eine  IPv6 IP hinzufügen:

ip -6 addr add aaaa:bbbb:cccc:dddd::1:1/112 dev docker0

Dabei muss natürlich aaaa:bbbb:cccc:dddd durch einen entsprechenden Teil des eigenen IPv6 Subnetzes ersetzt werden.

Um das Ganze zu Vereinfachen, bietet es sich an, Container dann mit folgendem Script zu starten:

#!/bin/bash

NW_IP=$1
shift
NW_GW=aaaa:bbbb:cccc:dddd::1:1
#shift
REST=$@

docker run \
    --lxc-conf="lxc.network.flags=up" \
    --lxc-conf="lxc.network.ipv6=$NW_IP" \
    --lxc-conf="lxc.network.ipv6.gateway=$NW_GW" \
    $REST

Beim Aufrufen muss die IPv6 Adresse inklusive Subnetzmaske angegeben werden, also aaaa:bbbb:cccc:dddd::1:2/112.

Résumé

Nun ist es möglich mit Docker Container zu verwenden und sogar IPv6 direkt in die Container zu leiten.