Friday, September 18, 2015

Wednesday, July 15, 2015

Installing netmiko on Windows

Netmiko is a Python module by Kirk Byers that provides a wrapper around the Paramiko SSH module for doing screen scraping and CLI automation on network devices.

Paramiko has some dependencies that make installation on Windows a tad tricky. Here's a quick way to get it done:

  1. Install Anaconda.
  2. From the Anaconda shell, run "conda install paramiko".
  3. From the Anaconda shell, run "pip install scp".
  4. Install git for Windows.
  5. Clone netmiko with "git clone https://github.com/ktbyers/netmiko"
  6. cd into the netmiko directory and run "python setup.py install".
Done! Screen scrape away, and don't forget to hound your vendors for real APIs... :-)

Tuesday, July 14, 2015

Extracting Traffic from Rolling Capture Files

Every so often I need to extract a subset of traffic from a set of rolling timestamped pcap files. One common place I do this is with Security Onion; one of the great features of SO is its full-packet-capture feature: you can easily pivot from Snort, Suricata, or Bro logs to a full packet capture view, or download the associated pcap file.

But what if you don't have an associated alert or Bro log entry? Or if you're doing pcap on some system that's not as user-friendly as Security Onion, but nonetheless supports rolling captures?

The way I usually do this is with find and xargs. Here's an example of my most common workflow, using timestamps as the filtering criteria for find:

> find . -newerct "16:07" ! -newerct "16:10" | xargs -I {} tcpdump -r {} -w /tmp/{} host 8.8.8.8
> cd /tmp
> mergecap -w merged.pcap *.pcap

Translated:
  1. Find all files in the current directory created after 16:07 but not created after 16:10. This requires GNU find 4.3.3 or later. It supports many different time and date formats.
  2. Using xargs, filter each file with the "host 8.8.8.8" BPF expression and write it to /tmp with the same filename.
  3. Merge all the .pcap files in /tmp into merged.pcap.
You can easily modify this workflow to fit other use cases.

Friday, May 15, 2015

More ADN (Awk Defined Networking)

Want to know how many IPv4 nodes are in each of your VLANs? Use ADN:

ssh myswitch 'sh arp | i Vlan' | awk '{print $NF}' | sort | uniq -c | sort -rn

     79 Vlan38
     65 Vlan42
     58 Vlan34
     22 Vlan36
     21 Vlan32
     20 Vlan40
      9 Vlan3
      7 Vlan8
      5 Vlan6
      5 Vlan204
      5 Vlan203
      5 Vlan2
      4 Vlan74
      3 Vlan82
      3 Vlan4

Friday, April 24, 2015

ADN - Awk Defined Networking

Because I have yet to transition to a completely software-defined network in which everything configures itself (wink wink), I still have to do tasks like bulk VLAN changes.

Thanks to a recent innovation called ADN, or "AWK Defined Networking", I can do this in a shorter time window that the average bathroom break. For example, I just had a request to change all ports on a large access switch stack that  are currently in VLAN 76 to VLAN 64:

# ssh switch_name.foo.com 'show int status | i _76_' | grep Gi | awk '{print "int ",$1,"\n","description PC/Phone","\n","switchport access vlan 64"}'
Password: ***


int  Gi1/0/25
 description PC/Phone
 switchport access vlan 64
int  Gi1/0/26
 description PC/Phone
 switchport access vlan 64

[many more deleted]

Then I copied and pasted the results into config mode. Back to lounging on the beach.

Not even any Python skills required!

Thursday, March 26, 2015

Quick Example: Elasticsearch Bulk Index API with Python

A quick example that shows how to use Elasticsearch bulk indexing from the Python client. This is dramatically faster than indexing documents one at a time in a loop with the index() method.

Saturday, February 28, 2015

Filtering .raw fields with Python Elasticsearch DSL High-Level Client

