Posts

Showing posts from August, 2017

Using an ASA and a single inside interace as your gateway for your CCIE Collaboration Lab

Before you think it will be a wonderful idea to run everything through your ASA as the default gateway of your lab, keep in mind that hairpins/u-turns off an ASA are not the best design.  For this reason you may have a bit of configuration to do. Please follow the current Cisco guide when you using an ASA your gateway device.

Found here:
https://supportforums.cisco.com/t5/security-documents/hairpin-u-turn-traffic-off-an-interface-on-an-asa-running-8-3-or/ta-p/3129668

CCIE Collaboration Lab NFS tidbits for Windows

Given the cost of storage for servers, you may want to use NFS for your CCIE lab.  If you are using Windows 10 and have a mass amount of storage on say your desktop vs your server, you could use a program called FreeNFS.  First download and install it, then run it and configure it from the directory where you would like to use it.  After this, connect to it using ESXi and validate you can access it.  Once you have done this, you can use NSSM to add FreeNFS as a service to run when your desktop boots.  I use it for all my VMDK backups to work around the 60 day demo licensing for my lab.  As well, if you are lacking resources, ensure you are thin provisioning your VM's and you can even go so far as using OVFtool to tweak the resources of each VM to dwindle them down to the 850Mhz range of processor usage.

Links to both:
http://freenfs.sourceforge.net/
https://nssm.cc/download

Lastly, should you have issues with ESXi inactive NFS shares, the best solution to avoid having to reconfigure any hosts is to use the CLI.

esxcli storage nfs remove -v <DATASTORE>
esxcli storage nfs add -H <HOST> -s <MOUNTPOINT> -v <DATASTORE>

CUCM certificate requests with an IP as the Subject Alternative Name (SAN)

If you have ever created a Certificate Signing Request using CUCM, you may have noticed a lack of setting an IP as a SAN.  A simple solution to this if you are dealing with a Microsoft Certificate Server is to fill out the additional attributes text field. However, by default prior to doing this you will need to open up an Administrator command prompt and issue the following command:

certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2

Now restart your certificate services under the AD CA management pane.

Finally, you simply need to add the following:

san:dns=<hostname>&dns=<fqdn>&ipaddress=<ip address>

Although this may seem like a trivial thing, you will cause errors in most modern browsers due to not having a correct SAN in the certificate. This allows you to override even improperly generated requests lacking a SAN.  But by default Microsoft Server CA doesn't even generate Certificates with a SAN from the CUCM CSR's.  As always, remember that you need your CA to be in the computer's certificate store for trust to be established.

Lastly, make sure you restart your Tomcat service via the CLI by entering:

utils service restart Cisco Tomcat

LetsEncrypt + ASA 5500, automated certificate renewal script.

So there is no API for the ASA 5500 series, and you probably do not have an X series for Firepower firewall in your lab.  You could use the ASAv and the github script out there, but then you need licensing.  Due to this, I have created a fairly lengthy script that creates and renews your certs from Let's Encrypt.  You will need a Linux box, a web server running on it, root access (you have to run this with sudo at least), Python 3.x and paramiko-expect installed.  Most values are at the top to be changed.  It assumes you will use SSL-Trustpoint as the name, you can modify as you like.  This script is BSD licensing.  You may want to change the prompts and all fields at the top as needed.

So install Python 3.x (64 bit if needed), ensure your Path environment variable is set properly, then you will need to pip install paramiko-expect

This should work on all platforms!

This will also create a log file in /var/log/letsRenewASA.log as it is meant to be set up as a Cron job so that you no longer have to manually grab new certificates.

This script does NOT configure your VPN for you, you still have to do that yourself.

You can download it directly here:
https://drive.google.com/file/d/0Bz9hJ3nepc_7bUwxVUUxRG50OEU/view?usp=sharing

#!/usr/bin/python
#Copyright 2017 Tim Nelson
#
#Redistribution and use in source and binary forms, with or without modification,
#are permitted provided that the following conditions are met:
#
#1. Redistributions of source code must retain the above copyright notice,
#this list of conditions and the following disclaimer.
#
#2. Redistributions in binary form must reproduce the above copyright notice,
#this list of conditions and the following disclaimer in the documentation and/or
#other materials provided with the distribution.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
#NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.


