Palo Alto and Ansible Example

In this blog post, I will show you how to use Ansible to deploy configurations to the Palo Alto Firewalls.

Setting up the environment

I'm using Ubuntu 18.04 server for this example.

  • Ansible Galaxy Role
  • pip
  • pan-os SDK

Palo Alto Networks Ansible Galaxy Role

https://ansible-pan.readthedocs.io/en/latest/

The Palo Alto Networks Ansible Galaxy role is a collection of modules that automate configuration and operational tasks on Palo Alto Firewalls and Panorama. The underlying protocol uses API calls that are wrapped within the Ansible framework.

You can install the collection by using ansible-galaxy collection install paloaltonetworks.panos command

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ ansible-galaxy collection install paloaltonetworks.panos
Process install dependency map
Starting collection install process
Installing 'paloaltonetworks.panos:2.4.0' to '/home/ubuntu/.ansible/collections/ansible_collections/paloaltonetworks/panos'

Install pip

Pip is a package management system that simplifies the installation and management of software packages written in Python.

You can install it by using sudo apt install python-pip command

Palo Alto Networks PAN-OS SDK for Python

The PAN-OS SDK for Python (pan-os-python) is a package to help interact with Palo Alto Networks devices

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ pip install pan-os-python
Collecting pan-os-python
  Downloading https://files.pythonhosted.org/packages/7b/3e/94d901ed6fc435307bdb34e5b7a8f2a69902a71f44a44a987c63359c5c1f/pan_os_python-1.0.2-py2.py3-none-any.whl (122kB)
    100% |████████████████████████████████| 122kB 2.6MB/s 
Collecting pan-python<0.17.0,>=0.16.0 (from pan-os-python)
  Using cached https://files.pythonhosted.org/packages/3b/fd/20127311eae9c31b7ae3e47d2279da7aea2aab2bf9f50b19d56ef367735e/pan_python-0.16.0-py2.py3-none-any.whl
Installing collected packages: pan-python, pan-os-python
Successfully installed pan-os-python-1.0.2 pan-python-0.16.0

Ansible Installation

You can install Ansible by using this guide: Installing Ansible — Ansible Documentation

$ sudo apt update
$ sudo apt install software-properties-common
$ sudo apt-add-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible

Ansible Installation

Ansible files

ubuntu@ubuntu_1804:~/palo-ansible$ tree 
.
├── inventory
│   └── host-file
└── playbooks
    ├── ansible.cfg
    └── palo.yml

2 directories, 3 files

Tree

ubuntu@ubuntu_1804:~/palo-ansible$ cat inventory/host-file 
[palo]
PALO-1 ansible_host=192.168.1.65

hosts

ubuntu@ubuntu_1804:~/palo-ansible$ cat playbooks/ansible.cfg 
[defaults]
inventory = /home/ubuntu/palo-ansible/inventory/host-file

Ansible.cfg

Diagram

Diagram
I created a local user name and password on the Palo Alto via the GUI

Get your API key

In this example, I'm using the API key instead of using username/password.

https://docs.paloaltonetworks.com/pan-os/9-0/pan-os-panorama-api/get-started-with-the-pan-os-xml-api/get-your-api-key.html

To use the API, you must generate the API key required for authenticating API calls. To generate an API key, make a GET or POST request to the firewall’s hostname or IP addresses using the administrative credentials and type keygen

curl -k -X GET 'https://<firewall>/api/?type=keygen&user=<username>&password=<password>'
curl -k -X POST 'https://192.168.1.65/api/?type=keygen&user=ansible&password=Cisco123'

A successful API call returns status="success" along with the API key within the key element.

<response status = 'success'><result><key>LUFRPT1lV0JxY1N0bWp5d2hpeCtIZ0Urc0oxZFdvQWM9c3RPNGhaeGpjLzA1N0JVRjJ5SGduZ252S2FrL09sL0JueUU5dlRITEdxcDl0bllxM3phbGc2K2FCdTNZZWVNdw==</key></result></response>

Playbook

