
Photo by Alex Machado on Unsplash
Nach berechtigter Frage von Adrienne Fichter zur Nutzung von Azure für den E-Collecting Pilot und den neusten Updates für die Generic Verifier und Generic Issuer Komponenten der Public Beta Trust Infrastucture habe ich mich entschieden, beide Themen im gleichen Schritt anzugehen und die Chance nutzen, die Architektur noch etwas anzupassen.
Meine Anforderungen:
Installation einer Docker Umgebung in einer Schweizer Cloud
Neue Generic Komponenten
Verbesserung bei der Virtual Host Konfiguration und Firewall
The Ethical Cloud — Infomaniak
Anstatt Microsoft Azure zu verwenden, habe ich mich auf die Cloud von Infomaniak entschieden. Nicht nur der Preis ist bei Infomaniak viel besser als bei Microsoft, auch das Setup und Management ist viel einfacher und schneller.
Disclaimer: Ich werde nicht von Infomaniak gesponsored
Für meinen E-Collecting Pilot habe ich mich für die kleinste Cloud Computing Einheit entschieden: VPS Lite
und diese in den nächsten Schritten entsprechend eingerichtet. Dabei habe ich mich für Debian Trixie, dem aktuellsten LTS Release.

VPS lite: https://www.infomaniak.com/de/hosting/vps-lite
Installation der Beta Cloud Komponenten
1. System aktualisieren und benötigte Pakete installieren
sudo apt update
sudo apt install ca-certificates curl2. Docker GPG-Schlüssel hinzufügen
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc3. Docker-Repository einrichten
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF4. Paketliste aktualisieren
sudo apt update5. Docker Engine installieren
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginInstallation von Git & clonen der neuen Repositories
Da die bisherigen Generic Komponenten nicht mehr aktuell sind, empfiehlt sich das Update auf die neue Version. Dazu habe ich mich entschieden, eine Neuinstallation durchzuführen. Doch dazu musste ich zuerst GIT installieren:
sudo apt-get install git-all .und anschliessend die neuen Repositories für meine 3 Komponenten laden:
Verifizieren der SWIYU E-ID
git clone https://github.com/swiyu-admin-ch/swiyu-verifier.git ./verifier-identity
Ausstellen der Quittung
git clone https://github.com/swiyu-admin-ch/swiyu-issuer.git ./issuer-receipt
Ausstellen des Stimmrechtsausweis
git clone https://github.com/swiyu-admin-ch/swiyu-issuer.git ./issuer-stimmrecht
Webserver Installation
Hier verwende ich den NGINX Server anstatt den eingebauten Apache Server. Dieser muss zudem zuerst deaktiviert werden.
NGINX Installieren:
sudo apt install nginx -yCORS Allow-List einrichten
damit ich nur von meinem Frontend auf die Services zugreifen kann konfiguriere ich eine “Allow List”:
sudo nano /etc/nginx/conf.d/cors_map.conf
# erlaubt alle Subdomains
map $http_origin $cors_origin {
default "";
# explizit erlaubte Origins
~^https://beta\.ecollecting\.ch$ $http_origin;
}Virtual Host: HTTP → HTTPS Weiterleitung konfigurieren
Als nächstes werden die Virutal Hosts eingerichtet, zunächst nur als HTTP:
sudo nano /etc/nginx/sites-available/verifier-identity
server {
listen 80;
server_name verifier-identity.ecollecting.ch;
# return 308 https://$host$request_uri;
# CORS-Header auch auf 80 ausliefern (wichtig für evtl. Preflights)
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
location / {
# Preflight auf 80 direkt beantworten, NICHT redirecten
if ($request_method = OPTIONS) { return 204; }
return 308 https://$host$request_uri;
}
}sudo nano /etc/nginx/sites-available/issuer-stimmrecht
server {
listen 80;
server_name issuer-stimmrecht.ecollecting.ch;
# return 308 https://$host$request_uri;
# CORS-Header auch auf 80 ausliefern (wichtig für evtl. Preflights)
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
location / {
# Preflight auf 80 direkt beantworten, NICHT redirecten
if ($request_method = OPTIONS) { return 204; }
return 308 https://$host$request_uri;
}
}sudo nano /etc/nginx/sites-available/issuer-receipt
server {
listen 80;
server_name issuer-receipt.ecollecting.ch;
# return 308 https://$host$request_uri;
# CORS-Header auch auf 80 ausliefern (wichtig für evtl. Preflights)
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
location / {
# Preflight auf 80 direkt beantworten, NICHT redirecten
if ($request_method = OPTIONS) { return 204; }
return 308 https://$host$request_uri;
}
}Zudem müssen noch die Sites verlinkt und der Dienst neu gestartet werden:
sudo ln -s /etc/nginx/sites-available/verifier-identity /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/issuer-receipt /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/issuer-stimmrecht /etc/nginx/sites-enabled/sudo nginx -t
sudo systemctl reload nginxBevor nun die SSL Zertifikate gelöst werden, müssen noch die DNS Einträge für die 3 Hosts auf meine IP Adresse der Beta Cloud eingerichtet werden:
issuer-stimmrecht.ecollecting.ch
issuer-receipt.ecollecting.ch
verifier-identity.ecollecting.ch
HTTPS aktivieren:
Jetzt kann HTTPS aktiviert werden, dazu muss aber noch certbot installiert werden.
sudo apt install certbot python3-certbot-nginx -yCerts löschen, falls vorhanden
sudo certbot certificates
sudo certbot delete - cert-name <certificate-name>Damit anschliessend auch die swiyu Wallet und beta.ecollecting.ch auf die Services zugreifen kann, muss in der Infomaniak Cloud die Firewall entsprechend konfiguriert werden. Bitte achte darauf, dass der SSH-Port nicht standardmässig für alle IP’s offen ist.
Ich nutze einen VPN, daher gebe ich jeweils die verwendete IP-Adresse frei, bevor ich mit SSH darauf verbinde.

