Monday, October 1, 2012

Testing Validations using RSpec

I just ran into an interesting issue while testing a Rails application with RSpec.  A spec with the following line in it was failing:

    ar2.should_not be_valid
Here ar2 is a model that was constructed in a way that violated a logical constraint.  I attempted to make this spec pass by adding a custom validation method to the model as such:
class Registration < ActiveRecord::Base
  validate :fields_match

  private

  def fields_match
    return true if model2.nil?
    model2.model1.id == model1.id
  end
end
All field names have had their names changed to protect their identity.

This didn't work.  As it turns out merely returning false from the validation method isn't enough to mark a model as invalid.  This answer on StackOverflow helped identify the problem as not adding an error to the list of validation errors for the model.

An updated version where I add an error finally made everything pass:

class Registration < ActiveRecord::Base
  validate :fields_match

  private

  def fields_match
    return true if model2.nil?
    return true if model2.model1.id == model1.id
    errors.add(:model2_id, "model2 doesn't match model1")
  end
end

Wednesday, May 23, 2012

Setting up LVM on LUKS

I recently worked with Raju Chauhan to setup encrypted storage for a database and related files.  He brought up an interesting requirement to see if the encrypted storage could grow with the data as needed.  I hadn't dealt with that specific requirement in the past so I figured I'd see what my options were.  Thanks for that requirement Raju; I don't think I would've thought of doing this were it not for that :-)

After preliminary performance testing in which LUKS barely edged out TrueCrypt, I chose LUKS for the setup since it's integrated into the Linux kernel and seemed to be a better choice for larger filesystems.  For those who don't know, LUKS is a disk-encryption specification that is implemented using cryptsetup and the dm_crypt module in modern Linux kernels.

My solution: LVM on a bunch of LUKS devices to get the encryption and dynamic growth working together.  Here is how to play with that on your own machines if you have about 5G of space to work with and want to see how it looks.

Pre-requisite Packages

I did this on an Ubuntu system so the following pre-requisite package installation instructions are for that. You'll need to ensure the appropriate packages for your distribution are installed before proceeding.

aptitude install cryptsetup-luks lvm2

Creating LVM over LUKS Setup

Create 4 1G files corresponding to physical volumes:
  1. for i in 0 1 2 3; do dd if=/dev/zero of=/pv0$i.luks bs=1M count=0 seek=1000; done
  2. ls -l /pv*.luks
Attach all of them to loopback devices:
  1. for i in 0 1 2 3; do losetup /dev/loop$i /pv0$i.luks; done
  2. losetup -a
Setup all devices as LUKS volumes (answer all prompts):
  1. for i in 0 1 2 3; do cryptsetup luksFormat /dev/loop$i; done
Open all LUKS devices:
  1. for i in 0 1 2 3; do cryptsetup luksOpen /dev/loop$i pv0$i.luks.device; done
Create LVM Physical Volumes from each LUKS device:
  1. for i in 0 1 2 3; do pvcreate /dev/mapper/pv0$i.luks.device; done
  2. pvdisplay
Create LVM Volume Group from all LUKS PVs:
  1. vgcreate vg0 `for i in 0 1 2 3; do echo /dev/mapper/pv0$i.luks.device; done`
  2. vgdisplay
Carve out a LVM Logical Volume from the vg0 Volume Group:
  1. lvcreate --size 3000M --name demolv vg0
  2. lvdisplay
At this time you have a LVM volume group named demolv that is sitting on top of two encrypted physical volumes that is each part of a single LUKS volume.  You can give each LUKS volume different passwords to increase security or you can give them all the same password to increase convenience.

Format and Mount Logical Volume

Format and mount the demolv Logical Volume with whatever filesystem you choose:
  1. mkfs.ext4 /dev/vg0/demolv
  2. mkdir /demo
  3. mount /dev/vg0/demolv /demo
At the end of all this, demolv will contain a filesystem that can be expanded by adding more LUKS volumes to the mix.  Feel free to create files and/or use this volume in any way you can think of with the knowledge that all the data you're storing is encrypted on disk.  Yes, this is quite cool!

Unmount and Detach

Once you're done playing with it (or when you're ready to shut down your system) you can run the following commands to unmount and detach everything.  These steps assume you followed the steps in this tutorial to the letter without changing any names.  If you changed names, you should change the corresponding names in the commands below:
  1. for lv in /dev/vg0/*; do lvchange -an $lv; done
  2. vgchange -an /dev/vg0
  3. for i in 0 1 2 3; do cryptsetup luksClose pv0$i.luks.device; done
  4. for i in 0 1 2 3; do losetup -d /dev/loop$i; done