diff --git a/nix/example-configuration.nix b/nix/example-configuration.nix index e1f6cec7..56f26037 100644 --- a/nix/example-configuration.nix +++ b/nix/example-configuration.nix @@ -114,6 +114,11 @@ policy = { mode = "file"; path = "/var/lib/headscale/policy.hujson"; + + # Validate policy before starting (default: true) + # If validation fails, nixos-rebuild switch will fail + # Set to false to bypass validation for edge cases + validatePolicy = true; }; # You can add ANY headscale configuration option here thanks to freeform settings diff --git a/nix/module.nix b/nix/module.nix index a75398fb..9c3915f0 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -514,6 +514,16 @@ in HuJSON file containing ACL policies. ''; }; + validatePolicy = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to validate the policy file before starting headscale. + If validation fails, the service will not start. + Only applies when policy.mode is set to "file" and policy.path is set. + Set to false to bypass validation for edge cases. + ''; + }; }; }; }; @@ -653,6 +663,15 @@ in isSystemUser = true; }; + system.activationScripts.headscale-policy-check = lib.mkIf ( + cfg.settings.policy.mode == "file" + && cfg.settings.policy.path != null + && cfg.settings.policy.validatePolicy + ) '' + # Validate headscale policy file + ${lib.getExe cfg.package} policy check -f "${cfg.settings.policy.path}" + ''; + systemd.services.headscale = { description = "headscale coordination server for Tailscale"; wants = [ "network-online.target" ]; @@ -716,6 +735,14 @@ in ]; SystemCallArchitectures = "native"; RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + } // lib.optionalAttrs ( + cfg.settings.policy.mode == "file" + && cfg.settings.policy.path != null + && cfg.settings.policy.validatePolicy + ) { + ExecStartPre = [ + "${lib.getExe cfg.package} policy check -f ${cfg.settings.policy.path}" + ]; }; }; };