Source Code

Now that FortiOS 7.0 has been out for a bit, I decided it was time to redesign my home SDWAN/ADVPN lab for 7.0.1. I started off building it manually, but then I thought, why not use Terraform to build the infrastructure so that I can easily add or remove devices as needed. I am going write this a multi part series starting with a single FortiGate that I use as a router.

Lab Design

The design is similar to this reference design. The lab I am creating will use FEC and packet duplication which are not part of the design in the link. I am using EVE-NG for this lab, my old lab is running in ESXI. I recently made the move to EVE so that I could adjust things like link quality on the fly without a wan emulator. I am running KVM/libvirt for any vms that cannot run in EVE. In this post will be focusing on the Core-DC device, which is a FGT that is used as a datacenter router.

Configuration

The Core-DC device has a pretty simple configuration. The IP address and interface information is as follows.

Interface IP Address Description
lo0 172.28.255.252/32 Loopback
port1 172.28.0.2/30 To-Hub1
port2 172.28.0.6/30 To-Hub2
port3 172.28.254.1/24 Local-LAN
port10 10.6.20.142/24 Management

The only interface that has been configured so far is the port10 for management access. We will also be setting up BGP to speak to both hubs. We will be advertising the Local-LAN subnet to both BGP peers.

Terraform Code

main.tf

This tells Terraform to download the FortiOS provider when you run terraform init

terraform {
  required_providers {
    fortios = {
      source = "fortinetdev/fortios"
    }
  }
}

This file tells Terraform to use the FortiOS provider, sets the hostname, and the api key. I have insecure set to true because I am running in a lab.

provider.tf

provider "fortios" {
  hostname = var.hostname
  token    = var.token
  insecure = "true"
}

The following variables are to setup communication to the FortiGate. You don’t need two files, I just do it so I can add terraform.tfvars to my .gitignore and still push my variables file to github/gitlab.

variables.tf

Variables for hostname and API key.

variable "hostname" {}
variable "token" {}

terraform.tfvars

Set these to your device’s management IP/FQDN and API key.

hostname = <MANAGEMENT IP>
token    = <API KEY>

interfaces.tf

This is where the bulk of the configuration happens. We are adding a loopback and configuring settings on three physical interfaces.

resource "fortios_system_interface" "lo0" {
  ip          = "172.28.255.253 255.255.255.255"
  allowaccess = "ping https ssh snmp http telnet fgfm radius-acct probe-response fabric ftm speed-test"
  name        = "lo0"
  role        = "lan"
  type        = "loopback"
  vdom        = "root"
  mode        = "static"
  status      = "up"
}

resource "fortios_system_interface" "port1" {
  name                  = "port1"
  ip                    = "172.28.0.2 255.255.255.252"
  status                = "up"
  device_identification = "enable"
  speed                 = "auto"
  role                  = "lan"
  allowaccess           = "ping"
  mode                  = "static"
  type                  = "physical"
  vdom                  = "root"
  alias                 = "To-Hub1"
}

resource "fortios_system_interface" "port2" {
  name                  = "port2"
  ip                    = "172.28.0.6 255.255.255.252"
  status                = "up"
  device_identification = "enable"
  speed                 = "auto"
  role                  = "lan"
  allowaccess           = "ping"
  mode                  = "static"
  type                  = "physical"
  vdom                  = "root"
  alias                 = "To-Hub2"
}

