class Proxy::RemoteExecution::Cockpit::Session
Public Class Methods
new(env)
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 114 def initialize(env) @env = env @open_ios = [] end
Public Instance Methods
hijack!()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 123 def hijack! @socket = nil if @env['ext.hijack!'] @socket = @env['ext.hijack!'].call elsif @env['rack.hijack?'] begin @env['rack.hijack'].call rescue NotImplementedError # This is fine end @socket = @env['rack.hijack_io'] end raise 'Internal error: request hijacking not available' unless @socket ssh_on_socket end
valid?()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 119 def valid? @env["HTTP_CONNECTION"] == "upgrade" && @env["HTTP_UPGRADE"].to_s.split(',').any? { |part| part.strip == "raw" } end
Private Instance Methods
buf_socket()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 248 def buf_socket @buf_socket ||= BufferedSocket.build(@socket) end
command()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 252 def command params["command"] end
flush_pending_writes(writers)
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 215 def flush_pending_writes(writers) writers.each do |writer| writer.respond_to?(:send_pending) ? writer.send_pending : writer.flush end end
host()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 256 def host params["hostname"] end
inner_system_ssh_loop2(out_buf, err_buf, in_buf, pid)
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 179 def inner_system_ssh_loop2(out_buf, err_buf, in_buf, pid) err_buf_raw = '' readers = [buf_socket, out_buf, err_buf] loop do # Prime the sockets for reading ready_readers, ready_writers = IO.select(readers, [buf_socket, in_buf], nil, 300) (ready_readers || []).each { |reader| reader.close if reader.fill.zero? } proxy_data(out_buf, in_buf) if out_buf.closed? code = Process.wait2(pid).last.exitstatus send_start if code.zero? # TODO: Why? err_buf_raw += "Process exited with code #{code}.\r\n" break end if err_buf.available.positive? err_buf_raw += err_buf.read_available end flush_pending_writes(ready_writers || []) end rescue # rubocop:disable Style/RescueStandardError send_error(400, err_buf_raw) unless @started ensure [out_buff, err_buf, in_buf].each(&:close) end
key_file()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 244 def key_file @key_file ||= Proxy::RemoteExecution::Ssh.private_key_file end
params()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 240 def params @params ||= MultiJson.load(@env["rack.input"].read) end
proxy_data(out_buf, in_buf)
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 208 def proxy_data(out_buf, in_buf) { out_buf => buf_socket, buf_socket => in_buf }.each do |src, dst| dst.enqueue(src.read_available) if src.available.positive? dst.close if src.closed? end end
runner_params()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 267 def runner_params ret = { secrets: {} } ret[:secrets][:ssh_password] = params["ssh_password"] if params["ssh_password"] ret[:secrets][:key_passphrase] = params["ssh_key_passphrase"] if params["ssh_key_passphrase"] ret[:ssh_port] = params["ssh_port"] if params["ssh_port"] ret[:ssh_user] = params["ssh_user"] # For compatibility only ret[:script] = nil ret[:hostname] = host ret end
script_runner()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 260 def script_runner @script_runner ||= Proxy::RemoteExecution::Ssh::Runners::ScriptRunner.build( runner_params, suspended_action: nil ) end
send_error(code, msg)
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 232 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) buf_socket.send_pending end
send_start()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 221 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") buf_socket.send_pending end end
ssh_on_socket()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 141 def ssh_on_socket with_error_handling { system_ssh_loop } end
system_ssh_loop()
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 162 def system_ssh_loop in_read, in_write = IO.pipe out_read, out_write = IO.pipe err_read, err_write = IO.pipe pid = spawn(*script_runner.send(:get_args, command), :in => in_read, :out => out_write, :err => err_write) [in_read, out_write, err_write].each(&:close) send_start # Not SSL buffer, but the interface kinda matches out_buf = MiniSSLBufferedSocket.new(out_read) err_buf = MiniSSLBufferedSocket.new(err_read) in_buf = MiniSSLBufferedSocket.new(in_write) inner_system_ssh_loop out_buf, err_buf, in_buf, pid end
with_error_handling() { || ... }
click to toggle source
# File lib/smart_proxy_remote_execution_ssh/cockpit.rb, line 145 def with_error_handling yield 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