The Playbook creates the following

  • Create address-objects for server1 and users
  • Management Profile which allows ping to the INSIDE interface
  • Create INSIDE and USERS zones
  • Adding eth1/1 and eth1/2 interfaces to each zone.
  • Security rule allowing ping from users-subnet to server1
ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ cat palo.yml 
---

- name: Palo Alto Playbook
  hosts: palo
  gather_facts: false
  connection: local

  collections:
    - paloaltonetworks.panos


  vars:
    palo_provider:
      ip_address: '192.168.1.65'
      api_key: LUFRPT1lV0JxY1N0bWp5d2hpeCtIZ0Urc0oxZFdvQWM9c3RPNGhaeGpjLzA1N0JVRjJ5SGduZ252S2FrL09sL0JueUU5dlRITEdxcDl0bllxM3phbGc2K2FCdTNZZWVNdw==

  tasks:
    - name: Create Server Object
      panos_address_object:
        provider: '{{ palo_provider }}'
        name: 'server1'
        value: '172.16.1.10'
        description: 'vmware'

    - name: Create Users Object
      panos_address_object:
        provider: '{{ palo_provider }}'
        name: 'users-subnet'
        value: '10.10.10.0/24'
        description: 'users subnet'

    - name: Management Profile
      panos_management_profile:
        provider: '{{ palo_provider }}'
        name: 'Allow Ping'
        ping: true

    - name: Create INSIDE Zone
      panos_zone:
        provider: '{{ palo_provider}}'
        zone: 'INSIDE'
        mode: 'layer3' 
    
    - name: Create USERS Zone
      panos_zone:
        provider: '{{ palo_provider}}'
        zone: 'USERS'
        mode: 'layer3'

    - name: ethernet1/1 config
      panos_interface:
        provider: '{{ palo_provider }}'
        if_name: 'ethernet1/1'
        zone_name: 'INSIDE'
        management_profile: 'Allow Ping'
        mode: 'layer3'
        enable_dhcp: false
        ip: ['172.16.1.1/24']

    - name: ethernet1/2 config
      panos_interface:
        provider: '{{ palo_provider }}'
        if_name: 'ethernet1/2'
        zone_name: 'USERS'
        mode: 'layer3'
        enable_dhcp: false
        ip: ['10.10.10.1/24']

    - name: Allow ping from users to server1
      panos_security_rule:
        provider: '{{ palo_provider }}'
        rule_name: 'allow ping'
        description: 'Allow ping from users to server1'
        source_zone: ['USERS']
        source_ip: ['users-subnet']
        destination_zone: ['INSIDE']
        destination_ip: ['server1'] 
        application: ['ping']
        action: 'allow'

    - name: Commit
      panos_commit:
        provider: '{{ palo_provider }}'

Playbook

Verification

verification

Encrypting sensitive data with Ansible Vault.

Ansible Vault encrypts variables or files so, the sensitive data such as passwords or keys are not visible. In our example, we can see that the api_key is visible in the Playbook.

I'm going to move the palo_provider variable to group_vars directory and add the api_key to the vault.

Create a vault and move the api_key

ubuntu@ubuntu_1804:~/palo-ansible/inventory/group_vars/palo$ ansible-vault create vault
New Vault password: 
Confirm New Vault password:
ubuntu@ubuntu_1804:~/palo-ansible/inventory/group_vars/palo$ ansible-vault view vault
Vault password: 
---

vault_api_key: LUFRPT1lV0JxY1N0bWp5d2hpeCtIZ0Urc0oxZFdvQWM9c3RPNGhaeGpjLzA1N0JVRjJ5SGduZ252S2FrL09sL0JueUU5dlRITEdxcDl0bllxM3phbGc2K2FCdTNZZWVNdw==
ubuntu@ubuntu_1804:~/palo-ansible/inventory/group_vars/palo$ cat palo.yml 
palo_provider:
  ip_address: '192.168.1.65'
  api_key: '{{ vault_api_key }}'