It took me a while to figure out how to search the not_analyzed ".raw" fields created by Logstash in Elasticsearch indices, using the high-level Python Elasticsearch client. Because keyword arguments can't have attributes, Python throws an error if you try it the intuitive way (this assumes you've already set up a client as es and an index as i, as shown in the docs):

Instead, you create a dictionary with your parameters and unpack it using the ** operator:

This produces the Elasticsearch query we want:

Thursday, February 12, 2015

Experimenting with the CSR1000v REST API

This all started because we occasionally want to block traffic from an IP address or two for a short time. Our firewall is a pain to configure for this sort of thing: adding a drop for a single IP address literally takes 10 minutes. You have to open a fat client, create an object, add the object to a group, save the config, verify the config, push the config, etc.

I thought that SRTBH (Source-based Real-Time Black Hole) implemented by BGP would be the ticket: fast, easy, and theoretically easy to automate with the REST API in the Cisco Cloud Services Router 1000v. SRTBH is a simple and elegant way of dropping selected traffic on BGP speaking routers. In a nutshell:
  1. You configure a "trigger router" that speaks iBGP with the rest of your BGP-speaking routers (usually your Internet edge or transit routers), but doesn't participate in traffic forwarding.
  2. On each edge/transit router you configure a static route to null0 for an unused /32, usually 192.0.2.1: ip route 192.0.2.1 255.255.255.255 null0
  3. On each edge/transit router you configure loose-mode unicast RPF filtering on your outside interfaces: ip verify source reachable-via any. This has a special property: packets from any source for which the uRPF next-hop resolves to Null0 will be dropped.
  4. When you want to drop traffic from a particular host (e.g. 1.1.1.2), you configure a static host route on the trigger router and redistribute it into BGP by matching its tag: ip route 1.1.1.2 255.255.255.255 null0 tag 666.
  5. At redistribution, you set its next-hop to 192.0.2.1:

    route-map SRTBH permit 10
     description BGP Black Hole
     match tag 666
     set origin igp
     set community no-export
     set ip next-hop 192.0.2.1
The effect of this is that the edge routers see the iBGP route to the "bad" address as a /32 with a next-hop of 192.0.2.1. Because the next-hop for 192.0.2.1 recursively resolves to null0, the loose uRPF feature drops traffic from the bad host at the edge.

