Wednesday, March 13, 2019

Executing an ansible playbook from within PHP

Ansible seems to be the perfect tool to create a device inventory and keep track of all the devices in the network. Having this in mind, I decided to write a web application in PHP with a Postgres database that would hold the data of each device. Below you can see the web app.


Starting with the basics I used the "ios_facts" function to get the data and insert into the database. The Ansible playbook was executed manually, it went through the devices connecting to one after the other and database was updated with the new data.

That worked very well until I decided to trigger the execution of the Ansible playbook from within PHP, in order to create a more dynamic inventory or execute a playbook against a certain network device. Also I wanted to get the output from the execution of the playbook and display it to the web user.

Since PHP runs as a www-data user the privileges for executing anything are rather limited. This is what I had to do in order to make it work.

1. Create a user www-data in Postgress and grand "connect" privileges to my database
2. Grand 'insert', 'update' & 'select' privileges to the www-data user for the table I was interested in
3. Put the ansible playbook in the directory where the PHP application files existed
4. Use the PHP command passthru to execute the playbook and get the output back to the web application as per below
<?php 
passthru("/usr/bin/ansible-playbook -i myinventory mytest.yml"); 
?>
5. Create an "ansible.cfg" file in the directory of the playbook to disable host key checking
****ansible.cfg****
[defaults]
host_key_checking = False
6. Modify write permissions of the application directory, to allow Ansible write on the disk

And this is the output that I get on my browser after executing the script. This is just a Javascript alert, but you get the point..




Please keep in mind my application is running in an internal lab network and the security of the application is not an issue. The above process took place just to make things work in an internal lab environment. You shouldn't take such actions in a production environment where the security of the application and the network itself is critical

Tuesday, February 26, 2019

Split a long string on a specific delimiter in Ansible playbook

I've been trying to split a long string in an Ansible playbook and there seems to be an easy way to do that.

You have to use the "split" function and specify the delimiter. Depending on which part of the "exploded" string you need, you can use positive or negative values as a parameter.

For example, when trying to get the IOS image from a router you may get something like the following:

"ansible_net_image": "unix:/opt/unetlab/addons/iol/bin/i86bin-linux-l3-adventerprisek9-15.4"

How would you get just the last part of the string in order to store it in a database? Using the split function you specify the delimiter ("/" in this case) and the substring location "-1", counting from the end of the string. 

{{ ansible_net_image.split('/')[-1] }}

In another case how would you get the first part of the string?

{{ ansible_net_image.split('/')[0] }}

Using ansible to write directly to postgresql

Lately I've been playing with ansible to create an inventory for network equipment. Actually I want to maintain an inventory and keep it updated.

The concept is to connect to a network device, "get facts" and write the results directly to a database. In this case I've been using postgresql primarily for its "inet" variable type.

Anyway, this is how I did it. I used the shell option in an Ansible task and passed data from "facts" directly to psql command.


- name: Writing facts to database
shell: "psql -U george -d inventory -c 'UPDATE device SET (osversion,osimage,model,hostname,serialnum) = ($${{ansible_net_version}}$$,$${{ ansible_net_image.split('/')[-1] }}$$,$${{ ansible_net_model }}$$,$${{ ansible_net_hostname }}$$,$${{ansible_net_serialnum}}$$) WHERE ipaddress = $${{ inventory_hostname }}$$'"


The best way to make it work seems to be by using "$$" around variables

Monday, August 21, 2017

EVE-NG - A GNS3 alternative for your network simulations

A few months ago I had been searching for GNS3 alternatives. Although GNS3 has helped me a lot the last few years, I needed another, more flexible tool, to run my network simulations. After quite a bit of testing, I decided to abandon GNS3 for EVE-NG.



Actually EVE-NG is the new generation of an older tool, which you may have heard of, known as UnetLab. I had tried UnetLab once in the past, but my experience wasn't that good, so I continued with GNS3. But now things have changed..

The major advantage of EVE-NG against GNS3 is that EVE-NG doesn't need client application to be running on your computer. You can install EVE-NG on a remote server and you'll be able to access it from anywhere, just using your browser. Of course you can still install it on your laptop or desktop computer without any issue.

It's a very powerful platform which supports many different vendor appliances, which, in my opinion, certainly deserves your time to give it a try.

Wednesday, June 1, 2016

BGP Best Path Selection Mnemonic

From Wendell Odom’s CCNP ROUTE 642-902 Official Certification Guide:

Hint:
* Up to AS Path higher values win
* After AS Path lower values win


N WLLA OMNI

N = next hop reachability
W = weight
L = local preference
L = locally injected in the BGP process
A = AS path length

O = origin (igp &lt; egp &lt; incomplete)
M = MED
N = neighbor type (ebgp &lt; ibgp)
I = IGP metric to BGP next-hop

And my own contribution to this list:

ORCA

O = Oldest route wins
R = Router-ID, lowest wins
C = Cluster list, shortest wins
A = Address of the BGP neighbor, lowest wins

