Ruby on Rails: Migrating to Devise from your own authentication engine – Using custom Encryptors

Having your own authentication engine can be fun. You get to know how things work, why you should use salt, pepper, SHA2 instead of MD5 and much more. It also allows you to work with many old systems built before anyone heard about Devise. Still, I must say, that in old, maintained systems, sometimes it is worth throwing your own solution in favour of something that is already out there. Thanks to this, you won’t have to support the whole dedicated authentication engine stack (code, tests, docs). I decided to do exactly this: move from my own engine that was maintained for last 6 years to the Devise based authentication.

Moving whole controllers logic is quite simple: you just drop whatever you have and you use Devise stuff ;) but what about your custom, self-build encryption engine? The easiest approach would be to reset all the passwords and ask users to provide a new one again. Unfortunately it is not as user-friendly as we would want it to be. Users might feel afraid that we ask them for their password again not in the sign in process.

Luckily there’s a much easier approach: you can just use your current custom encryptor with Devise (as long as it is safe).

To do this, you need to do following things:

  1. Adding devise-encryptable to your Gemfile
  2. Moving your encryption logic to a Devise proper namespace
  3. Setting your encryption engine as a default one for Devise

After that, you should be able to use Devise with any encryption engine you used to.

Adding devise-encryptable to your Gemfile

This is definitely the easiest part. In your Gemfile file just:

gem 'devise'
gem 'devise-encryptable'

and run bundle install.

Moving your encryption logic to a Devise proper namespace

This is the hardest part. Create a file in your initializers (or add it to /config/initializers/devise.rb). It should contain your encryptor inside following modules:

module Devise
  module Encryptable
    module Encryptors
      # Here you should but encryption class that inherits from Base
    end
  end
end

Inside of it, you need to create a class that will correspond to your encrypion engine. It must inherit from Encryptors Base class and should contain one method called digest:

module Devise
  module Encryptable
    module Encryptors
      class CustomAuthentication < Base
        def self.digest(password, stretches, salt, pepper)
        end
      end
    end
  end
end

This method accepts following parameters:

  1. password – password provided by user
  2. stretches – cost for hashing the password (default 10)
  3. salt – salt that should be used for hashing
  4. pepper – pepper that should be used for hashing

Now, once you have all of this, you should just implement your logic in the digest method and return a password hash. For example like this one:

require "digest/sha2"

module Devise
  module Encryptable
    module Encryptors
      class CustomAuthentication < Base
        def self.digest(password, stretches, salt, pepper)
          string_to_hash = "#{pepper}#{salt}#{password.reverse}"
          Digest::SHA2.hexdigest(string_to_hash)
        end
      end
    end
  end
end

Setting your encryption engine as a default one for Devise

Now in your devise.rb config file set:

# Require the `devise-encryptable` gem when using anything other than bcrypt
config.encryptor = :custom_authentication

also keep in mind, that your devise using models should include encryptable options as well:

devise :database_authenticatable,
  :trackable, :encryptable, :confirmable, :recoverable,
  :registerable, :validatable, :lockable, :rememberable,
  :omniauthable, omniauth_providers: [:facebook]
06
Oct 2014
POSTED BY
POSTED IN Rails Ruby Software
DISCUSSION 0 Comments

MongoDB monitoring with Munin – Setting up Munin to work with MongoDB

To monitor MongoDB you can use many tools, some like MongoDB Management Service (MMS) are cloud based, some like Munin might be installed locally.

Today we will focus on setting up Munin to monitor MongoDB.

Getting started with Munin

If you don’t know what Munin is, or how to install it on your platform, please refer to following articles:

This is not a tutorial on how to install Munin itself. I assume that from this point, you have Munin and Munin-node running on your system and that you see basic Munin stats charts.

MongoDB configuration

MongoDB provides a simple http interface listing information of interest to administrators. This interface may be accessed at the port with a numeric value 1000 more than the configured mongod port. The default port for the http interface is 28017 (description copy-pasted from here). By default it is not enabled but it is required by Munin MongoDB plugins, so we need to turn it on.

Warning! Keep in mind, that if you don’t block it, it will listed on your public interface and it will be accessible by default from internet. Please use iptables to make it work only from localhost.

To enable it, edit your /etc/mongod.conf file, find httpinterface line and uncomment it (or set to true if set to false):

# vim /etc/mongod.conf
# Enable the HTTP interface (Defaults to port 28017).

httpinterface = true

After that you need to restart MongoDB:

