Fortinet Network Infrastructure Automation w/ Consul+Terraform

Sebastian Maniak
6 min readOct 23, 2022

To start it’s useful to talk about what does a traditional environment look like in terms of end-to-end application workflow and how that typically requires some sort of manual intervention at the network layer.

Typically the build of an application starts like this, we have a proxy (F5 or NGINX), a Frontend, API service and a Database.

Phase 1
How we start

but of course there’s an underlying network that needs to support this, so typically between services we will install some firewalls and build some security zones. [External, DMZ, Inside, Database..etc]

These firewalls are being used to govern access so this might impose a rule that says

  • proxy-ip 10.10.1.11 needs to communicate to frontend-ip 10.10.2.12
  • frontend-ip 10.10.2.12 needs to communicate to api-ip 10.10.3.13

This might be a relatively classic pattern, the kind of challenge we typically seeing is that the application team continues to iterate and push a new version. When they scale up and down and they have to interface with the network in a relatively manual way so they might deploy a new instance, but now typically they have to file a ticket against the networking teams to update the address group on forigate firewall.

In this classic networking scenario, the application team is not empowered to really self-service and deploy their application and manage that life cycle. Yes they might be able to deploy it onto a platform such as kubernetes or cloud in an automated way but then they’re stuck filing a ticket and waiting potentially days or weeks for the underlying network to get automated right or updated in this case.

So where does consul start to fit into this story? Well consul acts as a service discovery mechanism at its core. When we deploy consul we are using it as a source of truth that helps us identify all our applications.

This gives us sort of a bird’s eye view or a universal catalog of what all of the services that are running in our environment. So instead of hard coding IPs into our application will query a Consul to say wheres my database running, this start to avoid the fragility of having hard-coded IP addresses allowing us to scale things up and down and manage failure in a more reliable way and overall enable sort of a micro service pattern.

In most cases we sill use firewalls and load balancers and these might hardware devices or software appliances that are between different services. Each firewall has a set of rules that need to be managed and the operates need to grant access on an IP layer level.

This is where terraform comes into the picture. Terraform focus on the Day 0 and Day 1 of implementing the firewall policies, rules and address groups across your organization.

For Day 2 operations we have Consul Terraform Sync.

The following code/demo provides a highlevel overview on how you can automatically manage day 2 network operations using identify based workflow automation.

The goal is to eliminate the shitty day 2 operational tasks, so we can focus on more important things.

  • Better Practices, Reduced Risk
  • Get Everyone on The Same Page
  • Update Fortinet Address Groups dynamically based on the health and availability of new services

How Does it Work?

First, let’s take a look at the overall architecture. We have different services, that all are all registered to Consul to provide us a universal service catalog.

We have our fortinet policies built using terraform, that outline which who needs to communicate to who based on the. This is what we call Day 0.

Next…Day 2 — w/ Consul Terraform CTS

CTS is a lightweight service that watches for changes in Consul.

Phase 1

CTS looks out for changes to services and pulls information from the Consul catalog…

Phase 2

based on the metadata/info from Consul the “Source of Trust”, CTS will generate a new Terraform manifest and apply it to the network devices it needs to configure.

Phase 3

As services go up and down, CTS will automatically update the Address groups in the forinet firewalls.

How to deploy the demo in AWS

Step 1 : Pull the git repo

git clone https://github.com/maniak-academy/medium-fortigate-aws-nia

Step 2: Deploy Fortinet and build a VPC

Let’s deploy the Fortinet Firewall for our lab. For this example will will just build a simple vpc with a single Fortigate deployment (Demo, no need for HA).

Execute the terraform code

  • Option 1 with Terraform
terraform apply -target=module.security
  • Option 2 with Mac M1
docker-compose -f docker-compose.yaml run --rm terraform apply -target=module.security

Note it takes about 5–7 minutes to spin up the foritgate OS in AWS. Be patient.

The output will look like this.

fortigate_password = <sensitive>
fortigate_public_ip = "https://3.215.158.76:8443"
ssh-foritgate-firewall = "ssh -i terraform-20221021021239128900000001.pem admin@3.215.158.76"

Step 3: Generate REST API Token

FortiOS Provider requires an API token to be authenticated. An API token is generated by creating a new REST API admin.

From the output of applying terraform -apply you will see “ssh-foritgate-firewall” this will allow you to ssh into the Fortigate Firewall and generate an API Token for CTS.

  • SSH into the Forigate {Note, your ip and key name might be different]
ssh -i terraform-202210<example>0000001.pem admin@<forinet-mgmt-ip>
  • Execute the following command
execute api-user generate-key tfapi
  • Output will show you the New API KEY:
FGTVM # execute api-user generate-key tfapiNew API key: Qm5kpc1zh1xggQzNgyts1gzGr7dH99NOTE: The bearer of this API key will be granted all access privileges assigned to the api-user tfapi.
  • Edit the terraform.tfvars file, and insert the New API Key in the fortigate_token variable.
fortigate_token = "Qm5kpc1zh1xggQzNgyts1gzGr7dH99"
  • Save the file

Step 4: Deploy Your Apps and Infrat Start NIA

Let’s deploy the infrastructure. The infrastructure consits of

  • Consul + Consul Terraform Sync
  • API Web Server
  • Web Server

Execute the following command to launch the infrastructure.

  • Option 1 with Terraform
terraform apply -target=module.infraStep 5

Once all the services have been deployed and registered to Consul. Log into the Consul and the FW to validate all the changes have been made.

So get the password for the firewall execute this command

docker-compose -f docker-compose.yaml run --rm terraform output -json | jq

Output example… you can take the password and log into the fortinet firewall.

{
"fortigate_password": {
"sensitive": true,
"type": "string",
"value": "!xYp_fcOp44JekREgLV87P"
},
"fortigate_public_ip": {
"sensitive": false,
"type": "string",
"value": "https://52.2.28.250:8443"
}
}
  • Option 2 with Mac M1
docker-compose -f docker-compose.yaml run --rm terraform apply -target=module.infra

Step 5: Log into the Fortigate Firewall

Validate the cts-web and cts-api address groups where created.

Log into the PAN and validate the cts-api or cts-web address group has been added.

Step 6: See CTS in action.

Next, lets scale the web and api applications to see CTS live in action.

Go into the /infra directory and in the variables.tf edit the web_count = 2 and api_count =3

Save the configuration and execute

terraform apply

Verify the new services are added to the address group

--

--