Ruby5 #2 – Rails Securing Passwords

May 28th, 2010

It's important to filter out any sensitive data such as passwords from your log files. You can easily filter out data across your while application by calling filter_paramter_logging from your ApplicationController. In the example below I'm passing :password and :password_confirmation to remove their values from being placed in the logs.

ActionController::Base

 
class ApplicationController < ActionController::Base
  filter_parameter_logging :password, :password_confirmation
end
 

You will now see "FILTERED" in place of sensitive data.

 
  Parameters: {"x"=>"37", "y"=>"14", "action"=>"login", "authenticity_token"=>"JRFNcG9chNIpcsHoJzcQRRy1D6lIenjl7cWmvp3UpaI=", "controller"=>"videos", "user_id"=>"7-Jonathan-SpoonerJune", "video"=>{"password"=>"[FILTERED]", "email"=>"june@gmail.com"}}
 

Ruby5 #1 – How does a Ruby block work?

May 21st, 2010

Ruby implements Blocks, Procs and lambdas which are referred to as closures in the computer science community.

If you beginning to learn Ruby you will quickly run into code that looks like this.

 
a = ["dog", "cat", "bird"]
a.alter_each! do |n, i|
  "#{i}_#{n}"
end
 

So whats going on here?
We start off with an array of animal names and call the alter_each! method passing a block. In this block of code we can specify how we want to alter each item. Our example will prefix each animal name with it's position in the array. As the alter_each! method iterates through each item it will execute our block passing the value and index. Our block captures these params, prefixes the index to the name and returns the result.

Now lets look at the alter_each! method.
Notice the method doesn't specify any params, that's because a block is automatically assigned to yield keyword. yield is called like a function passing in the value and index of each item in the array and overriding the original value.

 
class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
end
 

What if you need to pass a param to this method?
You can modify the method signature to accept params and finally catch the block with a param starting with an ampersand. In the example below our block will be captured with the &block param which we will invoke the call method. This is in place of using yield

 
class Array
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end
 

Here is a full example that will prefix the index with the yield method and append the index with the call method.

 
class Array
  def alter_each!
    self.each_with_index do |n, i|
      self[i] = yield(n,i)
    end
  end
  def modify_each!(add_one = true, &block)
    self.each_with_index do |n, i|
      j = (add_one) ? (i + 1) : i
      self[i] = block.call(n,j)
    end
  end
end
 
a = ["dog", "cat", "cow"]
 
a.alter_each! do |n, i|
  "#{i}_#{n}"
end
 
a.modify_each! false do |n,i|
  "#{n}_#{i}"
end
 
puts a
 

For blocks in the wild check out the ActiveRecord save method.

 
def save(perform_validation = true, &block)
    return false if perform_validation && block_given? && authenticate_with_sorenson? && !authenticate_with_sorenson
    result = super
    yield(result) if block_given?
    result
end
 

References:
Understanding Ruby Blocks, Procs and lambdas

Deploying a Rails app to Amazon EC2 with Capistrano

April 27th, 2010

@esilverberg originally posted this article that I've been referring to for the past year and now wanted to contribute some updates of my own.

Get the code

Arr! There are some problems with checking out code from github with SVN. I'll fix this soon but if your reading today modify deploy.rb to deploy from git.

We're going to deploy my stub site from github and we'll check it out with SubVersion

 
svn checkout  http://svn.github.com/jspooner/authlogic_cucumber_rspec_example.git basic
cd basic
 

You probably want to get the project running locally. Hopefully you have the mysql driver installed or you can edit the database.yml file to use sqlite.

 
rake db:create
rake db:migrate
rake db:seed
script/server
 

EC2 setup

  1. Read the getting started guide here at amazon.
  2. Install the developer tools. I have them located at
    ec2-ami-tools-1.3-45758
  3. Make sure you have downloaded the cert- and pk- .pem files. I have them in ~/.ec2/

Setup environment variables

Probably want to put these in your .bashrc or .bash_profile with the 'export' command in front.

 
     EC2_CERT=/home/USER/.ec2/cert-EZC7LAIYSKPC546UKT7E3PI.pem
     EC2_HOME=/home/USER/ec2-api-tools-1.3-30349
     EC2_PRIVATE_KEY=/home/USER/.ec2/pk-EZC7LAIYSKPC546UKT7EUEOOI.pem
     PATH=$PATH:$EC2_HOME/bin
 

Verify dev tools

Run this to make sure it works:

ec2-describe-images ami-5394733a

