TuxLabs LLC

All things DevOps

Author: tuxninja

Runner Features Have Been Updated !

Published / by tuxninja / Leave a Comment

Runner Reminder

Runner is a command line tool for running commands on thousands of devices that support SSH. I wrote Runner and use it every single day, because unlike Ansible, Runner truly has no dependencies on the client or server side other than SSH. I have used Runner to build entire datacenters, so it is proven and tested and has a lot of well thought out features, which brings me to todays post. Since I initially debuted Runner I have added a lot of features, but I had yet to check them into github, until now. Here is a run down of Runner’s features.

Features

  • Runner takes your login credentials & doesn’t require you to setup SSH keys on the client machines/devices.
  • Runner can be used through a bastion/jump host via an SSH tunnel (see prunner.py)
  • Runner reads it’s main host list from a file ~/.runner/hosts/hosts-all
  • Runner can accept custom hosts lists via -f
  • -e can be used to echo a command before it is run, this is useful for running commands on F5 load balancers for example, when no output is returned on success.
  • -T will allow you to tune the number of threads, but be careful you can easily exhaust your system or site resources (I.E. do NOT DOS your LDAP authentication servers by trying to do hundreds of threads across thousands of machines, unless you know they can handle it 😉 ).
  • -s is for sudo for those users who have permissions in the sudoers file.
  • -1 reduces any host list down to one host per pool. It uses a regex, which you will likely have to modify for your own host / device naming standard.
  • -r can be used to supply a regular expression for matching hosts. Remember sometimes you have to quote the regex and/or escape the shell when using certain characters.
  • -c will run a single command on many hosts, but -cf will run a series of commands listed in a file on any hosts specified. This is particularly useful for automations. For example, I used it to build out load balancer virtuals and pools on an F5.
  • -p enables you to break apart the number of hosts to run at a time using a percentage. This is a handy & more humanized way to ensure you do not kill your machine or the infrastructure you are managing when you crank threads through the roof 😉

Now that I have taken the time to explain some of those cool features, here’s an example of what it looks like in action.

Runner Demo

Host List

Basic Run Using Only -c, -u and defaults

Note: User defaults to the user you are logged in as if you don’t specify -u . Since I am logged in as ‘jriedel’ I have specified the user tuxninja instead.

The Same Run Using Sudo 

Note: I just realized if you do not prompt for a password for sudo it will fail, I will have to fix that ! Whoops ! P.S. You should always prompt for a password when using sudo !

Runner with a command file in super quiet mode  !

Example of a simple regex & a failure

I hope you enjoyed the overview and new features. You can clone Runner on github.

Enjoy,
Jason Riedel

Object-Oriented Programming With Python : Encapsulation (1/3)

Published / by tuxninja / Leave a Comment

Justification For Learning OOP

linux-python-logo1Since 1995, I have written some form of code, but over the years my career has never taken shape with a primary focus on development. Instead in my various roles of Systems, Network, Application and Datacenter engineering & architecture, I have found excuses to let the inner code hacker in me, out. Allowing me to consistently pursue my passion for coding and automation within the scope of my daily duties. However, this approach has never allowed me to work on a development team and thus the necessity for using object oriented programming rarely has occurred.

Sure I’ve written classes and modules for re-use, but I have never had to place a tremendous amount of forethought into the design for flexibility or inflexibility of the interfaces those classes provide because I was the only one using them. This hasn’t change, but as of late, I find myself realizing my career may be ready for a change. It is likely the next step for me in my career is a full blown software development role. This is partly because of boredom and mastery in my existing field after decades of experience, but mostly because of my own career planning and the apparent industry trends.

Simply put more jobs are moving to software development, and Systems Engineering is on a steady decline due to the advent of Cloud computing, Containers and more. Due to the amazing products, platforms and tools that are now available less and less Systems Engineers are required and I would say Systems Engineers that don’t code are already obsolete, whether they realize it yet or not.

In todays world if you are or are going to be a developer, knowing how to write object oriented code is a requirement and more importantly thinking ahead about who and how your code will be used becomes paramount when designing effective software. This article is intended to help you get a better understanding of how to write object oriented programming using Python.

