From cd3cfd6134b2e608984ff8d3105f6cb1db739be6 Mon Sep 17 00:00:00 2001 From: Jamil Bou Kheir Date: Thu, 2 Sep 2021 15:57:42 +0000 Subject: [PATCH] passing tests --- apps/fz_http/lib/fz_http/rules.ex | 4 +- apps/fz_http/lib/fz_http/rules/rule.ex | 2 +- .../live/rule_live/rule_list_component.ex | 4 +- .../20200228154815_create_rules.exs | 4 +- apps/fz_http/test/fz_http/rules_test.exs | 20 ++--- .../fz_http_web/live/rule_live/index_test.exs | 20 ++--- apps/fz_http/test/support/test_helpers.ex | 8 +- apps/fz_wall/lib/fz_wall/cli/live.ex | 90 ++++++++++--------- 8 files changed, 78 insertions(+), 74 deletions(-) diff --git a/apps/fz_http/lib/fz_http/rules.ex b/apps/fz_http/lib/fz_http/rules.ex index 02632c284..f848b6783 100644 --- a/apps/fz_http/lib/fz_http/rules.ex +++ b/apps/fz_http/lib/fz_http/rules.ex @@ -28,14 +28,14 @@ defmodule FzHttp.Rules do def allowlist do Repo.all( from r in Rule, - where: r.action == :allow + where: r.action == :accept ) end def denylist do Repo.all( from r in Rule, - where: r.action == :deny + where: r.action == :drop ) end diff --git a/apps/fz_http/lib/fz_http/rules/rule.ex b/apps/fz_http/lib/fz_http/rules/rule.ex index 453cfab08..ef393d408 100644 --- a/apps/fz_http/lib/fz_http/rules/rule.ex +++ b/apps/fz_http/lib/fz_http/rules/rule.ex @@ -10,7 +10,7 @@ defmodule FzHttp.Rules.Rule do schema "rules" do field :destination, EctoNetwork.INET - field :action, Ecto.Enum, values: [:deny, :allow], default: :deny + field :action, Ecto.Enum, values: [:drop, :accept], default: :drop timestamps(type: :utc_datetime_usec) end diff --git a/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.ex b/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.ex index 3331c49dd..d7c63f0b9 100644 --- a/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.ex +++ b/apps/fz_http/lib/fz_http_web/live/rule_live/rule_list_component.ex @@ -45,10 +45,10 @@ defmodule FzHttpWeb.RuleLive.RuleListComponent do defp action(id) do case id do :allowlist -> - :allow + :accept :denylist -> - :deny + :drop end end diff --git a/apps/fz_http/priv/repo/migrations/20200228154815_create_rules.exs b/apps/fz_http/priv/repo/migrations/20200228154815_create_rules.exs index 35ca90304..83b960535 100644 --- a/apps/fz_http/priv/repo/migrations/20200228154815_create_rules.exs +++ b/apps/fz_http/priv/repo/migrations/20200228154815_create_rules.exs @@ -1,7 +1,7 @@ defmodule FzHttp.Repo.Migrations.CreateRules do use Ecto.Migration - @create_query "CREATE TYPE action_enum AS ENUM ('deny', 'allow')" + @create_query "CREATE TYPE action_enum AS ENUM ('drop', 'accept')" @drop_query "DROP TYPE action_enum" def change do @@ -9,7 +9,7 @@ defmodule FzHttp.Repo.Migrations.CreateRules do create table(:rules) do add :destination, :inet, null: false - add :action, :action_enum, default: "deny", null: false + add :action, :action_enum, default: "drop", null: false timestamps(type: :utc_datetime_usec) end diff --git a/apps/fz_http/test/fz_http/rules_test.exs b/apps/fz_http/test/fz_http/rules_test.exs index 29456a98a..558eb28c6 100644 --- a/apps/fz_http/test/fz_http/rules_test.exs +++ b/apps/fz_http/test/fz_http/rules_test.exs @@ -27,7 +27,7 @@ defmodule FzHttp.RulesTest do test "creates rule" do {:ok, rule} = Rules.create_rule(%{destination: "::1"}) assert !is_nil(rule.id) - assert rule.action == :deny + assert rule.action == :drop end end @@ -47,11 +47,11 @@ defmodule FzHttp.RulesTest do setup [:create_rules] @iptables_rules [ - {"1.1.1.0/24", :deny}, - {"2.2.2.0/24", :deny}, - {"3.3.3.0/24", :deny}, - {"4.4.4.0/24", :deny}, - {"5.5.5.0/24", :deny} + {"1.1.1.0/24", :drop}, + {"2.2.2.0/24", :drop}, + {"3.3.3.0/24", :drop}, + {"4.4.4.0/24", :drop}, + {"5.5.5.0/24", :drop} ] test "prints all rules to iptables format", %{rules: _rules} do @@ -60,7 +60,7 @@ defmodule FzHttp.RulesTest do end describe "allowlist/0" do - setup [:create_allow_rule] + setup [:create_accept_rule] test "returns allow rules", %{rule: rule} do assert Rules.allowlist() == [rule] @@ -68,7 +68,7 @@ defmodule FzHttp.RulesTest do end describe "denylist/0" do - setup [:create_deny_rule] + setup [:create_drop_rule] test "returns deny rules", %{rule: rule} do assert Rules.denylist() == [rule] @@ -79,7 +79,7 @@ defmodule FzHttp.RulesTest do # describe "iptables_spec/1 IPv4" do # setup [:create_rule4] # - # @ipv4tables_spec {"10.0.0.1", "10.10.10.0/24", :deny} + # @ipv4tables_spec {"10.0.0.1", "10.10.10.0/24", :drop} # # test "returns IPv4 tuple", %{rule4: rule} do # assert @ipv4tables_spec = Rules.iptables_spec(rule) @@ -89,7 +89,7 @@ defmodule FzHttp.RulesTest do # describe "iptables_spec/1 IPv6" do # setup [:create_rule6] # - # @ipv6tables_spec {"::1", "::/0", :deny} + # @ipv6tables_spec {"::1", "::/0", :drop} # # test "returns IPv6 tuple", %{rule6: rule} do # assert @ipv6tables_spec = Rules.iptables_spec(rule) diff --git a/apps/fz_http/test/fz_http_web/live/rule_live/index_test.exs b/apps/fz_http/test/fz_http_web/live/rule_live/index_test.exs index ed3ce9194..5a686ddb0 100644 --- a/apps/fz_http/test/fz_http_web/live/rule_live/index_test.exs +++ b/apps/fz_http/test/fz_http_web/live/rule_live/index_test.exs @@ -2,10 +2,10 @@ defmodule FzHttpWeb.RuleLive.IndexTest do use FzHttpWeb.ConnCase, async: true describe "allowlist" do - setup :create_allow_rule + setup :create_accept_rule @destination "1.2.3.4" - @allow_params %{"rule" => %{"action" => "allow", "destination" => @destination}} + @allow_params %{"rule" => %{"action" => "accept", "destination" => @destination}} test "adds to allowlist", %{authed_conn: conn} do path = Routes.rule_index_path(conn, :index) @@ -13,7 +13,7 @@ defmodule FzHttpWeb.RuleLive.IndexTest do test_view = view - |> form("#allow-form") + |> form("#accept-form") |> render_submit(@allow_params) assert test_view =~ @destination @@ -25,11 +25,11 @@ defmodule FzHttpWeb.RuleLive.IndexTest do test_view = view - |> form("#allow-form") + |> form("#accept-form") |> render_submit(%{ "rule" => %{ "destination" => "not a valid destination", - "action" => "allow" + "action" => "accept" } }) @@ -50,10 +50,10 @@ defmodule FzHttpWeb.RuleLive.IndexTest do end describe "denylist" do - setup :create_deny_rule + setup :create_drop_rule @destination "1.2.3.4" - @deny_params %{"rule" => %{"action" => "deny", "destination" => @destination}} + @deny_params %{"rule" => %{"action" => "drop", "destination" => @destination}} test "adds to denylist", %{authed_conn: conn, rule: _rule} do path = Routes.rule_index_path(conn, :index) @@ -61,7 +61,7 @@ defmodule FzHttpWeb.RuleLive.IndexTest do test_view = view - |> form("#deny-form") + |> form("#drop-form") |> render_submit(@deny_params) assert test_view =~ @destination @@ -73,11 +73,11 @@ defmodule FzHttpWeb.RuleLive.IndexTest do test_view = view - |> form("#deny-form") + |> form("#drop-form") |> render_submit(%{ "rule" => %{ "destination" => "not a valid destination", - "action" => "deny" + "action" => "drop" } }) diff --git a/apps/fz_http/test/support/test_helpers.ex b/apps/fz_http/test/support/test_helpers.ex index 1c14057bb..593c1a840 100644 --- a/apps/fz_http/test/support/test_helpers.ex +++ b/apps/fz_http/test/support/test_helpers.ex @@ -60,13 +60,13 @@ defmodule FzHttp.TestHelpers do {:ok, session: session} end - def create_allow_rule(_) do - rule = Fixtures.rule(%{action: :allow}) + def create_accept_rule(_) do + rule = Fixtures.rule(%{action: :accept}) {:ok, rule: rule} end - def create_deny_rule(_) do - rule = Fixtures.rule(%{action: :deny}) + def create_drop_rule(_) do + rule = Fixtures.rule(%{action: :drop}) {:ok, rule: rule} end diff --git a/apps/fz_wall/lib/fz_wall/cli/live.ex b/apps/fz_wall/lib/fz_wall/cli/live.ex index 316a943ab..d9bac8cbd 100644 --- a/apps/fz_wall/lib/fz_wall/cli/live.ex +++ b/apps/fz_wall/lib/fz_wall/cli/live.ex @@ -1,76 +1,80 @@ defmodule FzWall.CLI.Live do @moduledoc """ - A low-level module for interacting with the iptables CLI. + A low-level module for interacting with the nftables CLI. - Rules operate on the iptables forward chain to deny outgoing packets to + Rules operate on the nftables forward chain to deny outgoing packets to specified IP addresses, ports, and protocols from FireZone device IPs. - - Note that iptables chains and rules are mutually exclusive between IPv4 and IPv6. """ import FzCommon.CLI + require Logger + @table_name "firezone" @egress_interface_cmd "route | grep '^default' | grep -o '[^ ]*$'" - @setup_chain_cmd "iptables -N firezone && iptables6 -N firezone" - @teardown_chain_cmd "iptables -F firezone &&\ - iptables -X firezone &&\ - iptables6 -F firezone &&\ - iptables6 -X firezone" @doc """ - Sets up the FireZone iptables chain. + Sets up the FireZone nftables table, base chain, and counts traffic + "forward" is the Netfilter hook we want to tie into. """ def setup do - exec!(@setup_chain_cmd) + exec!("#{nft()} add table ip #{@table_name}") + + exec!( + "#{nft()} 'add chain ip #{@table_name} forward { type filter hook forward priority 0 ; }'" + ) + + exec!("#{nft()} 'add rule ip #{@table_name} forward counter accept'") end @doc """ - Flushes and removes the FireZone iptables chain. + Flushes and removes the FireZone nftables table and base chain. """ def teardown do - exec!(@teardown_chain_cmd) + exec!("#{nft()} delete table ip #{@table_name}") end @doc """ - Adds iptables rule. + Adds nftables rule. """ - def add_rule({4, s, d, :deny}) do - exec!("iptables -A firezone -s #{s} -d #{d} -j DROP") - end - - def add_rule({4, d, :deny}) do - exec!("iptables -A firezone -d #{d} -j DROP") - end - - def add_rule({4, s, d, :allow}) do - exec!("iptables -A firezone -s #{s} -d #{d} -j ACCEPT") - end - - def add_rule({6, s, d, :deny}) do - exec!("iptables6 -A firezone -s #{s} -d #{d} -j DROP") - end - - def add_rule({6, s, d, :allow}) do - exec!("iptables6 -A firezone -s #{s} -d #{d} -j ACCEPT") + def add_rule({dest, action}) do + exec!("#{nft()} 'add rule ip #{@table_name} forward ip daddr #{dest} #{action}'") end @doc """ - Deletes iptables rule. + Deletes nftables rule. """ - def delete_rule({4, s, d, :deny}) do - exec!("#{nft()} -D firezone -s #{s} -d #{d} -j DROP") + def delete_rule_spec({dest, action} = rule_spec) do + case get_rule_handle("ip daddr #{dest} #{action}") do + {:ok, handle_num} -> + exec!("#{nft()} delete rule #{@table_name} forward handle #{handle_num}") + + {:error, cmd_output} -> + raise(""" + ###################################################### + Could not get handle to delete rule! + Rule spec: #{rule_spec} + + Current chain: + #{cmd_output} + ###################################################### + """) + end end - def delete_rule({4, s, d, :allow}) do - exec!("iptables -D firezone -s #{s} -d #{d} -j ACCEPT") + def get_rule_handle(rule_str) do + cmd_output = exec!("#{nft()} list table #{@table_name}") + + case rule_handle_regex(~r/#{rule_str}.*# handle (?\d+)/, cmd_output) do + [handle] -> + {:ok, handle} + + [] -> + {:error, cmd_output} + end end - def delete_rule({6, s, d, :deny}) do - exec!("iptables6 -D firezone -s #{s} -d #{d} -j DROP") - end - - def delete_rule({6, s, d, :allow}) do - exec!("iptables6 -D firezone -s #{s} -d #{d} -j ACCEPT") + defp rule_handle_regex(regex, cmd_output) do + Regex.run(regex, cmd_output, capture: :all_names) end def restore(_rules) do