Port firewall generation from Domtool1/fwtool
authorClinton Ebadi <clinton@unknownlamer.org>
Tue, 29 Nov 2011 07:08:16 +0000 (02:08 -0500)
committerClinton Ebadi <clinton@unknownlamer.org>
Tue, 29 Nov 2011 07:08:16 +0000 (02:08 -0500)
* fwtool was a bit of a hack -- try to clean things up...
* Parsing and generating the config are split (somewhat)
* Only one set of rules for all nodes with a firewall

configDefault/firewall.cfg [new file with mode: 0644]
configDefault/firewall.cfs [new file with mode: 0644]
configDefault/firewall.csg [new file with mode: 0644]
src/plugins/firewall.sig
src/plugins/firewall.sml

diff --git a/configDefault/firewall.cfg b/configDefault/firewall.cfg
new file mode 100644 (file)
index 0000000..f07ecad
--- /dev/null
@@ -0,0 +1,8 @@
+(* -*- sml -*- *)
+structure Firewall :> FIREWALL_CONFIG = struct
+
+val firewallRules = "/home/clinton/misc/hcoop/firewall/user.rules"
+val firewallDir = "/home/clinton/misc/hcoop/firewall/output"
+val firewallNodes = ["bog"]
+
+end
diff --git a/configDefault/firewall.cfs b/configDefault/firewall.cfs
new file mode 100644 (file)
index 0000000..b21e779
--- /dev/null
@@ -0,0 +1 @@
+structure Firewall : FIREWALL_CONFIG
diff --git a/configDefault/firewall.csg b/configDefault/firewall.csg
new file mode 100644 (file)
index 0000000..95bb6bd
--- /dev/null
@@ -0,0 +1,7 @@
+(* -*- sml -*- *)
+signature FIREWALL_CONFIG = sig
+
+    val firewallNodes : string list (* Nodes to firewall *)
+    val firewallDir : string (* Output directory for ferm config *)
+    val firewallRules : string (* Rules file *)
+end
index ec30375..94a838a 100644 (file)
@@ -23,4 +23,6 @@ signature FIREWALL = sig
     val query : string -> string list
     (* List a user's local firewall rules. *)
 
     val query : string -> string list
     (* List a user's local firewall rules. *)
 
+    val generateFirewallConfig : unit -> unit
+
 end
 end
index 6a4bb30..65c9db1 100644 (file)
@@ -1,5 +1,6 @@
 (* HCoop Domtool (http://hcoop.sourceforge.net/)
  * Copyright (c) 2006-2007, Adam Chlipala
 (* HCoop Domtool (http://hcoop.sourceforge.net/)
  * Copyright (c) 2006-2007, Adam Chlipala
+ * Copyright (c) 2011 Clinton Ebadi
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
-(* Firewall rule querying *)
+(* Firewall management *)
+
+(* Contains portions from Fwtool Copyright (C) 2005  Adam Chlipala, GPL v2 or later *)
 
 structure Firewall :> FIREWALL = struct
 
 
 structure Firewall :> FIREWALL = struct
 
+structure StringMap = DataStructures.StringMap
+
+fun parseRules _ =
+    let
+       val inf = TextIO.openIn Config.Firewall.firewallRules
+       val out_lines = ref StringMap.empty
+       val in_lines = ref StringMap.empty
+
+       fun confLine r (uname, line) =
+           let
+               val line = String.concat ["\t", line, "\n"]
+               val lines = case StringMap.find (!r, uname) of
+                               NONE => []
+                             | SOME lines => lines
+           in
+               r := StringMap.insert (!r, uname, line :: lines)
+           end
+
+       val confLine_in = confLine in_lines
+       val confLine_out = confLine out_lines
+
+       fun parsePorts ports =
+           case String.fields (fn ch => ch = #",") ports of
+               [pp] => pp
+             | pps => String.concat ["(", String.concatWith " " pps, ")"]
+                      
+       fun parseHosts addr hosts =
+           case hosts of
+               [] => ""
+             | [host] => String.concat [" ", addr, " ", host]
+             | _ => String.concat [" ", addr, " (", String.concatWith " " hosts, ")"]
+
+       fun loop () =
+           case TextIO.inputLine inf of
+               NONE => ()
+             | SOME line =>
+               case String.tokens Char.isSpace line of
+                   uname :: rest =>
+                   (case rest of
+                       "Client" :: ports :: hosts =>
+                       confLine_out (uname, String.concat ["dport ", parsePorts ports, parseHosts "daddr" hosts, " ACCEPT;"])
+                     | "Server" :: ports :: hosts =>
+                       confLine_in (uname, String.concat ["dport ", parsePorts ports, parseHosts "daddr" hosts, " ACCEPT;"])
+                     | ["LocalServer", ports] =>
+                       confLine_in (uname, String.concat ["saddr $WE dport ", parsePorts ports, " ACCEPT;"])
+                     | _ => print "Invalid config line\n";
+                    loop ())
+                 | _ => loop ()
+       val _ = loop ()
+    in
+       {server_rules = !in_lines, client_rules = !out_lines}
+    end
+
 fun query uname =
     let
 fun query uname =
     let
-        val inf = TextIO.openIn "/etc/firewall/users.rules"
-
-        fun loop rules =
-            case TextIO.inputLine inf of
-                NONE => List.rev rules
-              | SOME line =>
-                if String.sub (line, 0) = #"#" then
-                    loop rules
-                else case String.tokens Char.isSpace line of
-                        uname'::rest =>
-                        if uname = uname' then
-                             loop (String.concatWith " " rest :: rules)
-                        else
-                             loop rules
-                       | _ => loop rules
+       val rules = parseRules ()
     in
     in
-        loop []
-        before TextIO.closeIn inf
-    end handle IO.Io _ => []
+       getOpt (StringMap.find (#server_rules rules, uname), []) @ getOpt (StringMap.find (#client_rules rules, uname), [])
+    end
+
 
 
+fun generateFirewallConfig _ =
+(* rule generation must happen on the node (not really, but I'd rather
+    avoid codifying that uids must be consistent between hosts) *) 
+    let
+       val {server_rules, client_rules} = parseRules ()
+       val users_tcp_out_conf = TextIO.openOut (Config.Firewall.firewallDir ^ "/users_tcp_out.conf")
+       val users_tcp_in_conf = TextIO.openOut (Config.Firewall.firewallDir ^ "/users_tcp_in.conf")
+       val users_conf = TextIO.openOut (Config.Firewall.firewallDir ^ "/users.conf")
+
+       fun write_user_tcp_conf (rules, outf, suffix) =
+           StringMap.appi (fn (uname, lines) =>
+                              let
+                                  val uid = SysWord.toInt (Posix.ProcEnv.uidToWord (Posix.SysDB.Passwd.uid (Posix.SysDB.getpwnam uname)))
+                              in
+                                  TextIO.output (outf, String.concat
+                                                           ["mod owner uid-owner ",
+                                                            Int.toString uid,
+                                                            " { goto user_",
+                                                            uname,
+                                                            suffix,
+                                                            "; goto lreject; }\n"]);
+                                  (* Is there any point to splitting the rules like this? *)
+                                  TextIO.output (users_conf,
+                                                 String.concat ("chain user_"
+                                                                :: uname
+                                                                :: suffix
+                                                                :: " proto tcp {\n"
+                                                                :: lines
+                                                                @ ["}\n\n"]))
+                              end handle OS.SysErr _ => print "Invalid user in firewall config, skipping.\n")
+                          rules
+    in
+       write_user_tcp_conf (server_rules, users_tcp_in_conf, "_tcp_in");
+       write_user_tcp_conf (client_rules, users_tcp_out_conf, "_tcp_out");
 
 
+       TextIO.closeOut users_conf;
+       TextIO.closeOut users_tcp_out_conf;
+       TextIO.closeOut users_tcp_in_conf
+    end
+    
 end
 end