{ 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= # 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}" ]; # }]; # }]; }