resource "fortios_system_interface" "port3" {
  name                  = "port3"
  ip                    = "172.28.254.1 255.255.255.0"
  status                = "up"
  device_identification = "enable"
  speed                 = "auto"
  role                  = "lan"
  allowaccess           = "ping"
  mode                  = "static"
  type                  = "physical"
  vdom                  = "root"
  alias                 = "LAN"

bgp.tf

eBGP settings for the Fortigate.

resource "fortios_router_bgp" "bgp" {
  as               = "64999"
  ebgp_multipath   = "enable"
  router_id        = "172.28.255.252"
  graceful_restart = "enable"
  neighbor {
    ip                   = "172.28.0.1"
    remote_as            = "65000"
    soft_reconfiguration = "enable"
  }
  neighbor {
    ip                   = "172.28.0.5"
    remote_as            = "65000"
    soft_reconfiguration = "enable"
  }
  network {
    id     = "1"
    prefix = "172.28.254.0 255.255.255.0"
  }
}

policy.tf

This is last portion of the configuration. It is just and any <> any rule to allow all traffic in both directions. Which essentially disables bypasses all security and turns this FortiGate into a router.

resource "fortios_firewall_policy" "allow-all" {
  action     = "accept"
  logtraffic = "all"
  name       = "allow-all"
  policyid   = 1
  schedule   = "always"

  dstaddr {
    name = "all"
  }

  dstintf {
    name = "any"
  }

  service {
    name = "ALL"
  }

  srcaddr {
    name = "all"
  }

  srcintf {
    name = "any"
  }
}

Running terraform apply

When we run terraform apply we see the following. There are 8 total resources that will be created. A few of them I didn’t cover. The log settings and system global, but they are in the source code.

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # fortios_firewall_policy.allow-all will be created
  + resource "fortios_firewall_policy" "allow-all" {
      + action                      = "accept"
      + dynamic_sort_subtable       = "false"
      + logtraffic                  = "all"
      + policyid                    = 1
      + schedule                    = "always"

      + dstaddr {
          + name = "all"
        }

      + dstintf {
          + name = "any"
        }

      + service {
          + name = "ALL"
        }

      + srcaddr {
          + name = "all"
        }

      + srcintf {
          + name = "any"
        }
    }

  # fortios_log_setting.log will be created
  + resource "fortios_log_setting" "log" {
      + dynamic_sort_subtable   = "false"
      + fwpolicy_implicit_log   = "enable")
    }

  # fortios_router_bgp.bgp will be created
  + resource "fortios_router_bgp" "bgp" {
      + as                                 = 64999
      + dynamic_sort_subtable              = "false"
      + ebgp_multipath                     = "enable"
      + graceful_restart                   = "enable"
      + router_id                          = "172.28.255.252"

      + neighbor {
          + ip                            = "172.28.0.1"
          + remote_as                     = 65000
          + soft_reconfiguration          = "enable"
        }
    
      + neighbor {
          + ip                            = "172.28.0.5"
          + remote_as                     = 65000
          + soft_reconfiguration          = "enable"
        }

      + network {
          + id        = 1
          + prefix    = "172.28.254.0 255.255.255.0"
        }
    }

  # fortios_system_global.sysglobal will be created
  + resource "fortios_system_global" "sysglobal" {
      + admintimeout                             = 480
      + hostname                                 = "Core-DC"
      + timezone                                 = "12"
    }

  # fortios_system_interface.lo0 will be created
  + resource "fortios_system_interface" "lo0" {
      + allowaccess                                = "ping https ssh snmp http telnet fgfm radius-acct probe-response fabric ftm speed-test" 
      + dynamic_sort_subtable                      = "false"
      + ip                                         = "172.28.255.253 255.255.255.255"
      + mode                                       = "static"
      + name                                       = "lo0"
      + role                                       = "lan"
      + status                                     = "up"
      + type                                       = "loopback"
      + vdom                                       = "root"

  # fortios_system_interface.port1 will be created
  + resource "fortios_system_interface" "port1" {
      + alias                                      = "To-Hub1"
      + allowaccess                                = "ping"
      + device_identification                      = "enable"
      + dynamic_sort_subtable                      = "false"
      + ip                                         = "172.28.0.2 255.255.255.252"
      + mode                                       = "static"
      + name                                       = "port1"
      + role                                       = "lan"
      + speed                                      = "auto"
      + status                                     = "up"
      + type                                       = "physical"
      + vdom                                       = "root"

  # fortios_system_interface.port2 will be created
  + resource "fortios_system_interface" "port2" {
      + alias                                      = "To-Hub2"
      + allowaccess                                = "ping"
      + device_identification                      = "enable"
      + dynamic_sort_subtable                      = "false"
      + ip                                         = "172.28.0.6 255.255.255.252"
      + mode                                       = "static"
      + name                                       = "port2"
      + role                                       = "lan"
      + speed                                      = "auto"
      + status                                     = "up"
      + type                                       = "physical"
      + vdom                                       = "root"
    }

  # fortios_system_interface.port3 will be created
  + resource "fortios_system_interface" "port3" {
      + alias                                      = "LAN"
      + allowaccess                                = "ping"
      + device_identification                      = "enable"
      + dynamic_sort_subtable                      = "false"
      + ip                                         = "172.28.254.1 255.255.255.0"
      + mode                                       = "static"
      + name                                       = "port3"
      + role                                       = "lan"
      + speed                                      = "auto"
      + status                                     = "up"
      + type                                       = "physical"
      + vdom                                       = "root"
    }

Plan: 8 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

fortios_log_setting.log: Creating...
fortios_system_global.sysglobal: Creating...
fortios_system_interface.lo0: Creating...
fortios_firewall_policy.allow-all: Creating...
fortios_system_interface.port1: Creating...
fortios_system_interface.port3: Creating...
fortios_system_interface.port2: Creating...
fortios_router_bgp.bgp: Creating...
fortios_log_setting.log: Creation complete after 0s [id=LogSetting]
fortios_system_global.sysglobal: Creation complete after 0s [id=SystemGlobal]
fortios_firewall_policy.allow-all: Creation complete after 0s [id=1]
fortios_system_interface.port1: Creation complete after 0s [id=port1]
fortios_system_interface.lo0: Creation complete after 0s [id=lo0]
fortios_system_interface.port2: Creation complete after 0s [id=port2]
fortios_router_bgp.bgp: Creation complete after 0s [id=RouterBgp]
fortios_system_interface.port3: Creation complete after 0s [id=port3]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

Checking that it all worked

We see that 8 resources were create so lets take a look at a few of them.

BGP

config router bgp
    set as 64999
    set router-id 172.28.255.252
    set ebgp-multipath enable
    set graceful-restart enable
    config neighbor
        edit "172.28.0.1"
            set soft-reconfiguration enable
            set remote-as 65000
        next
        edit "172.28.0.5"
            set soft-reconfiguration enable
            set remote-as 65000
        next
    end
    config network
        edit 1
            set prefix 172.28.254.0 255.255.255.0
        next
    end
    config redistribute "connected"
    end
    config redistribute "rip"
    end
    config redistribute "ospf"
    end
    config redistribute "static"
    end
    config redistribute "isis"
    end
    config redistribute6 "connected"
    end
    config redistribute6 "rip"
    end
    config redistribute6 "ospf"
    end
    config redistribute6 "static"
    end
    config redistribute6 "isis"
    end  
end

Loopback interface

config system interface
    edit "lo0"
        set vdom "root"
        set ip 172.28.255.253 255.255.255.255
        set allowaccess ping https ssh snmp http telnet fgfm radius-acct probe-response fabric ftm speed-test
        set type loopback
        set role lan
        set snmp-index 15
    next
end

Everything went as expected. In the next post I plan to create some variables so I can create the two hubs at the same time. Here is the source code used in this post.