Thursday, December 26, 2013

ASUS USB-N10 Nano on OpenSuse 13.1 Linux

Today I bought myself a wifi adapter for my Opensuse desktop PC. I was tired of running a network cable across the carpet to my modem.

The model I bought was an ASUS USB-N10 Nano from PB Technologies in Henderson. It was less than $20 so I thought a good deal. When I bought it, I completely ignored all the printed garbage about support for Windows and Mac and figured, that it will be supported under linux.

It is supported under Linux but it was not too simple to set up on my Opensuse.

In the end and after much google-ing, I found this repository:
https://github.com/pvaret/rtl8192cu-fixes

The driver that ships with the kernel is not so hot. The network keeps dropping even though it appears to remain connected. With this version, things are much more stable.

Also it compiles nicely under the latest kernel. The proprietry driver from Realtek did not compile under the kernel shipped with Opensuse 13.1.

So here is what I ended up doing:

~ git clone https://github.com/pvaret/rtl8192cu-fixes.git
~ su - (become root)
# zypper in dkms kernel-devel kernel-headers gcc make
# cd /path/to/git/repo... (you get the picture)
# cd ../ (go to directory above tree) on my system I put this source tree under /usr/local/src/
# dkms add rtl8192cu-fixes
# dkms install 8192cu/1.8
# depmod -a
# cp blacklist-native-rtl8192.conf /etc/modprobe.d
# echo "options 8192cu rtw_power_mgnt=0 rtw_enusbss=0" > /etc/modprobe.d/50-8192cu.conf
# reboot

And that's it. Now if the kernel is ever updated during normal patching, dkms will recompile our driver.

EDIT:
I have made an RPM package for this driver. It's hosted on the openSUSE build service. Currently I have successfully managed to create rpm's for Centos, Redhat and Fedora (x86_64) only. The openSUSE one failed because the build environment does not have DKMS installed and so the install test fails at the very end.
Anyway - find the packages here: https://build.opensuse.org/package/show/home:linuxplayground/8192cu-1.8

so the openSUSE version is here: 8192cu-1.8-1.0.x86_64.rpm

These RPM packages will:
  • save the source in /usr/src/8192cu-1.8
  • Add the source tree to DKMS
  • Build the kernel module using dkms build ...
  • Install the kenrel module using dkms install ...

To uninstall the module and all the dkms source, builds and modules then just execute: #rpm -e 8192cu
:-)

Friday, December 20, 2013

Restrict Commands executed via pre-shared SSH keys

Introduction

Using pre-shared SSH keys is a great way to make logging into remote hosts quick and easy.  No pesky passwords to remember.  The downside is that if your organization relies on passwords for access control, the pre-shared key will negate any password control you have.  For example: If your organization rolls a password for a system account, and the new password is not shared with all the original people, then some people who should not have access any more will continue to have access via their pre-shared key.
Also, anyone with access to a user's account on a client host, could access the system account via the pre-shared key on the remote host.  This is not ideal.

Sometimes, it is neccessary to allow certain commands only to be executed over SSH from specified client hosts without a password.  Especially when thinking about automated tasks.

In my examples, I will demonstrate how we can create a simple remote procedure call type scenario using pre-shared keys and some built in linux security tools that lock things down.

These examples will use the "oracle" user which has been set up on both the client and remote hosts identically.
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

The Steps

Create the public private key on the private host

Use ssh-keygen to create a public/private key pair without a password.
[oracle@centos-dev-1 ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/oracle/.ssh/id_rsa): [ENTER]
Created directory '/home/oracle/.ssh'.
Enter passphrase (empty for no passphrase): [ENTER]
Enter same passphrase again: [ENTER]
Your identification has been saved in /home/oracle/.ssh/id_rsa.
Your public key has been saved in /home/oracle/.ssh/id_rsa.pub.
The key fingerprint is:
64:cf:72:de:f7:85:af:fe:36:87:ce:8c:6e:31:8d:ed oracle@centos-dev-1.vbox.local
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|                 |
|        o        |
|       o o       |
|        S +  +   |
|         + .+ o. |
|          . .+o..|
|            .=E++|
|           ooo*+*|
+-----------------+
[oracle@centos-dev-1 ~]$ ls -l .ssh/
total 8
-rw-------. 1 oracle oinstall 1675 Dec 20 10:33 id_rsa
-rw-r--r--. 1 oracle oinstall  412 Dec 20 10:33 id_rsa.pub

