SYMPLIFY LEARNING

Bootstrapping Palo Alto VM series in AWS using Terraform

Background

Palo Alto Networks firewalls support a process known as ‘bootstrapping’ which allows you create a repeatable way of provisioning your new firewalls. It allows you deploy basic configuration to the firewall upon startup.

Some of the basic configuration you can push to the firewall as part of the bootstrap include DNS servers, management interface IP address, Panorama server addresses, device group, template etc. The option to define the Panorama that the firewall should be managed by as well as the device group and template can be very useful in large environments where new firewalls constantly need to be provisioned.

At its core, the bootstrap process requires the firewall to locate a file called init-cfg.txt upon startup. In this file, the basic configuration for the firewall is defined. As a result, to successfully complete the bootstrap process, the firewall admin must provide the means for the firewall to retrieve this init-cfg.txt file.

Implementation

In this scenario, I use an S3 bucket in AWS to host my init-cfg-txt file. The different bootstrap methods support by Palo Alto Networks VM-series are documented here.

To successfully bootstrap this firewall, I need the following key resources:

  • an S3 bucket to host my init-cfg.txt file and other mandatory directories
  • an VM-series firewall EC2 instance
  • an IAM policy defining actions that are allowed on my S3 bucket
  • an IAM role that my VM-series instance can assume to access the S3 bucket
  • an IAM instance profile to be applied to my VM-series instance
  • a file called init-cfg.txt with my basic configuration defined

The diagram below depicts how these resources work together

Now that we’ve summarised the different resources needed, let’s take a look at the init-cfg.txt file and the terraform code. For brevity, I’ve excluded terraform code for resources such as VPC, subnets, route tables and internet gateway that were also created in order for me to provision and EC2 instance.

First, I created my init-cfg.txt file with the below basic configuration:

 type=dhcp-client
 hostname=bootstrap_lab_fw
 dns-primary=169.254.169.253
 dns-secondary=8.8.8.8
 dhcp-send-hostname=yes
 dhcp-send-client-id=yes
 dhcp-accept-server-hostname=no
 dhcp-accept-server-domain=yes

Then I created the S3 bucket and the mandatory directories for bootstrap. I also uploaded my init-cfg.txt file to the S3 bucket by creating an S3 object

### S3 Bucket and directories ###

resource "aws_s3_bucket" "bootstrap_bucket" {
  bucket = "palo-bootstrap-bucket-1123"

  tags = {
    Name        = "palo-bootstrap-bucket-1123"
    Environment = "Dev"
  }
}

resource "aws_s3_object" "config_object" {
  bucket = aws_s3_bucket.bootstrap_bucket.id
  key    = "config/init-cfg.txt"
  source = "./init-cfg.txt"

  etag = filemd5("./init-cfg.txt")

}

resource "aws_s3_object" "content_object" {
  bucket = aws_s3_bucket.bootstrap_bucket.id
  key    = "content/"


}

resource "aws_s3_object" "license_object" {
  bucket = aws_s3_bucket.bootstrap_bucket.id
  key    = "license/"


}

resource "aws_s3_object" "software_object" {
  bucket = aws_s3_bucket.bootstrap_bucket.id
  key    = "software/"

}

Following that, I created my IAM policy, IAM role and IAM instance profile

resource "aws_iam_policy" "firewall_instance_policy" {
  name = "firewall_bootstrap_policy"
  description = "Defines permissions that firewall being bootstrapped can carry out"
  policy = jsonencode(
      {
        Version = "2012-10-17"
        Statement = [
          {
            Action   = "s3:ListBucket"
            Effect   = "Allow"
            Resource = "arn:aws:s3:::${aws_s3_bucket.bootstrap_bucket.bucket}"
          },
          {
            Action   = "s3:GetObject"
            Effect   = "Allow"
            Resource = "arn:aws:s3:::${aws_s3_bucket.bootstrap_bucket.bucket}/*"
          },
        ]

      }
  )

}

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role" "firewall_instance_role" {
  name               = "firewall_instance_role"
  assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
  managed_policy_arns = [aws_iam_policy.firewall_instance_policy.arn]
}

resource "aws_iam_instance_profile" "test_profile" {
  name = "firewall_instance_profile"
  role = aws_iam_role.firewall_instance_role.name
}

With those created, I then provisioned my VM-series EC2 instance:

resource "aws_instance" "lab_firewall_instance" {
    ami = "var.ami"
    instance_type = "m5.large"

    network_interface {
      network_interface_id = aws_network_interface.management_interface.id
      device_index = 0
    }
  key_name      = "fw_bootstrap_key_pair"
  iam_instance_profile = aws_iam_instance_profile.test_profile.name
  user_data = "vmseries-bootstrap-aws-s3bucket=${aws_s3_bucket.bootstrap_bucket.id}"
  
}

After defining all the required resources, I ran terraform apply to provision them.

Verifying

After provisioning the resources, I accessed the firewall via SSH and ran the command show log system to verify that bootstrap completed successfully. The following snippets from the system logs verify that bootstrap was successful:

2023/11/11 14:04:23 info     general        general 0  init-cfg: initial configuration processed from init cfg file
...

2023/11/11 14:04:23 info     hw             bootstr 0  Media detected successfully
2023/11/11 14:04:23 info     hw             bootstr 0  Media sanity check successful

...

2023/11/11 14:04:40 info     hw             bootstr 0  Bootstrap successfully completed sw-version: 10.2.4; app-version: 8638-7689; threat-version: 8638-7689

The show system bootstrap status also provides of summary of the bootstrap status:

admin@bootstrap_lab_fw> show system bootstrap status

Bootstrap Phase               Status         Details
===============               ======         =======
Media Detection               Success        Media detected successfully
Media Sanity Check            Success        Media sanity check successful
Parsing of Initial Config     Successful
Auto-commit                   Successful

Additionally, I used the show system info command to verify settings such as the hostname specified in the init-cfg.txt file were successfully applied

admin@bootstrap_lab_fw> show system info

hostname: bootstrap_lab_fw

While this lab exercise only applied the hostname and DNS servers, other configuration can be applied to the Palo Alto Networks firewall during the bootstrap process. A sample init-cfg.txt file with other parameters that can be configured can be seen on the Palo Alto Networks website here.