ubuntu@ubuntu_1804:~/palo-ansible$ tree
.
├── inventory
│   ├── group_vars			<<<
│   │   └── palo			<<<
│   │       ├── palo.yml	<<<
│   │       └── vault		<<<
│   └── host-file
└── playbooks
    ├── ansible.cfg
    └── palo.yml

4 directories, 5 files
---

- name: Palo Alto Playbook
  hosts: palo
  gather_facts: false
  connection: local

  collections:
    - paloaltonetworks.panos

  tasks:
    - name: Create Server Object
      panos_address_object:
        provider: '{{ palo_provider }}'
        name: 'server1'
        value: '172.16.1.10'
        description: 'vmware'

    - name: Create Users Object
      panos_address_object:
        provider: '{{ palo_provider }}'
        name: 'users-subnet'
        value: '10.10.10.0/24'
        description: 'users subnet'

    - name: Management Profile
      panos_management_profile:
        provider: '{{ palo_provider }}'
        name: 'Allow Ping'
        ping: true

    - name: Create INSIDE Zone
      panos_zone:
        provider: '{{ palo_provider}}'
        zone: 'INSIDE'
        mode: 'layer3' 
    
    - name: Create USERS Zone
      panos_zone:
        provider: '{{ palo_provider}}'
        zone: 'USERS'
        mode: 'layer3'

    - name: ethernet1/1 config
      panos_interface:
        provider: '{{ palo_provider }}'
        if_name: 'ethernet1/1'
        zone_name: 'INSIDE'
        management_profile: 'Allow Ping'
        mode: 'layer3'
        enable_dhcp: false
        ip: ['172.16.1.1/24']

    - name: ethernet1/2 config
      panos_interface:
        provider: '{{ palo_provider }}'
        if_name: 'ethernet1/2'
        zone_name: 'USERS'
        mode: 'layer3'
        enable_dhcp: false
        ip: ['10.10.10.1/24']

    - name: Allow ping from users to server1
      panos_security_rule:
        provider: '{{ palo_provider }}'
        rule_name: 'allow ping'
        description: 'Allow ping from users to server1'
        source_zone: ['USERS']
        source_ip: ['users-subnet']
        destination_zone: ['INSIDE']
        destination_ip: ['server1'] 
        application: ['ping']
        action: 'allow'

    - name: Commit
      panos_commit:
        provider: '{{ palo_provider }}'

The most straightforward way of decrypting content at runtime is to have Ansible prompt you for the appropriate credentials. You can do this by adding the --ask-vault-pass to any ansible or ansible-playbook command.

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ ansible-playbook palo.yml --ask-vault-pass
Vault password: 

PLAY [Palo Alto Playbook] ************************************************

Using Ansible Vault with a Password File

If you do not wish to type in the Vault password each time you execute a task, you can add your Vault password to a file and reference the file during execution.

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ echo 'Cisco123' > .vault_pass

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ ls -la
total 20
drwxrwxr-x 2 ubuntu ubuntu 4096 Dec 22 15:27 .
drwxrwxr-x 4 ubuntu ubuntu 4096 Dec 21 18:03 ..
-rw-rw-r-- 1 ubuntu ubuntu   96 Dec 21 18:12 ansible.cfg
-rw-rw-r-- 1 ubuntu ubuntu 1931 Dec 22 15:05 palo.yml
-rw-rw-r-- 1 ubuntu ubuntu    9 Dec 22 15:27 .vault_pass	<<<

To make Ansible aware of the password file, you can edit your ansible.cfg file

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ cat ansible.cfg 
[defaults]
inventory = /home/ubuntu/palo-ansible/inventory/host-file
vault_password_file = ./.vault_pass

Now, when you run commands that require decryption, you will no longer be prompted for the vault password.

ubuntu@ubuntu_1804:~/palo-ansible/playbooks$ ansible-playbook palo.yml

PLAY [Palo Alto Playbook] ************************

TASK [Create Server Object] ************************
ok: [PALO-1]

Thanks for reading.

As always, your feedback and comments are more than welcome.

Reference

https://ansible-pan.readthedocs.io/en/latest/