Saturday, 24 May 2014

Python Uptime Function

If you take a look in /proc/uptime you'll see two floating point numbers:

 mugwriter@interzone ~ $ cat /proc/uptime  
 1877.17 2961.48  

The first number is seconds since boot and the second number is how long the system has been idle for. I'm using a dual core system, so for every second that the system is idle the second number is incremented by two.

We can use the first number to write to calculate uptime and present it in a tidy fashion. Yes, I know there is always the "uptime" command, but there's no learning to be done just by using that. I'm trying to learn Python here so little bits of code like this are good methods for learning a bit more.

Here's the function:

 def uptime():  
   Return uptimes based on seconds since last boot  
   with open('/proc/uptime', 'r') as f:  
     uptime_seconds = float(f.readline().split()[0])  
     seconds = str(int(uptime_seconds % 60))  
     minutes = str(int(uptime_seconds /60 % 60))  
     hours = str(int(uptime_seconds / 60 / 60 % 24))  
     days = str(int(uptime_seconds / 60 /60 / 24))    
     # Time unit strings  
     time_d = ' days, '  
     time_h = ' hours, '  
     time_m = ' minutes'  
     time_s = ' seconds.'  
     # Change time strings for lower units, prepend zeros  
   if int(days) == 1:  
     time_d = ' day, '      
   if int(hours) <= 9:  
     hours = '0' + hours  
     if int(hours) == 1:  
       time_h = 'hour '    
   if int(minutes) <= 9:  
     minutes = '0' + minutes  
     if int(minutes) == 1:  
       time_m = ' minute '        
   if int(seconds) <= 9:  
     seconds = '0' + seconds  
     if int(seconds) == 1:  
       time_s = ' second.'  
   print(days + time_d + hours + ':' + minutes + ':' + seconds)  
   print('Uptime is ' +days + time_d + hours + time_h + minutes + time_m +' and ' + seconds + time_s)  

It's not much, but it works. I've put an in loop in there to decide if the output should print "seconds" or "second", "minutes" or "minute", "hours" or "hour", "days" or "day."

Here's the output that you get:

 mugwriter@interzone ~ $ python   
 0 days, 00:42:12  
 Uptime is 0 days, 00 hours, 42 minutes and 12 seconds.  

There are two lines of output there, comment out the one that you don't like if you want to take the code.  Feel free to add it to your .profile or something if you find it useful

Sunday, 27 April 2014

Poject Euler: Problem #2

Continuing with my python journey, I'll be posting my efforts at solving puzzles from Project Euler as I work through them.

Problem #2

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

 fib = 1  
 fib2 = 2  
 temp = 0  
 total = 0  
 while temp <=4000000:  
   temp = fib2  
   if temp % 2:  
     total += temp  
     print total  
   temp = fib + fib2  
   fib = fib2  
   fib2 = temp  
 print total  

Not the most elegant way, but I've got the result I need to move on to number 3.

Friday, 25 April 2014

Project Euler

I'm trying to learn how to program in Python currently. I'm not going to say it's coming easily to me because some of the books and programming guides are a little dry. Something that I've looked at in the past however, is Project Euler. As a budding programmer, it's good to have a problem to work on and that's what Project Euler gives you.

I've decided that I'd like to leave my progress here on my blog, so that I can look back as I progress with the puzzles and see if I can notice any improvements as time goes along. I'm not going to say that these early efforts are especially Pythonic but hopefully that's something that will come with time.

Here's my first solution to Project Euler's problem #1:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.

 list = []  
 for n in range(1000):  
   if n % 3 == 0 or n % 5 == 0:  
 total = sum(list)  
 print total  

That gives me the answer I needed. It ain't pretty, but it works. One step at at time.


I'd originally posted my work for Euler problem #2 above. I've updated it now with the correct code.

Tuesday, 11 February 2014

SUSE: How to install only security patches

There appears to be no native method in SuSe Linux for installing only security patches. This is a problem when dealing with customers who like things to remain stable, adding features adds entropy and makes it harder to determine the root of what's caused your system to crash in the middle of the night.

On Red Hat, it's easy! Just make sure you have the yum-security plugin installed:

 # yum install yum-security

Check for security updates:

 # yum --security check-update  

And then apply them:

 # yum --security update  

This will update any packages that have security advisories marked against them. Easy right? Well, we're not here to do things the easy way!

In SuSe, you can also use Yast for updates and select the security options tab and install those only. From the command line it's not so easy but then you can't automate GUI tasks. What we need to do here is a bit of scripting to work out a one liner to do the same job.

