Monday, 15 December 2014

OpenVPN with 2 factor authentication on the Raspberry Pi

This guide, describes how to set up a VPN (Virtual Private Network) on your raspberry pi to allow you to remotely connect to your home network.



A VPN allows a remote computer to act as though its part of your home newtork - vpn clients can see computers sitting on your home lan.  I've found it really useful when I've been working away staying in hotels with relatively slow internet connections.  The VPN in itself does not take up much band width, so once you are connected, and remote desktopped to one of your home PC's, you can operate at your home networks speed.  As the VPN is configured to use UDP, you get a much more stable connection (believe it or not!).

Prerequisites


  • Vanilla install of Raspbian (You should be able to login using pi/raspberry)

Approach


We're going to use OpenVPN and Ansible to orchestrate the install, and set up the configuration.  

We'll use google authenticator to provide 2 factor authentication, configured as a PAM module.

To test, we'll use a mobile phone, connected to the internet via 3g.

Setup Ansible


We're going to install ansible directly onto the Pi.  Log onto the raspberry pi, using pi/raspberry:

ssh-keygen    (go with defaults and no password)
sudo apt-get update
sudo apt-get install python-dev python-pip sshpass git
sudo pip install ansible
git clone https://github.com/coder36/raspi-ansible.git
cd raspi-ansible
Register the raspberry pi with anisble:

ssh 127.0.0.1  (selecting yes when asked about key fingerprint, password: raspberry)
cd ~/raspi-ansible
ansible-playbook -i live bootstrap.yml -k     (password: raspberry)

Edit inventory


The live file is called the inventory.  This lists the network config, which openvpn needs.  Edit this to match your own network configuration:

[pi]
127.0.0.1 isp_ip="123.1.12.22" local_lan="192.168.101.0" local_lan_mask="255.255.255.0" vpn_lan="10.8.0.0" vpn_lan_mask="255.255.255.0"
  • isp_ip - set this to your ip address.  You can find your ip address here
  • local_lan - set this to your network subnet.  *** important!
  • local_lan_mask - usually 255.255.255.0
  • vpn_lan - go with the default.
  • vpn_lan_mask - go with the default.

Run the playbook

cd ~/raspi-ansible
ansible-playbook -i live vpn.yml
sudo /etc/network/if-up.d/openvpn_firewall
This will configure and start the openvpn server.

It also will create a client file: /etc/openvpn/client.ovpn which can be used to configure the openvpn client.

Home router configuration


  • Forward port 1194 (UDP) to your raspberry pi.

Testing

We'll test the vpn connection using a mobile phone, simply because its the quickest way to prove that its working:
  • Install openvpn on your phone.
  • Install google authenticator on your phone.
  • Somehow get the client.ovpn onto your phone. (You could email it to yourself then download it!)


Create VPN user 


For each user of your vpn, you will need to generate a user on the raspberry pi, and a run google-authenticator.  Log onto the raspberry pi using pi/raspberry:

sudo adduser fred (provide password as 'monday', and go with defaults for the remaining of the prompts)
su - fred   (password: monday)
google-authenticator  (Answer y to the questions)
This will generate a QR code. Scan this into your phone using the google authenticator app. There is also a URL, which you could email to the user:



On your phone



  • Disable wifi, to ensure that you are not connecting via your home network. Use mobile data ie. 3g.
  • Open openvpn and import client.ovpn
You will be prompted for a username and password:

username: fred password: monday13520

The password, is fred's password concatenated with the google authenticator verification code provided by the app.

If everything is happy, you should now be connected and able  to navigate to your routers home page ie.: http://192.168.101.254




SSL Certs


You should generate your own certificates and private keys and copy them into /home/vagrant/playbooks/roles/vpn-server/templates/etc/openvpn. Until you do this, a potential hacker would in theory be able to intercept your and inspect your VPN traffic.  On ubuntu I use tinyca2 and on windows XCA to generate my certificates.


Route all traffic through VPN


Edit  /home/vagrant/playbooks/roles/vpn-server/templates/etc/openvpn. Uncomment the line:

push "redirect-gateway def1"


Debugging

 If things are not working, try connecting using the openvpn client whilst running:

sudo tail -f /var/log/syslog 
sudo tail -f /var/log/auth.log
Look in  syslog for openvpn issues, and auth.log for google authenticator issues.

Good luck!

Links


Raspberry Pi

Latest Raspbian image

Ansible VPN playbook

Windows SSL Certificate Manager


Update (Feb 2016)

