The VyOS role

Before we go into the tasks, let's take a look at the variables we will be using. First of all, the content of roles/vyos-firewall/defaults/main.yml:

---

motd_asciiart: |
-----------------------------

VyOS Ansible Managed Firewall

-----------------------------

vyos_nameservers:
- 8.8.8.8
- 8.8.4.4

Here, we are setting just two key values. The first, motd_asciiart, is a multiline banner that will be configured to be displayed whenever we log in to the VyOS device. We are setting the variable as a multiline by using the | after declaring the key. The next key, vyos_nameservers, is a list of DNS resolvers to use. Here, we are using Google's Public DNS resolvers.

There are some other variables used in the playbook; these can be found in group_vars/common.yml as shown in this code:

---

vyos:
host: "192.168.50.10.nip.io"
username: "vagrant"
backup: "yes"
inside:
interface: "172.16.20.1/24"
subnet: "172.16.20.0/24"

whitelist_ips:
- 172.16.20.2

rules:
- { action: 'set', source_address: '0.0.0.0/0', source_port: '80', destination_port: '80', destination_address: '172.16.20.11', protocol: 'tcp', description: 'NAT port 80 to 172.16.10.11', rule_number: '10' }
- { action: 'set', source_address: '0.0.0.0/0', source_port: '443', destination_port: '443', destination_address: '172.16.20.11', protocol: 'tcp', description: 'NAT port 443 to 172.16.10.11', rule_number: '20' }
- { action: 'set', source_address: '123.123.123.123/32', source_port: '222', destination_port: '22', destination_address: '172.16.20.11', protocol: 'tcp', description: 'NAT port 443 to 172.16.10.11', rule_number: '30' }

As you can see, these are the bulk of the variables that could change depending on where our playbook is being run. To start off with, we are setting the details of our device and its basic configuration in a nested variable called vyos. You might have noticed that we are passing the details of the IP address and username for our VyOS device in here, rather than our host inventory file.

In fact, our host inventory file, called production, should just contain the following line of code:

localhost

What this means is that when our playbook is executed, it will not be executed against our VyOS device. Instead, the playbook will target our Ansible controller, and the module will then target the VyOS device. This approach is common among all the core network modules. As we have already discussed, Ansible is an agentless platform; it requires only an SSH or WinRM connection by default.

However, not every networking device has SSH or WinRM access; some may have only web-based APIs, while others may use a proprietary access method. Others, such as VyOS, may appear to have SSH access; however, you are SSHing into a custom shell that is designed to run only a few firewall commands. For this reason, most of the core network modules manage their connection and communication away from the host inventory file.

The remainder of the variables in the group_vars/common.yml file set up some basic firewall rules, which we will look at shortly.

The task for the role, which can be found at roles/vyos-firewall/tasks/main.yml, contains four parts. First of all, we are using the vyos_config module to set the hostname. Take a look at this code:

- name: set the hostname correctly
vyos_config:
provider:
host: "{{ vyos.host }}"
username: "{{ vyos.username }}"
lines:
- "set system host-name {{ vyos.host }}"

As you can see, we are passing the details of our VyOS device using the provider option; then we are passing a single vyos command to set the hostname. The vyos_config module also accepts template files, which we will be using in a moment to fully configure our device.

The next task configures the DNS resolvers using the vyos_system module. Take a look at this code:

- name: configure name servers
vyos_system:
provider:
host: "{{ vyos.host }}"
username: "{{ vyos.username }}"
name_server: "{{ item }}"
with_items: "{{ vyos_nameservers }}"

Next up, we are going to set the message of the day (MOTD) using the vyos_banner module. Take a look at this code:

- name: configure the motd
vyos_banner:
provider:
host: "{{ vyos.host }}"
username: "{{ vyos.username }}"
banner: "post-login"
state: "present"
text: "{{ motd_asciiart }}"

Finally, we are going to apply our main firewall configuration using the following task:

- name: backup and load from file
vyos_config:
provider:
host: "{{ vyos.host }}"
username: "{{ vyos.username }}"
src: "firewall.j2"
backup: "{{ vyos.backup }}"
save: "yes"

Rather than providing commands using lines, this time we are giving the name of a template file using src. We are also instructing the module to make a backup of the current configuration; this will be stored in the roles/vyos-firewall/backup folder, which is created when the playbook runs.

The template can be found at roles/vyos-firewall/templates/firewall.j2. This template contains the following code:

set firewall all-ping 'enable'
set firewall broadcast-ping 'disable'
set firewall ipv6-receive-redirects 'disable'
set firewall ipv6-src-route 'disable'
set firewall ip-src-route 'disable'
set firewall log-martians 'enable'
set firewall receive-redirects 'disable'
set firewall send-redirects 'enable'
set firewall source-validation 'disable'
set firewall state-policy established action 'accept'
set firewall state-policy related action 'accept'
set firewall syn-cookies 'enable'
set firewall name OUTSIDE-IN default-action 'drop'
set firewall name OUTSIDE-IN description 'deny traffic from internet'
{% for item in whitelist_ips %}
set firewall group address-group SSH-ACCESS address {{ item }}
{% endfor %}
set firewall name OUTSIDE-LOCAL rule 310 source group address-group SSH-ACCESS
set firewall name OUTSIDE-LOCAL default-action 'drop'
set firewall name OUTSIDE-LOCAL rule 310 action 'accept'
set firewall name OUTSIDE-LOCAL rule 310 destination port '22'
set firewall name OUTSIDE-LOCAL rule 310 protocol 'tcp'
set firewall name OUTSIDE-LOCAL rule 900 action 'accept'
set firewall name OUTSIDE-LOCAL rule 900 description 'allow icmp'
set firewall name OUTSIDE-LOCAL rule 900 protocol 'icmp'
set firewall receive-redirects 'disable'
set firewall send-redirects 'enable'
set firewall source-validation 'disable'
set firewall state-policy established action 'accept'
set firewall state-policy related action 'accept'
set firewall syn-cookies 'enable'
set interfaces ethernet eth0 firewall in name 'OUTSIDE-IN'
set interfaces ethernet eth0 firewall local name 'OUTSIDE-LOCAL'
set interfaces ethernet eth1 address '{{ vyos.inside.interface }}'
set interfaces ethernet eth1 description 'INSIDE'
set interfaces ethernet eth1 duplex 'auto'
set interfaces ethernet eth1 speed 'auto'
set nat source rule 100 outbound-interface 'eth0'
set nat source rule 100 source address '{{ vyos.inside.subnet }}'
set nat source rule 100 translation address 'masquerade'
{% for item in rules if item.action == "set" %}
{{ item.action }} nat destination rule {{ item.rule_number }} description '{{ item.description }}'
{{ item.action }} nat destination rule {{ item.rule_number }} destination port '{{ item.source_port }}'
{{ item.action }} nat destination rule {{ item.rule_number }} translation port '{{ item.destination_port }}'
{{ item.action }} nat destination rule {{ item.rule_number }} inbound-interface 'eth0'
{{ item.action }} nat destination rule {{ item.rule_number }} protocol '{{ item.protocol }}'
{{ item.action }} nat destination rule {{ item.rule_number }} translation address '{{ item.destination_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} action 'accept'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} source address '{{ item.source_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} destination address '{{ item.destination_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} destination port '{{ item.destination_port }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} protocol '{{ item.protocol }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} state new 'enable'
{% endfor %}
{% for item in rules if item.action == "delete" %}
{{ item.action }} nat destination rule {{ item.rule_number }}
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }}
{% endfor %}

There are a lot of commands in the template, the bulk of which are just applying some basic settings on the device. The ones we are interested in are the three for loops. The first loop is as follows:

{% for item in whitelist_ips %}
set firewall group address-group SSH-ACCESS address {{ item }}
{% endfor %}

This will simply loop through each of the IP addresses we have provided in the whitelist_ips variable, in a similar way to how we have been using with_items in previous playbooks. This is better demonstrated by the next loop, which takes the variables from the firewall variable and creates both the NAT and firewall rules. Take a look at this code:

{% for item in rules if item.action == "set" %}
{{ item.action }} nat destination rule {{ item.rule_number }} description '{{ item.description }}'
{{ item.action }} nat destination rule {{ item.rule_number }} destination port '{{ item.source_port }}'
{{ item.action }} nat destination rule {{ item.rule_number }} translation port '{{ item.destination_port }}'
{{ item.action }} nat destination rule {{ item.rule_number }} inbound-interface 'eth0'
{{ item.action }} nat destination rule {{ item.rule_number }} protocol '{{ item.protocol }}'
{{ item.action }} nat destination rule {{ item.rule_number }} translation address '{{ item.destination_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} action 'accept'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} source address '{{ item.source_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} destination address '{{ item.destination_address }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} destination port '{{ item.destination_port }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} protocol '{{ item.protocol }}'
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }} state new 'enable'
{% endfor %}

As you can see, the rule is included only if we have set action to set in the variable; the final loop takes care of any rules where we have set the action to delete, as shown in this code:

{% for item in rules if item.action == "delete" %}
{{ item.action }} nat destination rule {{ item.rule_number }}
{{ item.action }} firewall name OUTSIDE-IN rule {{ item.rule_number }}
{% endfor %}

If you have been following along, you should have content in all of the files we initially created, apart from the site.yml file. This should contain the following code:

---

- hosts: localhost
connection: local
gather_facts: false

vars_files:
- group_vars/common.yml

roles:
- roles/vyos-firewall

Now that we have all of the parts of our playbook together, we are able to launch the VyOS Vagrant box and run the playbook.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.82.23