Class: DeployAgent::ServerConnection
- Inherits:
-
Object
- Object
- DeployAgent::ServerConnection
- Defined in:
- lib/deploy_agent/server_connection.rb
Overview
The ServerConnection class deals with all communication with the Deploy server
Defined Under Namespace
Classes: ServerDisconnected
Instance Attribute Summary collapse
-
#agent ⇒ Object
readonly
Returns the value of attribute agent.
-
#destination_connections ⇒ Object
readonly
Returns the value of attribute destination_connections.
-
#nio_monitor ⇒ Object
writeonly
Sets the attribute nio_monitor.
Instance Method Summary collapse
- #destination_allowed?(destination) ⇒ Boolean
-
#initialize(agent, server_host, nio_selector, check_certificate = true) ⇒ ServerConnection
constructor
Create a secure TLS connection to the Deploy server.
-
#rx_data ⇒ Object
Receive and process packets from the control server.
-
#send_connection_close(id) ⇒ Object
Notify server of closed connection.
-
#send_connection_error(id, reason) ⇒ Object
Notify server of failed connection.
-
#send_connection_success(id) ⇒ Object
Notify server of successful connection.
-
#send_data(id, data) ⇒ Object
Proxy data (coming from the backend) to the Deploy server.
-
#tx_data ⇒ Object
Called by event loop to send all waiting packets to the Deploy server.
Constructor Details
#initialize(agent, server_host, nio_selector, check_certificate = true) ⇒ ServerConnection
Create a secure TLS connection to the Deploy server
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/deploy_agent/server_connection.rb', line 13 def initialize(agent, server_host, nio_selector, check_certificate=true) @agent = agent @agent.logger.info "Attempting to connect to #{server_host}" @destination_connections = {} @nio_selector = nio_selector # Create a TCP socket to the Deploy server @tcp_socket = TCPSocket.new(server_host, 7777) # Configure an OpenSSL context with server vertification ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = check_certificate ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE # Load the agent certificate and key used to authenticate this agent ctx.cert = OpenSSL::X509::Certificate.new(File.read(CERTIFICATE_PATH)) ctx.key = OpenSSL::PKey::RSA.new(File.read(KEY_PATH)) # Load the Deploy CA used to verify the server ctx.ca_file = CA_PATH # Create the secure connection @socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, ctx) @socket.connect # Check the remote certificate @socket.post_connection_check(server_host) if check_certificate # Create send and receive buffers @tx_buffer = String.new.force_encoding('BINARY') @rx_buffer = String.new.force_encoding('BINARY') @nio_monitor = @nio_selector.register(@tcp_socket, :r) @nio_monitor.value = self @agent.logger.info "Successfully connected to server" rescue => e @agent.logger.info "Something went wrong connecting to server." # Sleep between 10 and 20 seconds random_sleep = rand(10) + 10 @agent.logger.info "#{e.to_s} #{e.}" @agent.logger.info "Retrying in #{random_sleep} seconds." sleep random_sleep retry end |
Instance Attribute Details
#agent ⇒ Object (readonly)
Returns the value of attribute agent.
9 10 11 |
# File 'lib/deploy_agent/server_connection.rb', line 9 def agent @agent end |
#destination_connections ⇒ Object (readonly)
Returns the value of attribute destination_connections.
9 10 11 |
# File 'lib/deploy_agent/server_connection.rb', line 9 def destination_connections @destination_connections end |
#nio_monitor=(value) ⇒ Object (writeonly)
Sets the attribute nio_monitor
10 11 12 |
# File 'lib/deploy_agent/server_connection.rb', line 10 def nio_monitor=(value) @nio_monitor = value end |
Instance Method Details
#destination_allowed?(destination) ⇒ Boolean
113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/deploy_agent/server_connection.rb', line 113 def destination_allowed?(destination) return false unless File.file?(ACCESS_PATH) DeployAgent.allowed_destinations.each do |network| begin return true if IPAddr.new(network).include?(destination) rescue IPAddr::InvalidAddressError # Not a valid IP or netmask, deny and continue end end false end |
#rx_data ⇒ Object
Receive and process packets from the control server
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/deploy_agent/server_connection.rb', line 55 def rx_data # Ensure all received data is read @rx_buffer << @socket.readpartial(10240) while(@socket.pending > 0) @rx_buffer << @socket.readpartial(10240) end # Wait until we have a complete packet of data while @rx_buffer.bytesize >=2 && @rx_buffer.bytesize >= @rx_buffer[0,2].unpack('n')[0] length = @rx_buffer.slice!(0,2).unpack('n')[0] # Extract the packet from the buffered stream packet = @rx_buffer.slice!(0,length-2) # Check what type of packet we have received case packet.bytes[0] when 1 # Process new connection request id = packet[1,2].unpack('n')[0] host, port = packet[3..-1].split('/', 2) @agent.logger.info "[#{id}] Connection request from server: #{host}:#{port}" return send_connection_error(id, "Destination address not allowed") unless destination_allowed?(host) begin # Create conenction to the final destination and save info by id @destination_connections[id] = DestinationConnection.new(host, port, id, @nio_selector, self) rescue => e # Something went wrong, inform the Deploy server @agent.logger.error "An error occurred: #{e.}" @agent.logger.error e.backtrace send_connection_error(id, e.) end when 3 # Process a connection close request id = packet[1,2].unpack('n')[0] if @destination_connections[id] @agent.logger.info "[#{id}] Close requested by server, closing" @destination_connections[id].close else @agent.logger.info "[#{id}] Close requested by server, not open" end when 4 # Data incoming, send it to the backend id = packet[1,2].unpack('n')[0] @agent.logger.debug "[#{id}] #{packet.bytesize} bytes received from server" @destination_connections[id].send_data(packet[3..-1]) when 5 # This is a shutdown request. Disconnect and don't re-attempt connection. @agent.logger.warn "Server rejected connection. Shutting down." @agent.logger.warn packet[1..-1] Process.exit(0) when 6 # This is a shutdown request. Disconnect and don't re-attempt connection. @agent.logger.warn "Server requested reconnect. Closing connection." close end end rescue EOFError, Errno::ECONNRESET close end |
#send_connection_close(id) ⇒ Object
Notify server of closed connection
136 137 138 |
# File 'lib/deploy_agent/server_connection.rb', line 136 def send_connection_close(id) send_packet([3, id].pack('Cn')) end |
#send_connection_error(id, reason) ⇒ Object
Notify server of failed connection
131 132 133 |
# File 'lib/deploy_agent/server_connection.rb', line 131 def send_connection_error(id, reason) send_packet([2, id, 1, reason].pack('CnCa*')) end |
#send_connection_success(id) ⇒ Object
Notify server of successful connection
126 127 128 |
# File 'lib/deploy_agent/server_connection.rb', line 126 def send_connection_success(id) send_packet([2, id, 0].pack('CnC')) end |
#send_data(id, data) ⇒ Object
Proxy data (coming from the backend) to the Deploy server
141 142 143 |
# File 'lib/deploy_agent/server_connection.rb', line 141 def send_data(id, data) send_packet([4, id, data].pack('Cna*')) end |
#tx_data ⇒ Object
Called by event loop to send all waiting packets to the Deploy server
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/deploy_agent/server_connection.rb', line 146 def tx_data bytes_sent = @socket.write_nonblock(@tx_buffer[0,1024]) # Send as much data as possible if bytes_sent >= @tx_buffer.bytesize @tx_buffer = String.new.force_encoding('BINARY') @nio_monitor.interests = :r else # If we didn't manage to send all the data, leave # the remaining data in the send buffer @tx_buffer.slice!(0, bytes_sent) end rescue EOFError, Errno::ECONNRESET close end |