terraform-provider-routeros icon indicating copy to clipboard operation
terraform-provider-routeros copied to clipboard

Manage *all* Firewall Filters with Terraform?

Open Nornode opened this issue 1 year ago • 8 comments

Is your feature request related to a problem? Please describe. Yes, Managed all Filters with terraform I'm missing a clear and consise way of managing all firewall filters (and soon other parts too?) through Terraform.

Describe the solution you'd like With an input variable I'd like to manage all firewall filters. e.g.

---

firewall_filters:
  special_dummy_rule_to_show_fasttrack_counters:
    disabled: false
    action: "passthrough"
    chain: "forward"
    comment: "Default Config"

  defconf input accept established related untracked:
    disabled: false
    action: "accept"
    chain: "input"
    connection_state: "established,related,untracked"
    hw_offload: false
    priority: 0
    random: 0
    comment: "Default Config"

  defconf input drop invalid:
    disabled: false
    chain: "input"  
    action: "drop"
    connection_state: "invalid"
    
  defconf accept ICMP:
    disabled: false
    chain: "input"
    action: "accept"
    protocol: "icmp"
    comment: "Default Config - Allow ICMP"
    hw_offload: false

Placing another custom rule in between one rule or last, I'd wish for the module/resource to arrange the rules in order on the router.

I don't know if this is really something that is possible or even possible? I guess it would case many re-creates and potentially also locking out access during re-creates... ?

Additional context Add any other context or screenshots about the feature request here.

Nornode avatar Dec 19 '23 13:12 Nornode

Unfortunately the example and documentation is a bit shorthanded and added example or references to other implementations may work better?

As I see it in the example it only gives one option and that is to add more rules on place 0 after filtering out some crucial ones.?

place_before = "${data.routeros_firewall_filter.fw.rules[0].id}"

And if I use the above example reference, what would happen if I create two filters on the same apply? - They'd get teh same "place_after" position..

Nornode avatar Dec 19 '23 13:12 Nornode

Please see the end of this discussion.

vaerh avatar Dec 19 '23 13:12 vaerh

Please see the end of this discussion.

Looks like its linking back to this issue?

Nornode avatar Dec 19 '23 14:12 Nornode

Sorry #293 https://github.com/terraform-routeros/terraform-provider-routeros/blob/f2f13c0ccff5cecc75c2a9fc00c16ba30713385c/docs/resources/move_items.md#example-usage

vaerh avatar Dec 19 '23 14:12 vaerh

Thanks, Yeah, I think I was more towards the bit-bang approach... Given the var.firewall_filters above.

constructing: IT's easy to construct a locals block resulting in this:

firewall_filter = {
  "defconf accept ICMP TF_Managed" = "*3"
  "defconf accept in ipsec policy TF_Managed" = "*6"
  "defconf accept out ipsec policy TF_Managed" = "*7"
  "defconf accept to local loopback CAPsMAN TF_Managed" = "*4"
  "defconf drop all from WAN not DSTNATed TF_Managed" = "*B"
  "defconf drop all not coming from LAN TF_Managed" = "*5"
  "defconf fasttrack TF_Managed" = "*8"
  "defconf forward accept established,related, untracked TF_Managed" = "*9"
  "defconf forward drop invalid TF_Managed" = "*A"
  "defconf input accept established related untracked TF_Managed" = "*1"
  "defconf input drop invalid TF_Managed" = "*2"
  "special dummy rule to show fasttrack counters" = "*C"
  "special_dummy_rule_to_show_fasttrack_counters TF_Managed" = "*F"
}

It should be fairly simple to do something along the lines with:

locals {
    firewall_filter = { for key, value in data.routeros_firewall.fw.rules : value.comment => value.id }  # Seen above 

    ## Following is Pseudo Code and does not work! - But shows How I thought it can be solved.
    id_of_next_filter_in_variable = { for key, value in var.firewall_filters : key => (index(keys(var.firewall_filters), key) + 1) } 
    
    firewall_filters = for i in keys(local.firewall_filter) : i => { 
      place_before = local.id_of_next_filter_in_variable[i].value
      ... = ... ##Any other key & value pair in var.firewall_filter
    }
}

Idea is that you should end up with something looking like this: All dynamicly created because they are all configured in var.firewall_filters as a single structure.

local.firewall_filters = {
  special_dummy_rule_to_show_fasttrack_counters =
    place_before = "*1"
    ...
  defconf input accept established related untracked = 
    place_before = "*2"
    ...
  defconf input drop invalid = 
    place_before = "*3"
    ...
  ...
    ...
}

Then it can be then created as something like:

resource "routeros_ip_firewall_filter" "rule" {
  for_each =  { for key, value in local.firewall_filters : key => value  } 

  place_before          = try(each.value.place_before, "*1")
  disabled              = try(each.value.disabled, var.disabled)
  action                = each.value.action
  chain                 = each.value.chain
  connection_state      = try(each.value.connection_state, null)
  connection_nat_state  = try(each.value.connection_nat_state, null)
  hw_offload            = try(each.value.hw_offload, false )
  ... = ...
}

Or do you know of a reason this should not work?

I can just think of a few complications that can occur:

  1. Creating several filters at the same time when the data is not yet updated.
  2. Creating one "last" isn't possible

Nornode avatar Dec 19 '23 18:12 Nornode

There are a few things about your approach that I'm not sure work:

  • You don't know the rule ids before creating the rules, so it's not correct to operate on the id.
  • You don't know in what order the rules will arrive, for this reason place_before stops working on large rule sets.

vaerh avatar Dec 19 '23 18:12 vaerh

I think if not applying exclusively from Terraform after a reset without default configuration (which is also my preference I think, to keep defconf) it will be best to declare the resources for the defconf rules etc. and then import them?

Then they can not only be reference by other rules but also we can easily see (and accept or not) drift after an update. Except of course newly added rules.

The only thing better / that would account for rules added in an ROS update that I can think of would be a kind of meta resource that took ownership over all rules, like:

resource "routeros_ip_firewall" "example" {
    filters = [
      # ...
    ]

    # or:
    # filter {
    # ...
}

Then if you added that empty and applied against a default router it would refresh, see the defconf rules, and want to remove them. Just some one-time setup to add them in, handily planning again would point out any typos or bits you got wrong/missed, and then they'd be managed going forwards, and any new defconf added in an update would work the same way on next plan.

OJFord avatar Mar 27 '24 10:03 OJFord

It seems to me that the creation and management of the meta resource should be done from some higher level application. Now it is possible to comfortably operate with firewall rules by implementing the move command. It is possible to control the completeness and order of firewall rules created by TF, but there is no possibility to react adequately to changes made by external systems.

vaerh avatar Mar 27 '24 11:03 vaerh