Simple Python scp wrapper

Inspired by the following article I decided to write a simply python scp wrapper which is based on the ideas taken from the perl script also mentioned on the same page.

The whole point of this wrapper is dead simple: to allow certain users scp into their specific directories with out granting full ssh access.

The configuration file is straightforward:
/opt/etc/scponly.conf

scponly=/tmp:/home/scptest

Below is the wrapper in all its amature (must admit I’m not a developer at all) glory ;-)

/opt/bin/scp-wrapper.py:

#!/usr/bin/python
 
import sys
import os
import pwd
from subprocess import call, Popen, PIPE
import re
 
def fail(msg):
    """(str) -> str Prints error message to STDOUT
    >>> fail('Error')
    Error
    >>> fail('Wrong argument')
    Wrong argument
    """
    
    print msg
    sys.exit(1)
 
def access_verify(user, dirto):
    """(str) -> Boolean Returns TRUE iff user is allowed to scp
    """
  
    if user in users:
        for d in users[user]:
            print user, dirto[-1:],  d.rstrip()[-1:], dirto[:-1] == d.rstrip(), dirto == d[:-1].rstrip()
            if dirto == d.rstrip():
                return True
            elif (dirto[-1:] == "/" or d.rstrip()[-1:] == "/") and (dirto[:-1] == d.rstrip() or dirto == d.rstrip()[:-1]):
                return True
            else:
                return False
    return False
 
if __name__ == '__main__':
 
    users = {}
    conf = "/usr/local/etc/scponly.conf"
 
    # Reading configuration file
 
    fp = open(conf, "r")
    for line in fp.readlines():
        record = line.split("=")
        users[record[0]] = record[1].split(":")
    fp.close()
  
    command = sys.argv[2]
    scp_args = command.split()
 
    if scp_args[0] != "scp":
        msg = "Only scp is allowed"
        fail(msg)
 
    if scp_args[1] != "-t" and not "-f" and not "-v":
        msg = "Restricted; only server mode is allowed"
        fail(msg)
 
    destdir = scp_args[-1]
   if not os.path.isfile(destdir) or os.path.isfile(destdir):
        destdirv = os.path.dirname(destdir)
    else:
        destdirv = destdir
    uname = pwd.getpwuid(os.getuid())[0]
    if not access_verify(uname, destdirv):
        msg = "User " + uname + " is not authorized to scp to this host."
        fail(msg)
    else:
        scp_args.pop(0)
        if len(scp_args) == 2:
            call(["/usr/bin/scp", scp_args[0], destdir])
        elif len(scp_args) == 3:
            call(["/usr/bin/scp", scp_args[0], scp_args[1], destdir])

Just create a new user and set its shell (-s option in useradd) to /opt/bin/scp-wrapper.py. Hope it helps.
Enjoy and have fun!

How to create many ifcfg-ethN files in one shot

I had a quick task to create 254 ifcfg-eth1:N configuration files for a Linux installation and certainly spending the whole day doing that manually wasn’t part of my plan. Here is a quick and dirty one-liner I used for that:

# for f in `seq 2 254`; do cat ifcfg-eth1 | \ 
sed -e 's/DEVICE=.*$/DEVICE=eth1\:'$f'/' \
-e 's/IPADDR=.*$/IPADDR=173.xxx.xx.'$f'/' \
-e 's/ONBOOT=.*$/ONPARENT=yes/' > ifcfg-eth1\:$f; done

No way I want to make the same mistakes again

To avoid stepping on the same rake again and to fix the issue described in this post, I came out with a simple expect script to save current configuration of Qlogic Sanbox switches.

#!/usr/local/bin/expect -f

set switches "switch1 switch2"
set user {user}
set pass {pass}
set ftp_user {ftp_user}
set ftp_pass {ftp_pass}
set timeout 10
log_user 0
set prompt "(%|#|\\$) $"
catch {set prompt $env(EXPECT_PROMPT)}

set sec [clock seconds]
set date [clock format $sec -format %d%m%Y]

set back [clock add $sec -7 days]
set bdate [clock format $back -format %d%m%Y]

for {set x 0} {$x<[llength $switches]} {incr x} {

set current_switch [lindex $switches $x]

spawn telnet $current_switch

expect {
        timeout {puts "timeout while connecting to $host"; exit 1}
        "login:"
}
send "$user\r"

expect {
        timeout {puts "timed out waiting for the password prompt"; exit 1}
        "Password:"
}
send "$pass\r"

expect {
        timeout {puts "timed out after login"; exit 1}
        "#>"
}
send "admin start\r"

expect {
        timeout {puts "timed out waiting for admin mode"; exit 1}
        "(admin) #>"
}
send "config backup\r"

expect {
        "(admin) #>"
}
send "admin end\r"

expect {
        "#>"
}
send "quit\r"

spawn ftp sanbox4
expect {
        timeout {puts "timed out waiting for ftp login request"; exit 1}
        "Name" 
}
send "$ftp_user\r"

expect {
        timeout {puts "timed out waiting fro ftp password request"; exit 1}
        "Password:"
}
send "$ftp_pass\r"

expect {
        timeout {puts "timed out waiting for ftp prompt"; exit 1}
        "ftp>"
}
send "get configdata /pth_to_backup_directory/configdata_$current_switch-$date\r"
expect "ftp>"
send "quit\r"

if {[file exists /path_to_backup_directory/configdata_$current_switch-$bdate]} {
        exec /usr/bin/rm /path_to_backup_directory/configdata_$current_switch-$bdate
}
}

Sed-fu

We all know how incredible and flexible sed really is and recently I had another chance to experience its coolness in the field when one of our Qlogic Sanbox switch lost zone configuration completely. So obvious solution in such cases is just to restore from the backups but since it wasn’t possible, it’s a shame but there weren’t any on my hands, I simply restored zones information from another switch in the same fabric. That worked nicely with one drawback – lack of alias information. Luckily we had an old zone backup with aliases that could be used to restore the missing part of our configuration. Nevertheless, the information in that file was a bit outdated but in our situation it was better than nothing. From the inside the file I needed to parse had the following structure:

<ZoneAlias name="zone_name1">
<ZoneAliasMember>
Memeber1_WWN
</ZoneAliasMember>
<ZoneAliasMember>
Memeber2_WWN
</ZoneAliasMember>
</ZoneAlias>
<ZoneAlias name="zone_name2">
<ZoneAliasMember>
Memeber1_WWN
</ZoneAliasMember>
</ZoneAlias>

So to parse it to the format required by the switch I wrote a simple sed script that saved me from doing a monkey’s job and restoring everything manually:

for zn in `cat zoneset_with_alias.xml | \
sed -n ‘/<ZoneAlias/,/<\/ZoneAlias>/ s/.*name=\”\(.*\)\”.*/\1/p’`; \
do for m in `cat zoneset_with_alias.xml | \
sed -n ‘/<ZoneAlias name=”‘$zn'”/,/<\/ZoneAlias>/ p’ | \
sed -n ‘/<ZoneAliasMember>/,/<\/ZoneAliasMember>/ s/.*[a-z][0-9].*/&/p’`; \
do echo -n “$zn “; echo $m | \
sed ‘s/[0-9a-zA-Z][0-9a-zA-Z]/:&/g’ |sed ‘s/^://’; done; done

P.S. Frankly speaking, I spent more time writing this note than the script itself.