# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 96 def initialize(env) @env = env end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 104 def hijack! @socket = nil if @env['WEBRICK_SOCKET'] @socket = @env['WEBRICK_SOCKET'] elsif @env['rack.hijack?'] begin @env['rack.hijack'].call rescue NotImplementedError end @socket = @env['rack.hijack_io'] end raise 'Internal error: request hijacking not available' unless @socket ssh_on_socket end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 100 def valid? @env["HTTP_CONNECTION"] == "upgrade" && @env["HTTP_UPGRADE"].to_s.split(',').any? { |part| part.strip == "raw" } end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 218 def buf_socket @buffered_socket ||= BufferedSocket.build(@socket) end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 222 def command params["command"] end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 230 def host params["hostname"] end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 214 def key_file @key_file ||= Proxy::RemoteExecution::Ssh.private_key_file end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 210 def params @params ||= MultiJson.load(@env["rack.input"].read) end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 203 def send_error(code, msg) buf_socket.enqueue("Status: #{code}\r\n") buf_socket.enqueue("Connection: close\r\n") buf_socket.enqueue("\r\n") buf_socket.enqueue(msg) end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 193 def send_start unless @started @started = true buf_socket.enqueue("Status: 101\r\n") buf_socket.enqueue("Connection: upgrade\r\n") buf_socket.enqueue("Upgrade: raw\r\n") buf_socket.enqueue("\r\n") end end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 121 def ssh_on_socket with_error_handling { start_ssh_loop } end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 234 def ssh_options auth_methods = %w(publickey) auth_methods.unshift('password') if params["ssh_password"] ret = { } ret[:port] = params["ssh_port"] if params["ssh_port"] ret[:keys] = [ key_file ] if key_file ret[:password] = params["ssh_password"] if params["ssh_password"] ret[:passphrase] = params[:ssh_key_passphrase] if params[:ssh_key_passphrase] ret[:keys_only] = true ret[:auth_methods] = auth_methods ret[:verify_host_key] = true ret[:number_of_password_prompts] = 1 ret end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 226 def ssh_user params["ssh_user"] end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 146 def start_ssh_loop err_buf = "" Net::SSH.start(host, ssh_user, ssh_options) do |ssh| channel = ssh.open_channel do |ch| ch.exec(command) do |ch, success| raise "could not execute command" unless success ssh.listen_to(buf_socket) ch.on_process do if buf_socket.available > 0 ch.send_data(buf_socket.read_available) end if buf_socket.closed? ch.close end end ch.on_data do |ch2, data| send_start buf_socket.enqueue(data) end ch.on_request('exit-status') do |ch, data| code = data.read_long send_start.call if code.zero? err_buf += "Process exited with code #{code}.\r\n" ch.close end ch.on_request('exit-signal') do |ch, data| err_buf += "Process was terminated with signal #{data.read_string}.\r\n" ch.close end ch.on_extended_data do |ch2, type, data| err_buf += data end end end channel.wait send_error(400, err_buf) unless @started end end
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 125 def with_error_handling yield rescue Net::SSH::AuthenticationFailed => e send_error(401, e.message) rescue Errno::EHOSTUNREACH send_error(400, "No route to #{host}") rescue SystemCallError => e send_error(400, e.message) rescue SocketError => e send_error(400, e.message) rescue Exception => e logger.error e.message logger.debug e.backtrace.join("\n") send_error(500, "Internal error") unless @started ensure unless buf_socket.closed? buf_socket.wait_for_pending_sends buf_socket.close end end