Files
nixos/hosts/web-arm/modules/atticd.nix
2025-10-14 22:24:35 +02:00

223 lines
6.2 KiB
Nix

{ config, lib, pkgs, ... }:
let
atticHost = "attic.cloonar.com";
atticPort = 8080; # Internal port for atticd
in {
# Declare required secrets
sops.secrets.atticd = {
# This should contain environment variables for atticd
# Format: KEY=value per line
# Required:
# ATTIC_SERVER_TOKEN_HS256_SECRET=<random-256-bit-secret>
# Optional:
# ATTIC_SERVER_DATABASE_URL=postgresql://user:pass@localhost/attic
};
# Attic server service
services.atticd = {
enable = true;
# Credentials file from sops
environmentFile = config.sops.secrets.atticd.path;
settings = {
listen = "127.0.0.1:${toString atticPort}";
# API endpoint configuration
api-endpoint = "https://${atticHost}/";
# Require tokens for all operations
require-proof-of-possession = true;
# Chunking settings for large uploads
chunking = {
nar-size-threshold = 65536;
min-size = 16384;
avg-size = 65536;
max-size = 262144;
};
# Garbage collection
garbage-collection = {
# GC interval in seconds (12 hours)
interval = "12 hours";
# Delete unreferenced chunks after 7 days
default-retention-period = "6 months";
};
# Storage configuration
storage = {
# Use local filesystem storage
type = "local";
# Store in /var/lib/atticd
path = "/var/lib/atticd-storage";
};
# Optional: S3-compatible storage (commented out)
# storage = {
# type = "s3";
# region = "eu-central-1";
# bucket = "attic-cache";
# endpoint = "https://s3.eu-central-1.amazonaws.com";
# };
# Database configuration
database = {
# url = "postgresql://atticd@/atticd?host=/run/postgresql";
url = "postgresql:///atticd?host=/run/postgresql&user=atticd";
};
# Compression
compression = {
# Use zstd compression
type = "zstd";
level = 3; # Balance between speed and compression
};
};
};
# Create state directory with proper permissions
# systemd.services.atticd = {
# serviceConfig = {
# StateDirectory = "atticd";
# StateDirectoryMode = "0750";
# # Security hardening
# PrivateTmp = true;
# ProtectSystem = "strict";
# ProtectHome = true;
# NoNewPrivileges = true;
# RestrictNamespaces = true;
# RestrictRealtime = true;
# RestrictSUIDSGID = true;
# LockPersonality = true;
# ProtectProc = "invisible";
# ProtectClock = true;
# ProtectKernelLogs = true;
# ProtectControlGroups = true;
# ProtectKernelModules = true;
# ProtectKernelTunables = true;
# ProtectHostname = true;
# SystemCallFilter = "@system-service";
# SystemCallErrorNumber = "EPERM";
# # Resource limits
# LimitNOFILE = 65536;
# };
# };
# Nginx reverse proxy configuration
services.nginx.virtualHosts."${atticHost}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations."/" = {
proxyPass = "http://127.0.0.1:${toString atticPort}";
proxyWebsockets = true;
extraConfig = ''
# Increase timeouts for large uploads
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;
# Increase body size limit for large NAR uploads (500MB)
client_max_body_size 500M;
client_body_buffer_size 128k;
# Proxy headers
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;
# Buffering settings for better performance
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_temp_file_write_size 8k;
# Cache settings for static content
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
'';
};
# Health check endpoint
locations."/health" = {
proxyPass = "http://127.0.0.1:${toString atticPort}/health";
};
# API endpoint with stricter rate limiting
locations."~ ^/api/" = {
proxyPass = "http://127.0.0.1:${toString atticPort}";
extraConfig = ''
# Rate limiting for API endpoints
limit_req zone=attic_api burst=10 nodelay;
# Same proxy settings as above
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;
client_max_body_size 500M;
'';
};
};
# Configure Nginx rate limiting zones
services.nginx.appendHttpConfig = ''
# Rate limiting zones for Attic
limit_req_zone $binary_remote_addr zone=attic_api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=attic_upload:10m rate=5r/s;
# Connection limiting
limit_conn_zone $binary_remote_addr zone=attic_conn:10m;
'';
services.postgresql.ensureUsers = [
{
name = "atticd";
ensureDBOwnership = true;
}
];
services.postgresql.ensureDatabases = [ "atticd" ];
services.postgresqlBackup.databases = [ "atticd" ];
services.borgbackup.jobs.default.exclude = [
"/var/lib/atticd-storage"
];
systemd.tmpfiles.rules = [
"d /var/lib/atticd-storage 0755 atticd atticd -"
];
environment.systemPackages = [ pkgs.cifs-utils ];
fileSystems."/var/lib/atticd-storage" = {
device = "//u149513.your-backup.de/u149513-sub9/";
fsType = "cifs";
options = let
automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,user,users,file_mode=0770,dir_mode=0770";
in ["${automount_opts},credentials=${config.sops.secrets.atticd-smb-credentials.path},uid=atticd,gid=atticd"];
};
sops.secrets.atticd-smb-credentials = {};
# Monitoring with Prometheus (if you have it set up)
# services.prometheus.scrapeConfigs = [{
# job_name = "atticd";
# static_configs = [{
# targets = [ "127.0.0.1:${toString atticPort}" ];
# }];
# }];
}