{
  flake-inputs,
  pkgs,
  lib,
  config,
  ...
}:
let
  crowdsecCfg = config.security.crowdsec;
  cfg = crowdsecCfg.remediationComponents.firewallBouncer;
  settingsFormat = pkgs.formats.yaml { };
  crowdsec-firewall-bouncer = flake-inputs.self.packages.${pkgs.system}.crowdsec-firewall-bouncer;
in
{
  options.security.crowdsec.remediationComponents.firewallBouncer = {
    enable = lib.mkEnableOption "cs-firewall-bouncer";

    settings = lib.mkOption {
      inherit (settingsFormat) type;
      default = { };

      description = ''
        The bouncer configuration. Refer to
        <https://docs.crowdsec.net/u/bouncers/firewall/> for details
        on supported values.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    security.crowdsec.remediationComponents.firewallBouncer.settings = {
      mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}";
      log_mode = "stdout";
      iptables_chains = [
        "nixos-fw"
      ];

      # Don't let users easily override this; unfortunately we need to
      # set up this key through substitution at runtime.
      api_key = lib.mkForce "\${API_KEY}";
      api_url = lib.mkDefault "http://${crowdsecCfg.settings.api.server.listen_uri}";
    };

    systemd = {
      packages = [ crowdsec-firewall-bouncer ];

      services = {
        crowdsec-firewall-bouncer-setup = {
          description = "Crowdsec firewall bouncer config preparation";
          script = ''
            if [ ! -e '${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml' ]; then
                ${crowdsecCfg.package}/bin/cscli -oraw bouncers add "cs-firewall-bouncer-$(${pkgs.coreutils}/bin/date +%s)" > \
                  ${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml
            fi

            # Stdout redirection is deliberately used to forcibly
            # overwrite the file if it exists
            API_KEY="$(<${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml)" \
              ${lib.getExe pkgs.envsubst} \
              -i ${settingsFormat.generate "crowdsec-firewall-bouncer.yaml" cfg.settings} \
              > /var/lib/crowdsec/config/crowdsec-firewall-bouncer.yaml
          '';

          serviceConfig = {
            User = "crowdsec";
            Group = "crowdsec";

            Type = "oneshot";
            RemainAfterExit = true;
          };
        };

        crowdsec-firewall-bouncer = {
          enable = true;

          after = [ "crowdsec-firewall-bouncer-setup.service" ];
          bindsTo = [ "crowdsec-firewall-bouncer-setup.service" ];
          requiredBy = [ "crowdsec.service" ];

          path =
            lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [
              pkgs.ipset
            ]
            ++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables
            ++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables;
        };
      };
    };
  };
}