Of course there are several options that can affect the order of the above process.
For details check on Cisco: BGP Best Path Selection Algorithm


Sunday, February 28, 2016

Order of operations using NAT - Why do we need a static route when using Outside NAT?

After reading several docs online and trying to understand the reason we need a static route, I decided to lab it to better understand and verify the result.

My lab environment runs on GNS3 and I noticed a behavior that wasn't mentioned anywhere else, so I decided to post my findings here

We have the following topology.



No routing protocols running between the routers. We need to ping R3 router from R2 and the opposite, only by using NAT on R1.

Initial Configurations
R2
interface Ethernet0/0
 ip address 20.0.0.2 255.255.255.0
 
R3
interface Ethernet1/1
 ip address 30.0.0.3 255.255.255.0

R1
interface Ethernet0/0
 ip address 20.0.0.1 255.255.255.0
 ip nat inside

interface Ethernet1/1
 ip address 30.0.0.1 255.255.255.0
 ip nat outside
 ip nat inside source static 20.0.0.2 30.0.0.2
 ip nat outside source static 30.0.0.3 20.0.0.3
R1#sh ip nat tran

Pro Inside global      Inside local       Outside local      Outside global

--- ---                ---                20.0.0.3           30.0.0.3

--- 30.0.0.2           20.0.0.2           ---                ---

We start by pinging R3 from R2, so R1 performs Inside to Outside NAT translation. As we expected, ping is successful and correct addresses are displayed in the console of each router

R2#ping 30.0.0.3 rep 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 30.0.0.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 6/6/6 ms
R2#
R3#
*Feb 28 14:39:58.928: ICMP: echo reply sent, src 30.0.0.3, dst 30.0.0.2, topology BASE, dscp 0 topoid 0
R3#
R2#
*Feb 28 14:39:58.931: ICMP: echo reply rcvd, src 30.0.0.3, dst 20.0.0.2, topology BASE, dscp 0 topoid 0
R2#

Now let's try the opposite. We ping R2 from R3, so R1 performs Outside to Inside NAT translation.

R3#ping 20.0.0.2 rep 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 20.0.0.2, timeout is 2 seconds:
.
Success rate is 0 percent (0/1)
R2#
*Feb 28 14:40:34.232: ICMP: echo reply sent, src 20.0.0.2, dst 20.0.0.3, topology BASE, dscp 0 topoid 0
R2#

We see that ping fails, although the packet can reach R2. That means there must me an error in the return path. We enable "debug ip nat" and "debug ip packet" on R1 and retry pinging R2 from R3, trying to find the cause of the failure.

R1#
*Feb 28 14:46:40.686: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, Virtual Fragment Reassembly(33), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.686: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, Virtual Fragment Reassembly After IPSec Decryption(49), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.686: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, NAT Outside(76), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.686: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, MCI Check(90), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.687: NAT: s=30.0.0.3->20.0.0.3, d=20.0.0.2 [24]
*Feb 28 14:46:40.687: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, output feature, NAT Inside(8), rtype 1, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.687: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, output feature, NAT ALG proxy(59), rtype 1, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.687: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), g=20.0.0.2, len 100, forward
*Feb 28 14:46:40.688: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, sending full packet
*Feb 28 14:46:40.692: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3, len 100, input feature, Virtual Fragment Reassembly(33), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.692: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3, len 100, input feature, Virtual Fragment Reassembly After IPSec Decryption(49), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.692: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3, len 100, input feature, MCI Check(90), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.692: IP: tableid=0, s=20.0.0.2 (Ethernet0/0), d=20.0.0.3 (Ethernet0/0), routed via RIB
*Feb 28 14:46:40.692: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3 (Ethernet0/0), len 100, output feature, NAT Inside(8), rtype 1, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 14:46:40.693: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3 (Ethernet0/0), len 100, rcvd 3
*Feb 28 14:46:40.693: IP: s=20.0.0.2 (Ethernet0/0), d=20.0.0.3, len 100, stop process pak for forus packet
R1#

If we check the debugs carefully, we notice that there is only one line mentioning NAT. We see NAT info only in line #5. It triggers only in the direction from R3 to R2 and there is no trigger in the return direction. That leads us to research for the reason. Looking toward the bottom of the debug output we notice line #13

*Feb 28 14:46:40.692: IP: tableid=0, s=20.0.0.2 (Ethernet0/0), d=20.0.0.3 (Ethernet0/0), routed via RIB

We see that R1 believes 20.0.0.2 and 20.0.0.3 are on the same interface/segment, so there is no reason to perform any NAT action

And what is the solution? As mentioned in several blogs and docs, it is a matter of order of operations and you can solve it by adding a static route for 20.0.0.3 pointing toward R3 and R1. There are two possible ways to add that static route. Of course you can add the static route yourself, or you can use the "add-route" knob in the "ip nat outside" command. Any of these should have the same result.

So, let's pick the "add-route" option and see what happens
R1# conf t
R1(config)# ip nat outside source static 30.0.0.3 20.0.0.3 add-route
R1(config)# do sh ip route | b Gateway
Gateway of last resort is not set

      20.0.0.0/8 is variably subnetted, 3 subnets, 2 masks
