Ansible KVM Router Lab Part 5
date: 2021-10-17
Introduction
This is Part 5 of a multi-part series of blog posts for building a router lab automatically using a series of bash scripts and ansible.
Ansible KVM Router Lab Part 1 is an overview.
In Ansible KVM Router Lab Part 2, I break down the script build_vms.bash.
In Ansible KVM Router Lab Part 3, I explain define_bridge_networks.bash and shutdown_vms.bash scripts which are used to construct the lab.
In Ansible KVM Router Lab Part 4, I explain connect_vms_to_bridges.bash, start_vms.bash, and rebuild_known_hosts.bash scripts which are used to construct the lab.
In this post I explain how I use Ansible to finish constructing the lab.
In Ansible KVM Router Lab Part 6, I explain disconnect_vms_from_bridges.bash, undefine_and_remove_vms.bash, and remove_bridge_networks which are used to destroy the lab.
Setup Ansible
- Configure ansible host file
# ~/.ansible.cfg [defaults] inventory = ~/router-lab/ansible/hosts.yml
- Setup bashrc
# ~/.bashrc export LIBVIRT_DEFAULT_URI="qemu+ssh://<user>@<server>/system" alias ansible-pb=anspb anspb() { ANS_DIR=~/router-lab/ansible/playbooks; echo Changing to "${ANS_DIR}" and executing: ansible-playbook "${@}" (cd $ANS_DIR || exit ; ansible-playbook "${@}") }
- install apps
apt install ansible ansible-lint
Run Ansible
ansible-pb build_out_routers.yml -K
ansible-pb update_and_build.yml -K
Ansible Tasks
This is an explaination of the tasks in the Ansible Playbook. Playbooks are executed from top to bottom.
Install dnsmasq
, iptables-persistent
This task is only run against the first and second lab clients as they are the routers.
Install traceroute
Traceroute is parsed in a later task to confirm that traffic is
following the correct route.
(Also incidentally installs needrestart
and screen
.)
Backup /etc/network/interfaces
This is a simple bash command that tests if /etc/network/interfaces.bak
exists, and if not creates it.
Update Network Config
This task updates /etc/network/interfaces
in all the lab clients
to describe the network interfaces needed to connect to each other.
For instance, here is the new /etc/network/interfaces
file for dnettwo.
# /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug enp1s0
iface enp1s0 inet dhcp
# The primary network interface
allow-hotplug enp7s0
iface enp7s0 inet dhcp
auto enp8s0
iface enp8s0 inet static
address 10.4.4.1
network 10.4.4.0
netmask 255.255.255.0
broadcast 10.4.4.255
Backup /etc/dnsmasq.conf
This is a simple bash command that tests if /etc/dnsmasq.conf.bak
exists, and if not creates it. (only applies to the two router clients)
Configure dnsmasq
This task copies the templates for /etc/dnsmasq.conf
to each of
the two router clients.
dnsmasq
is used to provide DHCP (and name resolution).
For instance, here is the new /etc/dnsmasq.conf
for dnetone.
# /etc/dnsmasq.conf
dhcp-range=10.5.5.50,10.5.5.150
listen-address=127.0.0.1, 10.5.5.1
Configure Network ifup
This applies to all the lab clients except for the first one,
changes the default route. A bash script is copied from
template to /etc/network/if-up.d/ifup-script
.
For instance here is ifup-script
for dnetthree.
#!/bin/bash
# /etc/network/if-up.d/ifup-script
default_dev="$(ip route | head -1 | awk '{print $5}')"
echo "${default_dev}"
if [ "${default_dev}" == "enp1s0" ]
then
ip route del default via 10.55.44.1 dev enp1s0
fi
if [ "${default_dev}" != "enp7s0" ]
then
ip route add default via 10.4.4.1 dev enp7s0
fi
Restart Network and dnsmasq
This is sequential:
- enp7s0 is restarted on dnet
dnsmasq
is restarted on dnetone, offering service on enp7s0- enp7s0 and enp8s0 are restarted on dnettwo, thus soliciting dhcp service on enp7s0, and triggering
/etc/network/if-up.d/ifup-script
dnsmasq
is restarted on dnettwo, offering service on enp8s0- enp7s0 is restarted on dnetthree, dnetfour, and dnetfive, thus soliciting dhcp service on enp7s0, and triggering
/etc/network/if-up.d/ifup-script
Backup /etc/sysctl.conf
This is a simple bash command that tests if /etc/sysctl.conf.bak
exists, and if not creates it. (only applies to the two router clients)
Enable ipv4 forwarding
This is a simple bash command that uncomments the option for ipv4 forwarding
in /etc/sysctl.conf
, applies only to the two routers.
# /etc/sysctl.conf
...
# this
#net.ipv4.ip_forward=1
...
# becomes this
net.ipv4.ip_forward=1
...
Start ipv4 forwarding
This simple bash command starts ipv4 forwarding, applies only to the two routers.
bash -c "sysctl -w net.ipv4.ip_forward=1"
Configure iptables
workaround
This applies only to the two router clients.
From iptables
's point of view, the ansible connection isn't a RELATED INPUT
connection, thus it is necessary to bring up a firewall in a two-step
process that involves first ACCEPTING RELATED OUTPUT connections in a workaround.
From ansible template, the following is copied to /dev/shm/iptables_workaround
# /dev/shm/iptables_workaround
*filter
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
-A INPUT -j ACCEPT -m conntrack --ctstate ESTABLISHED,RELATED
-A OUTPUT -j ACCEPT -m conntrack --ctstate ESTABLISHED,RELATED
COMMIT
Apply iptables
workaround
This applies only to the two router clients. The following command is dispatched to apply the above iptables_workaround:
bash -c "iptables-restore < /dev/shm/iptables_workaround"
Configure iptables
This applies only to the two router clients.
From ansible template the following is copied to /etc/iptables/rules.v4
on dnetone.
*nat
-A POSTROUTING -o enp1s0 -j MASQUERADE
COMMIT
*filter
-A INPUT -i lo -j ACCEPT
# allow ssh, so that we do not lock ourselves
-A INPUT -i enp1s0 -p tcp -m tcp --dport 22 -j ACCEPT
# allow incoming traffic to the outgoing connections,
# et al for clients from the private network
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# prohibit everything else incoming
-A INPUT -i enp1s0 -j DROP
COMMIT
From ansible template the following is copied to /etc/iptables/rules.v4
on dnettwo.
*nat
-A POSTROUTING -o enp7s0 -j MASQUERADE
COMMIT
*filter
-A INPUT -i lo -j ACCEPT
# allow ssh, so that we do not lock ourselves
-A INPUT -i enp7s0 -p tcp -m tcp --dport 22 -j ACCEPT
# allow incoming traffic to the outgoing connections,
# et al for clients from the private network
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# prohibit everything else incoming
-A INPUT -i enp7s0 -j DROP
COMMIT
Apply iptables
firewall
This applies only to the two router clients.
The following command is dispatched to apply the above from /etc/iptables/rules.v4
:
bash -c "iptables-restore < /etc/iptables/rules.v4"
traceroute
test
The following script is dispatched to dnettwo:
#!/bin/bash
RESULT="$(traceroute 8.8.8.8)"
FIRST_HOP="$(echo "${RESULT}" | head -2 | tail -1 | awk '{print $2}')"
if [ "${FIRST_HOP}" == "10.5.5.1" ]
then
exit 0
else
exit 1
fi
#!/bin/bash
RESULT="$(traceroute 8.8.8.8)"
FIRST_HOP="$(echo "${RESULT}" | head -2 | tail -1 | awk '{print $2}')"
if [ "${FIRST_HOP}" != "10.4.4.1" ]
then
exit 1
fi
SECOND_HOP="$(echo "${RESULT}" | head -3 | tail -1 | awk '{print $2}')"
if [ "${SECOND_HOP}" == "10.5.5.1" ]
then
exit 0
else
exit 1
fi
To Be Continued
In Ansible KVM Router Lab Part 6, I explain disconnect_vms_from_bridges.bash, undefine_and_remove_vms.bash, and remove_bridge_networks which are used to destroy the lab.