# As a root or using sudo
/etc/init.d/mongod restart

[ ok ] Restarting database: mongod.

To test it, open http://localhost:28017/ (remember to replace localhost with your server host). You should see page similar to this one:

mongohttp

Installing Munin MongoDB plugins

Some of those plugins won’t work out of the box, but we will take care of that later. For now let’s focus on the install process:

# as a root or using sudo
git clone git://github.com/erh/mongo-munin.git ~/mongo-munin
cd ~/mongo-munin

# We copy all the plugins into munin plugins
cp mongo_* /usr/share/munin/plugins/

# We need to activate them now
ln -s /usr/share/munin/plugins/mongo_btree /etc/munin/plugins/
ln -s /usr/share/munin/plugins/mongo_conn /etc/munin/plugins/
ln -s /usr/share/munin/plugins/mongo_lock /etc/munin/plugins/
ln -s /usr/share/munin/plugins/mongo_mem /etc/munin/plugins/
ln -s /usr/share/munin/plugins/mongo_ops /etc/munin/plugins/

cd ~
# We don't need this anymore
rm -rf mongo-munin

# Restarting munin-node...
/etc/init.d/munin-node restart

After restarting munin-node and waiting few minutes, we should have a new section in your munin web ui (mongodb). Part of the graphs won’t display any data, but you should at least see the mongodb section.

mongo_mem-day

Debugging and fixing Munin MongoDB broken plugins

Some of the plugins (like mongo_lock) won’t work without a little tuneup. If you see graphs similar to this (without any data and -nan everywhere), then most likely those plugins aren’t working.

mongo_lock-day

To check each of the plugins, you need to run munin-run with appropriate plugin name (as root):

mongo_btree

# munin-run mongo_btree
Traceback (most recent call last):
  File "/etc/munin/plugins/mongo_btree", line 61, in <module>
    doData()
  File "/etc/munin/plugins/mongo_btree", line 35, in doData
    for k,v in get().iteritems():
  File "/etc/munin/plugins/mongo_btree", line 32, in get
    return getServerStatus()["indexCounters"]["btree"]
KeyError: 'btree'

mongo_conn

# munin-run mongo_conn
connections.value 0

mongo_lock

# munin-run mongo_lock
Traceback (most recent call last):
  File "/etc/munin/plugins/mongo_lock", line 54, in <module>
    doData()
  File "/etc/munin/plugins/mongo_lock", line 34, in doData
    print name + ".value " + str( 100 * getServerStatus()["globalLock"]["ratio"] )
KeyError: 'ratio'

mongo_mem

# munin-run mongo_mem
resident.value 37748736
virtual.value 376438784
mapped.value 83886080

mongo_ops

# munin-run mongo_ops
getmore.value 0
insert.value 1
update.value 0
command.value 1
query.value 53
delete.value 0

Errors summary

Based on the plugins output, we can see, that 2 out of 5 plugins aren’t working:

  • mongo_btree
  • mongo_lock

They aren’t working because the MongoDB HTTP interface response slightly differs from what it used to be when the plugins were developed.

Patching mongo_btree plugin

Apply following patch to /usr/share/munin/plugins/mongo_btree:

@@ -29,7 +29,7 @@ def getServerStatus():
     return json.loads( raw )["serverStatus"]
 
 def get():
-    return getServerStatus()["indexCounters"]["btree"]
+    return getServerStatus()["indexCounters"]
 
 def doData():
     for k,v in get().iteritems():

After patching, execute munin-run mongo_btree:

# munin-run mongo_btree
missRatio.value 0
resets.value 0
hits.value 2
misses.value 0
accesses.value 2

Patching mongo_lock plugin

Apply following patch to /usr/share/munin/plugins/mongo_lock:

@@ -31,7 +31,7 @@ def getServerStatus():
 name = "locked"
 
 def doData():
-    print name + ".value " + str( 100 * getServerStatus()["globalLock"]["ratio"] )
+    print name + ".value " + str( 100 * round(float(getServerStatus()["globalLock"]["lockTime"]["$numberLong"])/float(getServerStatus()["globalLock"]["totalTime"]["$numberLong"]), 8) )
 
 def doConfig():

After patching, execute munin-run mongo_lock:

# munin-run mongo_lock
locked.value 0.000785

After that (and few minutes) all of the stats should be working.

26
Sep 2014
POSTED BY
POSTED IN Hosting Linux Software
DISCUSSION 0 Comments