C        20.0.0.0/24 is directly connected, Ethernet0/0
L        20.0.0.1/32 is directly connected, Ethernet0/0
L        20.0.0.3/32 is directly connected, Ethernet0/0   <========
      30.0.0.0/8 is variably subnetted, 3 subnets, 2 masks
C        30.0.0.0/24 is directly connected, Ethernet1/1
L        30.0.0.1/32 is directly connected, Ethernet1/1
L        30.0.0.2/32 is directly connected, Ethernet1/1
As you can see above there is no static route for 20.0.0.3. But we notice a connected route, which probably overwhelms our static route. And this is where things are getting different from anything I've read online. I started thinking where could this static route come from and I remembered the alias option of the "ip nat inside/outside" command. Whenever you perform NAT, the device by default installs aliases for the involved IP addresses and you can verify that with the command "sh ip alias"
R1#sh ip alias
Address Type             IP Address      Port
Interface                20.0.0.1
Dynamic                  20.0.0.3   <========
Interface                30.0.0.1
Dynamic                  30.0.0.2   <========
So, why not use the "no-alias" knob to see what happens? We modify out NAT statements on R1 to include the "no-alias" option and check the result.
R1# conf t
R1(config)# ip nat inside source static 20.0.0.2 30.0.0.2 no-alias
R1(config)# ip nat outside source static 30.0.0.3 20.0.0.3 no-alias add-route

R1(config)# do sh ip alias
Address Type             IP Address      Port
Interface                20.0.0.1
Interface                30.0.0.1

R1(config)# do sh ip route | b Gateway
Gateway of last resort is not set

      20.0.0.0/8 is variably subnetted, 3 subnets, 2 masks
C        20.0.0.0/24 is directly connected, Ethernet0/0
L        20.0.0.1/32 is directly connected, Ethernet0/0
S        20.0.0.3/32 [1/0] via 30.0.0.3
      30.0.0.0/8 is variably subnetted, 2 subnets, 2 masks
C        30.0.0.0/24 is directly connected, Ethernet1/1
L        30.0.0.1/32 is directly connected, Ethernet1/1
Now our routing table seems ok, so let's retry pinging R2 from R3
R3#ping 20.0.0.2 rep 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 20.0.0.2, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 7/7/7 ms
R2#
*Feb 28 15:19:09.776: ICMP: echo reply sent, src 20.0.0.2, dst 20.0.0.3, topology BASE, dscp 0 topoid 0
R3#
*Feb 28 15:19:09.778: ICMP: echo reply rcvd, src 20.0.0.2, dst 30.0.0.3, topology BASE, dscp 0 topoid 0
As you can see, the ping is successful and the debug output depicts the NAT operation in both directions.
R1#
*Feb 28 15:19:09.772: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, Virtual Fragment Reassembly(33), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.772: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, Virtual Fragment Reassembly After IPSec Decryption(49), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.772: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, NAT Outside(76), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.773: IP: s=30.0.0.3 (Ethernet1/1), d=20.0.0.2, len 100, input feature, MCI Check(90), rtype 0, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.773: NAT: s=30.0.0.3->20.0.0.3, d=20.0.0.2 [39]
*Feb 28 15:19:09.773: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, output feature, NAT Inside(8), rtype 1, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.773: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, output feature, NAT ALG proxy(59), rtype 1, forus FALSE, sendself FALSE, mtu 0, fwdchk FALSE
*Feb 28 15:19:09.774: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), g=20.0.0.2, len 100, forward
*Feb 28 15:19:09.774: IP: s=20.0.0.3 (Ethernet1/1), d=20.0.0.2 (Ethernet0/0), len 100, sending full packet
*Feb 28 15:19:09.777: NAT*: s=20.0.0.2, d=20.0.0.3->30.0.0.3 [39]
After all this research I'm not sure if all this is normal behavior or it appears only in the IOS release/platform I'm running. If anyone can verify my findings are correct, I'd be more than happy to know..

Saturday, January 23, 2016

Open Telnet links from Chrome/Chromium Linux

Credit for all the following instructions to the author of the post  Open Telnet and SSH links from Chrom/Chromium Linux.
I just copy it here for my own reference.
1. Install putty. You can use any other terminal apps too. Change the Exec in the below desktop file accordingly
apt-get install putty
2. Create a telnet.desktop file on /$HOME/.local/share/applications
vi telnet.desktop
[Desktop Entry]
Version=1.0
Name=Telnet
GenericName=Telnet
Comment=Telnet Client
Exec=/usr/bin/putty %U
TryExec=/usr/bin/putty
Terminal=false
Type=Application
Categories=TerminalEmulator;Network;Telnet;Internet;BBS;
MimeType=x-scheme/telnet
X-KDE-Protocols=telnet
Keywords=Terminal;Emulator;Network;Internet;BBS;Telnet;Client;
3. Register the handler as
xdg-mime default telnet.desktop x-scheme-handler/telnet