1) VPN SSL certificate updated to never expire
2) server.conf updated to point to correct pam module: openvpn-plugin-auth-pam.so
3) Added config to optionally route all traffic through vpn

Thursday, 13 November 2014

Question framework for govuk sites

Online forms


I've spent most of my career working on government projects, creating websites which ask a set of questions and collect data.  On every project I work on, we seemingly reinvent the same thing,   This is where GDS have really added value, by creating a common set of libraries and tools to standardize how gov.uk sites look and feel.

What's missing, is a common approach to writing questionnaire type applications.  That's why I developed doopgovuk.


Doopgovuk


Doop is a Ruby on Rails question framework for govuk sites, inspired by the work GDS have done to standardize the cross government internet presence.  It allows you to easily create a questionnaire using a simple DSL.  The results are amazing.  It looks even better on a mobile device.

Try out the Child Benefit Demo developed with doop.




Self Assessment Demo which uses radio / button combo's to answer questions:



Getting started

Assuming ruby, rails and nodejs is installed:

sudo apt-get install qt4-dev-tools libqt4-dev libqt4-core libqt4-gui xvfb

These are needed to support the headless capybara test suite.

$ rails new govsite
$ cd govsite
$ echo "gem 'doop'" >> Gemfile
$ bundle
$ rails generate doopgovuk demo
$ rails s

Navigate to http://localhost:3000/demo/index


The DSL


When developing a doop application, to get a feel for what's involved, I've outlined some of the steps:

1) Start by defining a YAML structure which lists all the questions to be asked (see here):

page: {
  preamble: {
   _page: "preamble",
   _nav_name: "Preamble",
   income_more_than_50000: {
   _question: "Do you have an income of more than £50,000 a year ?"
  },
  do_you_still_want_to_apply: {
    _question: "Do you still want to apply for child benefit?"
  },
  proof_of_id: {
    _question: "We need proof of your identity",
    _answer: {}
  }
},

2) You then define a set of handlers which are called whenever a question is answered:

on_answer "/page/preamble/enrolled_before"  do |question,path, params, answer|
  answer_with( question, { "_summary" => answer } )
end
on_answer "/page/preamble/enrolled_before"  do |question,path, params, answer|
  answer_with( question, { "_summary" => answer } )
  enable( "/page/preamble/year_last_applied", answer == "Yes" )
end
3) And finally write the .erb to display the questions (see here):

%=question_form doop, res do %>
  <%=question "/page/preamble" do |root, answer| %>

    <%=question "/page/preamble/income_more_than_50000" do |root,answer| %>
      <button name="b_answer" value="Yes">Yes</button><br/>
      <button name="b_answer" value="No"></button>
    <% end %>

    <%=question "/page/preamble/do_you_still_want_to_apply" do |root,answer| %>
      <button name="b_answer" value="Yes">Yes, I still want to apply for child benefit</button
    <% end %>

    <% when_answered "/page/preamble" do %>
      <button>Continue and Save</button>
    <% end %>

  <% end %>
<% end %>


Give it a go...


Links


Doop github project

Child Benefit Demo

Self Assessment Demo

GDS

GOV UK Elements

Tuesday, 21 October 2014

Why fake RPM's ?

In IT companies across the globe, infrastructure and servers are provisioned at the click of a button - this is the world the Devops engineer is creating.  The deployment tool we used at this particular organisation is called ansible.  Ansible allows the programmatic install of RPM's and config files on target servers - our whole estate was built off the back of ansible scripts.

We wanted to develop the ansible scripts using TDD (Test Driven Development), so that we could put the whole regression test and deployment into a pipeline.

Importantly for TDD to work you get yourself into a feedback loop:

1) Write specs
2) Update ansible scripts
3) Run specs  (this will start with an empty base server build then apply the ansible scripts)
4) Repeat 2 and 3 until specs pass

The problem we had was that running the specs could take up an hour.  The feedback loop was way too long!  It turned out that some of the RPM's which the ansible scripts install took the best part of 30 mins to complete.

So we made a compromise - we would install fake rpms for the longest running offenders (luckily these were "bloated 3rd party monitoring tools" so not critical to the servers functionality)

FPM

FPM is a ruby gem to create .rpm's (or .debs if running on ubuntu).  To install you will need ruby:

curl -sSL https://get.rvm.io | bash -s stable
rvm install 1.9.3
rvm use 1.9.3 --default
gem install fpm


Fake it

The idea, is to add the fake rpm to the yum repository (alongside the original), but give the fake a higher version number, so it gets picked up by the yum install command.

