Tuesday, January 14, 2014

Protecting SSH Accesss

Problem

In home instead of a typical router I have a PandaBoard with Debian installed. This very flexible solution allows for running fully customized firewall (iptables), web-proxy (squid) with parental-control and blacklists (dansguardian). This box is a front device of my internal network and allows SSH access. I was rather uncomfortable observing logwatch reports which say about continuous break-in attempts into root and other accounts. While it is quite impossible to guess my password, I rarely change it, so it could be somehow hijacked. So I made a few additional obstacles to reach the SSH while keeping it relatively simple to use.

First option is to limit the access to SSH service itself by terms of PAM. Here are requirements. First I edited /etc/pam.d/sshd file and uncommented third line in the excerpt below:

[...]
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
account  required     pam_access.so
Then I adjusted /etc/security/access.conf:
[...]
+ : root : 10.1.     89.174.214.
+ : root : kedra.org .kedra.org
# User "root" should be denied to get access from all other sources.
- : root : ALL
# jurek accesses from all sources
+ : jurek : ALL
Above looks good enough to limit unauthorized access for root. The local network and known remote locations are granted SSH access to root. Connection as root from other location is impossible.

OneTimePasswords

Google Authenticator is one time passwords generator, which is use to protect my Gmail and AWS accounts. It gives my a sense of security - without my phone which generates one time password, it is very hard to gain the access to the account. Naturally one time password is an additional level of access so it means I have to provide the classic password + the Google Authenticator one.

First step is to install libpam-google-authenticator (apt-get install), then create an access file which allows to skip pam_google_authenticator when connecting from local network (/etc/security/access-local.conf) with the following content:

# only allow from local IP range
+ : ALL : 10.1.0.0/24
+ : ALL : LOCAL
- : ALL : ALL

then adjust /etc/pam.d/sshd:

[...]
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
account  required     pam_access.so

# skip one-time password if logging in from the local network
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required pam_google_authenticator.so

# Standard Un*x authorization.
@include common-account

# Standard Un*x session setup and teardown.
@include common-session
[...]

The last change is to allow for one time passwords authentication in /etc/ssh/sshd_config:


# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes

Restart the SSH daemon /etc/init.d/ssh restart and generate the key by google-authenticator. The command is verbose and intuitive. On my Android phone I use "Google Authenticator" - it supports QR code scanning or I have to retype just generated key from google-authenticator command. The google-authenticator command creates ~/.google_authenticator only-owner-readable file which stores the key. That's should be it - it works. Well not really.


Configuration Extension

