Tobserver/modules/mempool.nix
Tobias Ostner 41cb36d15f Setup sops
2026-04-11 18:52:36 +02:00

211 lines
5.8 KiB
Nix

{ config, pkgs, lib, ...}:
{
sops.secrets."mempool/mariadb-password" = {
owner = "root";
group = "root";
mode = "0400";
};
sops.secrets."mempool/mariadb-root-password" = {
owner = "root";
group = "root";
mode = "0400";
};
sops.templates."mempool-db.env" = {
owner = "root";
group = "root";
mode = "0400";
content = ''
MARIADB_DATABASE=mempool
MARIADB_USER=mempool
MARIADB_PASSWORD=${config.sops.placeholder."mempool/mariadb-password"}
MARIADB_ROOT_PASSWORD=${config.sops.placeholder."mempool/mariadb-root-password"}
'';
};
sops.templates."mempool-backend.env" = {
owner = "root";
group = "root";
mode = "0400";
content = ''
MEMPOOL_NETWORK=mainnet
MEMPOOL_BACKEND=electrum
MEMPOOL_HTTP_PORT=8999
ELECTRUM_HOST=host.containers.internal
ELECTRUM_PORT=50001
ELECTRUM_TLS_ENABLED=false
CORE_RPC_HOST=host.containers.internal
CORE_RPC_PORT=8332
CORE_RPC_COOKIE=true
CORE_RPC_COOKIE_PATH=/bitcoin/.cookie
DATABASE_ENABLED=true
DATABASE_HOST=mempool-db
DATABASE_PORT=3306
DATABASE_DATABASE=mempool
DATABASE_USERNAME=mempool
DATABASE_PASSWORD=${config.sops.placeholder."mempool/mariadb-password"}
STATISTICS_ENABLED=true
'';
};
virtualisation.oci-containers = {
backend = "podman";
containers = {
mempool-db = {
image = "mariadb:11";
autoStart = true;
environmentFiles = [
config.sops.templates."mempool-db.env".path
];
volumes = [
"/srv/mempool/mysql:/var/lib/mysql"
];
};
mempool-backend = {
image = "mempool/backend:latest";
autoStart = true;
dependsOn = [ "mempool-db" ];
ports = [ "127.0.0.1:8999:8999" ];
extraOptions = [
"--add-host=host.containers.internal:host-gateway"
];
environmentFiles = [
config.sops.templates."mempool-backend.env".path
];
volumes = [
"/run/mempool/bitcoind.cookie:/bitcoin/.cookie:ro"
];
};
mempool-frontend = {
image = "mempool/frontend:latest";
autoStart = true;
ports = [ "127.0.0.1:8998:8080" ];
environment = {
NGINX_HOSTNAME = "mempool.tobiasostner.de";
NGINX_PORT = "443";
NGINX_PROTOCOL = "https";
};
};
};
};
systemd.tmpfiles.rules = lib.mkAfter [
"d /run/mempool 0755 root root - -"
];
# Copy cookie once
systemd.services.mempool-cookie = {
description = "Expose bitcoind cookie for mempool-backend";
after = [ "bitcoind-mainnet.service" ];
wants = [ "bitcoind-mainnet.service" ];
serviceConfig = {
Type = "oneshot";
ExecStart = pkgs.writeShellScript "mempool-cookie" ''
set -euo pipefail
for i in $(seq 1 60); do
if [ -f /var/lib/bitcoind/.cookie ]; then
install -m 0644 -o root -g root /var/lib/bitcoind/.cookie /run/mempool/bitcoind.cookie
exit 0
fi
sleep 1
done
echo "Cookie not found after 60s" >&2
exit 1
'';
};
};
# Re-run the cookie copy whenever bitcoind rotates the cookie
systemd.paths.mempool-cookie = {
description = "Watch bitcoind cookie and refresh mempool copy";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "/var/lib/bitcoind/.cookie";
};
};
# When the path triggers, it starts the service
systemd.services.mempool-cookie.wantedBy = lib.mkForce [ ];
systemd.paths.mempool-cookie.unitConfig = {
Unit = "mempool-cookie.service";
};
systemd.services.podman-mempool-backend = {
after = [ "mempool-cookie.service" ];
wants = [ "mempool-cookie.service" ];
};
services.nginx = {
appendHttpConfig = ''
limit_req_zone $binary_remote_addr zone=mempool_api:10m rate=60r/m;
limit_conn_zone $binary_remote_addr zone=mempool_ws:10m;
'';
virtualHosts."mempool.tobiasostner.de" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8998";
extraConfig = ''
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 $scheme;
'';
};
locations."/api/" = {
proxyPass = "http://127.0.0.1:8999";
extraConfig = ''
limit_req zone=mempool_api burst=30 nodelay;
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 $scheme;
proxy_connect_timeout 2s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
'';
};
locations."/api/v1/ws" = {
proxyPass = "http://127.0.0.1:8999/api/v1/ws";
proxyWebsockets = true;
extraConfig = ''
limit_conn mempool_ws 3;
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 $scheme;
proxy_connect_timeout 2s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
'';
};
};
};
networking.firewall.interfaces."podman0".allowedTCPPorts = [ 8332 50001 ];
}