What we found was that in some of the in house rpm's, the epoch had been set to the unix epoch time and in fact whenever the rpm was regenerated the epoch would increase.  In this scenario, the highest epoch wins, irrespective of the version number.

So the strategy for creating the winning the version, is very much dependent on how the original RPM was versioned:
mkdir -p /tmp/fake_package
fpm -s dir -t rpm -n sysmontool -v 999999999 --epoch 2000000000 -f -a noarch -C /tmp/fake_package ./
fpm -s dir -t rpm -n patchmontool -v 0.0.1 --epoch 2000000000 --iteration 999 -f -C /tmp/fake_package ./


Strangely, you can not just set the epoch to 9999999999,   We actually set the epoch to `date +%s`

Conclusion

The fpm command will create a .rpm.  Copy this into your yum repository and do a yum update.  You make need to do a yum clean all if its not getting picked up.

From experience, for server builds which are automated, it might be a good idea to set the yum,conf metadata_expire field to a low value.  This avoids the confusing scenario of thinking that something has installed/updated when it hasn't.



Wednesday, 15 October 2014

How to pair using TMUX

Why?

Tmux provides a way to remotely pair using a shared terminal.  This is great as it means you can pair when working from another office or from home.  It's ideal if you are comfortable using a command line, and enjoy memorizing keyboard shortcuts.

Issues


Re-sizing windows - when one side of the pair has a different resolution or differently sized terminal window, if you're not careful, you will see screen sheering.  This was completely solved by using putty or cygwin and ensuring that both clients use the same font settings.  Once its working, it works really well:





Weird control characters - If you see weird characters on the screen then set the window translation -> character set translation to UTF-8.  This seems to be only a problem in putty.

Git bash - Tmux does not work nicely with git bash

Server Setup

Assuming you are using vagrant on your local PC, create a vagrant server on 192.168.33.10, using the following vagrant snippet when setting up the network:

config.vm.define "a" do |server|
  ## usual config
  ## ...
  server.vm.network "private_network", ip: "192.168.33.10"
  server.vm.network "forwarded_port", guest: 2222, host: 22
end

What's important here is that you forward 2222 to the ssh port 22.

ssh onto your vagrant instance and install and run tmux:
     
yum install -y tmux
tmux

Now use the ip of your physical box to ssh in:

Client Setup

Install cygwin or putty.  From cygwin remotely attach to the tmux session:

ssh vagrant@<ip>  
tmux attach  (this will attach to the session started above)

Remote Connectivity

  • Use a wired connection.  Once I plugged in directly to the home router a lot of the connectivity issues went away (despite having a 5 bar wireless connection).
  • If using vmware or virtual box to host vagrant instance, ensure you use Bridged Mode.  If you use NAT mode, we found the connection would occasionally drop.  
  • If you have both vmware and virtual box running, then it may not be possible to enable bridged mode.  Uninstall one of them!


Viber

We tried several voip clients, including skype and googlr hangouts... The most stable turned out to be Viber.  I installed viber as a client on the remote PC.  Combined with a wired connection, it made the voip experience rock solid.
  • Use viber with a hands free headset.  I used a cheap bluetooth headset from amazon.
  • Be warned that voip eats bandwidth - try and get a wifi connection.


Conclusion

Tmux takes a bit of getting used to - you may find the default bind key is a bit of a stretch (ctrl-b).  I changed it to (ctrl-a).  Use this Tmux cheat sheet to quickly get up and running.  I've put together a set of dotfiles to make the whole tmux experience a lot nicer: https://github.com/coder36/dotfiles



Back button bindings

Sometimes you need to change what happens when the browser back button is pressed - for example you might want to disable the back button, or as I did, bind it to a hidden button.

The simplest way is as follows (assuming you are using jquery):

Javascript:

$( function() {
  history.pushState("back", null, null);
  if (typeof history.pushState === "function") {
    history.pushState("back", null, null);
    window.onpopstate = function (evt)
    {
      history.pushState('back', null, null);
      alert( "Back button pressed!" )
     };
   } 
})


Coffeescript:

$( () ->
  history.pushState("back", null, null);
  if typeof history.pushState == "function"
    history.pushState("back", null, null);
    window.onpopstate = (evt) ->
      history.pushState('back', null, null);
      $( "#back_a_page" ).click()
)


The coffeescript example will result in the button with id="back_a_page" getting clicked, whenever the back button is pressed.  

In chrome, to quickly test, this, go to  github  (which uses jquery) and hit the F12 button, then select console.  Paste in the javascript code and hit enter.  Now hit the back button :)