Ubiquiti EdgeOS Terraform Provider

Frank Greco Jr
2 min readJan 16, 2022

I needed an excuse to try Terraform’s new provider framework and implement some IaaS automation for my Ubiquiti EdgeOS router which led me to create a Terraform provider for EdgeOS! This post will cover how to use the provider. You can always find the Terraform documentation here and the GitHub repo here.

In this guide, we’ll configure our router’s firewall. Specifically, we’ll create a ruleset that only allows access to our router over specific ports. In practice, this ruleset would work in conjunction with other rulesets to provide a holistic security posture for your network.

The first thing we need to do is configure the Terraform provider and tell it where to find our router’s configuration API and how to authenticate to it.

provider "edge" {
username = var.username # optionally use EDGE_USERNAME env var
password = var.password # optionally use EDGE_PASSWORD env var
host = var.host # optionally use EDGE_HOST env var
insecure = var.insecure # optionally use EDGE_INSECURE env var
}

The next thing we need to do is create an address group that will represent our router. In this example, we three /24 interfaces that each can access the router over the first available address.

resource "edge_firewall_address_group" "router" {
name = "router"
description = "router interface addresses"

cidrs = [
"192.168.2.1",
"192.168.3.1",
"192.168.4.1"
]
}

We then construct a port group that we’ll use to encapsulate the set of ports we want to whitelist.

resource "edge_firewall_port_group" "whitelist" {
name = "whitelist"
description = "common known ports"

port_ranges = [{
from = 8080
to = 8081
}]

ports = [80, 443, 22]
}

Then, we start to pull it all together by creating our ruleset which will contain two rules. The first will accept all traffic destined for our address and port groups. The second will overwrite the first rule but only for one address on a single port. If no rules are matched, the ruleset’s default drop action will deny the traffic.

resource "edge_firewall_ruleset" "router" {
name = "router"
description = "router bound traffic"
default_action = "drop"

rule {
priority = 10
description = "common known ports"
action = "accept"
protocol = "all"

destination = {
address_group = edge_firewall_address_group.router.name
port_group = edge_firewall_port_group.whitelist.name
}
}

rule {
priority = 20
description = "no ssh from a specific address"
action = "drop"
protocol = "tcp"

destination = {
address_group = edge_firewall_address_group.router.name
port = {
from = 22
to = 22
}
}

source = {
address = "192.168.2.44/32"
}
}
}

So far, we’ve created our ruleset but haven’t yet attached it to our interface. We can attach a ruleset for inbound, outbound, and local traffic in the context of the interface. Note that this means that the destination and source specified in our ruleset will be evaluated in the context of the direction we attached it to.

data "edge_interface_ethernet" "eth1" {
id = "eth1"
}
resource "edge_firewall_ruleset_attachment" "eth1" {
interface = data.edge_interface_ethernet.eth1.id
in = edge_firewall_ruleset.router.name
}

And that’s it! All that’s left is to apply it. The next EdgeOS feature this provider will support will be VPNs!

--

--