Parsing Network Device Output Part 2: Status or Configuration?

Reading Time: 3 minutes

A Brief Interlude

In my first post in this series, I dove into utilizing Regular Expressions (Regex) to parse network device output. Before I continue with some of the other parsing options, I thought it would be worthwhile to post a short blog laying out some definitions that I’ll be relying on. Specifically I’ll be using them for delineation among the different parsing options and their use cases.

At the heart of this problem, is that when interacting with traditional network devices there aren’t many programmatic options, such as a REST API, to return structured data. Sure, you could use SNMP for some things, but not for everything. In addition, working with SNMP can be it’s own type of nightmare, believe me, I’ve fought that fight many times in the past. This is changing as the industry matures, Cisco even has an entire certification track around utilizing their APIs now, however even today most Network Engineers’ first foray into automation uses SSH to query a device for information. Which brings us to output.

What Is Output?

For our purposes, output will be defined as any response that a network device returns when given a command via it’s Command Line Interface (CLI). This could be accomplished by many means; depending on the network device in question the CLI could be reached via Telnet, SSH, or even HTTPS. No matter the access method, the network device is still returning the same data, our output.

Execute show version on a Cisco-like device, and it will return some form of the status of the version as that device understands it. It will be structured, loosely, for human consumption with perhaps headings/titles or indentation/punctuation for legibility. It will not generally be structured in a way that would easily allow a computer program to understand and interact with it.

Just as you can issue show version and get output, you can similarly on most network devices execute a command akin to show running-config and get output. This would return some form of data about the configuration that was currently in use on that network device. Again, it will have some form of structure, usually either delineated by indentation or curly braces ({}), but mostly it will be in a form that is not easily understood or manipulated by a computer program that you are writing.

I make the specific call out that it may not easily be understood by a program that you are writing, because of course the network device itself can take that configuration and turn it into actionable information. However, that does us no good, as most vendors aren’t open sourcing their internal operating system parsing logic.

Status vs Configuration

In the two examples above, we received output from a network device after we issued a command via the CLI. One gave us some sort of status about the device, and the other gave us information about it’s configuration. Broadly speaking, all CLI commands on a network device fall into these two categories. You are either gathering or changing the status of the device with commands such as show version, show flash, or clear counters. Or you are gathering or changing the configuration of the device with commands such as show running-config, interface e1/0 <return> no shutdown.

It is vital to keep in mind which type of information you’re looking for or what changes you are trying to make, and whether it involves the device’s status or configuration. This will heavily influence how you parse/interact with the output that the device has given you. Some parsing methods or libraries are made specifically for turning configuration into something easy to manipulate/change in Python (CiscoConfParse, Heir-Config), some are more generic and have the ability to parse both status and configuration (Regex, TextFSM, Genie).

In the next post, I’ll dive into CiscoConfParse first, and we’ll see how it can make the act of understanding and manipulating traditional unstructured network device configuration much simpler.

Parsing Network Device Output Part 1: Regex

Reading Time: 4 minutes

In the Beginning…

Often when making their first steps into Network Automation, people may have an idea of what they want to do, but not exactly how to get there. For example, an engineer may want to simplify a single time-intensive task “Gather the SW version, serial numbers, and uptime from all of my Cisco ASAs.” However, upon getting the information, they’re unsure what to do with it or how to parse it and use it for something else. I’ve seen this question come up a few different times, and thought a series of blog posts was in order. Each of the posts (including this one) outline some sample parsing options. However, it is worth noting that Regex is not better or worse than using a parsing library, for example, they may just have different use cases.

  • First, in this post, I will outline the basics of gathering our information and parsing it directly with our own Regular Expressions (Regex).
  • Next, I will demonstrate parsing this information with the popular CiscoConfParse and HeirConfig libraries.
  • Third, I will demonstrate simple parsing with Google’s TextFSM and the awesome library of templates provided by Network To Code, ntc-templates.
  • Finally, I will demonstrate simple parsing with Cisco’s Genie/PyATS libraries.

Note that I am going to skip over a lot of the minutia of what Netmiko is, and how to use it to gather network device information, as there are many wonderful resources out there already on this. We’re going to focus primarily on what to do with the output once you have it.

In this series of posts, we’ll just use the simple network shown here. There is a Management Station with Python 3.8 installed, and two Cisco ASAv firewalls (ASAv1 and ASAv2) in a tiered setup.