Share the public key to the remote host

You can use the ssh-copy-id tool or you could manually do it. I will use the ssh-copy-id tool to share the key.
[oracle@centos-dev-1 ~]$ ssh-copy-id -i /home/oracle/.ssh/id_rsa.pub oracle@centos-dev-2
The authenticity of host 'centos-dev-2 (192.168.56.102)' can't be established.
RSA key fingerprint is df:87:48:48:0a:15:fa:ca:e4:a7:41:8e:9e:af:19:b5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'centos-dev-2,192.168.56.102' (RSA) to the list of known hosts.
oracle@centos-dev-2's password: 
Now try logging into the machine, with "ssh 'oracle@centos-dev-2'", and check in:

  .ssh/authorized_keys

to make sure we haven't added extra keys that you weren't expecting.

[oracle@centos-dev-1 ~]$ ssh oracle@centos-dev-2
Last login: Thu Dec 19 13:38:03 2013 from centos-dev-1.vbox.local
[oracle@centos-dev-2 ~]$ hostname
centos-dev-2.vbox.local
[oracle@centos-dev-2 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-2 ~]$ exit
logout
Connection to centos-dev-2 closed.
At this point, we have successfully enabled transparent ssh logins using a pre-shared public key from centos-dev-1 to centos-dev-2 as the "oracle" user.


The result was a file created on the remote host called /home/oracle/.ssh/authorized_keys which looks like this:
[oracle@centos-dev-2 ~]$ hostname
centos-dev-2.vbox.local
[oracle@centos-dev-2 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-2 ~]$ cd /home/oracle/.ssh/
[oracle@centos-dev-2 .ssh]$ ls -l
total 4
-rw-------. 1 oracle oinstall 412 Dec 20 10:35 authorized_keys
[oracle@centos-dev-2 .ssh]$ cat authorized_keys 
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqdZI3PbacEoZ4uIk1hIc9Tl2pmLZtv0Y1/ZowqNOLRRQV84ziiNXoUXa8y+x9DxMzIPxKHdyYy0k0HFl06oIjfP4l3P3GY9L0MM/oYxYNr0UaJz46hDy7p3cKVjmeY/Xn3PsepNY3kAdp4zdNb1Hfs9G/PiB70LTfseJZjqwBJ6edYAt7YztWuO9OgrmDoW7QAdsPTlQGF9iW0gUqH/1UA5hXtGePRq2My9ahXmXOu1hy3H1wFygCBZaxP7A3qohgvVIT76DQ1mxhgLDiQCVkTzxVhHgMmVi4A7C0vpr2G5WxpLblVHWpjnXn/eAdQBQLnFtj/pza1U5K3Dy/GDxMQ== oracle@centos-dev-1.vbox.local

Restrict the public key on the remote host

Here we edit the /home/oracle/.ssh/authorized_keys file that was created in the previous step to include some restrictions for the key. We prepend the following to the key:
from="centos-dev-1.vbox.local",command="/usr/local/bin/remote-commands.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty 
The key now looks like this:
from="centos-dev-1.vbox.local",command="/usr/local/bin/remote-commands.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqdZI3PbacEoZ4uIk1hIc9Tl2pmLZtv0Y1/ZowqNOLRRQV84ziiNXoUXa8y+x9DxMzIPxKHdyYy0k0HFl06oIjfP4l3P3GY9L0MM/oYxYNr0UaJz46hDy7p3cKVjmeY/Xn3PsepNY3kAdp4zdNb1Hfs9G/PiB70LTfseJZjqwBJ6edYAt7YztWuO9OgrmDoW7QAdsPTlQGF9iW0gUqH/1UA5hXtGePRq2My9ahXmXOu1hy3H1wFygCBZaxP7A3qohgvVIT76DQ1mxhgLDiQCVkTzxVhHgMmVi4A7C0vpr2G5WxpLblVHWpjnXn/eAdQBQLnFtj/pza1U5K3Dy/GDxMQ== oracle@centos-dev-1.vbox.local
The extra options are broken down as follows:

  • from="centos-dev-1" Only allow this key to be used when connections are made from centos-dev-1 host.
  • command="/usr/local/bin/remote-commands.sh" This is a script that will be executed by the owner of this key (oracle) whenever the key is used to establish an ssh connection. We will write the script soon.
  • no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty These options add extra security and prevent the key being used for anything other than running the command specified in command="".
