Disabling dangerous redis commands in Production redux
I recently read a great article from Honeybadger that muses on what could happen to a production environment if a fat fingered developer did something like:
Redis.current.flushdb
.
This scared me enough that I decided we needed something like this at Weedmaps. The only problem is that we want to keep these commands available in lower environments. We also do not allow new code into our codebases without tests. We make exceptions but they are rare, since we use our tests as documentation and a sanity check.
Here is my adaptation to this [gist]https://gist.github.com/joshuap/c1ff2657c150df6fb1257398b1d2716b) which was provided in the Honeybadger article above:
############### config/initializers/redis.rb #########################
require 'redis'
require 'redis_dangerous_commands'
class Redis
prepend DangerousCommands
end
############### lib/redis_dangerous_commands.rb #########################
module DangerousCommands
class ProhibitedCommandError < StandardError; end
def self.prepended(base)
return unless Rails.env.production?
base.send(:define_method, :flushdb) do
raise ProhibitedCommandError, error_message('EMPTY THE ENTIRE DATABASE')
end
base.send(:define_method, :flushall) do
raise ProhibitedCommandError, error_message('FLUSH ALL DATABASES')
end
end
def error_message(inner_message)
"DANGEROUS destructive operation. If you really want to #{inner_message} do it from `redis-cli`."
end
end
############### spec/lib/redis_dangerous_commands_spec.rb #########################
describe 'DangerousCommands' do
context 'Production' do
let(:dumb) { Dummy.new }
before do
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
class Dummy
prepend DangerousCommands
end
end
it 'raises ProhibitedCommandError' do
expect{ dumb.flushdb }.to raise_error DangerousCommands::ProhibitedCommandError
end
it 'raises ProhibitedCommandError' do
expect{ dumb.flushall }.to raise_error DangerousCommands::ProhibitedCommandError
end
end
context 'Non-production' do
let(:dumb) { DifferentDummy.new }
before do
class DifferentDummy
prepend DangerousCommands
end
end
it 'does not respond to flushdb' do
expect(dumb).not_to respond_to(:flushdb)
end
it 'does not respond to flushall' do
expect(dumb).not_to respond_to(:flushall)
end
end
end
And a link to a gist if that's easier for ya (ᵔᴥᵔ)
The basic idea is that we needed a way to a) optionally include the disabled override methods based on the Rails.env
and b) test this. The trick was figuring out how to stub Rails.env
in an initializer spec. Turns out it isn't possible since the environment loads those initializers as it boots up to run your specs. By time you stub out your Rails.env
it is too late.
This approach only defines those methods in the prepended hook provided by Ruby. Since, this module lives in lib
we can stub our Rails.env
and prepend our module into some dummy classes. If the dummy class receives those methods in when we stub out a production env then we know we are in business.
~(˘▾˘~)
Congratulations @davidpm! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!