Code samples and “requirements.txt” for this post can be found on my Github.

Environment Preparation

For this entire series of posts, I will be working the examples in a python virtual environment setup with the following dependencies installed. Note that we’re installing iPython, which is a much better alternative to the built in Python interactive shell. I highly recommend checking it out by executing ipython in this virtual environment. iPython allows us to do some more interactive introspection on objects and method/object doc_strings in real time, which we’ll use in later posts.

  • iPython
  • Netmiko
  • CiscoConfParse
  • HeirConfig
  • NTC Templates
  • Genie/PyATS

First, I create a virtual environment with whatever name you wish, and then activate that environment:

python3.8 -m venv nn_examples
cd nn_examples;source bin/activate

Next, I install the dependencies as discussed above:

pip install ciscoconfparse genie hier-config ipython netmiko ntc_templates pyats 

Gathering our Output

We need to instantiate a connection to our ASAs, and then execute the command “show version | inc So|Serial| up” against them to gather the needed output. Simple enough with Netmiko as shown below (or on Github):

#!/usr/bin/env python3

Example code for entry on Parsing Network Device Output

import getpass
import re
import sys

from netmiko import ConnectHandler

Test parsing network device configuration

# Gather the needed credentials
net_device_username = input("Username: ")
net_device_password = getpass.getpass()
# Set enable/secret = password for now
net_device_secret = net_device_password