Google Authenticator setup in a way as above is a global configuration for all users. It means the given user has to create its own .google_authenticator file (can't be system shared file until you want to have it world readable) or the user won't be able to login. This is not really what I want here. I'd like to have a situation when user (actually me only) has an option to decide by himself if he wants or not to be protected by it. I'd like also an option to have only a single key (.google_authentication file) but I don't like the idea of having it readable for all users.

There are multiple options to archive above goals. The most straightforward is suggested by the google-authenticator man page. It requires to add a following line:
auth [default=1 success=ignore] pam_succeed_if.so quiet user ingroup root
auth required pam_google_authenticator.so

It requires for user to be in group "root" (last argument) before GoogleAuth is required. I'm not PAM guru so in effect it removed the effect of previous line, that with accessfile=/etc/security/access-local.conf. There is probably an option to overcome it somehow but there was no curiosity to follow this path.

apt-get remove libpam-google-authenticator
apt-get install libpam0g-dev libqrencode3
wget https://google-authenticator.googlecode.com/files/libpam-google-authenticator-1.0-source.tar.bz2
You can check if there is a new version here.
tar jvxf libpam-google-authenticator-1.0-source.tar.bz2
cd libpam-google-authenticator-1.0/
make
make install

Now I can log in without one time password for users where .google_authenticator file is not present the home directory. I also can preview one-time-passwords when typing. The sshd_config needs to be adjusted:
auth required pam_google_authenticator.so nullok echo_verification_code
This is something good to know and good to have. However, since I'm the only user of the system, I'd rather have a common .google_authenticator file, root owned, root only readable. For the internal network I'd like to login without one time passwords, but for the external I want to have it as mandatory. So the final solution is:
# Uncomment and edit /etc/security/access.conf if you need to set complex
# access limits that are hard to express in sshd_config.
#account required pam_access.so

# skip one-time password if logging in from the local network
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required pam_google_authenticator.so user=root secret=/root/.google_authenticator echo_verification_code

Finally I decided to open the SSH world wide, no exceptions (pam_access.so commented out in /etc/pam.d/sshd).

References

  1. Remi Bergsma's blog
  2.  How to Setup Two-Factor Authentication (Google Authenticator) for SSH Logins

Friday, January 10, 2014

JDBC Connection Failover

FAN

With JDBC thin 11.2 and simplefan.jar (oracle.simplefan package) I was able to perform tests only with ONS client running locally. The program and the test environment has been described at Martin Bach's blog. Ons can be installed with the full version of Oracle 11.2 Windows client (standard install). It requires changes in the ons.config: usesharedinstall=false and nodes=node1:6200,node2:6200,node3:6200. By default usessharedinstall is set to true, which makes the lock file appending the hostname to it. Consequently it renders the example being not able to found it by the standard name. I didn't investigate how to fix it at the code level.

Speaking on JDBC thin it is suprising how little of information I could find in the Internet. The only valuable information from Oracle comes with Oracle Database RAC FAN Events Java 11g Release 2 (11.2) E13993-01 and there is not working example in Oracle Database JDBC Developer's Guide 11g Release 2 (11.2). Not working not only because it is not quite complete, but because it refers to not existing methods: Actually didn't find methods like getServiceMemberStatus, getServiceCompositeEvent and some others within simplefan.jar and the above API documentation. Searching with Google for these methods returns the above (not working) example only. In a similar way, existing function don't handle coming events properly. The "SHUTDOWN" event is an example here.

The code below is original Oracle's example mixed with Martin Bach's code:

package org.kedra.fan1;
import java.util.Properties;
import oracle.simplefan.FanEventListener;
import oracle.simplefan.FanManager;
import oracle.simplefan.FanSubscription;
import oracle.simplefan.LoadAdvisoryEvent;
import oracle.simplefan.NodeDownEvent;
import oracle.simplefan.ServiceDownEvent;
import oracle.simplefan.ServiceDownEvent.ServiceMemberEvent;

public class Test1 {
 Test1() {
  FanManager fm = FanManager.getInstance();

  //Properties fmProps = new Properties();
  //fmProps.setProperty("onsNodes",
  //  "plabb241:6200,plabb230:6200,plabb229:6200");
  // fmProps.setProperty(key, value)
  // fm.configure(fmProps);

  System.setProperty("oracle.ons.oraclehome",
    "c:\\Oracle2\\product\\11.2.0\\client_1");
  System.out.println(System.getProperty("oracle.ons.oraclehome"));

  Properties props = new Properties();
  props.put("serviceName", "AMQS1");

  FanSubscription sub = fm.subscribe(props);
  System.out.println("I'm subscribed!");

  sub.addListener(new FanEventListener() {

   public void handleEvent(ServiceDownEvent event) {
    System.out.println("ServiceDownEvent!");

    try {
     System.out.println(event.getTimestamp());
     System.out.println("getServiceName: "
       + event.getServiceName());
     System.out.println("getDatabaseUniqueName: "
       + event.getDatabaseUniqueName());
     System.out.println("getReason: " + event.getReason());
     System.out.println("getKind: " + event.getKind());

     ServiceMemberEvent me = event.getServiceMemberEvent();
     if (me != null) {
      System.out.println("getServiceMemberEvent");
      System.out.println("\t getInstanceName: "
        + me.getInstanceName());
      System.out.println("\t getNodeName: "
        + me.getNodeName());
      // System.out.println(me.getServiceMemberStatus());
     }
     // ServiceCompositeEvent ce = se.getServiceCompositeEvent();
     // if (ce != null) {
     // System.out.println(ce.getServiceCompositeStatus());
     // }
    } catch (Throwable t) {
     // handle all exceptions and errors
     System.err.println("handleEvent - ServiceDownEvent");
     t.printStackTrace(System.err);
    }
   }

   public void handleEvent(NodeDownEvent event) {
    System.out.println("Node Down Event!");

    try {
     System.out.println(event.getTimestamp());
     System.out.println("getNodeName: " + event.getNodeName());
     // ServiceCompositeEvent ce = se.getServiceCompositeEvent();
     // if (ce != null) {
     // System.out.println(ce.getServiceCompositeStatus());
     // }
    } catch (Throwable t) {
     System.err.println("handleEvent - ServiceDownEvent");
     t.printStackTrace(System.err);
    }
   }

   public void handleEvent(LoadAdvisoryEvent arg0) {
    System.out.println("Load Advisory event");

    System.out.println("originating database: "
      + arg0.getDatabaseUniqueName());
    System.out.println("originating instance: "
      + arg0.getInstanceName());
    System.out.println("Service Quality     : "
      + arg0.getServiceQuality());
    System.out.println("Percent             : " + arg0.getPercent());
    System.out.println("Service Name        : "
      + arg0.getServiceName());
    System.out.println("Service Quality     : "
      + arg0.getServiceQuality());
    System.out.println("Observed at         : "
      + arg0.getTimestamp() + "\n\n");
   }
  });
 }

 public static void main(String[] args) {
  Test1 tc = new Test1();

  int i = 0;
  while (i < 100000) {
   try {
    Thread.sleep(100);
    i++;
   } catch (Exception e) {
    System.out.println(e);
   }
  }

  System.out.println("execution ended");
 }
}

I would like to see a support for a remote ONS. There is a method configure() in the FanManager class which takes a list of ONS servers but requires mandatory oracle wallet and password as well. This is not something I expect - for I don't want the communication with ONS server over SSL.

Oracle admits it is "the initial release" so the functionality of this library is fairly limited.

Fast Connection Failover

Fast Connection Failover (FCF) and for JDBC thin it us usable with Universal Connection Pool (UCP). There is an option to use JDBC Implicit Connection Cache (ICC) but Oracle considers the feature to be replaced by UCP.

  1. Automatic Workload Management with Oracle Real Application Clusters 11g Release 2 - FAN, FCF
  2. Maximum Availability Architecture, Oracle Best Practices For High Availability: Client Failover Best Practices for Highly Available Oracle  Databases: Oracle Database 11g Release 2 - Standby database oriented.
  3. Fast Connection Failover Example JDBC Thin - this is about 10g High Availability and JDBC. Requires ons.jar or ONS working on the client side. Great resource for hands-on experience.
  4. How to Verify Universal Connection Pool (UCP) / Fast Connection Failover (FCF) Setup (Metalink Doc ID 1064652.1)
  5. JDBC and Universal Connection Pool
  6. UCP Demos Page (Oracle)
  7. TAF vs FAN, FCF vs ONS
  8.  Metalink Resources: