[Backgroundrb-devel] Backgroundrb in shared hosting environment

Gaël SECHAUD gael.sechaud at dev-ruby.zefiris.org
Thu May 28 11:16:22 EDT 2009


Hi,

Considering the problem of backgroundrb in shared hosting environment,
I've come with another temporary patch, which allow encrypted
communication between the MiddleMan and the server.

I still haven't found a better way to secure backgroundrb in shared
hosting environment, but at least, it's better than nothing.

Regards.

----- revision info -----

svn info
Path: .
URL: http://svn.devjavu.com/backgroundrb/trunk
Repository Root: http://svn.devjavu.com/backgroundrb
Repository UUID: 69d54aea-511f-0410-a924-81c4482807e4
Revision: 331
Node Kind: directory
Schedule: normal
Last Changed Author: gethemant at gmail.com
Last Changed Rev: 330
Last Changed Date: 2008-10-14 12:51:23 +0200 (Tue, 14 Oct 2008)

----- patch -----
diff -crB backgroundrb/lib/backgroundrb/bdrb_connection.rb backgroundrb-patched/lib/backgroundrb/bdrb_connection.rb
*** backgroundrb/lib/backgroundrb/bdrb_connection.rb	2009-05-25 17:18:35.000000000 +0200
--- backgroundrb-patched/lib/backgroundrb/bdrb_connection.rb	2009-05-28 16:38:44.000000000 +0200
***************
*** 8,13 ****
--- 8,15 ----
        @server_port = port
        @cluster_conn = cluster_conn
        @connection_status = true
+       @password = BDRB_CONFIG[:backgroundrb][:password].nil? ? false : BDRB_CONFIG[:backgroundrb][:password]
+       @key = Digest::MD5.hexdigest(File.read(File.join(RAILS_ROOT, "config", "backgroundrb.yml")))
      end
  
  
***************
*** 65,74 ****
        end
      end
  
      def dump_object data
        establish_connection
        raise BackgrounDRb::BdrbConnError.new("Error while connecting to the backgroundrb server #{server_info}") unless @connection_status
! 
        object_dump = Marshal.dump(data)
        dump_length = object_dump.length.to_s
        length_str = dump_length.rjust(9,'0')
--- 67,121 ----
        end
      end
  
+     def decrypt_data(t_data)
+       if @key && t_data
+         data = Base64.decode64(t_data)
+         res = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
+         res.decrypt
+         res.key = Digest::SHA2.hexdigest(@key)
+         decrypt_data = res.update(data)
+         decrypt_data << res.final
+         decrypt_data = Base64.decode64(decrypt_data)
+         tdata = decrypt_data.split("\b")
+ 
+         t_data = []
+         tdata.each do |t|
+           data = {}
+           t.split("\a").each do |e|
+             key_data = e.split("\r")
+             if key_data[1] 
+               data[key_data[0].to_sym] = key_data[1].to_sym
+             else
+               data[key_data[0].to_sym] = ""
+             end
+           end
+           t_data << data
+         end
+       end
+       return t_data
+     end
+     def encrypt_data(data)
+       if @key
+         tdata = ""
+         data.each do |key, d|
+           tdata << "#{key}\r#{d}\a"
+         end
+         tdata = Base64.encode64(tdata)
+         res = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
+         res.encrypt
+         res.key = Digest::SHA2.hexdigest(@key)
+         encrypt_data = res.update(tdata)
+         encrypt_data << res.final
+         data = Base64.encode64(encrypt_data)
+       end
+       return data
+     end
+ 
      def dump_object data
+       data[:password] = @password
        establish_connection
        raise BackgrounDRb::BdrbConnError.new("Error while connecting to the backgroundrb server #{server_info}") unless @connection_status
!       data = encrypt_data(data)
        object_dump = Marshal.dump(data)
        dump_length = object_dump.length.to_s
        length_str = dump_length.rjust(9,'0')
***************
*** 100,106 ****
        bdrb_response = nil
        @mutex.synchronize { bdrb_response = read_from_bdrb() }
        close_connection
!       bdrb_response
      end
  
      def all_worker_info
--- 147,153 ----
        bdrb_response = nil
        @mutex.synchronize { bdrb_response = read_from_bdrb() }
        close_connection
!       decrypt_data(bdrb_response)
      end
  
      def all_worker_info
***************
*** 110,116 ****
        bdrb_response = nil
        @mutex.synchronize { bdrb_response = read_from_bdrb() }
        close_connection
!       bdrb_response
      end
  
      def delete_worker p_data
--- 157,163 ----
        bdrb_response = nil
        @mutex.synchronize { bdrb_response = read_from_bdrb() }
        close_connection
!       decrypt_data(bdrb_response)
      end
  
      def delete_worker p_data
***************
*** 148,153 ****
--- 195,201 ----
          bdrb_response = nil
          @mutex.synchronize { bdrb_response = read_from_bdrb() }
          close_connection
+         bdrb_response = decrypt_data(bdrb_response)
          bdrb_response ? bdrb_response[:data] : nil
        end
      end
***************
*** 170,175 ****
--- 218,224 ----
        bdrb_response = nil
        @mutex.synchronize { bdrb_response = read_from_bdrb(nil) }
        close_connection
+       bdrb_response = decrypt_data(bdrb_response)
        bdrb_response ? bdrb_response[:data] : nil
      end
    end
diff -crB backgroundrb/server/lib/master_worker.rb backgroundrb-patched/server/lib/master_worker.rb
*** backgroundrb/server/lib/master_worker.rb	2009-05-25 17:18:35.000000000 +0200
--- backgroundrb-patched/server/lib/master_worker.rb	2009-05-28 16:38:39.000000000 +0200
***************
*** 25,38 ****
    end
  
    class MasterWorker
!     attr_accessor :debug_logger
      include BackgrounDRb::BdrbServerHelper
      # receives requests from rails and based on request type invoke appropriate method
      def receive_data p_data
        @tokenizer.extract(p_data) do |b_data|
          begin
            t_data = load_data b_data
            if t_data
              case t_data[:type]
                # async method invocation
              when :async_invoke: async_method_invoke(t_data)
--- 25,94 ----
    end
  
    class MasterWorker
!     attr_accessor :debug_logger,:password, :key
      include BackgrounDRb::BdrbServerHelper
+ 
+     def decrypt_data(t_data)
+       if @key && t_data
+         data = Base64.decode64(t_data)
+         res = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
+         res.decrypt
+         res.key = Digest::SHA2.hexdigest(@key)
+         decrypt_data = res.update(data)
+         decrypt_data << res.final
+         decrypt_data = Base64.decode64(decrypt_data)
+         t_data = {}
+         tdata = decrypt_data.split("\a")
+         tdata.each do |e|
+           key_data = e.split("\r")
+           t_data[key_data[0].to_sym] = key_data[1].to_sym
+         end
+       end
+       return t_data
+     end
+     def encrypt_data(data)
+       if @key && data
+         if data.is_a?(Array)
+           tdata = []
+           data.each do |e|
+             sdata = ""
+             e.each do |key, d|
+               sdata << "#{key}\r#{d}\a"
+             end
+             tdata << sdata
+           end
+           tdata = tdata.join("\b")
+         else
+           tdata = ""
+           data.each do |key, d|
+             tdata << "#{key}\r#{d}\a"
+           end
+         end
+         tdata = Base64.encode64(tdata)
+         res = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
+         res.encrypt
+         res.key = Digest::SHA2.hexdigest(@key)
+         encrypt_data = res.update(tdata)
+         encrypt_data << res.final
+         data = Base64.encode64(encrypt_data)
+       end
+       return data
+     end
+ 
      # receives requests from rails and based on request type invoke appropriate method
      def receive_data p_data
        @tokenizer.extract(p_data) do |b_data|
          begin
            t_data = load_data b_data
            if t_data
+             t_data = decrypt_data(t_data)
+               # check password
+             if @password && t_data[:password].to_s != @password
+               debug_logger.info("Invalid password : #{t_data.inspect}")
+               error_password(t_data)
+               return
+             end
+ 
              case t_data[:type]
                # async method invocation
              when :async_invoke: async_method_invoke(t_data)
***************
*** 55,67 ****
        end
      end
  
      # Send worker info to the user
      def pass_worker_info(t_data)
        worker_name_key = gen_worker_key(t_data[:worker],t_data[:worker_key])
        worker_instance = reactor.live_workers[worker_name_key]
        info_response = { :worker => t_data[:worker],:worker_key => t_data[:worker_key]}
        worker_instance ? (info_response[:status] = :running) : (info_response[:status] = :stopped)
!       send_object(info_response)
      end
  
      # collect all worker info in an array and send to the user
--- 111,132 ----
        end
      end
  
+     # Send password require info to the user
+     def error_password(t_data)
+       worker_name_key = gen_worker_key(t_data[:worker],t_data[:worker_key])
+       worker_instance = reactor.live_workers[worker_name_key]
+       info_response = { :error => "Password required / Wrong password" }
+       worker_instance ? (info_response[:status] = :running) : (info_response[:status] = :stopped)
+       send_object(encrypt_data(info_response))
+     end
+ 
      # Send worker info to the user
      def pass_worker_info(t_data)
        worker_name_key = gen_worker_key(t_data[:worker],t_data[:worker_key])
        worker_instance = reactor.live_workers[worker_name_key]
        info_response = { :worker => t_data[:worker],:worker_key => t_data[:worker_key]}
        worker_instance ? (info_response[:status] = :running) : (info_response[:status] = :stopped)
!       send_object(encrypt_data(info_response))
      end
  
      # collect all worker info in an array and send to the user
***************
*** 71,77 ****
          worker_key = (value.worker_key.to_s).gsub(/#{value.worker_name}_?/,"")
          info_response << { :worker => value.worker_name,:worker_key => worker_key,:status => :running }
        end
!       send_object(info_response)
      end
  
      # Delete the worker. Sends TERM signal to the worker process and removes
--- 136,142 ----
          worker_key = (value.worker_key.to_s).gsub(/#{value.worker_name}_?/,"")
          info_response << { :worker => value.worker_name,:worker_key => worker_key,:status => :running }
        end
!       send_object(encrypt_data(info_response))
      end
  
      # Delete the worker. Sends TERM signal to the worker process and removes
***************
*** 155,161 ****
  
      # Receieve responses from workers and dispatch them back to the client
      def worker_receive p_data
!       send_object(p_data)
      end
  
      def unbind; end
--- 220,226 ----
  
      # Receieve responses from workers and dispatch them back to the client
      def worker_receive p_data
!       send_object(encrypt_data(p_data))
      end
  
      def unbind; end
***************
*** 163,168 ****
--- 228,235 ----
      # called whenever a new connection is made.Initializes binary data parser
      def post_init
        @tokenizer = Packet::BinParser.new
+       @password = BDRB_CONFIG[:backgroundrb][:password].nil? ? false : BDRB_CONFIG[:backgroundrb][:password]
+       @key = Digest::MD5.hexdigest(File.read(File.join(RAILS_ROOT, "config", "backgroundrb.yml")))
      end
      def connection_completed; end
    end


-- 
SECHAUD Gaël
-------------- next part --------------
A non-text attachment was scrubbed...
Name: backgroundrb.patch
Type: text/x-patch
Size: 10506 bytes
Desc: not available
URL: <http://rubyforge.org/pipermail/backgroundrb-devel/attachments/20090528/ddd4a7fe/attachment.bin>


More information about the Backgroundrb-devel mailing list