import paramiko, re, time
from paramiko_expect import SSHClientInteraction

asaHost = '<HOSTNAME OR IP OF ASA>'

hostname = '<ACTUAL HOSTNAME FIELD ON ASA>'
localHost = '127.0.0.1'
asaUser = '<ASAUSERNAME>'
asaPassword = '<ASAPASSWORD>'
enablePassword = '<ENABLEPASSWORD>'
localUser = '<LINUXUSERNAME>'
localPassword = '<LINUXPASSWORD>'
csrFilename = 'asa.csr'
logFile = '/var/log/letsRenewASA.log'
fqdn = '<YOUR FQDN>'
certString = 'CN=' + (fqdn) + ',OU=<YOUROU>,O=<ORGANIZATION>,C=US,St=<ST>,L=<CITY>'
letsEncrypt = '/usr/bin/letsencrypt/letsencrypt-auto'
email = '<YOUR ADMIN EMAIL>'
webRoot = '<ROOT DIRECTORY OF WEBSERVER>'

localPrompt = localUser + '@.*'
prompt0 = (hostname) + '> '
prompt1 = (hostname) + '# '
authPrompt = 'Password: '
confPrompt = '.*\)\# '
keyPrompt = '.*\[yes\/no\]: '
accept = 'Continue \(y\/n\)\?'
logFile = open(logFile ,'a+')
csrFile = open(csrFilename ,'w')
i = 10

logFile.write('\n\nStarting to Renew!\n')

client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=asaHost, username=asaUser, password=asaPassword)
interact = SSHClientInteraction(client, timeout=120, display=False)
try:
        interact.expect(prompt0)
except Exception:
        logFile.write('could not read prompt0\n')
interact.send('en')
try:
        interact.expect(authPrompt)
except Exception:
        logFile.write('could not read authPrompt\n')
interact.send(enablePassword)
try:
        interact.expect(prompt1)
except Exception:
        logFile.write('could not read prompt1\n')
interact.send('conf t')
try:
        interact.expect(confPrompt)
except Exception:
        logFile.write('could not read confPrompt\n')
interact.send('crypto key generate rsa label SSL-Keypair modulus 2048')
interact.expect([keyPrompt, confPrompt])
if interact.last_match == keyPrompt:
        interact.send('n')
interact.send('crypto ca authenticate SSL-Trustpoint')
interact.expect(['.*delete.*', '.*unknown.*'])
if interact.last_match == '.*delete.*':
        interact.send('no crypto ca trustpoint SSL-Trustpoint')
        interact.expect([keyPrompt, '.*in use.*'])
        if interact.last_match == '.*in use.*':
                i = 0
                interact.send('no crypto ikev2 remote-access trustpoint SSL-Trustpoint')
                interact.expect(confPrompt)
                interact.send('no crypto ca trustpoint SSL-Trustpoint')
                interact.expect(keyPrompt)
        interact.send('y')
interact.send('crypto ca trustpoint SSL-Trustpoint')
interact.expect(confPrompt)
interact.send('enrollment terminal')
interact.expect(confPrompt)
interact.send('fqdn ' + (fqdn))
interact.expect(confPrompt)
interact.send('subject-name ' + (certString))
interact.expect(confPrompt)
interact.send('keypair SSL-Keypair')
interact.expect(confPrompt)
interact.send('exit')
interact.expect(confPrompt)
interact.send('crypto ca enroll SSL-Trustpoint')
interact.expect(keyPrompt)
interact.send('y')
interact.expect(keyPrompt)
interact.send('n')
interact.expect(keyPrompt)
interact.send('y')
interact.expect(keyPrompt)
cert = interact.current_output_clean
cert = re.sub('Certificate Request follows:\n', '', cert)
cert = re.sub('\x00', '', cert)
interact.send('n')
interact.expect(confPrompt)
interact.send('exit')
interact.expect(prompt1)
interact.send('exit')
csrFile.write(cert)
csrFile.close()
client.connect(hostname=localHost, username=localUser, password=localPassword)
interact = SSHClientInteraction(client, timeout=120, display=False)
try:
        interact.expect(localPrompt)