Using zypper, SuSe's command line update manager, let's list the patches that are available (just a snippet below):

 linux-8nb5:~ # zypper lp  
 Loading repository data...  
 Reading installed packages...  
 Repository                   | Name              | Version | Category    | Status | Summary  
 openSUSE-13.1-Update         | openSUSE-2013-984 | 1       | recommended | needed | gmime: Update to 2.6.19  
 openSUSE-13.1-Update         | openSUSE-2013-985 | 1       | recommended | needed | yast2: fixed reading bridge configuration  
 openSUSE-13.1-Update         | openSUSE-2013-991 | 1       | recommended | needed | libmtp: update current device list  
 openSUSE-13.1-Update-Non-Oss | openSUSE-2013-992 | 1       | security    | needed | update for flash-player  
 openSUSE-13.1-Update         | openSUSE-2014-104 | 1       | recommended | needed | folks: fix empathy-crash when using IRC channels  
 openSUSE-13.1-Update         | openSUSE-2014-105 | 1       | recommended | needed | clutter: Update to 1.16.4  
 openSUSE-13.1-Update         | openSUSE-2014-107 | 1       | recommended | needed | sysstat: rename nfsiostat to avoid a name collision with nfs-utils  
 openSUSE-13.1-Update-Non-Oss | openSUSE-2014-109 | 1       | security    | needed | flash-player: security update to  

Ok, so we can see there that there are some patches listed there that are in the security category. We can use awk to grab those, note they are in the 4th column  but awk will grab these with the "$7" token because it sees the pipe symbol as a valid token. We could escape the pipe character I suppose, but it will make the command longer for no real gain, so we'll stick with $7.

Let's test this:

 linux-8nb5:~ # zypper lp | awk ' { print $7 }'  

Ok, we're getting the correct section (the Catgory column) but we're only interested in the security patches. More work is needed. Let's try to match the security only.

 linux-8nb5:~ # zypper lp | awk ' $7=="security" { print $7 }'  

Right, this is looking more like it. But we can't just install anything from this, this is useless I hear you mutteirng. Let's forge ahead with more awk.

If $7 matches the category, we can see that $3 will be the name of the patch that we want to apply should we be able to match them. Let's have a go:

 linux-8nb5:~ # zypper lp | awk ' $7=="security" { print $3 }'  

Ok, this looks better. Breaking down the command, we are listing patches with $7 that match the "security" string and then printing the name of the patch using $3. So we can see that these are the patches we want to install, we're getting there now. Can we get zypper to install them from a single command? Of course we can, this is Linux!

We're going to need to install these patches by name if we're using the output from awk. Helpfully we can do that with "-n" or "--name":

 -n, --name         Select packages by plain name, not by capability.  

Since we're using zypper and listing patches with the "lp" option let's continue to work with patches, using zypper's help (not man page) we can see that we will need to use -t option:

 -t, --type <type>      Type of package (package, patch, pattern, product, srcpackage).  

To use this command normally, one might use something along the lines of:

 linux-8nb5:~ # zypper install -n -t patch openSUSE-2014-78  
 Loading repository data...  
 Reading installed packages...  
 Resolving package dependencies...  
 The following NEW patch is going to be installed:  
 The following 2 packages are going to be upgraded:  
  flash-player flash-player-gnome   
 2 packages to upgrade.  
 Overall download size: 5.1 MiB. No additional space will be used or freed   
 after the operation.  
 Continue? [y/n/? shows all options] (y):   

But because we want to use awk to extract all of the names of the patches from the security category for us, we'll need to roll it all up together:

 linux-8nb5:~ # zypper lp | awk '$7=="security" { print "zypper install -n in -t patch "$3}'  
 zypper install -n in -t patch openSUSE-2013-992  
 zypper install -n in -t patch openSUSE-2014-109  
 zypper install -n in -t patch openSUSE-2014-114  
 zypper install -n in -t patch openSUSE-2014-78  

Well, I guess we're getting closer. We need to have these commands executed by the shell, not printed out as text. So let's pipe the output back to the shell and execute it.

And here it is, our final working command:

 linux-8nb5:~ # zypper lp | awk '$7=="security" { print "zypper install -n -t patch "$3}' | sh +x 

Loading repository data...
Reading installed packages... 
Resolving package dependencies... 

The following NEW patch is going to be installed: 

The following 2 packages are going to be upgraded: 
flash-player flash-player-gnome 

2 packages to upgrade. Overall download size: 5.1 MiB. No additional space will be used or freed after the operation.  

If you've got any upgrades, such as flash-player in the above example, you'll need to "zypper up" and sort them out separately in order to agree with the licenses associated with them. After that, run the command again and it should run through successfully. Perhaps it would be useful to put this in as a cron job and have this command run every night. I hope you find this command useful.

Tuesday, 7 January 2014

Secure Your Cisco: Custom Privilege Levels

Custom Privelige levels

When you log into your Cisco, you are logging in at privilege level 1. You can  this by typing "show privilege":

 Router>show privilege   
 Current privilege level is 1  

When you log in to the privileged exec shell, you by default log in on privilege level 15:

 Router#show privilege   
 Current privilege level is 15  