None of this is very useful if the oracle user can simply log into the host using his password and edit this file. Use the chattr command to lock the file for editing as follows: (NOTE: Only the root user can do this)
[root@centos-dev-2 ~]# hostname
centos-dev-2.vbox.local
[root@centos-dev-2 ~]# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@centos-dev-2 ~]# lsattr /home/oracle/.ssh/authorized_keys 
-------------e- /home/oracle/.ssh/authorized_keys
[root@centos-dev-2 ~]# chattr +i /home/oracle/.ssh/authorized_keys 
[root@centos-dev-2 ~]# lsattr /home/oracle/.ssh/authorized_keys 
----i--------e- /home/oracle/.ssh/authorized_keys
[root@centos-dev-2 ~]# rm /home/oracle/.ssh/authorized_keys 
rm: remove regular file `/home/oracle/.ssh/authorized_keys'? y
rm: cannot remove `/home/oracle/.ssh/authorized_keys': Operation not permitted
[root@centos-dev-2 ~]# ls -l /home/oracle/.ssh/authorized_keys 
-rw-------. 1 oracle oinstall 551 Dec 20 10:41 /home/oracle/.ssh/authorized_keys
[root@centos-dev-2 ~]# su - oracle
[oracle@centos-dev-2 ~]$ cat .ssh/authorized_keys 
from="centos-dev-1.vbox.local",command="/usr/local/bin/remote-commands.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqdZI3PbacEoZ4uIk1hIc9Tl2pmLZtv0Y1/ZowqNOLRRQV84ziiNXoUXa8y+x9DxMzIPxKHdyYy0k0HFl06oIjfP4l3P3GY9L0MM/oYxYNr0UaJz46hDy7p3cKVjmeY/Xn3PsepNY3kAdp4zdNb1Hfs9G/PiB70LTfseJZjqwBJ6edYAt7YztWuO9OgrmDoW7QAdsPTlQGF9iW0gUqH/1UA5hXtGePRq2My9ahXmXOu1hy3H1wFygCBZaxP7A3qohgvVIT76DQ1mxhgLDiQCVkTzxVhHgMmVi4A7C0vpr2G5WxpLblVHWpjnXn/eAdQBQLnFtj/pza1U5K3Dy/GDxMQ== oracle@centos-dev-1.vbox.local
As you can see, once this attribute is set on the file, not even the root user can edit or delete it. The root user will first need to remove the "immutable" attribute with "chattr -i /home/oracle/.ssh/authorized_keys".

The oracle user can still read the file which is a requirement of the SSH daemon.

Write a command parser on the remote host

This script will parse the command sent by the ssh client and execute based on some simple tests.
[oracle@centos-dev-2 ~]$ ls -l /usr/local/bin/remote-commands.sh 
-rwxr-xr-x. 1 root root 315 Dec 19 13:30 /usr/local/bin/remote-commands.sh
[oracle@centos-dev-2 ~]$ cat /usr/local/bin/remote-commands.sh 
#!/bin/sh
#
if [ -z "${SSH_ORIGINAL_COMMAND}" ]; then
        echo "This script can only be executed via SSH remote calls"
        exit
fi

case "$SSH_ORIGINAL_COMMAND" in
  "get hosts")
    cat /etc/hosts
    ;;
  "get resolv.conf")
    cat /etc/resolv.conf
    ;;
  "get env")
        env
        ;;
  *)
   echo "Bad Command" 
    ;;