except Exception:
        logFile.write('could not read localPrompt\n')
        print('could not read localPrompt')
interact.send(
 (letsEncrypt) +
 ' certonly --authenticator manual --server https://acme-v01.api.letsencrypt.org/directory --text --email '+
 (email) + ' --csr ' + (csrFilename)
 )
interact.expect('.*o: ')
interact.send('y')
interact.expect('Press Enter to Continue')
cleanOutput = interact.current_output_clean
challengeFile = cleanOutput.splitlines()[8]
challengeFile = re.sub('http://' + (fqdn) + '/', (webRoot), challengeFile)
challengeContent = cleanOutput.splitlines()[4]
logFile.write('Created challengeFile at ' + (challengeFile) + '\n')
challengeFile = open(challengeFile, 'w')
challengeFile.write(challengeContent)
logFile.write('Put the following content: ' + (challengeContent) + ' in challengeFile\n')
challengeFile.close()
interact.send('\r')
interact.expect(localPrompt)
cleanOutput = interact.current_output_clean
interact.send('exit')
certPath = (cleanOutput.splitlines()[4])
certPath = re.sub('Server issued certificate; certificate written to ', '', certPath)
logFile.write('Grabbed the certificate from: ' + (certPath) + '\n')
chainPath = (cleanOutput.splitlines()[10])
chainPath = re.sub('\s+', '', chainPath)
chainFile = open(chainPath, 'r')
certFile = open(certPath, 'r')
caFile = chainFile.read()
caCert = re.findall(
 r'-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----',
 caFile, re.DOTALL
 )
caCert = '-----BEGIN CERTIFICATE-----' + (caCert[1]) + '-----END CERTIFICATE-----'
chainFile.close()
logFile.write('Grabbed the certificate chain from: ' + (chainPath) + '\n')
client.connect(hostname=asaHost, username=asaUser, password=asaPassword)
interact = SSHClientInteraction(client, timeout=120, display=False)
try:
        interact.expect(prompt0)
except Exception:
        logFile.write('could not read prompt0\n')
interact.send('en')
try:
        interact.expect(authPrompt)
except Exception:
        logFile.write('could not read authPrompt\n')
interact.send(enablePassword)
try:
        interact.expect(prompt1)
except Exception:
        logFile.write('could not read prompt1\n')
interact.send('conf t')
try:
        interact.expect(confPrompt)
except Exception:
        logFile.write('could not read confPrompt\n')
interact.send('crypto ca authenticate SSL-Trustpoint')
interact.expect('.*by itself.*')
for line in caCert.splitlines():
        interact.send(line)
        time.sleep(.1)
interact.send('quit')
interact.expect(keyPrompt)
interact.send('y')
interact.expect(confPrompt)
interact.send('crypto ca import SSL-Trustpoint certificate')
interact.expect(keyPrompt)
interact.send('y')
interact.expect('.*by itself.*')
lines2 = certFile.read().splitlines()
for line2 in lines2:
        interact.send(line2)
        time.sleep(.1)
certFile.close()
interact.send('quit')
interact.expect(confPrompt)
interact.send('ssl trust-point SSL-Trustpoint outside')
interact.expect(confPrompt)
if i == 0:
        interact.send('crypto ikev2 remote-access trustpoint SSL-Trustpoint')
        interact.expect(confPrompt)
interact.send('exit')
interact.expect(prompt1)
interact.send('wr')
interact.expect(prompt1)
logFile.write('SUCCESSFULLY installed the cert and chain to your ASA!\n')
logFile.close()
interact.send('exit')

Adding 2 Factor Authentication to Guacamole through Duo and Yubikey

In order to do this you will need to grab the current duo extension as such:

wget https://gigenet.dl.sourceforge.net/project/guacamole/current/extensions/guacamole-auth-duo-0.9.13-incubating.tar.gz

Once downloaded, decompress and move it to the appropriate folder:

tar xzf guacamole-auth-duo-0.9.13-incubating.tar.gz && mkdir /etc/guacamole/extensions && mv ./guacamole-auth-duo-0.9.13-incubating/guacamole-auth-duo-0.9.13-incubating.jar /etc/guacamole/extensions/

