Die Corona-Warn-App - Das cwa-server Backend in einer Docker Umgebung

Da der Code der Corona-Warn-App nun komplett auf GitHub veröffentlicht wurde, ergibt sich nun für uns die Chance Möglichkeiten und Probleme in der Software-Entwicklung an einem sehr aktuellen Projekt live mitzuverfolgen. Mir ist bewusst, dass Tracking-Apps politisch und ethisch enormes Konfliktpotenzial bieten. Meiner Meinung nach wird der Nutzen derartiger Apps stark überschätzt und die entstehenden ethischen Probleme leider ignoriert. Erfolg oder Misserfolg werden sich wohl erst hinterher genau mit Fakten belegen lassen. Es gibt also einiges zu lernen! Im nachfolgenden Post möchte ich Euch zeigen wie Ihr das cwa-server Backend der App in einer Docker Umgebung aufsetzen könnt. Danach werden wir mit einem kleinen Python-Script einen Submission-Key an das Backend schicken und das Ergebnis in der Datenbank überprüfen.

CWA – Das cwa-server Backend in einer Docker Umgebung

[Durch Click auf die Play-Schaltfläche erfolgt ein Verbindungsaufbau zu YouTube/Google und es werden Daten an an diese externen Dienste übertragen]

Um Euch lokal eine passende Entwicklungs-Umgebung aufzubauen empfehle ich folgende Werkzeuge:

Sobald Ihr die Tools installiert habt, könnt Ihr den Code auch schon von GitHub clonen und die Umgebung mit Docker-Compose aufbauen lassen:

mkdir -p /c/cwa
cd /c/cwa
git clone https://github.com/corona-warn-app/cwa-server.git
cd cwa-server
docker-compose build
docker-compose up

Sobald die Docker Container laufen könnt Ihr z.B. mit einem eigenen kleinen Python-Client auf das Backend zugreifen. Der HTTP Body des POST-Request besteht in diesem Fall leider nicht aus klassischem Text oder Daten im JSON-Format. Für die API wird Protobuf von Google genutzt, dabei handelt es sich um ein schlankes binär Format. Für unseren Python-Client müssen wir zunächst mit den .proto-Files aus dem Source Code und dem Protobuf-Compiler von Google passende Python-Klassen bauen. Bei dieser Gelegenheit installieren wir auch gleich noch das Protobuf-Binding für Python. Evtl. müsst Ihr noch die Pfade an Eure Umgebung anpassen:

pip install protobuf
mkdir -p /c/cwa/cwa-client
cd /c/cwa/cwa-client
/c/tools/protoc/bin/protoc.exe --python_out=/c/cwa/cwa-client \
 --proto_path=/c/cwa/cwa-server/common/protocols/src/main/proto \
 app/coronawarn/server/common/protocols/external/exposurenotification/temporary_exposure_key_export.proto \
 app/coronawarn/server/common/protocols/internal/submission_payload.proto

Damit müsstet Ihr dann folgenden Python-Client ausführen können:

import http.client
from datetime import datetime, timedelta
from app.coronawarn.server.common.protocols.internal.submission_payload_pb2 import SubmissionPayload

submission = SubmissionPayload()
key = submission.keys.add()
key.key_data = bytes("FranksDemoKey222", "utf-8")
key.rolling_start_interval_number = int(((datetime.today() - timedelta(days=14)).timestamp())/600)
key.rolling_period = 144
key.transmission_risk_level = 4

conn = http.client.HTTPConnection('localhost', 8000)
headers = {'Content-Type': 'application/x-protobuf',
           'cwa-fake': '0',
           'cwa-authorization': 'edc07f08-a1aa-11ea-bb37-0242ac130002'}
conn.request("POST", "/version/v1/diagnosis-keys", submission.SerializeToString(), headers)
response = conn.getresponse()
print(response.status, response.reason)

Um auf die Postgres Datenbank zugreifen zu können, haben uns die CWA-Entwickler pgAdmin als Web-Oberfläche in einem eigenen Container mitgeliefert. Ihr müsst dort noch die Datenbank-Verbindung einrichten. Danach könnt Ihr mit folgendem SQL Statement prüfen ob unser POST-Request zu einem Eintrag in der Datenbank geführt hat:

SELECT encode(key_data, 'escape')
     , rolling_period
	 , rolling_start_interval_number
	 , submission_timestamp
	 , transmission_risk_level
	FROM public.diagnosis_key
	WHERE key_data like 'FranksDemoKey%';

Unser YouTube Kanal - Einfach DevOps

Um Euch zukünftig einfacher Tipps und Tricks rund um das Thema DevOps geben können, haben wir einen eigenen YouTube Kanal aufgebaut.

Einfach DevOps - Intro Video

[Durch Click auf die Play-Schaltfläche erfolgt ein Verbindungsaufbau zu YouTube/Google und es werden Daten an an diese externen Dienste übertragen]

Datenschutz ist uns wichtig, daher versuchen wir die Videos möglichst datensparsam einzubinden. Unsere Webseite sollte auch weiterhin keine Cookies bei Euch setzen. Hierzu nutzen wir das WordPress Plugin WP YouTube Lyte.

IPv6 Konfiguration bei OVH VPS Instanzen

Manchmal ist er schwer für einfache Dinge eine saubere technische Lösung zu finden. Gutes Beispiel ist die IPv6 Konfiguration für eine VPS Instanz bei der Firma OVH. Nach meiner Erfahrung ist die Netzwerkkonfiguration, gerade bei Cloud-Angeboten, je nach Hosting Anbieter etwas komplizierter. Das Angebot von OVH basiert aber auf OpenStack und die Basis-Konfiguration innerhalb der VM erfolgt via cloud-init. D.h. egal ob Amazon AWS, Microsoft Azure oder eben OVH, unter Linux kommt immer cloud-init zum Einsatz. Aber im Fall von VPS-Instanzen bei OVH werden wohl zumindest für IPv6 keine Metadaten bereitgestellt.

Dies lässt sich auf der Kommandozeile über entsprechende HTTP-Requests testen:

[root@vps593928 ~]# curl http://169.254.169.254/2009-04-04/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
hostname
instance-action
instance-id
instance-type
local-hostname
local-ipv4
placement/
public-hostname
public-ipv4
public-keys/
reservation-id

Via DHCP ist es offenbar ebenfalls nicht möglich die IPv6-Einstellungen dynamisch zu beziehen. Bei IPv4 funktioniert dies, bei IPv6 aber wohl nicht. Nun könnte man die Netzwerk-Konfiguration via cloud-init abschalten und die entsprechenden Konfigurationsdateien manuell pflegen. Ich wollte aber möglichst nah an der Standard-Installation bleiben, deshalb liefere ich die fehlenden IPv6-Daten in der cloud-init Konfiguration nach.

Beim Hostnamen hatte ich ähnliche Schwierigkeiten. Die Web-GUI von OVH erlaubt zwar den Namen der VPS Instanz zu ändern, die cloud-init zur Verfügung gestellten Metadaten enthalten aber immer noch den ursprünglichen Instanz-Namen von OVH. Für den Hostnamen habe ich noch keine saubere Lösung gefunden. Hier hilft nur den Hostnamen einmalig auf den gewünschten Wert zu setzen und danach die dynamische Änderung des Hostname in cloud-init zu deaktivieren.

[root@vps593928 ~]# hostnamectl set-hostname s20.e1.fm-berger.de

Die cloud-init Konfiguration für IPv6 und Hostname erfolgt dann über neue Datei /etc/cloud/cloud.cfg.d/99-custom-networking.cfg mit folgendem Inhalt:

network:
  version: 1
  config:
  - type: physical
    name: eth0
    mac_address: fa:16:3e:07:47:dd
    subnets:
      - type: dhcp
      - type: static
        address: 2001:41d0:701:1100::ab3/128
        gateway: 2001:41d0:701:1100::1

preserve_hostname: true

Nach einem Reboot der VPS Instanz sollte die IPv6-Konfiguration richtig gesetzt sein und der Hostname dürfte nicht mehr ändern.