At privilege level 15 we have all of the most important commands, those that we don't necessarily want users logged in on a lower privilege level to have access to. We can also move commands from priliege level 1 up, perhaps we want to be able to have a networks assistant to have access to commands that we don't want other techs to be able to access. This is where the levels between 1 and 15 come in.

We might want to prevent a user at level 1 from using the ping command, for whatever reason. We'll test first of all that we currently have access to the command:

 Router>show privilege   
 Current privilege level is 1  
 Type escape sequence to abort.  
 Sending 5, 100-byte ICMP Echos to, timeout is 2 seconds:  
 Success rate is 100 percent (5/5), round-trip min/avg/max = 1/4/14 ms  

If we want to take the ability away from users at pivilege level 1, we first need to create the new prvilege level:

 Router#config t  
 Enter configuration commands, one per line. End with CNTL/Z.  
 Router(config)#enable secret level 5 0 topsecret  

If you check now in your running config you will see a new entry for pivilege level 5. Next, we need to move the command away from privilege level 1 and up to our newly created level 5:

 Router#conf t  
 Enter configuration commands, one per line. End with CNTL/Z.  
 Router(config)#privilege exec level 5 ping  

Lets test this by seeing if we can still use ping in level 1:

 Translating "ping"...domain server (  
 Translating "ping"...domain server (  
 % Unknown command or computer name, or unable to find computer address  

The ping command no longer even shows up in the interactive help:

 % Unrecognized command  

As a sidenote, I found that this didn't work in Packet Tracer, but it does work in GNS3. There are a few commands that I found don't work in Packet Tracer so I'm now trying to get my labs set up in GNS3 as I continue my studies.

Monday, 6 January 2014

Secure Your Cisco: Local Users

I've covered how to add an enable secret to your router/switch but if you want to further improve your security posture you should consider adding local user accounts so that you are able to track who is logging in.

Adding a user is easy stuff, but first we need to make sure we've got passwords set in our console, auxillary and VTY lines:

 Router#conf t   
 Router(config-line)#line console 0  
 Router(config-line)#password cisco123  
 Router(config-line)#line vty 0 4  
 Router(config-line)#password cisco123  
 Router(config-line)#line aux 0  
 Router(config-line)#password cisco123  

Try to choose different passwords for each of the lines, not like on my example here! These will be saved in plain text in the running config, so let's make them a little less vulnerable:

 Router(config)#service password-encryption   

This won't make them difficult to crack should somebody be so inclined, but it will prevent those looking over your shoulder at your running-config from seeing what your passwords are.

Let's now set up the local users so that logins will check the local database when somebody tries to connnect. Set up a couple of sample users, staying in configuration mode to do so:

 Router(config)#username root privilege 15 secret 0 cisco123  
 Router(config)#username user privilege 5 secret 0 userpass  

We can take a look in our running config and see that using "secret" has scrambled the passwords that were entered as plain text:

 Router(config)#do show run | inc username  
 username user privilege 5 secret 5 $1$HdVg$.GMFfsfjIrtdNIQ75IZfZ/  
 username root privilege 15 secret 5 $1$/OAi$uSE6rzk.3gN026rQTulKv.  

Next step is to enforce the use of the local database for all logins:

 Router(config)#line console 0   
 Router(config-line)#login local   
 Router(config-line)#line aux 0  
 Router(config-line)#login local  
 Router(config-line)#line vty 0 4   
 Router(config-line)#login local  

We're all set now, anybody who tries to connect either via ssh or telnet, via the console port or the auxillary port will now be prompted for a username as well as a password. Furthermore we've got a privilege level 15 user and a privilege level 5 user so that we can give the level 5 password and username to a user who we want to issue a set of restricted commands to.

Friday, 3 January 2014

Secure Your Cisco: Enable Secret

Enable Secret

This is the most basic step for securing the management plane on your Cisco router and possibly the most simple. Out of the box, you can type "enable" at an exec prompt and you will be elevated to the all powerful priveliged exec shell. Unfortunately, so will anybody else who logs in. What you need to do is put a password on there. It's not a great idea to use the "enable password" command, because it doesn't encrypt it for you. Using "enable secret" uses MD5, which isn't the most secure but it's better than plain test in your running config.

To put the enable secret, you'll need to type "enable" to enter privileged exec mode, and type "config t" (short for configure terminal). Then type "enable secret 15 0 yourpassword" as below:

 Router#config t  
 Enter configuration commands, one per line. End with CNTL/Z.  
 Router(config)#enable secret level 15 0 Cisco2014  

The 15 means you are entering an enable secret for privilege level 15 while the 0 means you are typing in plain text. Should you already have an MD5 hashed password that you want to enter, in case somebody is looking over your shoulder perhaps, you would type "enable secret 15 5 yourpassword".

We can see in the running config that it is encrypted:

 enable secret level 15 5 $1$mERr$p2OmsMs3HNDyiBPyIT0m20  

We could type the above as mentioned should you not want to type the unencrypted password on your screen, we are doing this with security in mind after all!