Now add the Guacamole as an application to your Duo management page.  Select, Protect an Application under the Applications menu, then you will select the Web SDK application, rename it something like Guacamole.

Next you will need to edit your /etc/guacamole/guacamole.properties and add the following info to it from the Duo site (minus the last key):

duo-api-hostname: <Your API key found on the Duo site>
duo-integration-key: <Integration key found on the Duo site>
duo-secret-key: <Secret Key found on Duo site>
duo-application-key: <40 random characters, I used pwgen 40 1>


The duo-application-key is just any 40 characters that you save into this file, it is not found on the Duo site.  Once you add these to the bottom, restart the tomcat service.

Next you will need to ensure you have your token registered as per instructions here:
https://duo.com/docs/yubikey

Ensure the assigned user matches your Guacamole username and that if you are using the second token slot, you are holding the button on your Yubikey down for the token input (as well, make sure you are on token).

That's it!  Enjoy 2 factor authentication to your Guacamole server!


Guacamole, Tomcat and LetsEncrypt, for VPN-less CCIE Lab Access

So you just built your CCIE lab but you can't access it from work because VPNs are restricted?  No problem!  Guacamole solves this issue, below is a quick dirty method of implementing it properly.

I highly recommend that you use the most current Guacamole release always (not Git).  Once you have compiled Guacamole from source (binaries generally do not work as well and I run my portal on a 7watt ARM dev board).  You can simply follow the guide off the Guacamole site: https://guacamole.incubator.apache.org/ you may have unmet dependencies prior to doing this. For Ubuntu, the following is done:

add-apt-repository ppa:webupd8team/java
apt -y update
apt -y upgrade
apt -y dist-upgrade
apt -y install libcairo2-dev libjpeg-turbo8-dev libpng12-dev libossp-uuid-dev \

libfreerdp-dev libpango1.0-dev libssh2-1-dev libtelnet-dev libvncserver-dev \
libpulse-dev libssl-dev libvorbis-dev libwebp-dev git build-essential autoconf \
libtool oracle-java8-installer tomcat8 tomcat8-admin tomcat8-common tomcat8-docs \
tomcat8-user maven mysql-server mysql-client mysql-common mysql-utilities \
libpulse-dev libvorbis-dev freerdp ghostscript wget


Once this is complete you should be good.  Next, you will need to get Letsencrypt:

git clone https://github.com/letsencrypt/letsencrypt ~/letsencrypt
cd ~/letsencrypt
./letsencrypt-auto certonly --expand --webroot --webroot-path \

/<CATALINA_HOME>/webapps/ROOT/ -d <YOURDOMAIN> --staging

Ensure you are using staging until successful, once successful, drop the --staging and rerun.  At this point we now have our certificate issued by LetsEncrypt.  For Tomcat, it's easiest to convert it into a .jks  Once you get the success, cd into the proper directory where your certs are stored.
Then do the following:

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out cert_and_key.p12 \
-name tomcat -CAfile chain.pem -caname root


fullchain.pem should be the host and the intermediate certificate in a single file, or else you will only present a single certificate, which is a security risk.

Convert the pkcs12 into a jks now:

keytool -importkeystore -deststorepass <changeit> -destkeypass <changeit> \
-destkeystore myKeyStore.jks -srckeystore cert_and_key.p12 -srcstoretype \
PKCS12 -srcstorepass <PasswordUsedAbove> -alias tomcat


Now edit your /<CATALINA_HOME>/server.xml with the following connector options:

scheme="https" secure="true" SSLEnabled="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="/etc/letsencrypt/live/<YOURDOMAIN>/myKeyStore.jks"

keystorePass="<changeit>" keyAlias="tomcat" keyPass="<changeit>"


Next, lets forward port 443 to 8443 since tomcat doesn't always run on 443 and it's much quicker to do it this way:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443

Test your connection from an outside source and done!
I recommend using https://www.ssllabs.com/ssltest/

If you want to install Tomcat 8 from source, use this guide:
https://www.digitalocean.com/community/tutorials/how-to-install-apache-tomcat-8-on-ubuntu-14-04

And if you need an init.d script, here's mine:
#!/bin/bash
export CATALINA_HOME=/opt/tomcat
export JAVA_HOME=<WHATEVERYOURJAVAHOMEIS>
export PATH=$JAVA_HOME/bin:$PATH