In my case, I wanted to use the CSR1000v as the trigger router. (At this point, somebody's thinking "Why not use a Linux BGP daemon?") That's a valid approach; I just want to use the CSR1000v.

I followed the REST setup instructions, then wrote a simple CLI tool to make testing easier. It's easy enough to test the API with cURL:

$ curl -X POST https://10.1.1.1:55443/api/v1/auth/token-services -H "Accept:application/json" -u "testuser" -d "" --insecure -3
Enter host password for user 'testuser':
{"kind": "object#auth-token", "expiry-time": "Thu Feb 12 17:24:30 2015", "token-id": "xNOvrJh[...]rW3x0=", "link": "https://10.1.1.1:55443/api/v1/auth/token-services/2285580579"}


but because the API requires that you obtain a temporary authorization token (demonstrated above) before doing anything interesting, writing the Python tool made sense.

Then the problems started: the REST API on the CSR1000v doesn't support tags on static routes. You can't add them, or even retrieve them. Here's what happens normally if you retrieve static routes:

>python rest.py --device 10.1.1.1 --user testuser --resource /routing-svc/static-routes
Password:
{u'items': [{u'admin-distance': 1,
             u'destination-network': u'10.0.0.0/8',
             u'kind': u'object#static-route',
             u'next-hop-router': u'10.30.254.152',
             u'outgoing-interface': u''},
 u'kind': u'collection#static-route'}


If one of the routes has a tag, it throws an error:

{u'detail': u' ',
 u'error-code': -1,
 u'error-message': u"invalid literal for int() with base 10: 'tag'"}


You can't set tags either. Furthermore, the same thing holds true for static routes with the "name" argument. (Incidentally, the error message seems to indicate that the IOS XE API is written in Python.)

The problem with this is that without tags, there's no convenient way to filter the RTBH black hole trigger routes at the time of redistribution. You can't set route-maps on BGP network statements via the API either, so no joy there.

The best option to me seems to be to redistribute all static routes, but filter on prefix-length rather than tag:

route-map SRTBH permit 10
 description BGP Black Hole
 match ip address prefix-list HOST_ROUTES_ONLY
 set origin igp
 set community no-export
 set ip next-hop 192.0.2.1


But then you have to make sure that you never have any /32s that aren't trigger routes... a strategy which is prone to configuration mistakes.

Because SRTBH seems like a natural use-case for API automation, I hope Cisco fixes this soon.

[added]
Other interesting things I've found: the static route configuration API doesn't allow you to add equal-cost static routes. For example, if I already have a 1.1.1.5/32 route and I add another one, I get a 404:

python rest.py --device 10.1.1.1 --user testuser --password foo --resource /routing-svc/static-routes --method post --json '{"destination-network":"1.1.1.5/32","outgoing-interface":"null0"}' --verbose
[snip]
DEBUG:root:got status code: 404
DEBUG:root:reponse content: Static route already exists
got status code: 404


The same thing goes for adding other items: I tried adding a name-server twice, and got a similar error. I would think that the API would be idempotent where possible (such as when adding a duplicate nameserver), and require clarifying attributes otherwise. With equal-cost static routes for example, it could throw a 40x unless an extra attribute like {"allow-equal-cost":true} is present in the POST request.

Thursday, January 15, 2015

Pleasing terminal colors on Security Onion

To get the lovely Solarized theme working in Security Onion:
  1. sudo apt-get install gnome-terminal
    1. I'm sure there's a way to get in working in the default xfce4 terminal, but I couldn't figure it out.
  2. Follow instructions here: http://stackoverflow.com/questions/23118916/configuring-solarized-colorscheme-in-gnome-terminal-tmux-and-vim

Thursday, January 8, 2015

Problems with kvm-ok in VIRL with VMWare Player

I'm installing Cisco VIRL, and despite following the instructions regarding nested virtualization settings, the kvm-ok command was still complaining. I needed to edit the .vmx file for the VIRL VM and add/edit the following:

monitor.virtual_mmu = "hardware"
monitor.virtual_exec = "hardware"
vhv.enable = "TRUE"
monitor_control.restrict_backdoor = "true"
 
 

Friday, January 2, 2015

My Network Toolkit

A while back, Chris Marget of Fragmentation Needed posted a run-down of his comprehensive and extremely clever network toolkit. Because I'm something of a weight weenie, mine is a lot more slimmed down. I thought I'd post it here:




The contents:

  1. Two random USB drives (in case I need to leave one with somebody).
  2. Single-mode and multi-mode LC fiber loopback plugs.
  3. Rack PDU plug adapter.
  4. Awesome PicQuic compact screwdriver (thanks to Chris's post).
  5. T1 loopback plug (red) (because we still have T1s out here in the boonies).
  6. Cat-6 pass-through plug (white).
  7. Crossover adapter (orange).
  8. Sharpie.
  9. Console setup:
    1. USB-to-DB9 adapter.
    2. DB9-to-RJ45 adapter.
    3. Flat Cat-6 cable.
    4. Rollover adapter.
    5. Velcro tie
    6. Flat Cat-6 cable with velcro tie.
The console setup could probably be improved by adding a DB9 null-modem adapter. The coolest thing (IMO) that I'm missing from Chris's setup is the Bluetooth console adapter -- maybe one day.

The Fenix AA light and Leatherman Skeletool CX almost always live in a pocket rather than the kit and go with me everywhere. The kit all fits into a small zippered case that used to hold a Dell laptop power supply.

My main goal here was to have all the hard-to-find professional stuff in one small package. I have a separate "personal" kit that contains stuff like headphones, USB cables, and chargers for personal electronics.