Start up an instance

  1. Click "Launch Instance" https://console.aws.amazon.com/ec2/home
  2. Click "Community AMI's" and select one of the AMI's listed when you ran cap ec2onrails:ami_ids. I'm using ami-5394733a

  3. Select an instance size and continue.
  4. Advanced Instance Options. Click Continue.
  5. Create a new Key Pair named testkey and click download. Move this file to ~/.ssh/testkey.pem and chmod the key to 600.
     
       mv testkey.pem ~/.ssh/testkey.pem
       cd ~/.ssh/
       chmod 600 testkey.pem
       ls -al
     

    You should see the file permissions set to -rw-------
    This key is used to SSH into your instance and is used set on line 13 of deploy.rb.

    ssh_options[:keys] = ["#{ENV['HOME']}/.ssh/testkey.pem"]
  6. Create a new Security Group and rules for HTTP HTTPS & SSH.
  7. Verify your settings and Launch the Instance
  8. After you instance has launched find the Public DNS that looks something like this ec2-xxx-xxx-xx-xxx.compute-1.amazonaws.com and add that to you deploy.rb file on lines 18-21

Update tools on the image

ssh into the machine and run the following

ssh -i ~/.ssh/testkey.pem root@ec2-xxx-xxx-xx-xxx.compute-1.amazonaws.com
sudo apt-get update
sudo apt-get -y install build-essential
sudo apt-get -y install emacs22
sudo gem update --system

The original post had you installing imagemagick with apt-get but that wan't pulling the latest version; which the rmagick gem requires. So here we will pull down the latest version and install from source.

 
wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
tar -zxvf ImageMagick.tar.gz
cd ImageMagick-6.6.1-5/
./configure
make install
gem install rmagick
ldconfig /usr/local/lib
 

Set up capistrano and ec2onrails

Capistrano is a tool for automating tasks on one or more remote servers. We are going to use it to push our site up to EC2 with the help of the ec2onrails gem.

capify .
gem install ec2onrails

Open Capfile and require ec2onrails.

require 'ec2onrails/recipes'

Open deploy.rb and replace with this.

 
# This is a sample Capistrano config file for EC2 on Rails.
# It should be edited and customized.
 
set :application, "basic"
set :user, "root"
set :use_sudo, true
set :repository, "http://svn.github.com/jspooner/authlogic_cucumber_rspec_example.git "
 
# NOTE: for some reason Capistrano requires you to have both the public and
# the private key in the same folder, the public key should have the
# extension ".pub".
ssh_options[:keys] = ["#{ENV['HOME']}/.ssh/testkey.pem"]
 
# Your EC2 instances. Use the ec2-xxx....amazonaws.com hostname, not
# any other name (in case you have your own DNS alias) or it won't
# be able to resolve to the internal IP address.
role :web,      "ec2-xx-xx-xx-xx.compute-1.amazonaws.com"
role :app,      "ec2-xx-xx-xx-xx.compute-1.amazonaws.com"
role :db,       "ec2-xx-xx-xx-xx.compute-1.amazonaws.com", :primary => true
role :memcache, "ec2-xx-xx-xx-xx.compute-1.amazonaws.com"
 
# Whatever you set here will be taken set as the default RAILS_ENV value
# on the server. Your app and your hourly/daily/weekly/monthly scripts
# will run with RAILS_ENV set to this value.
set :rails_env, "production"
 
