Rasperry Pi + SSHoogr

The idea

Well some time ago I wanted to build a mini raspberry pi mini tower to do some proofs of concept about distributed computing. The time passed and I didn’t do anything…​until today. But the thing is attending last GreachConf I saw sshoogr (pronounced "sugar"). sshoogr is a Groovy-based DSL library for working with remote servers through SSH.

It seemed something easy to use and most importantly…​fun! It may well be not as powerful as Ansible but I don’t really need that (at least at the moment) and I thought it would be nice to do some devops while using Groovy.

So steps are:

  • Installing OS

  • Initializing systems

  • Install ssh keys

  • Install Docker

OS

First of all I had to install the OS. Although many people is using Raspbian, or HypriotOS, I chose ArchlinuxARM. Among other things I liked the idea of using the same linux flavor I’m using everyday, and Arch normally is lighter than Debian distros.

To install the OS for a rpi-2 you can find a step-by-step here. Of course there are distros for many ARM micros.

Of course once I have install it in the first rpi-2 then clone it to the other 3 machines.

Commons

Base script

Because I’m dealing with Groovy scripts I’m using Grapes to get sshoogr dependencies. However I don’t want to repeat the dependencies all over again in every script, so I created a base script, and I will be using it through the rest of scripts.

Base script
package common

@Grapes([
    @Grab('com.aestasit.infrastructure.sshoogr:sshoogr:0.9.25'),
    @Grab('commons-codec:commons-codec:1.10')
])
import com.aestasit.infrastructure.ssh.DefaultSsh

class Sshoogr extends DefaultSsh { }

How to use this script in the other scripts ? Well because we are only interested in methods within DefaultSsh then we should add a static import in your script:

Static import
import static common.Sudo.*
import static common.Util.*
import static common.Sshoogr.*

Common variables

At this point we can only access the rpi2 using the default usernames and passwords. In order to avoid copy-pasting the user names and password every time, I put all of them in the same place.

Common variables
Unresolved directive in <stdin> - include::../../../../../../../sources/2016/04/sshoogr/common/secrets.groovy[indent=0]
1 Ip of the host we’re setting
2 Sudo prefix
3 Default usernames and passwords
4 New credentials used to replace default ones
5 Path to ssh key files
6 A way of gaining root access through su -

How these variables are included in the rest of scripts ? Throug the evaluate method:

Evaluating other scripts
evaluate('common/secrets.groovy' as File)

When invoking the evaluate method, that script will be evaluated and included in the current script scope.

Install new credentials

First time you access a machine it could be normal to access through user/password, but once you’ve entered the first time afterwards you should be accessing using a ssh key. The following script will be installing our public ssh key as an authorized key in the remote host.

Install new credentials
/**
 * 1.SSHOOGR CONFIG
 */
import static common.Sudo.*
import static common.Util.*
import static common.Sshoogr.*

(1)
options.trustUnknownHosts = true

/**
 * 2.SHARED VARIABLES
 */
evaluate('common/secrets.groovy' as File)

/**
 * 3.OWN VARIABLES
 */
PREFIX = "echo $RPI_ROOT_PASSWORD | su -c '"
SUFFIX = "'"

/**
 * 4.CHANGE DEFAULT CREDENTIALS
 *
 * This step is done without online connection to avoid
 * any attack at this point due the fact that machines have
 * default usernames and passwords.
 */
remoteSession("$DEF_RPI_USER_TUPLE@$IP"){
    prefix("echo $DEF_RPI_ROOT_PASSWORD | su -c '") {
        suffix(SUFFIX){
            exec "echo \"$RPI_ROOT_TUPLE\" | chpasswd -m"
        }
    }

    prefix(PREFIX) {
        suffix(SUFFIX) {
            exec "pacman --noconfirm -S sudo"
        }
    }

    prefix(PREFIX) {
        suffix(SUFFIX) {
            exec "useradd -m $DOCKER_USERNAME"
            exec "echo \"$DOCKER_TUPLE\" | chpasswd -m"
            exec "echo \"${noPasswd(DOCKER_USERNAME)}\" >> /etc/sudoers"
        }
    }
}

/**
 * 3.INSTALL SSH KEY
 *
 * Once machines have been initialized, they should be accessed via
 * ssh key instead of using username and password. In order to do that
 * we need to install an authorized key in every one of them.
 */

AUTHORIZED_KEYS_FILE = "/home/$DOCKER_USERNAME/.ssh/authorized_keys"
DOCKER_PUBLIC_KEY = new File(SUPERVISOR_SSH_PUB_KEY)
DOCKER_PRIVATE_KEY = new File(SUPERVISOR_SSH_PRI_KEY)
DOCKER_CREDENTIALS = [keyFile: DOCKER_PRIVATE_KEY]

remoteSession("$DOCKER_TUPLE@$IP") {
    exec "mkdir -p ~/.ssh"
    exec "touch $AUTHORIZED_KEYS_FILE"

    remoteFile(AUTHORIZED_KEYS_FILE).text = DOCKER_PUBLIC_KEY.text
}

/**
 * 5.DELETE DEFAULT USER
 *
 */
remoteSession("$DOCKER_USERNAME@$IP") {
    keyFile = DOCKER_PRIVATE_KEY

    exec "history -c"

    prefix(SUDO) {
        exec "userdel -fr $DEF_RPI_USER_USERNAME"
        exec "pacman --noconfirm -Syu"  (1)
        exec "reboot now"
    }
}
Because hosts at this point doesn’t have a valid ssl certification we are telling ssh to trust a server anyway.
the --noconfirm flag should be used when you don’t want the process to ask for confirmation.

Install firewall

Now it’s time to protect your machine, install the firewall, this time I will be using ufw:

Firewall
/**
 * 1.SSHOOGR CONFIG
 */
import static common.Sudo.*
import static common.Util.*
import static common.Sshoogr.*

options.trustUnknownHosts = true

/**
 * 2.SHARED VARIABLES
 */
evaluate('common/secrets.groovy' as File)

DOCKER_PRIVATE_KEY = new File(SUPERVISOR_SSH_PRI_KEY)
/**
 * 4.INSTALL FIREWALL
 */
remoteSession("$DOCKER_USERNAME@$IP") {
    keyFile = DOCKER_PRIVATE_KEY

    prefix(SUDO) {
        exec "pacman --noconfirm -S ufw"  (1)
        exec "ufw allow ssh"              (2)
        exec "systemctl enable ufw"       (3)
        exec "systemctl start ufw"        (4)
    }
}
1 Install ufw
2 Add ssh exception
3 Enable ufw on startup
4 Start ufw service now

Install Docker

Docker
/**
 * 1.SSHOOGR CONFIG
 */
import static common.Sudo.*
import static common.Util.*
import static common.Sshoogr.*

options.trustUnknownHosts = true

/**
 * 2.SHARED VARIABLES
 */
evaluate('common/secrets.groovy' as File)

DOCKER_PRIVATE_KEY = new File(SUPERVISOR_SSH_PRI_KEY)
/**
 * 3.INSTALL DOCKER
 *
 * Installing docker engine in all machines
 */
remoteSession("$DOCKER_USERNAME@$IP") {
    keyFile = DOCKER_PRIVATE_KEY

    prefix(SUDO) {
        exec "pacman --noconfirm -S docker"
        exec "usermod -a -G docker $DOCKER_USERNAME"
        exec "systemctl enable docker"
        exec "systemctl start docker"
    }

    exec "history -c"
}

Check it out!

You can find Sshoogr at https://github.com/aestasit/sshoogr