Procedural Programming vs. Object Oriented Programming

Procedural programming  is a more antiquated paradigm for software design that can be described as:

  • Code that processes data
  • The data we process is stored in variables
  • Then we create functions to process the data in those variables

In the object oriented programming paradigm:

  • We organize data into Objects
  • Functions become Methods
  • And The design of these objects & methods is specified in a Class, which is essentially a blue print.

Object oriented programming is the primary software design paradigm in existence today because it provides programmers with a way to effectively organize and share code for re-use while to protecting the integrity of the existing code. To understand how this works we need to jump right into it with The Three Pillars of Object Oriented Programming.

The Three Pillars (Encapsulation, Inheritance, and Polymorphism)

Encapsulation

Encapsulation is about ensuring the safe storage of data as attributes in an instance.

Encapsulation tells us that :

  • Data should only be accessed through instance methods.
  • Data should always be correct based on the validation requirement set in the class methods.
  • And Data should be safe from changes by external processes.

Great so what does that mean 🙂 It means we should be using ‘setter’ and ‘getter’ methods to access object attribute values. Here’s an example of a class that uses a setter and getter method (note the name setter & getter is not actually required) to set and get the variable/attribute value.

The output of this code is: 

It’s important to recognize that encapsulation is not enforced by Python. So a programmer using a class is not required to access the data through the getter and setter methods. For example, in the class above someone could set website, without ever interacting with the defined setter and getter classes. This is called breaking encapsulation, and it’s bad form / practice for a programmer to this because the class author & maintainer can no longer validate the data that is being accessed and this can cause unforeseen problems with a program that is dependent on the class it is inheriting from. Here’s a modified example where we are breaking encapsulation. 

Here is the output of the above example: 

A number of things have changed.

  1. We added a variable website to make the example more clear and easy to follow.
  2. We added validation to our setter() method, it now checks to make sure ‘http’ is contained in the value that we are setting for website.
  3. If ‘http’ is not in website the setter() method will throw an error and will not set website!
  4. We added some additional formatting to our getter() method so we can tell when it is being used verses when encapsulation is being broken. getter() now pre-pends any website passed with ‘Website is now set to:’

So let’s walk thru how these changes effected the program and what actually happened. First, we tried to set website to ‘http://www.tuxlabs.com’ and then we changed it to ‘http://www.google.com’. In both of these examples we followed the rules of encapsulation, setting and getting the attribute value through methods in the class. These examples are correct and set website to the value passed and outputed them with the correct formatting we implemented in getter().

Next, we tried setting website to a really long string, without using setter() ! So we did tuxlabs.website = website directly on the instance of our class ! We also circumvented using getter() and just printed in exactly the same, broken way print tuxlabs.website. Unfortunately, as previously mentioned nothing requires the programmer to use our setter and getter methods directly, and thus website is not being checked for containing ‘http’ here and so it can printed our very long string (“I should not be accessing website in the class directly!, but since I am breaking encapsulation it does still work, it\’s just naughty!”).

The last and final example, is an example of encapsulations value proposition. We try to set website to something that does not contain ‘http’ and therefore is not a website. An error is thrown and website is not set, which is exactly what we want.

Encapsulation is  simple, easy to follow and powerful when your programming on or with a team of developers that are going to share code amongst each other. In these scenarios encapsulation is obviously a must promoting collaboration through protecting programmers from potentially mis-using each others code. It should be easy to see now that breaking encapsulation is bad and very poor practice because code that perfectly functions one day, could be broken the next without any changes from you, because you are violating the contract that encapsulation methods of the class provide.

This concludes our learning about encapsulation. Still to come, Inheritance, Polymorphism, and some real world examples!

Until next time…Keep learning,
Jason Riedel

Upgrading to Python 2.7 on CentOS 6.5

Published / by tuxninja / 1 Comment on Upgrading to Python 2.7 on CentOS 6.5

Hey Folks,

The systems running Tuxlabs are currently running CentOS 6.5 to emulate a production RHEL like setup for an Openstack Cloud. Running an operating system this old has it’s drawbacks such as dependencies. I was recently installing a well know Python framework and ran into compatibility issues. The framework required Python 2.7 and CentOS 6.5 comes with 2.6. The below is a step by step procedure for how to upgrade to Python 2.7 on CentOS 6.5 if  you ever should need it. However, as a reminder run a newer OS when possible and for god sakes if you don’t need Redhat support, run Ubuntu.

Step one, we verify we are indeed running Python 2.6

Ok then, let’s upgrade Python to 2.7. First let’s update all of our system applications, just in case for version dependencies and it’s good for security etc.

Next, we have to install Develop Tools, it is a required dependency to install Python.

Additionally, we will need these…

Now, let’s install Python 2.7

It is important to use ‘altinstall’ otherwise you will end up with two different versions of Python on your filesystem, both named ‘python’.

You can verify the install like so

That’s it ! Enjoy.

SSH Tunneling

Published / by tuxninja / Leave a Comment

In my last post about Runner I briefly explained needing to modify your ~/.ssh/config to use a ProxyCommand to allow for automatic tunneling with SSH.

What I didn’t explain is there is an alternative method that is arguably simpler. It requires creating three small shells scripts & placing them in your path or a common host path like /usr/local/bin/ with the chmod +x permission. Here is the script that sets up the ssh tunnel.

Script: starttunnel

Running starttunnel, will connect you to your bastion/jump box and then background this connection with keep alives on. It will listen / dynamically forward ssh requests to 8081 through or to tlbastion.tuxlabs.com. Additionally, if you wanted to tunnel a web port specifically on a machine that sits within your network back to the machine you are tunneling from, you can add it to the script. Such that the required host/port always gets tunneled and is available on your machine when you run starttunnel. Example config would look like.

Script: starttunnel + forwarding http

 

Now that you have authenticated to your bastion and have a working tunnel you need to get ssh requests to go through this tunnel. However, if your like me you still want the ability to ssh to other stuff without going through that tunnel. So I created a new script called ‘sshp’. When I want to ssh through the tunnel / proxy I use ‘sshp’, when I want to ssh to somewhere else on the internet or another network I use plain old ‘ssh’. Here is my sshp script used to connect to machines behind the bastion.

Script: sshp

Now, when you run sshp tuxlabs1@tuxlabs.com you will be connection through the tuxlabs bastion into tuxlabs1. Also notice in my previous post I used sconnect as the proxy command in this one we are using ‘nc’ aka netcat. I have found this method of tunneling to be the most simplistic and effective in my daily life. One more script you need is if you want to copy files you need to use scp. So you have to make a similar command ‘scpp’ for tunneling your copying of files. Here’s the script.

Script: scpp

One final note…if you need use ‘*’ aka splat for copying many files you cannot use the script above, because the shell or script converts that incorrectly. Instead just use the full command yourself from the command line.

scp’ing with *

This would copy all files named ‘copy.all.<whatever>’ to the  bastion. Hope this hopes the folks out there feeling limited by bastions. They provide great security and are an absolute requirement in secure environments so learning tricks that make sure you only need to authenticate once for an extended period of time can come in real handy.

Enjoy,
Jason Riedel

 

 

Runner: Multi-threaded SSH with Sudo support using Python & Paramiko

Published / by tuxninja / Leave a Comment

Example of Runner

 

Why Runner ?

I have been working as a Systems & Network Administrator since 1999. In that time I have repeatedly had the need for rapidly executing commands across thousands of servers. There are many applications out there that solve this problem in various ways…to name a few…pdsh, Ansible, Salt, Chef, Puppet (mcollective),  even Cfengine and more. Some require agents running on the machines, some use SSH, but require keys…or learning curves. Alternatively, you can write your own code to solve this problem, which is what I did mostly for fun. I don’t recommend re-inventing the wheel if you need this for your job, just use what is already out there, or download runner and hack it to your hearts content for your purposes.

Fabric vs. Paramiko

Because I use Python for most of my work code these days, I decided to write my multi-threaded SSH command runner in Python this way I can use Runner for parallel SSH transport & easily bolt on my other Python scripts for additional functionality. Python has fantastic support for SSH via two libraries Fabric & Paramiko. Fabric is built on top of Paramiko. Fabric provides a simpler interface than Paramiko does for doing just about anything you can think of. Create a fabfile run it, and wolla instant results from commands ran via SSH. Fabric is really great for running & re-running a set of commands to automate an install or reporting for example. All that being said I still chose to use Paramiko over Fabric for three reasons.

  1. I don’t like abstraction. Fabric hides the ugly-ness of Paramiko, which I prefer to understand better.
  2. Writing this using Paramiko lent itself better to a command line utility used for adhoc commands than Fabric did.
  3. I wasn’t sure if Fabric’s abstraction would limit me later based on needing custom functionality. So for Runner I chose Paramiko, but to be clear, 9 times out of 10 I think I would choose Fabric.

Bastions

A bastion or jump box is a machine that is used as the gatekeeper of access to the rest of the machines in your network. In secure environments where your Corp network is separate from your Production network, you will have to SSH into a bastion, which usually has some form of 2-factor authentication (at least it should !) and then from there you may SSH into other hosts. A bastion can throw a real wrench in trying to manage thousands of machines in seconds, because you would have to authenticate to the bastion 1000 times ! The way around this, is by setting up your SSH config to proxy commands.

ProxyCommand & Sconnect

Sconnect (or connect.c) is a binary that is most commonly used as the proxy command for SSH. You can download / read more about sconnect here : https://bitbucket.org/gotoh/connect/wiki/Home and it will also tell you how to setup your SSH config. Using a ProxyCommand with Runner is required, you can however use any ProxyCommand you would like. Really quickly here is what you basically need to do.

  1. Download / Compile connect.c
  2. Copy it to /usr/local/bin/sconnect and set executable permissions
  3. In your SSH Config (.ssh/config) add…
    1. Host <ssh-config-profile-name>
      User tuxninja
      ForwardAgent yes
      HostName <bastion_name>
      DynamicForward 8081 (any uncommon port is fine)
    2. Host *.tuxlabs.com
      User tuxninja
      ProxyCommand /usr/local/bin/sconnect -4 -w 4 -S localhost:8081 %h.tuxlabs.com %p

That is basically it. Then you should start a screen session so you can background the SSH session, since you will leave this open for other SSH sessions to proxy through so you don’t have to go through 2-factor authentication more than once. So something like…

After you authenticate, detach yourself from the screen using CTRL A then D. Now you can ssh to anything @ domain name in my case tuxlab.com and it will forward through the bastion. At this point you still have to authenticate using a username / password, which is fine. Runner deals with this.

Hosts

Runner requires a hosts file to run. By default it is configured to look in hosts/hosts-all for a list of all hosts. I use a script called ‘update-runner-hosts.pl’, which is included in my github to gather hosts from a URL and update the required hosts file. Once you have populated hosts/hosts-all with the FQDN for your hosts, you are ready to use Runner.

Note: You can use ‘-f’ to provide a custom location for your hosts file.

Great Flags / Features

So some of the really great features of Runner are threading (-t), sudo (-s), list only mode (-l) and the regular expression (-r). -r is for pattern matching your hosts lists, which is incredibly handy and absolutely required in an environment with hundreds to thousands of hosts and you only want to select hosts with -r ‘web’ in them.

(-1) one host per pool mode is a great feature, however it is dependent on understanding your environments hostname pattern so you will have to modify the regular expression in the code to make sure it works for you. It is currently setup to identify hostnames in pools when the naming convention is something like apache1234.tuxlabs.com.

Ok I could go on and on about runner, but it’s better to just share the code at this point and let you go! Note the statically defined proxy_command in the code, you may need to change this if you didn’t use sconnect or the same port.

Note: by default runner uses the user you are logged in as to SSH, you can prompt input for a different user with ‘-u’.

All code and accessories are available for download on github : https://github.com/tuxninja/tuxlabs-code/tree/master/runner

Email tuxninja@tuxlabs.com with any question ! Happy SSH’ing admins!

Note: In various versions of this code I had a ‘-h’ allowing you to pass a CSV list of hosts, somehow I let that drop out of this version, sorry ! Feel free to re-add it !

The Runner Code