diff --git a/3modules/lass/iptables.nix b/3modules/lass/iptables.nix
new file mode 100644
index 000000000..6d567ca23
--- /dev/null
+++ b/3modules/lass/iptables.nix
@@ -0,0 +1,166 @@
+arg@{ config, lib, pkgs, ... }:
+
+let
+  inherit (pkgs) writeScript writeText;
+  inherit (lib) concatMapStringsSep concatStringsSep attrNames unique fold any attrValues catAttrs filter flatten length hasAttr mkEnableOption mkOption mkIf types;
+
+  cfg = config.lass.iptables;
+
+  out = {
+    options.lass.iptables = api;
+    config = mkIf cfg.enable imp;
+  };
+
+  api = {
+    enable = mkEnableOption "iptables";
+
+    #tables.filter.INPUT = {
+    # policy = "DROP";
+    # rules = [
+    #   { predicate = "-i retiolum"; target = "ACCEPT"; priority = -10; }
+    # ];
+    #};
+    #new api
+    tables = mkOption {
+      type = with types; attrsOf (attrsOf (submodule ({
+        options = {
+          policy = mkOption {
+            type = str;
+            default = "-";
+          };
+          rules = mkOption {
+            type = nullOr (listOf (submodule ({
+              options = {
+                predicate = mkOption {
+                  type = str;
+                };
+                target = mkOption {
+                  type = str;
+                };
+                precedence = mkOption {
+                  type = int;
+                  default = 0;
+                };
+              };
+            })));
+            default = null;
+          };
+        };
+      })));
+    };
+  };
+
+  imp = {
+    networking.firewall.enable = false;
+
+    systemd.services.lass-iptables = {
+      description = "lass-iptables";
+      wantedBy = [ "network-pre.target" ];
+      before = [ "network-pre.target" ];
+      after = [ "systemd-modules-load.service" ];
+
+      path = with pkgs; [
+        iptables
+      ];
+
+      restartIfChanged = true;
+
+      serviceConfig = {
+        Type = "simple";
+        RemainAfterExit = true;
+        Restart = "always";
+        ExecStart = "@${startScript} lass-iptables_start";
+      };
+    };
+  };
+
+  #buildTable :: iptablesAttrSet` -> str
+  #todo: differentiate by iptables-version
+  buildTables = iptv: ts:
+    let
+      declareChain = t: cn:
+        #TODO: find out what to do whit these count numbers
+        ":${cn} ${t."${cn}".policy} [0:0]";
+
+      buildChain = tn: cn:
+      #"${concatStringsSep " " ((attrNames t."${cn}") ++ [cn])}";
+
+      #TODO: sort by precedence
+      #TODO: double check should be unneccessary, refactor!
+        if (hasAttr "rules" ts."${tn}"."${cn}") then
+          if (ts."${tn}"."${cn}".rules == null) then
+            ""
+          else
+            concatMapStringsSep "\n" (rule: "\n-A ${cn} ${rule}") ([]
+              ++ map buildRule ts."${tn}"."${cn}".rules
+            )
+        else
+          ""
+        ;
+
+
+      buildRule = rule:
+        #TODO implement rule validation-test here
+        #
+        #target:
+        #target needs to be an existing chain (in the same table) or ACCEPT, REJECT, DROP, LOG, QUEUE, RETURN
+
+        #predicate:
+        #maybe use iptables-test
+        #TODO: howto exit with evaluation error by shellscript?
+          #apperantly not possible from nix because evalatution wouldn't be deterministic.
+        "${rule.predicate} -j ${rule.target}";
+
+      buildTable = tn:
+        "*${tn}\n" +
+        concatStringsSep "\n" ([]
+          ++ map (declareChain ts."${tn}") (attrNames ts."${tn}")
+        ) +
+        #this looks dirty, find a better way to do this (maybe optionalString)
+        concatStringsSep "" ([]
+          ++ map (buildChain tn) (attrNames ts."${tn}")
+        ) +
+        "\nCOMMIT";
+    in
+      concatStringsSep "\n" ([]
+        ++ map buildTable (attrNames ts)
+      );
+
+#=====
+
+  rules4 = iptables-version:
+    let
+      #TODO: find out good defaults.
+      tables-defaults = {
+        nat.PREROUTING.policy = "ACCEPT";
+        nat.INPUT.policy = "ACCEPT";
+        nat.OUTPUT.policy = "ACCEPT";
+        nat.POSTROUTING.policy = "ACCEPT";
+        filter.INPUT.policy = "ACCEPT";
+        filter.FORWARD.policy = "ACCEPT";
+        filter.OUTPUT.policy = "ACCEPT";
+
+        #if someone specifies any other rules on this chain, the default rules get lost.
+        #is this wanted beahiviour or a bug?
+        #TODO: implement abstraction of rules
+        filter.INPUT.rules = [
+          { predicate = "-m conntrack --ctstate RELATED,ESTABLISHED"; target = "ACCEPT"; }
+        ];
+      };
+      tables = tables-defaults // cfg.tables;
+
+    in
+      writeText "lass-iptables-rules${toString iptables-version}" ''
+        ${buildTables iptables-version tables}
+      '';
+
+  startScript = writeScript "lass-iptables_start" ''
+    #! /bin/sh
+    set -euf
+    iptables-restore < ${rules4 4}
+    ip6tables-restore < ${rules4 6}
+  '';
+
+in
+out
+