esac
exit
The script is owned by root:root but all users have read and execute permissions on the script.



That's it! We are ready to begin our tests now.

Test

The script we write allows clients to execute 3 commands:
  • get hosts
  • get resolv.conf
  • get env
Any other command will be rejected.

Using the key to establish an ssh connection without passing a command will be rejected. So logins are now disabled.
Executing the script from the local host will be rejected too. This script should only be called by remote hosts.

Test "get hosts"

[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh centos-dev-2 get hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

Test "get resolv.conf"

[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh centos-dev-2  resolv.conf
; generated by /sbin/dhclient-script
search vbox.local
nameserver 192.168.56.1

Test "get env"

My fake oracle environment is nothing like what a real one would look like. This example is given just to prove that the remote script does execute with all of the expected environment variables.
[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh centos-dev-2 get env
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash
SSH_CLIENT=192.168.56.101 46697 22
SELINUX_USE_CURRENT_RANGE=
USER=oracle
PATH=/usr/local/bin:/bin:/usr/bin
MAIL=/var/mail/oracle
PWD=/home/oracle
LANG=en_US.UTF-8
SELINUX_LEVEL_REQUESTED=
HOME=/home/oracle
SHLVL=2
SSH_ORIGINAL_COMMAND=get env
LOGNAME=oracle
CVS_RSH=ssh
SSH_CONNECTION=192.168.56.101 46697 192.168.56.102 22
LESSOPEN=|/usr/bin/lesspipe.sh %s
G_BROKEN_FILENAMES=1
_=/bin/env

Test some bad commands

[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh centos-dev-2 userdel oracle
Bad Command
[oracle@centos-dev-1 ~]$ ssh centos-dev-2 "su -c \"userdel oracle\""
Bad Command
[oracle@centos-dev-1 ~]$ ssh centos-dev-2 rm -fr /*
Bad Command

Test logging in without a password

[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh centos-dev-2
PTY allocation request failed on channel 0
This script can only be executed via SSH remote calls
                                                     Connection to centos-dev-2 closed.

Using SSH to force the requirement for a password

Because a shared key exists, ssh will automatically try to use it for authentication. We now need to explicitly declare that no public private key should be used for authentication.
[oracle@centos-dev-1 ~]$ hostname
centos-dev-1.vbox.local
[oracle@centos-dev-1 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-1 ~]$ ssh -o PubkeyAuthentication=no centos-dev-2
oracle@centos-dev-2's password: 
Last login: Fri Dec 20 10:35:22 2013 from centos-dev-1.vbox.local
[oracle@centos-dev-2 ~]$ hostname
centos-dev-2.vbox.local
[oracle@centos-dev-2 ~]$ id
uid=500(oracle) gid=501(oinstall) groups=501(oinstall) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[oracle@centos-dev-2 ~]$ 

Thursday, December 12, 2013

Python suds (SOAP Library) does not support SSLv3

The Oracle Virtual Machine (OVM) manager exposes a SOAP (wsdl) web-service. At the time of writing this can be found here: (for version 3.2.x)

https://<hostname>:7002/ovm/core/wsapi/soap?wsdl

You can access this url in your web browser and will be prompted to accept the self signed cert etc.  (There is a way to add a real certificate but that's not what this article is about.)

In order to consume this webservice in python, I thought to try out the (now quite old) "suds" library.  Opensuse still ship this library and you can install it with:
zypper in python-suds
EDIT: I have added the forked library to github here: https://github.com/linuxplayground/suds-sslv3-fork
The problem I found was that when trying to connect to the service, I was receiving an exception error around SSL.

Here is how it looked...
The example code:
#!/usr/bin/python
from suds.client import Client
client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
print client
Output:
david.latham@davidpc:~/bin/ovm-webservices> python broken.py 
Traceback (most recent call last):
  File "broken.py", line 3, in 
    client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
  File "/usr/lib/python2.7/site-packages/suds/client.py", line 112, in __init__
    self.wsdl = reader.open(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 152, in open
    d = self.fn(url, self.options)
  File "/usr/lib/python2.7/site-packages/suds/wsdl.py", line 136, in __init__
    d = reader.open(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 79, in open
    d = self.download(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 95, in download
    fp = self.options.transport.open(Request(url))
  File "/usr/lib/python2.7/site-packages/suds/transport/https.py", line 60, in open
    return  HttpTransport.open(self, request)
  File "/usr/lib/python2.7/site-packages/suds/transport/http.py", line 62, in open
    return self.u2open(u2request)
  File "/usr/lib/python2.7/site-packages/suds/transport/http.py", line 118, in u2open
    return url.open(u2request, timeout=tm)
  File "/usr/lib64/python2.7/urllib2.py", line 404, in open
    response = self._open(req, data)
  File "/usr/lib64/python2.7/urllib2.py", line 422, in _open
    '_open', req)
  File "/usr/lib64/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib64/python2.7/urllib2.py", line 1222, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib64/python2.7/urllib2.py", line 1184, in do_open
    raise URLError(err)
urllib2.URLError: 
That last line, "SSL23_GET_SERVER_HELLO:unknown protocol" had me stuck for ages. Eventually I found a blog post that suggested this can be fixed by adding a new HTTPHandler to the URLLIB2 Opener.
Rather than mess with the urllib2 library, I decided to download the source for the suds library, and fix the library.
Download suds from here:
svn co http://svn.fedorahosted.org/svn/suds/trunk
In the source tree add the following httpsconnectionv3.py to /path/to/downloaded/source/suds/transport/httpsconnectionv3.py
#!/usr/bin/python
import httplib, ssl, urllib2, socket

class HTTPSConnectionV3(httplib.HTTPSConnection):
    def __init__(self, *args, **kwargs):
        httplib.HTTPSConnection.__init__(self, *args, **kwargs)
        
    def connect(self):
        sock = socket.create_connection((self.host, self.port), self.timeout)
        if self._tunnel_host:
            self.sock = sock
            self._tunnel()
        try:
            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3)
        except ssl.SSLError, e:
            print("Trying SSLv3.")
            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23)
            
class HTTPSHandlerV3(urllib2.HTTPSHandler):
    def https_open(self, req):
        return self.do_open(HTTPSConnectionV3, req)
Then update /path/to/downloaded/source/suds/transport/http.py with the following additions:
  • include the new library in the imports
    • from httpsconnectionv3 import *
  • Add the following line to the function "u2handlers" after the line "handlers.append(u2.ProxyHandler(self.proxy))"
    • handlers.append(HTTPSHandlerV3())
Now you are ready to test using this library instead of the default suds library that ships with your OS.
The example code but this time you place your example code in the downloaded source tree so that python uses the local library. OR You can make the above modifications to your installed library which should be somewhere in /usr/lib/python-2.7/site-packages/suds.
Up to you.
Example Code:
david.latham@davidpc:~/bin/ovm-webservices/ovm_wsapi> cat getVMList.py
#!/usr/bin/python
from suds.client import Client
client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
username='admin'
password='xxxxxxxx'
loginResponse = client.service.login(username,password)
print "Logging in .... <%s>" % loginResponse
if loginResponse == True:
        vmlist = client.service.vmGetAll()
        for vm in vmlist:
                if vm.vmRunState == 'RUNNING' or vm.vmRunState == 'STOPPED':
                        print "%s | %s" % (vm.name, vm.vmRunState)
        logoutResponse = client.service.logout()
        print "Logging out ... <%s>" % logoutResponse
else:
        print "login failure"
Output:
david.latham@davidpc:~/bin/ovm-webservices/ovm_wsapi> ./getVMList.py 
Logging in .... 
puppetmaster | RUNNING
ksplice-test | RUNNING
zimbra-test-el6 | STOPPED
ksplice-client | RUNNING
DB2_Server_RHEL58 | STOPPED
ora133 | STOPPED
secret_host_1 | STOPPED
zimbra-test | RUNNING
sqlsandpit | RUNNING
secret_host_2 | STOPPED
repo_server | RUNNING
Logging out ... 
So that fixes suds for sslv3