forked from mirrors_public/oddlama_nix-config
238 lines
7.6 KiB
Nix
238 lines
7.6 KiB
Nix
{
|
|
config,
|
|
globals,
|
|
lib,
|
|
nodes,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
paperlessDomain = "paperless.${globals.domains.me}";
|
|
paperlessBackupDir = "/var/cache/paperless-backup";
|
|
in
|
|
{
|
|
microvm.mem = 1024 * 9;
|
|
microvm.vcpu = 8;
|
|
|
|
globals.wireguard.proxy-sentinel.hosts.${config.node.name}.firewallRuleForNode.sentinel.allowedTCPPorts =
|
|
[
|
|
config.services.paperless.port
|
|
];
|
|
|
|
globals.wireguard.proxy-home.hosts.${config.node.name}.firewallRuleForNode.ward-web-proxy.allowedTCPPorts =
|
|
[
|
|
config.services.paperless.port
|
|
];
|
|
|
|
globals.services.paperless.domain = paperlessDomain;
|
|
# FIXME: also monitor from internal network
|
|
globals.monitoring.http.paperless = {
|
|
url = "https://${paperlessDomain}";
|
|
expectedBodyRegex = "Paperless-ngx";
|
|
network = "internet";
|
|
};
|
|
|
|
nodes.sentinel = {
|
|
services.nginx = {
|
|
upstreams.paperless = {
|
|
servers."${
|
|
globals.wireguard.proxy-sentinel.hosts.${config.node.name}.ipv4
|
|
}:${toString config.services.paperless.port}" =
|
|
{ };
|
|
extraConfig = ''
|
|
zone paperless 64k;
|
|
keepalive 2;
|
|
'';
|
|
# direct upstream monitoring doesn't work because
|
|
# paperless allowed hosts fails for ip-based queries.
|
|
# But that's fine, we just monitor it via the domain above anyway.
|
|
#monitoring.enable = true;
|
|
};
|
|
virtualHosts.${paperlessDomain} = {
|
|
forceSSL = true;
|
|
useACMEWildcardHost = true;
|
|
extraConfig = ''
|
|
client_max_body_size 512M;
|
|
'';
|
|
locations."/" = {
|
|
proxyPass = "http://paperless";
|
|
proxyWebsockets = true;
|
|
X-Frame-Options = "SAMEORIGIN";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
nodes.ward-web-proxy = {
|
|
services.nginx = {
|
|
upstreams.paperless = {
|
|
servers."${
|
|
globals.wireguard.proxy-home.hosts.${config.node.name}.ipv4
|
|
}:${toString config.services.paperless.port}" =
|
|
{ };
|
|
extraConfig = ''
|
|
zone paperless 64k;
|
|
keepalive 2;
|
|
'';
|
|
# direct upstream monitoring doesn't work because
|
|
# paperless allowed hosts fails for ip-based queries.
|
|
# But that's fine, we just monitor it via the domain above anyway.
|
|
#monitoring.enable = true;
|
|
};
|
|
virtualHosts.${paperlessDomain} = {
|
|
forceSSL = true;
|
|
useACMEWildcardHost = true;
|
|
extraConfig = ''
|
|
client_max_body_size 512M;
|
|
allow ${globals.net.home-lan.vlans.home.cidrv4};
|
|
allow ${globals.net.home-lan.vlans.home.cidrv6};
|
|
# Firezone traffic
|
|
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv4};
|
|
allow ${globals.net.home-lan.vlans.services.hosts.ward.ipv6};
|
|
deny all;
|
|
'';
|
|
locations."/" = {
|
|
proxyPass = "http://paperless";
|
|
proxyWebsockets = true;
|
|
X-Frame-Options = "SAMEORIGIN";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
age.secrets.paperless-admin-password = {
|
|
generator.script = "alnum";
|
|
mode = "440";
|
|
group = "paperless";
|
|
};
|
|
|
|
# Mirror the original oauth2 secret
|
|
age.secrets.paperless-oauth2-client-secret = {
|
|
inherit (nodes.ward-kanidm.config.age.secrets.kanidm-oauth2-paperless) rekeyFile;
|
|
mode = "440";
|
|
group = "paperless";
|
|
};
|
|
|
|
environment.persistence."/persist".directories = [
|
|
{
|
|
directory = "/var/lib/paperless";
|
|
user = "paperless";
|
|
group = "paperless";
|
|
mode = "0750";
|
|
}
|
|
];
|
|
|
|
services.paperless = {
|
|
enable = true;
|
|
address = "0.0.0.0";
|
|
passwordFile = config.age.secrets.paperless-admin-password.path;
|
|
consumptionDir = "/paperless/consume";
|
|
mediaDir = "/paperless/media";
|
|
settings = {
|
|
PAPERLESS_URL = "https://${paperlessDomain}";
|
|
PAPERLESS_ALLOWED_HOSTS = paperlessDomain;
|
|
PAPERLESS_CORS_ALLOWED_HOSTS = "https://${paperlessDomain}";
|
|
PAPERLESS_TRUSTED_PROXIES = lib.concatStringsSep "," [
|
|
globals.wireguard.proxy-home.hosts.ward-web-proxy.ipv4
|
|
globals.wireguard.proxy-sentinel.hosts.sentinel.ipv4
|
|
];
|
|
|
|
# Authentication via kanidm
|
|
PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";
|
|
PAPERLESS_SOCIALACCOUNT_PROVIDERS = builtins.toJSON {
|
|
openid_connect = {
|
|
OAUTH_PKCE_ENABLED = "True";
|
|
APPS = [
|
|
rec {
|
|
provider_id = "kanidm";
|
|
name = "Kanidm";
|
|
client_id = "paperless";
|
|
# secret will be added dynamically
|
|
#secret = "";
|
|
settings.server_url = "https://${globals.services.kanidm.domain}/oauth2/openid/${client_id}/.well-known/openid-configuration";
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
# Ghostscript is entirely bug-free.
|
|
PAPERLESS_OCR_USER_ARGS = builtins.toJSON {
|
|
continue_on_soft_render_error = true;
|
|
# The original will always be kept, so just invalidate it. Otherwise the import will fail.
|
|
invalidate_digital_signatures = true;
|
|
};
|
|
|
|
# virtiofsd doesn't send inotify events (not sure if generally, or because we
|
|
# mount the same host share on another vm (samba) and modify it there).
|
|
PAPERLESS_CONSUMER_POLLING = 1; # seconds
|
|
# Wait three seconds between file-modified checks. After 5 consecutive checks
|
|
# where the file wasn't modified it will be consumed.
|
|
PAPERLESS_CONSUMER_POLLING_DELAY = 3;
|
|
|
|
PAPERLESS_CONSUMER_ENABLE_BARCODES = true;
|
|
PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE = true;
|
|
PAPERLESS_CONSUMER_BARCODE_SCANNER = "ZXING";
|
|
PAPERLESS_CONSUMER_RECURSIVE = true;
|
|
PAPERLESS_FILENAME_FORMAT = "{{ owner_username }}/{{ created_year }}-{{ created_month }}-{{ created_day }}_{{ asn }}_{{ title }}";
|
|
|
|
# Nginx does that better.
|
|
PAPERLESS_ENABLE_COMPRESSION = false;
|
|
|
|
#PAPERLESS_IGNORE_DATES = concatStringsSep "," ignoreDates;
|
|
PAPERLESS_NUMBER_OF_SUGGESTED_DATES = 8;
|
|
PAPERLESS_OCR_LANGUAGE = "deu+eng";
|
|
PAPERLESS_TASK_WORKERS = 4;
|
|
PAPERLESS_WEBSERVER_WORKERS = 4;
|
|
};
|
|
};
|
|
|
|
systemd.services.paperless.serviceConfig.RestartSec = "60"; # Retry every minute
|
|
|
|
systemd.tmpfiles.settings."10-paperless".${paperlessBackupDir}.d = {
|
|
inherit (config.services.paperless) user;
|
|
mode = "0700";
|
|
};
|
|
|
|
# Add secret to PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
|
systemd.services.paperless-web.script = lib.mkBefore ''
|
|
oidcSecret=$(< ${config.age.secrets.paperless-oauth2-client-secret.path})
|
|
export PAPERLESS_SOCIALACCOUNT_PROVIDERS=$(
|
|
${pkgs.jq}/bin/jq <<< "$PAPERLESS_SOCIALACCOUNT_PROVIDERS" \
|
|
--compact-output \
|
|
--arg oidcSecret "$oidcSecret" '.openid_connect.APPS.[0].secret = $oidcSecret'
|
|
)
|
|
'';
|
|
|
|
systemd.services.paperless-backup =
|
|
let
|
|
cfg = config.systemd.services.paperless-consumer;
|
|
in
|
|
{
|
|
description = "Paperless documents backup";
|
|
serviceConfig = lib.recursiveUpdate cfg.serviceConfig {
|
|
ExecStart = "${config.services.paperless.package}/bin/paperless-ngx document_exporter -na -nt -f -d ${paperlessBackupDir}";
|
|
ReadWritePaths = cfg.serviceConfig.ReadWritePaths ++ [ paperlessBackupDir ];
|
|
Restart = "no";
|
|
Type = "oneshot";
|
|
};
|
|
inherit (cfg) environment;
|
|
requiredBy = [ "restic-backups-storage-box-dusk.service" ];
|
|
before = [ "restic-backups-storage-box-dusk.service" ];
|
|
};
|
|
|
|
# Needed so we don't run out of tmpfs space for large backups.
|
|
# Technically this could be cleared each boot but whatever.
|
|
environment.persistence."/state".directories = [
|
|
{
|
|
directory = paperlessBackupDir;
|
|
user = "paperless";
|
|
group = "paperless";
|
|
mode = "0700";
|
|
}
|
|
];
|
|
|
|
backups.storageBoxes.dusk = {
|
|
subuser = "paperless";
|
|
paths = [ paperlessBackupDir ];
|
|
};
|
|
}
|