start() {
 echo "Starting Tomcat 8..."
 sh $CATALINA_HOME/bin/startup.sh
}
stop() {
 echo "Stopping Tomcat 8..."
 sh $CATALINA_HOME/bin/shutdown.sh
}
case $1 in
  start|stop) $1;;
  restart) stop; start;;
  *) echo "Usage : $0 <start|stop|restart>"; exit 1;;
esac

exit 0




PhantomJS, Python, Selenium, Java notifications and popups, how to confirm?

When dealing with Java alerts/notifications that you need to accept and using a headless browser such as PhantomJS, there is no method (currently) for handling the alerts.  Instead what you have to do is send a driver.execute script into the mix.

driver.execute_script("window.confirm = function(msg) { return true; }");

However, this simply cannot be placed after the pop up occurs, it must be pushed before the step you will use in Selenium that causes the pop up notification.

Example, in CUCM you have certain actions that create two consecutive pop-ups that you need to accept or press 'OK' on.  For this you would need to do the following:

try:
driver.execute_script("window.confirm = function(msg) { return true; }");
driver.execute_script("window.confirm = function(msg) { return true; }");
elem = driver.find_element_by_name("button2")

elem.click()

Selenium how to simply grab an html value in Python

While dealing with Selenium, you may run into object that have no ID and no Name, or a duplicate name.  At this point the unique identifier might be the Value field.  While normally Xpath values are a pain to deal with, you can simply wild card them if the Value field is indeed unique to the page.

elem = driver.find_element_by_xpath(".//*[@value='<WhatIamLookingFor>']")
elem.click()

CUCM Selenium, inability to skip steps

When dealing with CUCM, one of the things you need to take notice of is that you are dealing with Tomcat JSP's. This means that even through there are URL shortcuts, you can not take them when automating.

Example:
'https://'+host+'/ccmservice/communitystring.jsp?NodeID='+(host)+'.'+(domain)+'&action=add'

This is the third step, after you have selected the server and then clicked the Add New button.  While you would think you can just negate all the clicking and go directly to the URL and have shorter code, you cannot.  You must click through as if you were doing it manually.

So you really have to use all this:
elem = driver.find_element_by_name("submit1")
elem.click()
elem = driver.find_element_by_name("button1")
elem.click()
elem = driver.find_element_by_name("communityString")
elem.send_keys(communityString)

How to update CUCM DNS through automation in Windows

So I spent a lot of time trying to find something that works in both Windows and other OS's under python that has similar output as expect (Yes, I happen to be a TCL fan....)

My solution is Python + Paramiko-Expect

Here is a snippet:


client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname=hostAddress, username=u, password=p, banner_timeout=120)
prompt = 'admin:'
accept = 'Continue \(y\/n\)\?'
interact = SSHClientInteraction(client, timeout=120, display=False)
interact.expect(prompt)
try:
interact.send('set network dns primary ' + str(args.primarydns))
cmd_output_pd = interact.current_output_clean
#split what you expect as output and do tasks for each
interact.expect([accept, prompt])
if interact.last_match == prompt:
print((threadName) + ' -> ' + str(args.primarydns) + ' -> FAIL')
if interact.last_match == accept:
print((threadName) + ' -> ' + str(args.primarydns) + ' -> SUCCESS')
interact.send('y')
interact.expect(accept)
interact.send('y')
interact.expect(prompt)
interact.send('exit')
#throw no exception, you lose connectivity when changing this
except Exception:
interact.send('exit')

Example Python Selenium Script for logging into CUCM


import getpass
u = input("Enter the OS Administration Username: ")
p = getpass.getpass ("Enter the OS Administration Password:")
hostList = ['host1','host2']
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')

driver = webdriver.Chrome(chrome_options=options)

for host in hostList:
url = "https://" + host + "/cmplatform"
driver.get(url)
elem = driver.find_element_by_name("j_username")
elem.clear()
elem.send_keys(u)
elem = driver.find_element_by_name("j_password")
elem.clear()
elem.send_keys(p)
elem.send_keys(Keys.RETURN)
url = "https://" + host + "/cmplatform/"
driver.get(url)