Files
nixos/services/firefly-iii-data-importer.nix
Simon Gardling c1f1959aa1
All checks were successful
Build and Deploy / mreow (push) Successful in 1m14s
Build and Deploy / yarn (push) Successful in 56s
Build and Deploy / muffin (push) Successful in 1m13s
firefly-iii-data-importer: fix allowlist
2026-05-05 02:21:31 -04:00

133 lines
5.0 KiB
Nix

{
config,
lib,
service_configs,
...
}:
let
fidi = service_configs.firefly_iii_data_importer;
in
{
imports = [
# Same chicanery as services/firefly-iii.nix: the upstream module's
# actual systemd units are firefly-iii-data-importer-setup.service and
# phpfpm-firefly-iii-data-importer.service. Hook the zfs mount into the
# `-setup` unit; the upstream `requiredBy` chain pulls phpfpm forward.
(lib.serviceMountWithZpool "firefly-iii-data-importer-setup" service_configs.zpool_ssds [
fidi.dataDir
])
];
services.firefly-iii-data-importer = {
enable = true;
dataDir = fidi.dataDir;
# Same trick as firefly-iii: run as group caddy so caddy can read the
# php-fpm unix socket without us having to override `listen.group`.
group = "caddy";
virtualHost = fidi.domain;
settings = {
APP_ENV = "production";
APP_KEY_FILE = config.age.secrets.firefly-iii-data-importer-app-key.path;
LOG_CHANNEL = "syslog";
LOG_LEVEL = "info";
# Importer → Firefly III. The /etc/hosts override below pins this hostname
# to 127.0.0.1 on muffin so the importer's outbound HTTPS lands on the
# local Caddy without hairpinning through the WAN gateway. The TLS cert
# is the same wildcard Caddy serves externally, so verification works.
FIREFLY_III_URL = "https://${service_configs.firefly_iii.domain}";
FIREFLY_III_ACCESS_TOKEN_FILE = config.age.secrets.firefly-iii-fidi-token.path;
TRUSTED_PROXIES = "127.0.0.1,::1";
# SimpleFIN polls the last 90d every run; without this, every daily run
# logs hundreds of "duplicate transaction" warnings as Firefly's server-
# side dedup catches them. Firefly still rejects the duplicates; we just
# don't see the noise.
IGNORE_DUPLICATE_ERRORS = true;
# `artisan importer:import <path>` rejects any config path whose parent
# directory is not in this allow-list. Same dir we hand to the CLI below.
IMPORT_DIR_ALLOWLIST = "${fidi.dataDir}/storage/configurations";
# CLI-driven imports only. Leave the /autoimport HTTP endpoint disabled
# (default) — the systemd timer below uses `artisan importer:import`
# against a static config file, which avoids exposing another web-
# reachable surface that needs its own shared secret.
CAN_POST_AUTOIMPORT = false;
CAN_POST_FILES = false;
};
};
# Pin the firefly-iii hostname to loopback on muffin so the importer's
# outbound API calls hit local Caddy directly. No effect on any other host.
networking.extraHosts = "127.0.0.1 ${service_configs.firefly_iii.domain}";
services.caddy.virtualHosts.${fidi.domain}.extraConfig = ''
encode zstd gzip
import ${config.age.secrets.caddy_auth.path}
root * ${config.services.firefly-iii-data-importer.package}/public
php_fastcgi unix/${config.services.phpfpm.pools.firefly-iii-data-importer.socket}
file_server
'';
# Daily SimpleFIN import via the importer's CLI. The unit is gated by
# ConditionPathExists so it silently no-ops until the user has uploaded
# `simplefin.json` (per the post-deploy operational steps).
systemd.services.firefly-iii-data-importer-import = {
description = "Run scheduled Firefly III data importer config (SimpleFIN)";
after = [
"phpfpm-firefly-iii-data-importer.service"
"network-online.target"
];
wants = [ "network-online.target" ];
unitConfig.ConditionPathExists = fidi.simplefinConfigPath;
serviceConfig = {
Type = "oneshot";
User = config.services.firefly-iii-data-importer.user;
Group = config.services.firefly-iii-data-importer.group;
WorkingDirectory = config.services.firefly-iii-data-importer.package;
ExecStart = "${config.services.firefly-iii-data-importer.package}/artisan importer:import ${fidi.simplefinConfigPath}";
# Same hardening posture as the upstream commonServiceConfig.
ReadWritePaths = [ fidi.dataDir ];
PrivateTmp = true;
PrivateDevices = true;
ProtectSystem = "strict";
ProtectHome = "tmpfs";
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectClock = true;
ProtectHostname = true;
ProtectProc = "invisible";
ProcSubset = "pid";
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
LockPersonality = true;
NoNewPrivileges = true;
RemoveIPC = true;
CapabilityBoundingSet = "";
AmbientCapabilities = "";
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service @resources"
"~@obsolete @privileged"
];
};
};
systemd.timers.firefly-iii-data-importer-import = {
description = "Daily Firefly III SimpleFIN import";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
RandomizedDelaySec = "1h";
Persistent = true;
};
};
}