SSL Zertifikate erstellen:
Hier nutze ich den freien Dienst von EFF: https://certbot.eff.org/
sudo certbot - nginx -d verifier-identity.ecollecting.ch
sudo certbot - nginx -d issuer-stimmrecht.ecollecting.ch
sudo certbot - nginx -d issuer-receipt.ecollecting.chBlock Management Access
Obwohl es nur eine Demo-Umgebung ist, möchte ich den Zugriff auf geschützte Serivceendpunkte unterbinden. Dazu sind folgende Einstellungen an den Virutal Hosts notwendig. In der Datei unten sind zudem auch noch die CORS & SSL Einstellungen ergänzt.
sudo nano /etc/nginx/sites-available/issuer-receipt
server {
server_name issuer-receipt.ecollecting.ch;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/verifier-identity.ecollecting.ch/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/verifier-identity.ecollecting.ch/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# >>> CORS-Header für ALLE Antworten (auch 4xx/5xx) <<<
add_header Access-Control-Allow-Origin $cors_origin always; # oder '*' wenn du keine Credentials brauchst
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
# >>> /management und alle Unterpfade
location ^~ /management {
# 1) Preflight IMMER durchlassen (204), egal von wem
# Browser-CORS lässt es später trotzdem nur für whitelisted Origins zu
if ($request_method = OPTIONS) {
return 204;
}
# 2) Für echte Requests (GET/POST/…) nur Whitelist-Origins zulassen
if ($cors_origin = "") {
return 403;
}
proxy_set_header Origin ""; # Backend muss sich nicht um CORS kümmern
proxy_pass http://localhost:8081; # /management/... bleibt als Pfad erhalten
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_set_header Origin ""; # <- Workaround
# Preflight direkt beantworten
if ($request_method = OPTIONS) { return 204; }
proxy_pass http://localhost:8081; # <-- YOUR port
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo nano /etc/nginx/sites-available/issuer-stimmrecht
server {
server_name issuer-stimmrecht.ecollecting.ch;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/verifier-identity.ecollecting.ch/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/verifier-identity.ecollecting.ch/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# >>> CORS-Header für ALLE Antworten (auch 4xx/5xx) <<<
add_header Access-Control-Allow-Origin $cors_origin always; # oder '*' wenn du keine Credentials brauchst
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
# >>> /management und alle Unterpfade
location ^~ /management {
# 1) Preflight IMMER durchlassen (204), egal von wem
# Browser-CORS lässt es später trotzdem nur für whitelisted Origins zu
if ($request_method = OPTIONS) {
return 204;
}
# 2) Für echte Requests (GET/POST/…) nur Whitelist-Origins zulassen
if ($cors_origin = "") {
return 403;
}
proxy_set_header Origin ""; # Backend muss sich nicht um CORS kümmern
proxy_pass http://localhost:8082; # /management/... bleibt als Pfad erhalten
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_set_header Origin ""; # <- Workaround
# Preflight direkt beantworten
if ($request_method = OPTIONS) { return 204; }
proxy_pass http://localhost:8082; # <-- YOUR port
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}sudo nano /etc/nginx/sites-available/verifier-identity
server {
server_name verifier-identity.ecollecting.ch;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/verifier-identity.ecollecting.ch/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/verifier-identity.ecollecting.ch/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# >>> CORS-Header für ALLE Antworten (auch 4xx/5xx) <<<
add_header Access-Control-Allow-Origin $cors_origin always; # oder '*' wenn du keine Credentials brauchst
add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With, Accept" always;
add_header Access-Control-Max-Age 86400 always;
add_header Vary "Origin" always;
# >>> /management und alle Unterpfade
location ^~ /management {
# 1) Preflight IMMER durchlassen (204), egal von wem
# Browser-CORS lässt es später trotzdem nur für whitelisted Origins zu
if ($request_method = OPTIONS) {
return 204;
}
# 2) Für echte Requests (GET/POST/…) nur Whitelist-Origins zulassen
if ($cors_origin = "") {
return 403;
}
proxy_set_header Origin ""; # Backend muss sich nicht um CORS kümmern
proxy_pass http://localhost:8080; # /management/... bleibt als Pfad erhalten
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_set_header Origin ""; # <- Workaround
# Preflight direkt beantworten
if ($request_method = OPTIONS) { return 204; }
proxy_pass http://localhost:8080; # <-- YOUR port
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}System-Service einreichten
Damit die Beta Cloud auch nach einem Neustart wieder einwandfrei funktioniert, bspw. wenn ein Update eingespielt werden muss, wurde folgender Service eingerichtet:
Docker script: sudo nano /etc/systemd/system/docker-up.service
[Unit]
Description=Start multiple docker compose projects at boot
After=network-online.target docker.service
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/home/debian/docker-up.sh
RemainAfterExit=yes
User=debian
WorkingDirectory=/home/debian
[Install]
WantedBy=multi-user.targetsudo nano docker-up.sh
#!/bin/bash
set -e # Falls ein Befehl fehlschlägt, Skript beenden
# SWIYU
cd /home/debian/verifier-identity
sudo docker compose up -d
# Quittung
cd /home/debian/issuer-receipt
sudo docker compose up -d
# Stimmrecht Gemeinde
cd /home/debian/issuer-stimmrecht
sudo docker compose up -dSo kann der Dienst anschliessend gestartet werden:
sudo systemctl enable docker-up.serviceSwagger UI
Die Services können nun unter folgenden URLs getestet werden:
https://issuer-stimmrecht.ecollecting.ch/swagger-ui/index.html#
https://issuer-receipt.ecollecting.ch/swagger-ui/index.html#
https://verifier-identity.ecollecting.ch/swagger-ui/index.html#
Changelog
Für eine einfache Verwendung der Generic Issuer / Verifier Komponenten hat sich nicht viel geändert.
Bei Issuer und Verifier sollten die neuen Ports für die DB und die Services beachtet werden. Vorallem, da mit den neuen Komponenten die Services für Management und den Wallet Service unter dem gleichen Endpoint laufen:
Verifier:
Statt zwei Container (…-management und …-oid4vp) gibt es jetzt nur einen: verifier-service.
Neues Image: ghcr.io/swiyu-admin-ch/swiyu-verifier:latest ersetzt beide alten Images.
Port: Alt: 8082 (Management) + 8083 (OID4VP). Neu: nur noch 8083.
Neue Metadata Config für die VP-Formate.
Issuer:
Aus 2 Containern (issuer-agent-management + issuer-agent-oid4vci) wird 1 Container: swiyu-issuer-service.
Neues kombiniertes Image: ghcr.io/swiyu-admin-ch/swiyu-issuer:latest.
Port: Alt: 8080 (Management) + 8081 (OID4VCI) / Neu: nur 8080 für alles.
Token-Endpoint-Pfad geändert:
Alt: /api/v1/token
Neu: /oid4vci/api/token → d. h. dein Client muss den neuen Pfad verwenden.
Credential Endpoint Analoger Pfadwechsel wie beim Token-Endpoint:
Alt: /api/v1/credential
Neu: /oid4vci/api/credential
cryptographic_binding_methods_supported: → Der Eintrag wurde von “did:jwk” auf “jwk” geändert.
Das kann Auswirkungen auf Wallets/Clients haben, die auf den exakten Binding-Method-Namen achten.
