{ lib, config, flakes, pkgs, ... }: with lib; let cfg = config.local.nspawn.dmz; inherit (config.local) mailHost; dmzNet = config.local.nets.${cfg.netName}; hassPort = config.services.home-assistant.config.http.server_port; hassEnable = config.local.home-assistant.enable; in { options.local.nspawn.dmz = { enable = mkEnableOption "DMZ services in a container"; dns64 = mkOption { type = types.str; }; netName = mkOption { type = types.str; }; net6 = mkOption { type = types.str; readOnly = true; }; hostAddr6 = mkOption { type = types.str; readOnly = true; }; system = mkOption { type = types.raw; }; }; # Situación con os-release # # La idea aquí es poder hacer 'btrfs subvol create /var/lib/machines/foo' y # dejar que systemd-nspawn y el activation script creen todo lo demás. Esto # no sirve bien debido a la prueba barata que hace systemd para revisar si el # árbol parece contener una imagen de sistema operativo. Esta prueba falla en # dos momentos distintos: # # 1. Inmediatamente tras crear un árbol vacío, puesto que os-release no existe. # La solución naive es 'mkdir rootfs/etc && touch rootfs/etc/os-release'. # # 2. Luego de reiniciar el contenedor una vez que NixOS ha preparado /etc, ya que # systemd espera un archivo regular y no telera el symlink a la store. # # Resulta ser que systemd revisa tanto /etc/os-release como /usr/lib/os-release. # NixOS evidentemente no usa la segunda ruta por ser FHS, así que la duct tape # final es 'mkdir rootfs/usr/lib && touch rootfs/usr/lib/os-release'. config = mkIf cfg.enable { local = { mailHost.mdaListen = cfg.hostAddr6; nspawn.dmz = { hostAddr6 = dmzNet.hosts.gateway.v6.address; system = let containerModule = { ... }: { #TODO: urgente: bloquear puertos de dovecot a non-postfix con iptables config = { local = { preset.dmz = { enable = true; container = true; }; mta = { mdaAddr = "[${mailHost.mdaListen}]"; inherit (mailHost) saslPort lmtpPort; }; web.sites = { home = { enable = hassEnable; proxyUrl = "http://[${cfg.hostAddr6}]:${toString hassPort}"; }; }; }; nixpkgs = { pkgs = mkDefault pkgs; localSystem = mkDefault pkgs.stdenv.hostPlatform; }; services.nginx.virtualHosts = { "${config.local.domains.imap.main}".locations."^~ /.well-known/acme-challenge/" = { root = "/var/lib/acme/acme-challenge"; extraConfig = '' auth_basic off; auth_request off; ''; }; }; systemd.network.networks."40-host0" = { name = "host0"; networkConfig = { DNS = [ cfg.dns64 ]; DHCP = "no"; IPv6AcceptRA = "yes"; LinkLocalAddressing = "ipv6"; }; ipv6AcceptRAConfig = { Token = [ "static:::${dmzNet.hosts.dmz.v6.suffix}" "eui64" "static:::${dmzNet.hosts.mta.v6.suffix}" "static:::${dmzNet.hosts.web.v6.suffix}" ]; UseDNS = false; }; }; }; }; in # Tomado de la definición de pkgs.nixos junto con definición de nixpkgs.{pkgs,localSystem} arriba import "${flakes.nixpkgs}/nixos/lib/eval-config.nix" { modules = [ ../. containerModule ]; system = null; specialArgs = { inherit flakes; }; }; }; }; services = { home-assistant.config.http = mkIf hassEnable { server_host = [ cfg.hostAddr6 ]; trusted_proxies = [ dmzNet.hosts.web.v6.address ]; use_x_forwarded_for = true; }; }; systemd = { nspawn.dmz = { execConfig.PrivateUsers = "pick"; filesConfig.BindReadOnly = [ # idmap porque algunos hacks en nixpkgs (postfix-setup.service) # asumen que la store es de root "/nix/store:/nix/store:idmap" "${cfg.system.config.system.build.toplevel}/init:/sbin/init" ]; }; network.networks."40-ve-dmz" = { matchConfig = { Name = "ve-dmz"; Driver = "veth"; }; networkConfig = { Address = [ dmzNet.hosts.gateway.v6.cidr ]; LinkLocalAddressing = "yes"; DHCPServer = "no"; IPMasquerade = "no"; LLDP = "yes"; EmitLLDP = "customer-bridge"; IPv6SendRA = "yes"; }; ipv6Prefixes = [ { Assign = "yes"; Prefix = dmzNet.v6.cidr; } ]; }; services = { dovecot2.after = [ "systemd-nspawn@dmz.service" ]; "systemd-nspawn@dmz" = { overrideStrategy = "asDropin"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "machines.target" ]; }; }; }; networking.firewall = { allowedTCPPorts = [ 25 80 443 ]; interfaces.ve-dmz = { allowedTCPPorts = [ mailHost.saslPort mailHost.lmtpPort ] ++ optional hassEnable hassPort; allowedUDPPorts = [ 67 ]; # DHCP }; }; }; }