# Setup a dict with our ASAvs in it, in real world this could be read
# from a CSV or the CLI or any other source
firewalls = {
    "ASAv1": {"ip": "", "platform": "cisco_asa"},
    "ASAv2": {"ip": "", "platform": "cisco_asa"},

# Setup an empty dict for our results:
results = {}

# Instantiate netmiko connection objects and gather the output of
# `show version | inc So|Serial| up` on these two firewalls
for fw_name, fw_data in firewalls.items():
    print(f"Connecting to {fw_name}...")
    fw_connection = ConnectHandler(
    results[fw_name] = fw_connection.send_command(
        command_string="show version | inc So|Serial| up"

# Print our results
for fw_name, result in results.items():
    print(f"{fw_name} information:\n")


Successfully executed with “python3.8”, this will output:

 Username: cisco
 Connecting to ASAv1…
 Connecting to ASAv2…
 ASAv1 information:

 Cisco Adaptive Security Appliance Software Version 9.12(1)2 
 ASAv1 up 23 mins 50 secs
 Serial Number: 123456789AB

 ASAv2 information:

 Cisco Adaptive Security Appliance Software Version 9.13(1)2 
 ASAv2 up 24 mins 5 secs
 Serial Number: 123456789AC

Now that we have an example of gathering the information, let’s parse it to python variables via Regex.

Parsing with Regex

Much like above, I will not exhaustively cover what Regex (Regular Expressions) are, as there are boundless resources on the internet (and good old O’Reilly books) that do this. Suffice to say for our purposes, that it is a way to match patterns in a string of text and gather output based on that, and that I strongly recommend finding a good test bed like to help you along the way. You can get extremely complicated with Regex, and it can also bite you in the behind, just ask Cloudflare… Here is the three Regular Expressions I am using to match relevant output from the ASA.

Now here is what the program looks like utilizing the python re Regex module. The only difference is the addition of a section for parsing the output we’ve received, so that is all I’ll list below. The complete file is on Github as

# Parse our results:
print("Parsing Results...")
parsed_results = {}
for fw_name, result in results.items():
    parsed_results[fw_name] = {}
    parsed_results[fw_name]["version"] =
        r"Software Version (\d.\d{1,2}(?:\(?\d{1,2}?\)?\d{1,2}?)?)\W*$",
    parsed_results[fw_name]["uptime"] =
        r"up (.*)$", result, re.MULTILINE
    parsed_results[fw_name]["serial"] =
        r"Serial Number: (\S*)$", result, re.MULTILINE

# Print our results
for fw_name in results.keys():
    print(f"{fw_name} information:\n")
    print(f"\tVersion: {parsed_results[fw_name]['version'].group(1)}")
    print(f"\tUptime: {parsed_results[fw_name]['uptime'].group(1)}")
    print(f"\tSerial Number: {parsed_results[fw_name]['serial'].group(1)}")

This will give us the results of:

Username: cisco
 Connecting to ASAv1…
 Connecting to ASAv2…
 Parsing Results…
 ASAv1 information:
     Version: 9.12(1)2
     Uptime: 1 hour 25 mins
     Serial Number: 123456789AB
 ASAv2 information:
     Version: 9.13(1)2
     Uptime: 1 hour 25 mins
     Serial Number: 123456789AC

As you can see, this is a much more structured and useful output. We could also similarly output this to a CSV or other data source, now that we have parsed the data into something of more value than a raw string of all the data.

Next Up, Parsing Libraries!

In the next post I publish, I will utilize this same environment, but make use of some common configuration parsing libraries (CiscoConfParse and HeirConfig) to show how they could be useful for certain use cases.

#CiscoChampion 2019

Reading Time: < 1 minute

Happy 2019 everyone! Since I was lucky enough to be selected as a Cisco Champion for 2019, I thought it was time to revive my zombie blog!

I’ve got quite a few posts in the hopper, and will focus on not being such a perfectionist to posts that I don’t just hit that “Publish” button. Posts will be coming about:

  • Network Security
  • Network Programability
  • Python
  • Systems Architecture
  • And more!

Here’s to an exciting and educational 2019!

Testing ISAKMP with netcat

Reading Time: 6 minutes

The Scenario

In the course of my day to day job, I interact with VPNs on many devices (primarily IPSec VPNs on the Cisco ASA).  Often times the the simplest way to test an IPSec VPN is to fire up vpnc in a VM, change the config file as needed, and validate the connection.

Sometimes though, you need to be more granular with your testing.  Perhaps the person controlling the other endpoint/peer is not in charge of the intermediate network, and you need to validate that ISAKMP traffic is allowed all the way through to the peer.  Perhaps you want to validate an ACL that should be blocking ISAKMP.  Or perhaps you’re just curious to gain a deeper understanding of what is happening at the protocol level.  Either way, there’s not a really simple way to validate that ISAKMP is permitted end to end.

Because ISAKMP utilizes UDP port 500 for transport, you can’t simply Telnet to the port and validate that it is permitted.  We’re talking about UDP not TCP, which means there is no Layer 4 validation of an open socket (the Three-Way-Handshake of TCP).  This means that you can’t do something like `nc -uvv $host 500`, because while netcat can certainly open and utilize UDP sockets, netcat alone can’t tell you if a UDP port is open and listening.  To test this below, I attempt to use netcat to check whether my local box is listening on UDP port 500 (which I know it is not):

$ nc -uvv 500
found 0 associations
found 1 connections:
1: flags=82
outif (null)
src port 59894
dst port 500
rank info not available

Connection to port 500 [udp/isakmp] succeeded!

Succeeded indeed…  Although we obviously need to do something more to validate whether UDP/500 is open and listening for ISAKMP datagrams, as mentioned above we can still actually utilize netcat.  We can use it to open the UDP socket and then pipe in semi-valid ISAKMP data for netcat to pass to our destination.  This way, we can at least get some sort of response or validation via either an ISAKMP reply message or debugs/counters on the far side peer.

RFC2048 – Internet Security Association and Key Management Protocol

ISAKMP is defined in RFC2048, which describes in great detail the underlying structure of an ISAKMP UDP datagram.  In section 3.1, it lays out the header format which is all we really need for testing:

                 1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
! Initiator !
! Cookie !
! Responder !
! Cookie !
! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
! Message ID !
! Length !

Essentially, if you haven’t seen this style of packet diagram, each number across the top represents a bit, and each row represents 32 bits or 4 octets/bytes whichever you prefer.  In this case the ISAKMP header is 7 rows tall , which means that the ISAKMP header is 224 bits (7*32), or more commonly, 28 bytes long.

Now we know how long the data has to be, but what are we going to put into it?  The header breaks down into the following chunks:

  • Initiator and Responder Cookies – 8 bytes per Cookie, these are tokens that enable each endpoint to identify Security Associations (RFC2048, Sec 2.4), and to act as a method to assist in securing the communication (RFC2048, Sec 2.5.3).
  • Next Payload – 1 byte, indicates what type of message is following this header.
  • Major Version and Minor Version – 4 bits each, set to 1 and 0 respectively in RFC2048.
  • Exchange Type – 1 byte, tells the other system what type of messages and payloads to expect.
  • Flags – 1 byte, set specific ISAKMP options; only the first three bits are specified in RFC2048 rest are to be zeros.
  • Message ID – 4 bytes, unique ID used in Phase 2 negotiations; as we’re simulating a Phase 1 datagram these are set to all zeros.
  • Length – 4 bytes, the combined length of the header and payloads represented in bytes/octets.

Taking this information, we are now able to determine what fields we need to fill out and with what values to create our testing datagram.  Once we are complete, we can simply feed the appropriate arrangement of binary into netcat, and it should be able to simulate the beginning of a valid ISAKMP exchange with the peer.

The only hitch with this plan is the need for binary.  For mere humans, visually tracking long strings of binary is not easy; at a glance can you tell if this is 7 or 8 digits: 00000001.  In addition, writing out for each byte would get tedious and increase the likelihood of human error.

Hexadecimal to the Rescue

Luckily we can shortcut this  process by representing the binary values with Hexadecimal in `\x` notation.  This allows us to simply represent each byte or octet as a four character string.  For example:

Binary: 000000000000000100000010000000011
Hexadecimal: \x00\x01\x02\x03
Validation using xxd:
printf '\x01\x02\x03\x04' | xxd -b 0000000: 00000001 00000010 00000011 00000100 ....

Working with that format, and referencing the above breakdown of the header format gives us the following Hexadecimal:

Initiator Cookie: 8 bytes We’ll make this a value of 1 since it has to be something and we don’t care what it is. \x00\x00\x00\x00\x00\x00\x00\x01

Responder Cookie: 8 bytes All zeros because this is an initial communication.

Next Payload: 1 byte No next payload, so a value of 0.

Major and Minor Version: 4 bits each, mandatory value of 1 and 0 In binary, that would look like 00010000, which equals 10 in Hex.

Exchange Type: 1 byte We’re making a fake datagram with no payload, so we’ll try none (0) which appears to be permitted by RFC2048.

Flags: 1 byte No specific flags needed, so we’re also setting this to 0.

Message ID: 4 bytes Must be zeros per RFC2048 for an initial Phase 1 datagram like we’re simulating.

Length: 4 bytes This will be equal to 28 since we have a header with no payload. 28 in Hex is 1C.

Finally, our complete string of Hex for the simulated initial ISAKMP datagram would be:


Sending Our Data

Now that we have the complete Hex string for our test datagram, how do we go about sending it?  Well as described earlier, we’re going to simply pass it into netcat by printing the string and piping it into netcat. But first we need to get a destination setup that is listening for ISAKMP traffic. In my case, I’ve spun up a virtual Cisco ASA in my lab to act as our peer, and have it listening for ISAKMP traffic on the INSIDE (

ASAv-1A/pri/act# show run int G0/4
interface GigabitEthernet0/4
nameif INSIDE
security-level 100
ip address
ASAv-1A/pri/act# show run crypto ikev1
crypto ikev1 enable INSIDE

Taking the above Hex string and using `printf` to send it into netcat gives us the following output (all of the Hex should be on one line, however for readability I have broken it up here):

$ printf \x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00
       \x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x1C | nc -uvv 500
Connection to 500 port [udp/isakmp] succeeded!

As you can see, netcat says “Connection succeeded” again, and the output on our end is not so different from when we tested to a non-ISAKMP speaking endpoint. However, on the ASA IKEv1 statistics, we can see something very interesting:

ASAv-1A/pri/act# show crypto ikev1 stats | inc Drop
In Drop Packets: 6 Out Drop Packets: 0

So the ASA saw the packet, and marked it as invalid in some way and dropped it (which makes sense because we didn’t send any payload at all).

This validates that the path for UDP/500 is open from my test box to the peer!

Bonus Validations

I’m a curious soul though, so I wanted to dig deeper still.  For more detail we can turn on the IKEv1 debugs on the ASA and see as it is receiving and discarding each of the packets:

ASAv-1A/pri/act# debug crypto ikev1 255
May 21 03:25:04 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:04 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from
May 21 03:25:04 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:04 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from
May 21 03:25:05 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:05 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from
May 21 03:25:06 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:06 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from
May 21 03:25:07 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:07 [IKEv1]IKE Receiver: Runt ISAKMP packet discarded on Port 500 from
May 21 03:25:07 [IKEv1]IKE Receiver: Packet received on from
May 21 03:25:07 [IKEv1]IKE Receiver: Discarding packet, invalid IKE version

And for the coup, I did a packet capture on the ASA for all UDP/500 traffic and decoded it to see if the protocol decoder could interpret the frames as ISAKMP:

ASAv-1A/pri/act# cap cap1 int INSIDE match udp any any eq 500
ASAv-1A/pri/act# show cap cap1 decode 6 packets captured
1: 03:34:01.443916 > udp 1 [ISAKMP header incomplete]
2: 03:34:01.444007 > udp 1 [ISAKMP header incomplete]
3: 03:34:02.444892 > udp 1 [ISAKMP header incomplete]
4: 03:34:03.445609 > udp 1 [ISAKMP header incomplete]
5: 03:34:04.447059 > udp 1 [ISAKMP header incomplete]
6: 03:34:04.450126 > udp 75 ISAKMP Header Initiator COOKIE: 78 30 30 78 30 30 78 30 Responder COOKIE: 30 78 30 30 78 30 30 78 Next Payload: IKEV2 LEAP PAYLOAD Version: 3.0 Exchange Type: DOI Specific Use Flags: MessageID: 30783031 Length: 2016424056 [ISAKMP payload corrupted or truncated]

And there you have it, a means to quickly and easily validate a UDP/500 path!

300-208 SISAS – What Is Cisco ISE?

Reading Time: 3 minutesIn my earlier post about the Cisco 300-208 SISAS (Implementing Cisco Secure Access Solutions) exam, I gave a brief overview of the exam and listed the exam topics as laid out by the Cisco Learning Community.  However, I felt that these largely boil down to a few key concepts related to Cisco ISE (Identity Services Engine):

  1. Understand what ISE is.
  2. Understand why you might use ISE in a wired or wireless network.
  3. Understand what ISE does at a protocol level.
  4. Understand how ISE interacts with Network Access Devices and other systems.
  5. Understand how to configure ISE and the Network Access Devices.

This post will deal with Concept 1, Understand what Cisco ISE is.

A Little History

Before you can really understand what ISE is, I feel that you need to know where it came from.  Cisco’s NAC (Network Access Control) offerings have been fairly scattershot over the years.  They even tried unsuccessfully to market their own meaning of NAC for several years, attempting to sell their services as Network Admission Control.  Thankfully, they’ve given in and joined the rest of us in Network Access Control land.

Cisco ACS (Access Control Server) was the direct predecessor to ISE, and still exists today.  It provides RADIUS and TACACS services, and is able to integrate with central Identity Stores such as Active Directory or another LDAP speaking software.  This means that you would often find ACS in an enterprise network serving to authenticate VPN and wireless users, or control access to network devices.  Over time ACS evolved, as most Cisco applications have, from something you installed on top of Windows, to a full blown Linux based appliance (as of ACS 5.0 in 2009).  The common “appliance” model in use today, means that without a little (non-Cisco approved) tinkering you don’t have access to the Linux guts of Cisco ACS, or ISE for that matter, you’re simply presented an application.

At the same time that ACS was growing and advancing in capabilities, there was also the burst of BYOD onto the market and a growing market in servers/services to manage and administer user Identity on the network.  Cisco saw an opportunity to forklift the capabilities of ACS into a new product that was targeted directly at the “new” Identity Management market, not just traditional “access control”.  They took the underlying operating system from ACS, a RHEL/CentOS based distribution that they call ADE-OS (Application Deployment Engine), and added in a few new capabilities and features to the application.  This creation is what we know today as Cisco ISE.  Essentially, you can think of ISE as ACS version 6.0.

The only thing left out of ISE (until the recent release of Cisco ISE v2.0) was TACACS, as it was intended that you still purchase ACS to control network device administrative access.  For the purposes of the 300-208 SISAS exam today, you can consider TACACS to be out of scope for ISE, and to be the sole purview of ACS, although you still have to understand TACACS for the exam.

What Is Identity Management?

A full discussion of what Identity and Identity Management is, could take up many pages and posts (see wikipedia), however for the purposes of the 300-208 SISAS exam it can be summed up as this from the Official Cert Guide:

An identity is a representation of who a user or device is. Cisco ISE uses an endpoint’s MAC address to uniquely identify that endpoint. A username is one method of uniquely identifying an end user. Although SSIDs and IP addresses can be used as conditions or attributes in ISE policies, they are not identities.

Woland, Aaron; Redmon, Kevin (2015-04-27). CCNP Security SISAS 300-208 Official Cert Guide (Certification Guide) (Kindle Locations 13023-13026). Cisco Press. Kindle Edition.

An Identity Management system can then be defined as a way to keep track of Identity information for many users or devices, as well as the associated Authorization and Authentication information for those entities.  This is precisely what Cisco ISE is and does.  It provides all manner of services related to managing who users and devices are, and what network resources they may access.  Cisco ISE does not directly stop an entity from accessing a portion of the network (there are sometimes Inline Policy Nodes, but they are not common).  The Network Access Devices themselves handle the heavy lifting of granting/denying access by utilizing IEEE 802.1x which I will discuss in a subsequent post.  ISE simply provides a centralized location to set policy, gather reporting, and then interact with the NAD via Radius.

In the next post I will delve into Concept 2, the Whys surrounding Cisco ISE, as well as give a few example use cases.

The Elusive A+ Rating on SSL Labs

Reading Time: 6 minutesI often have to talk people off a cliff because of their website’s (sometimes perceived) vulnerabilities on Qualys’ SSL Labs testing site.  They have received an A- score for their site after running the tests, and see a few things in yellow.  Suddenly, they begin to believe that every villain on the web is now running amok with their data.  The truth of the matter is that if your SSL configuration is rating at an A-, that configuration is usually just fine.  Out of curiosity, I recently spent a few hours tweaking and managed to upgrade from an A- to an A+ for this domain.  I wanted to share that process so that anyone else can know what it might take to get an A+.

A+ Rating for on Qualys SSL Labs Testing

A+ Rating for on Qualys SSL Labs Testing

Caveats and Advice

The things that get you to an A+, are mostly semantic and not always the most obvious and open vulnerabilities.  The SSL Labs scoring process, while open and based on common sense/best practices, is simply one entity’s opinion of your SSL configuration.  In addition to the somewhat arbitrary nature of any benchmarking site, such as SSL Labs, remember that a configuration that gets an A+ today is one new vulnerability away from an F.  Your security posture on any system must be continuously evolving, along with the with the threats you’re defending against.

These caveats also hold true for any configurations listed here.  These are not guarantees that your site will be secure, or even recommendations.  They merely reflect the Apache and mod_ssl configurations that suited my needs, in my circumstances, at the time of this writing.  Please use common sense when establishing a SSL policy for your website.

The tweaks really boiled down to either explicitly defining things in my Apache VirtualHost configuration that I assumed didn’t really matter, or bending my strict configuration a little bit to meet the requirements of older browsers.  For example, I was initially only going to enable TLSv1.2.  If you can’t support TLSv1.2, I wasn’t really worried if the site was unavailable to you.  However, as I thought about it, I realized that it was a pointless stance to take.  Why isolate this site from an unfortunately large, albeit less secure, portion of the web when I don’t have to?  I’m not hosting any sensitive information on these servers; I am not asking users to make credit card transactions or give me their Social Security numbers.  I eventually walked my SSL configuration backwards and enabled TLSv1.0, TLSv1.1, and TLSv1.2 as part of my remediation efforts to get the elusive A+ from SSL Labs.

How to Get an A- Without Really Trying

Below is an example of the Apache VirtualHost configuration I had in place before beginning this process.  It was graded at an A- by SSL Labs, and is pretty standard.  In fact, most of it was pulled directly from the default-ssl.conf file in Apache 2.4.

<IfModule mod_ssl.c>
    <VirtualHost *:443>
        # SSL Configuration
        SSLEngine on
        SSLProtocol +TLSv1.1 +TLSv1.2
        SSLCipherSuite HIGH:!aNULL:!MD5

        # Fixes for IE being, well, IE...
        BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown 
                \downgrade-1.0 force-response-1.0
        BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
        # Browser cacheing for static content
        <filesMatch ".(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$">
                Header set Cache-Control "max-age=1209600, public"

        # Redirect "/" to "/blog/"
        <If "%{REQUEST_URI} == '/'">
                Redirect "/" "/blog/"


Now, here’s what the good folks at Qualys SSL Labs had to say about that configuration:

  1. “The server does not support Forward Secrecy with the reference browsers. Grade reduced to A-. MORE INFO »
    1. “Forward Secrecy – With some browsers (more info)”
  2. “Incorrect SNI Alerts –
  3. In addition, unless a browser was one of the newest versions available, there was also a slew of “Protocol or cipher suite mismatch”.

Working Towards an A

The easiest fix, was enabling TLSv1.o as I described earlier.  By simply adding +TLSv1 to the SSLProtocol directive, I opened up a much larger range of possible Protocol/Cipher Suite combinations:

        SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2

Unfortunately, this change resulted in no movement whatsoever of my A- rating.  At the very least, issue #3 was resolved; the wide majority of browsers could negotiate a connection.  The question remains however, why would Forward Secrecy only work with some browsers?  I have configured all of the DHE and ECDHE ciphers needed, and was able to confirm this fact because many browser simulations are able to connect with an ECDHE cipher.  Upon further investigation of the output from SSL Labs, I found the following, “Cipher Suites (sorted by strength as the server has no preference)”.  The Forward Secrecy enabled ciphers are available to all browsers, but I wondered if SSL Labs was indicating that some might not pick them because they weren’t offered first.

I always assumed cipher suite order was generally arbitrary, but to appease SSL Labs I sorted them by strength.  I added “@strength” to the end of my SSLCipherSuite directive, and also added the SSLHonorCipherOrder directive.  The SSLHonorCipherOrder directive was because, if I’m going to sort my cipher suite, your browser better obey.

        SSLCipherSuite HIGH:!aNULL:!MD5:@STRENGTH
        SSLHonorCipherOrder on

Upon testing again I had… Success! I was upgraded to an A!  From SSL Labs:  “Forward Secrecy – Yes (with most browsers)   ROBUST (more info)”  There is now only one more issue in yellow, so I assumed that fixing the SNI mixup should get me to an A+.

SNI-ed Remarks

The SNI error given was not exactly verbose: “Incorrect SNI Alerts –“.  This was a little puzzling, as I’m not using SNI for this site; there’s only one domain hosted the server.  After Googling for a few minutes, I found myself at a Qualys Community post from August of 2014 detailing this issue.  Most helpfully it gave a quick and dirty test to validate the issue yourself using openssl, which I tweaked as shown below to look for unrecognized name SSL alerts.  You can see in the first example using, that there was no issue.  However, when using, the server was indeed giving back an unrecognized name error:

#; echo -e "HEAD / HTTP/1.0\r\nHost: $SERVER\r\n\r\n" | openssl s_client -servername $SERVER -connect $SERVER:443 -state 2>&1 | grep unrecognized

#; echo -e "HEAD / HTTP/1.0\r\nHost: $SERVER\r\n\r\n" | openssl s_client -servername $SERVER -connect $SERVER:443 -state 2>&1 | grep unrecognized
SSL3 alert read:warning:unrecognized name

This is a perfect example of the minutia you have to wade through in search of an A+.  My DNS for this domain is configured with as a CNAME for, so no one should ever wind up at  However, since it is a SAN in my SSL certificate, according to Qualys I need to specify it as a ServerAlias in my Apache VirtualHost configuration:


This change cleared up the SNI error on SSL Labs, and in my testing, however my grade is still (only?) an A.  Now it’s time to think this through and re-read the output from SSL Labs.

Headed for an A+

One of the lines indicated that I did not have the HSTS (HTTP Strict Transport Security) enabled.  There’s a great summary of HSTS, including sample configurations at, but essentially it is an HTTP header that is returned to your browser on first visit.  This header tells your browser to always use HTTPS for any subsequent visits to your domain.  Since I have all traffic redirecting to HTTPS already, I figured this would be a simple addition.  After all, at this point I was grasping at straws and I added the following configuration:

        Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"

And finally, lo and behold, the elusive A+!

Quote from the test: “This server supports HTTP Strict Transport Security with long duration. Grade set to A+.”

Final Results

Now that we have the A+, I figured I should go ahead and complete the other task that I was considering as a possibility, configuring OSCP Stapling.  This was a simple enough configuration consisting of two additional lines, one defining where the OSCP Stapling cache would be and another turning the feature on.  While this change didn’t upgrade my SSL Labs score any further (A++ anyone?), it did green up another field.  Which after all this time working on the configuration was still fairly satisfying.

Below is an example what my SSL VirtualHosts file looked like after all of these contortions.  I hope you all find it useful, and please let me know if you all have any recommendations, corrections, or your own stories of optimizing SSL.

<IfModule mod_ssl.c>
    SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
    <VirtualHost *:443>

        # SSL Configuration
        SSLEngine on
        SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
        SSLCipherSuite HIGH:!aNULL:!MD5:@STRENGTH
        SSLHonorCipherOrder on
        SSLUseStapling on

        # HSTS Configuration
        Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"

        # Fixes for IE being, well, IE...
        BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown 
                \downgrade-1.0 force-response-1.0
        BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
        # Browser cacheing for static content
        <filesMatch ".(js|css|png|jpeg|jpg|gif|ico|swf|flv|pdf|zip)$">
                Header set Cache-Control "max-age=1209600, public"

        # Redirect "/" to "/blog/"
        <If "%{REQUEST_URI} == '/'">
                Redirect "/" "/blog/"


300-208 SISAS – How to Tackle the Beast

Reading Time: 2 minutesThe best way to finish something, is to begin it.  So I decided I would begin my prep for the 300-208 SISAS (Implementing Cisco Secure Access Solutions) exam, by laying out my personal study plan against the exam topics, found here on the Cisco Learning Community.

The exam topics are broken into five broad categories, and Cisco also gives a general indication of what percentage of the exam is on each topic:

1.0 Identity Management/Secure Access – 33%
2.0 Threat Defense – 10%
3.0 Troubleshooting, Monitoring and Reporting Tools – 7%
4.0 Threat Defense Architectures – 17%
5.0 Identity Management Architectures – 33%

Each of those broad topics has several sub-categories; 1.0 Identity Management/Secure Access, for example, is broken down into over 60 sub-sections.  This depth of expected knowledge can seem quite daunting at first, especially given the vague nature of some of the topics.  However, I’ve found that the exam really breaks down into 5 key areas, and are all focused primarily on Cisco ISE (Identity Services Engine):

  1. Understand what ISE is.
  2. Understand why you might use ISE in a wired or wireless network.
  3. Understand what ISE does at a protocol level.
  4. Understand how ISE interacts with Network Access Devices and other systems.
  5. Understand how to configure ISE and the Network Access Devices.

This is why my primary focus in these exam preparations is getting hands on with Cisco ISE.  I have been spending at least 10 minutes per night, getting familiar with both the GUI itself and the operations that ISE can do.  10 minutes may not seem like much, but getting any hands on time with a complex system matters.  You have to keep yourself sharp and fresh when preparing for an exam.  Especially if, like in my current job, you do not interact with ISE at all on a day-to-day basis.

In the next few posts I’ll dig further into each of these 5 key areas, as well as discuss how to setup an ISE test lab for yourself and talk through some of the scenarios I’m using for my lab.


CCNP Security – Halfway There

Reading Time: < 1 minuteI am halfway towards my CCNP Security, and am finally gearing up to finish it.  When I completed the 642-618 FIREWALL and 642-648 VPN exams in the beginning of 2014, I was promptly sidetracked with the little things in life.  (Such as moving across the country, starting a new job, and finishing my BSIT at WGU.)  Knowing that the old CCNP Security exams had cycled out in April of 2014, I used Cisco’s CCNP Security Migration Path tool to validate that I was left with these two exams:

  • 300-207 SITCS (Implementing Cisco Threat Control Solutions)
  • 300-208 SISAS (Implementing Cisco Secure Access Solutions)

I am starting first with the 300-208 SISAS exam as it covers a range of topics, such as 802.1x, Cisco ISE, and Radius, that I am very familiar with.  However, from everything I’ve read, it goes into great depth on minutia of the ISE interface.  As I haven’t touched ISE in a production environment in over a year now, I’ve been spending time most evenings in my lab spinning up and down many different scenarios.

My lab for this study is entirely virtual, and is really a dry run for building my CCIE Security lab.  It currently consists of the following, largely coordinated and controlled via GNS3:

  • ISE 1.2 – VMWare Fusion on my MacBook
  • ACS 5.6 – VMWare Fusion on my MacBook
  • CentOS test boxes (x2) – VMWare Fusion on my MacBook
  • IOU L2 Image (x2) – Rackspace Cloud Server
  • IOU L3 Image (x6) – Rackspace Cloud Server
  • ASA 8.4 (x2) – QEMU on a Rackspace Cloud Server

In the weeks to come I’ll be posting more about my exam preparations, including lab scenarios and links.  This is mostly for myself, but if anyone else gets some use out of it too, even better.