{ pkgs, lib, config, ... }:
let
  inherit (builtins) hasAttr readFile;
  inherit (pkgs.lib) unlinesAttrs;
  inherit (config) users;
  inherit (config.services) shorewall shorewall6;
  fw2net = ''
    # By protocol
    Ping(ACCEPT)   $FW net

    # By port
    DNS(ACCEPT)    $FW net {user=${users.users.unbound.name}}
    DNS(ACCEPT)    $FW net:217.70.177.40 # for knot to notify ns6.gandi.net
    DNS(ACCEPT)    $FW net:78.192.65.63  # for knot to notify ns0.muarf.org
    Git(ACCEPT)    $FW net
    HKP(ACCEPT)    $FW net {user=${users.users.julm.name}}
    HTTP(ACCEPT)   $FW net
    HTTPS(ACCEPT)  $FW net
    SMTP(ACCEPT)   $FW net
    SMTPS(ACCEPT)  $FW net
    SSH(ACCEPT)    $FW net
  '';
  net2fw = ''
    # By protocol
    Ping(ACCEPT)   net $FW

    # By port
    DNS(ACCEPT)    net $FW
    HTTP(ACCEPT)   net $FW
    HTTPS(ACCEPT)  net $FW
    IMAPS(ACCEPT)  net $FW
    Mosh(ACCEPT)   net $FW
    POP3S(ACCEPT)  net $FW
    SMTP(ACCEPT)   net $FW
    SMTPS(ACCEPT)  net $FW
    SSH(ACCEPT)    net $FW {rate=s:1/min:10}
    Sieve(ACCEPT)  net $FW
  '';
  fw2lan = ''
    Ping(ACCEPT)   $FW lan
    DNS(ACCEPT)    $FW lan
    HTTPS(ACCEPT)  $FW lan
  '';
  lan2fw = ''
    Ping(ACCEPT)   lan $FW
    SSH(ACCEPT)    lan $FW
    HTTP(ACCEPT)   lan $FW
    HTTPS(ACCEPT)  lan $FW
    DNS(ACCEPT)    lan $FW
  '';
  macros = {
    "macro.Git" = ''
      ?FORMAT 2
      #ACTION SOURCE  DEST    PROTO   DEST    SOURCE  RATE    USER/
      #                               PORT(S) PORT(S) LIMIT   GROUP
      PARAM   -       -       tcp     9418
    '';
    "macro.Mosh" = ''
      ?FORMAT 2
      #ACTION SOURCE  DEST    PROTO   DEST    SOURCE  RATE    USER/
      #                               PORT(S) PORT(S) LIMIT   GROUP
      PARAM   -       -       udp     60000-61000
    '';
  };
in
{
  services.shorewall = {
    enable  = true;
    configs = macros // {
      "shorewall.conf" = ''
        ${readFile "${shorewall.package}/etc-example/shorewall/shorewall.conf"}
        #
        ## Custom config
        ###
        STARTUP_ENABLED=Yes
        ZONE2ZONE=2
      '';
      zones = ''
        # DOC: shorewall-zones(5)
        fw firewall
        net    ipv4
        lan    ipv4
        unused ipv4
      '';
      interfaces = ''
        # DOC: shorewall-interfaces(5)
        ?FORMAT 2
        net    enp1s0 arp_filter,nosmurfs,routefilter=1,tcpflags
        lan    enp2s0 arp_filter,nosmurfs,routefilter=1,tcpflags
        unused enp3s0 arp_filter,nosmurfs,routefilter=1,tcpflags
      '';
      policy = ''
        # DOC: shorewall-policy(5)
        $FW    all DROP
        lan    all DROP   none
        net    all DROP   none
        unused all DROP   none
        # WARNING: the following policy must be last
        all    all REJECT none
      '';
      rules = ''
        # DOC: shorewall-rules(5)
        #SECTION ALL
        #SECTION ESTABLISHED
        #SECTION RELATED
        ?SECTION NEW

        ${fw2net}
        ${net2fw}

        ${fw2lan}
        ${lan2fw}
      '';
    };
  };
  services.shorewall6 = {
    enable  = true;
    configs = macros // {
      "shorewall6.conf" = ''
        ${readFile "${shorewall6.package}/etc-example/shorewall6/shorewall6.conf"}
        #
        ## Custom config
        ###
        STARTUP_ENABLED=Yes
        ZONE2ZONE=2
        '';
      zones = ''
        # DOC: shorewall-zones(5)
        fw firewall
        net    ipv6
        lan    ipv6
        unused ipv6
      '';
      interfaces = ''
        # DOC: shorewall-interfaces(5)
        ?FORMAT 2
        net    enp1s0 nosmurfs,tcpflags
        lan    enp2s0 nosmurfs,tcpflags
        unused enp3s0 nosmurfs,tcpflags
      '';
      policy = ''
        # DOC: shorewall-policy(5)
        $FW    all DROP
        lan    all DROP   none
        net    all DROP   none
        unused all DROP   none
        # WARNING: the following policy must be last
        all    all REJECT none
      '';
      rules = ''
        # DOC: shorewall-rules(5)
        #SECTION ALL
        #SECTION ESTABLISHED
        #SECTION RELATED
        ?SECTION NEW

        ${fw2net}
        ${net2fw}

        ${fw2lan}
        ${lan2fw}
      '';
    };
  };
}