248 lines
13 KiB
YAML
248 lines
13 KiB
YAML
services:
|
|||
|
|
# umbrelOS genereert deze service zelf; wij vullen alleen in waar hij heen moet
|
||
|
|
# wijzen. De hostnaam heeft de vorm <app-id>_<compose-service>_1.
|
||
|
|
app_proxy:
|
||
|
|
environment:
|
||
|
|
APP_HOST: whatsnext-electrum-gate_server_1
|
||
|
|
APP_PORT: 80
|
||
|
|
|
||
|
|
# De agent. Hij schrijft status.json, leest de certificaten, bevraagt de
|
||
|
|
# Electrum-server en neemt de certificaatkeuze aan.
|
||
|
|
#
|
||
|
|
# Waarom een tweede container en niet een shell-lus in de server hieronder:
|
||
|
|
# dit werk is inmiddels een programma. Een certificaatdatum uitlezen, een
|
||
|
|
# JSON-RPC-verzoek doen, een geschiedenis bijhouden en een keuze valideren zijn
|
||
|
|
# geen dingen die je met openssl en nc aan elkaar knoopt zonder dat het stil
|
||
|
|
# verkeerde antwoorden gaat geven. Bijkomend voordeel: de app hangt niet meer
|
||
|
|
# af van de vraag of die twee gereedschappen in de nginx-image zitten.
|
||
|
|
agent:
|
||
|
|
# TODO (fase 4 van het plan Appstore): pinnen op de multi-arch index-digest,
|
||
|
|
# te bepalen met `docker buildx imagetools inspect python:3-alpine` op de
|
||
|
|
# Umbrel. Geldt voor beide images in dit bestand.
|
||
|
|
image: python:3-alpine
|
||
|
|
restart: on-failure
|
||
|
|
environment:
|
||
|
|
# Het adres van de Electrum-server die de gebruiker in umbrelOS gekozen
|
||
|
|
# heeft. umbrelOS vult dit in op grond van de afhankelijkheid hieronder,
|
||
|
|
# dus dit werkt met Electrs, Fulcrum en ElectrumX.
|
||
|
|
GATE_ELECTRUM_HOST: ${APP_ELECTRS_NODE_IP}
|
||
|
|
GATE_ELECTRUM_PORT: ${APP_ELECTRS_NODE_PORT}
|
||
|
|
GATE_TLS_PORT: "50022"
|
||
|
|
GATE_INTERVAL: "60"
|
||
|
|
GATE_API_PORT: "8000"
|
||
|
|
GATE_STATE_DIR: /var/lib/gate
|
||
|
|
# Bron-naam=map. Een bron die niet gemount is, bestaat niet en wordt
|
||
|
|
# overgeslagen; dat is de normale toestand voor een reverse proxy die de
|
||
|
|
# gebruiker niet draait.
|
||
|
|
GATE_CERT_SOURCES: "Zoraxy=/certs/zoraxy,Own folder=/certs/own"
|
||
|
|
# De enige beschrijfbare bron, en dus de map waarin een upload van de
|
||
|
|
# pagina landt. Expliciet en niet afgeleid uit de lijst hierboven: welke
|
||
|
|
# bron beschrijfbaar is, hoort te staan naast de mount die dat toestaat.
|
||
|
|
# Leeg zetten schakelt uploaden uit.
|
||
|
|
GATE_UPLOAD_DIR: /certs/own
|
||
|
|
PYTHONUNBUFFERED: "1"
|
||
|
|
volumes:
|
||
|
|
# Door umbrelOS ingevuld uit agent.py.template bij het starten. Er staat
|
||
|
|
# geen accolade-variabele in dat bestand, dus de invulling laat de
|
||
|
|
# Python-code ongemoeid; de agent leest zijn instellingen uit de omgeving
|
||
|
|
# hierboven. De test in tests/ controleert die aanname.
|
||
|
|
- ${APP_DATA_DIR}/agent.py:/app/agent.py:ro
|
||
|
|
# De gedeelde toestand: status.json, cert.conf, de herlaadvlag en de
|
||
|
|
# gekozen certificaat-id. Beide containers zitten hierin.
|
||
|
|
#
|
||
|
|
# Onder data/ en niet naast de templates, want dat is wat andere apps doen:
|
||
|
|
# electrs mount ${APP_DATA_DIR}/data/electrs, mempool ${APP_DATA_DIR}/data.
|
||
|
|
# Verplaatst op 20-08-2026 met het oog op publicatie in de officiele store.
|
||
|
|
- ${APP_DATA_DIR}/data/runtime:/var/lib/gate
|
||
|
|
# De certificaatbronnen. Zoraxy alleen lezen: dat zijn de certificaten van
|
||
|
|
# een andere app en die raakt deze app niet aan.
|
||
|
|
- ${UMBREL_ROOT}/app-data/zoraxy/data/config/conf/certs:/certs/zoraxy:ro
|
||
|
|
# De eigen map is de enige die beschrijfbaar is, want hier landt een upload
|
||
|
|
# van de pagina. Alleen de agent; de nginx-container houdt hem alleen-lezen,
|
||
|
|
# want die hoeft er nooit iets neer te zetten.
|
||
|
|
- ${APP_DATA_DIR}/data/certs:/certs/own
|
||
|
|
#
|
||
|
|
# Nginx Proxy Manager hoort hier als derde bron bij en staat er bewust nog
|
||
|
|
# niet in. Het pad hieronder is een gok en niet geverifieerd, en een
|
||
|
|
# bind-mount naar een pad dat niet bestaat laat Docker het aanmaken. Dat zou
|
||
|
|
# een lege maphierarchie neerzetten in de app-data van een app die
|
||
|
|
# misschien niet eens geinstalleerd is, en die rommel blijft daar staan.
|
||
|
|
# Eerst op de Umbrel controleren waar NPM zijn certificaten neerzet, dan
|
||
|
|
# deze regel aanzetten en de naam toevoegen aan GATE_CERT_SOURCES.
|
||
|
|
#
|
||
|
|
# - ${UMBREL_ROOT}/app-data/nginx-proxy-manager/data/letsencrypt/live:/certs/npm:ro
|
||
|
|
command:
|
||
|
|
- python
|
||
|
|
- /app/agent.py
|
||
|
|
|
||
|
|
server:
|
||
|
|
image: nginx:alpine
|
||
|
|
restart: on-failure
|
||
|
|
# De agent moet er zijn voordat nginx start, want de proxy_pass naar
|
||
|
|
# http://agent:8000 wordt bij het starten opgelost en een onbekende naam
|
||
|
|
# laat nginx afbreken. Op de cert.conf van de agent wordt niet gewacht: de
|
||
|
|
# pagina komt hoe dan ook omhoog, zie het command-blok hieronder.
|
||
|
|
depends_on:
|
||
|
|
- agent
|
||
|
|
ports:
|
||
|
|
# De TLS-poort voor Electrum-wallets. Dit is de enige poort die deze app
|
||
|
|
# zelf publiceert; de web-UI loopt via app_proxy.
|
||
|
|
#
|
||
|
|
# 50022 en niet de conventionele 50002, omdat Fulcrum die op de host
|
||
|
|
# bezet: met Fulcrum als backend zou de container niet starten, en dat is
|
||
|
|
# precies het omschakelen dat deze app moet ondersteunen. De poort naar
|
||
|
|
# buiten is toch al een andere, want die staat in de router doorgestuurd.
|
||
|
|
# Beslist 19-08-2026, open punt 1 van het plan Configuratie.
|
||
|
|
- "50022:50022"
|
||
|
|
volumes:
|
||
|
|
# Beide door umbrelOS ingevuld uit een .template bij het starten. De
|
||
|
|
# pagina wordt als los bestand gemount en niet als map: de bron staat in
|
||
|
|
# de app-root, want alleen daar wordt hij bij een update ververst.
|
||
|
|
- ${APP_DATA_DIR}/nginx.conf:/etc/nginx/nginx.conf:ro
|
||
|
|
- ${APP_DATA_DIR}/index.html:/usr/share/nginx/html/index.html:ro
|
||
|
|
# Het app-icoon, voor de kop van de pagina en het tabblad. Geen template,
|
||
|
|
# dus dit bestand komt alleen bij een installatie mee en niet bij een
|
||
|
|
# update; voor een plaatje dat vrijwel nooit wijzigt is dat goed genoeg.
|
||
|
|
# De pagina valt terug op een ingebouwd merkje als de mount er niet is.
|
||
|
|
- ${APP_DATA_DIR}/icon.png:/usr/share/nginx/html/icon.png:ro
|
||
|
|
# Het stream-blok, dus de TLS-poort zelf. Het staat los van nginx.conf
|
||
|
|
# omdat nginx niet start met een 'listen ssl' zonder certificaat, en het
|
||
|
|
# command-blok hieronder zet het pas in /var/lib/gate/tls/ zodra de agent
|
||
|
|
# een certificaat gekozen heeft. Tot die tijd draait alleen de pagina.
|
||
|
|
- ${APP_DATA_DIR}/stream.conf:/etc/nginx/stream.conf:ro
|
||
|
|
# Dezelfde gedeelde toestand als de agent. nginx leest hier cert.conf en
|
||
|
|
# status.json, en schrijft de sessielog.
|
||
|
|
- ${APP_DATA_DIR}/data/runtime:/var/lib/gate
|
||
|
|
# De certificaten zelf moeten ook hier gemount zijn: de agent bepaalt
|
||
|
|
# welk pad in cert.conf komt, maar nginx moet het bestand kunnen openen.
|
||
|
|
- ${UMBREL_ROOT}/app-data/zoraxy/data/config/conf/certs:/certs/zoraxy:ro
|
||
|
|
- ${APP_DATA_DIR}/data/certs:/certs/own:ro
|
||
|
|
command:
|
||
|
|
- /bin/sh
|
||
|
|
- -c
|
||
|
|
- |
|
||
|
|
set -eu
|
||
|
|
mkdir -p /var/lib/gate
|
||
|
|
|
||
|
|
# Het log_format van de stream-sessies wordt hier geschreven en niet in
|
||
|
|
# nginx.conf.template. Een log_format bestaat uit nginx-variabelen met
|
||
|
|
# een dollarteken, en de template-invulling van umbreld zou die
|
||
|
|
# stilzwijgend leegmaken. In dit blok is een dollarteken als $$ te
|
||
|
|
# ontsnappen, dus hier kan het wel.
|
||
|
|
#
|
||
|
|
# Let op wat er NIET in staat: geen client-adres en geen bronpoort. Dat
|
||
|
|
# is precies het soort gegeven dat deze app van het netwerk af houdt, en
|
||
|
|
# om te zien dat het werkt is het niet nodig.
|
||
|
|
cat > /var/lib/gate/stream-log.conf <<'CONF'
|
||
|
|
log_format gate '$$time_iso8601 $$status $$bytes_received $$bytes_sent $$session_time';
|
||
|
|
access_log /var/lib/gate/stream.log gate;
|
||
|
|
CONF
|
||
|
|
|
||
|
|
# De TLS-poort aan of uit zetten, naar de toestand van cert.conf.
|
||
|
|
#
|
||
|
|
# Hier zat tot 0.0.3 een lus die wachtte tot de agent een certificaat
|
||
|
|
# gekozen had, en dat was fout: nginx startte dan niet, dus de pagina
|
||
|
|
# kwam niet omhoog, en de pagina is juist waar je dat certificaat kiest.
|
||
|
|
# Zonder Zoraxy, of met twee kandidaten, hing de app daarmee vast op een
|
||
|
|
# keuze die nergens te maken was.
|
||
|
|
#
|
||
|
|
# Nu start nginx altijd. Het stream-blok komt erbij zodra cert.conf er
|
||
|
|
# is, en verdwijnt weer als de agent zijn keuze intrekt. nginx.conf haalt
|
||
|
|
# deze map op met een jokerteken; die matcht dan niets en dat is geen
|
||
|
|
# fout.
|
||
|
|
mkdir -p /var/lib/gate/tls
|
||
|
|
#
|
||
|
|
# Deze functie eindigt altijd geslaagd, en dat is met opzet: het script
|
||
|
|
# draait onder 'set -e', dus een mislukte cp zou de aanroeper afbreken.
|
||
|
|
# In de lus hieronder is die aanroeper de wachtlus, en die mag om geen
|
||
|
|
# enkele reden stoppen.
|
||
|
|
sync_tls() {
|
||
|
|
if [ -f /var/lib/gate/cert.conf ]; then
|
||
|
|
# Kopieren en niet linken: nginx opent dit pad als de gebruiker
|
||
|
|
# nginx, en een symlink naar een read-only mount is nodeloos fragiel.
|
||
|
|
if cp /etc/nginx/stream.conf /var/lib/gate/tls/stream.conf; then
|
||
|
|
echo "Certificaat aanwezig, TLS luistert op 50022."
|
||
|
|
else
|
||
|
|
echo "stream.conf kon niet worden weggezet; TLS blijft uit."
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
rm -f /var/lib/gate/tls/stream.conf
|
||
|
|
echo "Nog geen certificaat gekozen; TLS staat uit, de pagina werkt."
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
sync_tls
|
||
|
|
|
||
|
|
# De verbindingsteller. nginx stream schrijft zijn logregel pas bij het
|
||
|
|
# sluiten van een sessie, en een wallet houdt zijn verbinding uren open;
|
||
|
|
# zonder deze teller lijkt een actieve wallet dus afwezig. Dat was de
|
||
|
|
# vraag van de gebruiker op 20-08-2026, en het open punt uit §4e van het
|
||
|
|
# plan Webinterface: hoe die toestand binnen de container af te lezen is.
|
||
|
|
#
|
||
|
|
# Antwoord: /proc/net/tcp. Dat geldt per netwerk-namespace, dus dit ziet
|
||
|
|
# alleen de sockets van deze container, en daarom staat deze teller hier
|
||
|
|
# en niet in de agent: die zit in een andere namespace en kan er niet bij.
|
||
|
|
# Kolom 2 is het lokale adres met de poort in hex, kolom 4 de toestand.
|
||
|
|
# C366 is 50022 en 01 is ESTABLISHED, dus de luisterende socket (0A) en de
|
||
|
|
# verbindingen naar de backend en naar de pagina vallen er buiten.
|
||
|
|
#
|
||
|
|
# Geteld wordt alleen het aantal. Geen adres en geen bronpoort, net als in
|
||
|
|
# het log_format hierboven.
|
||
|
|
count_sessions() {
|
||
|
|
cat /proc/net/tcp /proc/net/tcp6 2>/dev/null \
|
||
|
|
| awk '$$4 == "01" && $$2 ~ /:C366$$/ { n++ } END { print n+0 }'
|
||
|
|
}
|
||
|
|
|
||
|
|
# Meteen een 0 neerzetten, zodat de pagina "geen verbindingen" kan tonen
|
||
|
|
# in plaats van "onbekend" in het minuutje voor de eerste ronde.
|
||
|
|
count_sessions > /var/lib/gate/sessions
|
||
|
|
|
||
|
|
# De herlaadlus. De agent kan nginx niet zelf herladen: dat zou de
|
||
|
|
# Docker-socket vragen en die is er bewust uit. In plaats daarvan zet hij
|
||
|
|
# een vlagbestand neer en herlaadt nginx zichzelf. Een reload leest het
|
||
|
|
# nieuwe certificaat in zonder bestaande verbindingen te verbreken.
|
||
|
|
#
|
||
|
|
# Deze lus draait op de achtergrond en nginx wordt hieronder met exec het
|
||
|
|
# hoofdproces. Andersom gaat twee keer mis: de shell blijft dan PID 1 en
|
||
|
|
# geeft signalen niet door, en als nginx omvalt eindigt het script met
|
||
|
|
# exitcode 0, waardoor Docker een geslaagde afsluiting ziet en
|
||
|
|
# 'restart: on-failure' niet ingrijpt.
|
||
|
|
(
|
||
|
|
while true; do
|
||
|
|
sleep 10
|
||
|
|
|
||
|
|
# Eerst schrijven, dan mv: de agent leest dit bestand op zijn eigen
|
||
|
|
# moment en mag geen half bestand zien.
|
||
|
|
count_sessions > /var/lib/gate/sessions.tmp
|
||
|
|
mv /var/lib/gate/sessions.tmp /var/lib/gate/sessions
|
||
|
|
|
||
|
|
if [ -f /var/lib/gate/reload ]; then
|
||
|
|
echo "Certificaat gewijzigd, nginx herladen ..."
|
||
|
|
# Eerst wegzetten, dan herladen. Andersom zou een herlading die
|
||
|
|
# mislukt de vlag toch opruimen, en dan probeert hij het nooit meer.
|
||
|
|
mv /var/lib/gate/reload /var/lib/gate/reload.done
|
||
|
|
# Voor de herlading, niet erna: een reload leest de configuratie
|
||
|
|
# opnieuw, dus het stream-blok moet er dan al liggen.
|
||
|
|
sync_tls
|
||
|
|
# De herlading mag niet met 'set -e' meegaan. Een certificaat dat
|
||
|
|
# nginx niet aanneemt laat de reload falen, en dan zou deze lus
|
||
|
|
# verdwijnen: geen enkele latere wijziging wordt dan nog opgepikt,
|
||
|
|
# zonder dat er iets te zien is.
|
||
|
|
if ! nginx -s reload; then
|
||
|
|
echo "Herladen mislukt; nginx houdt de vorige configuratie."
|
||
|
|
fi
|
||
|
|
elif [ -f /var/lib/gate/cert.conf ] && [ ! -f /var/lib/gate/tls/stream.conf ]; then
|
||
|
|
# De vangnetregel. De agent zet de vlag bij elke wijziging, dus
|
||
|
|
# normaal komt hier niets langs; wel als de vlag verloren gaat of
|
||
|
|
# als cert.conf van een vorige installatie al klaarlag. Zonder deze
|
||
|
|
# tak zou TLS dan uit blijven staan tot de volgende wijziging.
|
||
|
|
echo "Certificaat gevonden zonder herlaadvlag, TLS aanzetten ..."
|
||
|
|
sync_tls
|
||
|
|
if ! nginx -s reload; then
|
||
|
|
echo "Herladen mislukt; nginx houdt de vorige configuratie."
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
) &
|
||
|
|
|
||
|
|
exec nginx -g 'daemon off;'
|