# EC2 on Rails config.
# NOTE: Some of these should be omitted if not needed.
set :ec2onrails_config, {
  # S3 bucket and "subdir" used by the ec2onrails:db:restore task
  # :restore_from_bucket => "your-bucket",
  # :restore_from_bucket_subdir => "database",
 
  # S3 bucket and "subdir" used by the ec2onrails:db:archive task
  # This does not affect the automatic backup of your MySQL db to S3, it's
  # just for manually archiving a db snapshot to a different bucket if
  # desired.
  # :archive_to_bucket => "your-other-bucket",
  # :archive_to_bucket_subdir => "db-archive/#{Time.new.strftime('%Y-%m-%d--%H-%M-%S')}",
 
  # Set a root password for MySQL. Run "cap ec2onrails:db:set_root_password"
  # to enable this. This is optional, and after doing this the
  # ec2onrails:db:drop task won't work, but be aware that MySQL accepts
  # connections on the public network interface (you should block the MySQL
  # port with the firewall anyway).
  # If you don't care about setting the mysql root password then remove this.
  :mysql_root_password => "blamblam12343",
 
  # Any extra Ubuntu packages to install if desired
  # If you don't want to install extra packages then remove this.
  :packages => ["logwatch"],
 
  # Any extra RubyGems to install if desired: can be "gemname" or if a
  # particular version is desired "gemname -v 1.0.1"
  # If you don't want to install extra rubygems then remove this
  :rubygems => ["rails -v 2.3.5"],
 
  # Set the server timezone. run "cap -e ec2onrails:server:set_timezone" for
  # details
  :timezone => "Canada/Eastern",
 
  # Files to deploy to the server (they'll be owned by root). It's intended
  # mainly for customized config files for new packages installed via the
  # ec2onrails:server:install_packages task. Subdirectories and files inside
  # here will be placed in the same structure relative to the root of the
  # server's filesystem.
  # If you don't need to deploy customized config files to the server then
  # remove this.
  # :server_config_files_root => "../server_config",
 
  # If config files are deployed, some services might need to be restarted.
  # If you don't need to deploy customized config files to the server then
  # remove this.
  :services_to_restart => %w(apache2 postfix sysklogd),
 
  # Set an email address to forward admin mail messages to. If you don't
  # want to receive mail from the server (e.g. monit alert messages) then
  # remove this.
  :admin_mail_forward_address => "me@gmail.com",
 
  # Set this if you want SSL to be enabled on the web server. The SSL cert
  # and key files need to exist on the server, The cert file should be in
  # /etc/ssl/certs/default.pem and the key file should be in
  # /etc/ssl/private/default.key (see :server_config_files_root).
  :enable_ssl => false
}
 

Be sure to customize those files and read the comments.

Now run cap -T and you should see all the ec2onrails tasks like this.

Also, use the hostname “db_primary” in your database.yml file. After running “cap ec2onrails:server:set_roles” it will resolve to the instance defined in your Capistrano “db” role.

Deploy the site

cap ec2onrails:setup
cap ec2onrails:db:set_root_password
cap deploy:cold

That should be it! Now goto http://ec2-xxx-xxx-xx-xxx.compute-1.amazonaws.com and your site should be running.


Extended Set Up

This was a very basic set up and you will want to customize your installation.

Cron services

EC2onrails lets you do hourly and daily cron tasks, but not more frequent
than that. I installed craken and have this in my deploy.rb to compensate:

 
namespace :craken do
	desc "Install raketab"
	task :install, :roles => :cron do
		set :rails_env, "production" unless
		exists?(:rails_env)
		set :env_args, (exists?(:env_args) ? env_args :"app_name=#{application} deploy_path=#{current_path}")
			run "cd #{current_path} && rake #{env_args} RAILS_ENV=#{rails_env} craken:install"
			end
			task :uninstall, :roles => :cron do
				set :rails_env, "production" unless exists?(:rails_env)
				set :env_args, (exists?(:env_args) ? env_args :"app_name=#{application} deploy_path=#{current_path}")
				run "cd #{current_path} && rake #{env_args} RAILS_ENV=#{rails_env} craken:uninstall; true"
	end
end
 
before "deploy:symlink", "craken:uninstall"
after "deploy:symlink", "craken:install"
 

You can see more about the capistrano events here. The recipe was taken from here.

Gotchas

  • Don't forget to open up port 443 on Amazon's management console if
    you need ssl support
  • Follow these instructions to configure your SSL certs
  • Setting up cron is described here
  • If you include submodules you can't do the git shallow copy

Useful links

Advanced - Setting up a db server

adduser admin

Now, add your public keypair via these instructions here:
blogs.techrepublic.com.com

I think you have to manually copy the keypair; who knows where it is on
the system??

Now, add the admin user to the /etc/sudoers file so it is what capistrano
is expecting.

# The 'admin' user can run sudo without a password
admin ALL=(ALL) NOPASSWD: ALL

OSMF Hello World in 17 lines of code

April 24th, 2010

This is the most basic way to get a video playing through the Open Source Media Framwork.

Add OSMF FC1 to your class path and the class below is your document root.

 
package {
	import flash.display.Sprite;
	import org.osmf.elements.VideoElement;
	import org.osmf.media.MediaPlayer;
	import org.osmf.media.URLResource;
	public class SMILSample extends Sprite
	{
		public function SMILSample()
		{
			var mediaPlayer:MediaPlayer = new MediaPlayer();
			var resource:URLResource 	= new URLResource(FLV);
			mediaPlayer.media 			= new VideoElement(resource);
			addChild(mediaPlayer.displayObject);
		}
		public static const FLV:String = "http://mediapm.edgesuite.net/strobe/content/test/AFaerysTale_sylviaApostol_640_500_short.flv";
	}
}
 

Authlogic + BDD Rails example

April 13th, 2010

Example on github http://github.com/jspooner/authlogic_cucumber_rspec_example