Merge branch 'ssh-gateway' into 'master'

SSH Proxy

Closes #37

See merge request honeyryderchuck/httpx!33
This commit is contained in:
HoneyryderChuck 2018-12-30 01:23:26 +00:00
commit bef4315ded
21 changed files with 252 additions and 12 deletions

View File

@ -19,6 +19,14 @@ platform :mri do
gem "brotli", require: false
gem "pry-byebug", require: false
gem "benchmark-ips", require: false
gem "net-ssh-gateway", require: false
gem "ed25519", require: false
gem "bcrypt_pbkdf", require: false
end
platform :mri_21 do
gem "rbnacl", require: false
gem "rbnacl-libsodium", require: false
end
# gem "guard-rspec", :require => false
# gem "nokogiri", :require => false

View File

@ -9,6 +9,7 @@ services:
- HTTPX_SOCKS4_PROXY=socks4://socksproxy:8080
- HTTPX_SOCKS4A_PROXY=socks4a://socksproxy:8080
- HTTPX_SOCKS5_PROXY=socks5://socksproxy:8080
- HTTPX_SSH_PROXY=ssh://sshproxy:22
- PARALLEL=1
- CI=1
- JEKYLL_ENV=production
@ -20,6 +21,7 @@ services:
depends_on:
- httpproxy
- socksproxy
- sshproxy
- nghttp2
volumes:
- ./:/home
@ -28,6 +30,15 @@ services:
links:
- "nghttp2:another"
sshproxy:
build:
context: .
dockerfile: ./test/support/ssh/Dockerfile
volumes:
- ./test/support/ssh:/config
depends_on:
- nghttp2
socksproxy:
image: qautomatron/docker-3proxy
ports:

View File

@ -77,11 +77,25 @@ module HTTPX
@write_buffer = Buffer.new(BUFFER_SIZE)
@pending = []
on(:error) { |ex| on_error(ex) }
transition(:idle)
if @options.io
# if there's an already open IO, get its
# peer address, and force-initiate the parser
transition(:already_open)
@io = IO.registry(@type).new(@uri, nil, @options)
parser
else
transition(:idle)
end
end
# this is a semi-private method, to be used by the resolver
# to initiate the io object.
def addresses=(addrs)
@io = IO.registry(@type).new(@uri, addrs, @options)
@io ||= IO.registry(@type).new(@uri, addrs, @options) # rubocop:disable Naming/MemoizedInstanceVariableName
end
def addresses
@io && @io.addresses
end
def mergeable?(addresses)
@ -320,7 +334,6 @@ module HTTPX
def transition(nextstate)
case nextstate
# when :idle
when :idle
@error_response = nil
@timeout_threshold = @options.timeout.connect_timeout
@ -343,6 +356,11 @@ module HTTPX
@io.close
@read_buffer.clear
when :already_open
nextstate = :open
send_pending
@timeout_threshold = @options.timeout.operation_timeout
@timeout = @timeout_threshold
end
@state = nextstate
rescue Errno::EHOSTUNREACH

View File

@ -120,12 +120,21 @@ module HTTPX
end
def register_channel(channel)
@timeout.transition(:idle)
monitor = @selector.register(channel, :w)
monitor = if channel.state == :open
# if open, an IO was passed upstream, therefore
# consider it connected already.
@connected_channels += 1
@selector.register(channel, :rw)
else
@selector.register(channel, :w)
end
monitor.value = channel
channel.on(:close) do
unregister_channel(channel)
end
return if channel.state == :open
@timeout.transition(:idle)
end
def unregister_channel(channel)

View File

@ -17,24 +17,25 @@ module HTTPX
@state = :idle
@hostname = uri.host
@addresses = addresses
@ip_index = @addresses.size - 1
@options = Options.new(options)
@fallback_protocol = @options.fallback_protocol
@port = uri.port
if @options.io
@io = case @options.io
when Hash
@ip = @addresses[@ip_index]
@options.io[@ip] || @options.io["#{@ip}:#{@port}"]
@options.io[uri.authority]
else
@ip = @hostname
@options.io
end
_, _, _, @ip = @io.addr
@addresses ||= [@ip]
@ip_index = @addresses.size - 1
unless @io.nil?
@keep_open = true
@state = :connected
end
else
@ip_index = @addresses.size - 1
@ip = @addresses[@ip_index]
end
@io ||= build_socket

View File

@ -0,0 +1,75 @@
# frozen_string_literal: true
require "httpx/plugins/proxy"
module HTTPX
module Plugins
module Proxy
module SSH
def self.load_dependencies(_klass, *)
# klass.plugin(:proxy)
require "net/ssh/gateway"
end
module InstanceMethods
def with_proxy(*args)
branch(default_options.with_proxy(*args))
end
private
def __send_reqs(*requests, **options)
ssh_options = @options.proxy
ssh_uris = ssh_options.delete(:uri)
ssh_username = ssh_options.delete(:username)
ssh_uri = URI.parse(ssh_uris.shift)
ssh_options[:port] ||= ssh_uri.port || 22
if @options.debug
ssh_options[:verbose] = @options.debug_level == 2 ? :debug : :info
end
request_uri = URI(requests.first.uri)
@_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
begin
@_gateway.open(request_uri.host, request_uri.port) do |local_port|
io = build_gateway_socket(local_port, request_uri)
super(*requests, **options.merge(io: io))
end
ensure
@_gateway.shutdown!
end
end
def build_gateway_socket(port, request_uri)
case request_uri.scheme
when "https"
ctx = OpenSSL::SSL::SSLContext.new
ctx_options = SSL::TLS_OPTIONS.merge(@options.ssl)
ctx.set_params(ctx_options) unless ctx_options.empty?
sock = TCPSocket.open("localhost", port)
io = OpenSSL::SSL::SSLSocket.new(sock, ctx)
io.hostname = request_uri.host
io.sync_close = true
io.connect
io.post_connection_check(request_uri.host) if ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
io
when "http"
TCPSocket.open("localhost", port)
else
raise Error, "unexpected scheme: #{request_uri.scheme}"
end
end
end
module OptionsMethods
def self.included(klass)
super
klass.def_option(:proxy) do |pr|
Hash[pr]
end
end
end
end
end
register_plugin :"proxy/ssh", Proxy::SSH
end
end

View File

@ -36,7 +36,10 @@ module HTTPX
end
def early_resolve(channel, hostname: channel.uri.host)
addresses = ip_resolve(hostname) || Resolver.cached_lookup(hostname) || system_resolve(hostname)
addresses = channel.addresses ||
ip_resolve(hostname) ||
Resolver.cached_lookup(hostname) ||
system_resolve(hostname)
return unless addresses
emit_addresses(channel, addresses)

View File

@ -30,7 +30,10 @@ module HTTPX
def <<(channel)
hostname = channel.uri.host
addresses = ip_resolve(hostname) || system_resolve(hostname) || @resolver.getaddresses(hostname)
addresses = channel.addresses ||
ip_resolve(hostname) ||
system_resolve(hostname) ||
@resolver.getaddresses(hostname)
return emit_resolve_error(channel, hostname) if addresses.empty?
emit_addresses(channel, addresses)

View File

@ -22,7 +22,7 @@ if [[ $RET = 0 ]] && [[ ${RUBY_VERSION:0:3} = "2.6" ]]; then
RUBYOPT="--jit" bundle exec rake test:ci
fi
if [[ $RET = 0 ]] && [[ ${RUBY_VERSION:0:3} = "2.5" ]]; then
if [[ $RET = 0 ]] && [[ ${RUBY_VERSION:0:3} = "2.6" ]]; then
bundle exec rake website_rdoc && \
cd www && bundle install && \
bundle exec jekyll build -d public

View File

@ -44,6 +44,14 @@ module ProxyHelper
end)
end
def ssh_proxy
Array(ENV["HTTPX_SSH_PROXY"] || begin
http_proxies_list.select { |_, _, https| https }.map do |ip, port, _|
"ssh://#{ip}:#{port}"
end
end)
end
def http_proxies_list
proxies_list(parse_http_proxies)
.map do |line|

View File

@ -36,6 +36,18 @@ module Requests
verify_status(response, 200)
verify_body_length(response)
end
def test_plugin_ssh_proxy
client = HTTPX.plugin(:"proxy/ssh").with_proxy(uri: ssh_proxy,
username: "root",
auth_methods: %w[publickey],
host_key: "ssh-rsa",
keys: %w[test/support/ssh/ssh_host_ed25519_key])
uri = build_uri("/get")
response = client.get(uri)
verify_status(response, 200)
verify_body_length(response)
end if ENV.key?("HTTPX_SSH_PROXY")
end
end
end

View File

@ -0,0 +1,19 @@
FROM connesc/ssh-gateway
MAINTAINER Tiago Cardoso <cardoso_tiago@hotmail.com>
RUN apk add --no-cache shadow
RUN echo -e $'\n\
PermitRootLogin prohibit-password\n\
AllowAgentForwarding no\n\
X11Forwarding no\n\
PermitTTY yes\n\
LogLevel VERBOSE\n\
AllowTcpForwarding yes\n\
PermitEmptyPasswords yes\n\
UsePAM no\n\
PasswordAuthentication no\n\
RSAAuthentication yes\n\
PubkeyAuthentication yes\n' >> /etc/ssh/sshd_config
RUN usermod --shell /bin/sh root

View File

@ -0,0 +1,5 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6RapYEVlNX/Yih+Q5VLR8rSidCm9Alhvu49Qs5xiRr+rHij7cSfYWCZn5HCoy4JDpeF2ictWD8o1XTnpUIwqYq2igmrQIjoy+9kx6fQXyfFQZnjvm8VZLpMTjYatRTstGCKEAhdnPlARV9+YKsvBghsplM7w7JHAuhk89lCZAJDJZSw3vtg/IZs2v25vAYD2CWvuNzhh74uzj6HSSkeXas0ICDT5HdKoDvSDN6rDIMBJ4cd8hlqd/kuFb9z2sJMZciEHl94UXjjTY1byA6jlhGjlRLT0dKJZ3JePiHijzuxgI7jtt1lDsVvjlSUNiG70CuSUSdmqUPrWYn05MR0qn root@sshproxy
ssh-dss AAAAB3NzaC1kc3MAAACBAMRor1S/BZzZFPUulMH2AiqN8p2FrllgiSIHCkl3hlpLityiDn/PzFdFOCZkjLsHTAw7KepvcvhHn4OUon+jGnKLEgLEvTkz1bz/k2/m0/MZT+FIYMpSfPXrYutLhiRB7r03oNU1pvCzao2oferfwPYKBRrtbaYhSsqG6qxiyK5HAAAAFQDim+TZZbe/hitGQV2iDbhtcQeTpQAAAIAOGNsqXOLq4aXX8npsxptEtF3p66PRdf07JiM/ZAbZmXW7rHcgaGD4uGd6KEelJ/wGlNWx3s0/uhRp8iA6IezRIh40fRs2lTfhYM7fhGBbayEY8W3AUp/ubepdS2iGblrkZMev0AgPMam+/dv1SHPJ3nrvxWVvYErXhJ9FELqJqwAAAIA9R3TuuB0Gsx0TSN7l+Vu/64ll6YJixoqOSG4YORfA53JGgfq7Kvmmrsc94qJjnfhYrPxIgYgbv7vu6ecbQAv+SWuV+EF27ZjvHP6PnbA6waqyFQXHnVbylq5UVx8tM6zcZ2/kFSPP5dJbl+ulPVdqlER/WYAn1SWl2nIG0Bimcg== root@sshproxy
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCISSdrc7xRNeLIkt4M0cvdq4gsUoLerH/uhkecA6iPKWnnpd0m6MUIgAQe3YP5EIaS2zHYaPqjuxh5iwxMBhfs= root@sshproxy
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrNpVMaIAgnLlgo40ENibW4Wc+pG+36Uo2GiDfIE2LX root@sshproxy
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWHaFVaUDAlDXE9wZCIX3Oe06DtpZN2MujV/Zil3EaUkw1WEs9vKSKdGWd4I4ECRwIeeXjPV2OyEpR9O3w4GkEiu2WSNpJsTNbii4mRh1OnuJRPozM79Uns2z59sQ6LGf09qlnFjgBV/8FJR3qwLIaz34xroxLFFWrv/0dbqVBeVtl5srU2PQyAjGt5vjKPnPIgLZ6TKdlRO4gxhpr+qy75LH9ZPQHJMlZXDsGaOrirkaIzfsmrXFpwNe4kivk8WfkKgqTCRES5XqHsCug4Zh57tqKQEt0qEKEJf5vPLzJK1mXqtmMHRAOhfyLOBflOjQvh4Kh7/uTevez9pdUta1P root@sshproxy

View File

@ -0,0 +1,21 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
NzAAAAgQDEaK9UvwWc2RT1LpTB9gIqjfKdha5ZYIkiBwpJd4ZaS4rcog5/z8xXRTgmZIy7
B0wMOynqb3L4R5+DlKJ/oxpyixICxL05M9W8/5Nv5tPzGU/hSGDKUnz162LrS4YkQe69N6
DVNabws2qNqH3q38D2CgUa7W2mIUrKhuqsYsiuRwAAABUA4pvk2WW3v4YrRkFdog24bXEH
k6UAAACADhjbKlzi6uGl1/J6bMabRLRd6euj0XX9OyYjP2QG2Zl1u6x3IGhg+LhneihHpS
f8BpTVsd7NP7oUafIgOiHs0SIeNH0bNpU34WDO34RgW2shGPFtwFKf7m3qXUtohm5a5GTH
r9AIDzGpvv3b9Uhzyd5678Vlb2BK14SfRRC6iasAAACAPUd07rgdBrMdE0je5flbv+uJZe
mCYsaKjkhuGDkXwOdyRoH6uyr5pq7HPeKiY534WKz8SIGIG7+77unnG0AL/klrlfhBdu2Y
7xz+j52wOsGqshUFx51W8pauVFcfLTOs3Gdv5BUjz+XSW5frpT1XapREf1mAJ9UlpdpyBt
AYpnIAAAH42yZJ+tsmSfoAAAAHc3NoLWRzcwAAAIEAxGivVL8FnNkU9S6UwfYCKo3ynYWu
WWCJIgcKSXeGWkuK3KIOf8/MV0U4JmSMuwdMDDsp6m9y+Eefg5Sif6MacosSAsS9OTPVvP
+Tb+bT8xlP4UhgylJ89eti60uGJEHuvTeg1TWm8LNqjah96t/A9goFGu1tpiFKyobqrGLI
rkcAAAAVAOKb5Nllt7+GK0ZBXaINuG1xB5OlAAAAgA4Y2ypc4urhpdfyemzGm0S0Xenro9
F1/TsmIz9kBtmZdbusdyBoYPi4Z3ooR6Un/AaU1bHezT+6FGnyIDoh7NEiHjR9GzaVN+Fg
zt+EYFtrIRjxbcBSn+5t6l1LaIZuWuRkx6/QCA8xqb792/VIc8neeu/FZW9gSteEn0UQuo
mrAAAAgD1HdO64HQazHRNI3uX5W7/riWXpgmLGio5Ibhg5F8DnckaB+rsq+aauxz3iomOd
+Fis/EiBiBu/u+7p5xtAC/5Ja5X4QXbtmO8c/o+dsDrBqrIVBcedVvKWrlRXHy0zrNxnb+
QVI8/l0luX66U9V2qURH9ZgCfVJaXacgbQGKZyAAAAFQDGA0xquV12BTtbxlxdd7ym/qW0
sAAAAB10aWFnb2NhcmRvc29ATUJQLWRlLVRpYWdvLmxhbgECAwQF
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-dss AAAAB3NzaC1kc3MAAACBAMRor1S/BZzZFPUulMH2AiqN8p2FrllgiSIHCkl3hlpLityiDn/PzFdFOCZkjLsHTAw7KepvcvhHn4OUon+jGnKLEgLEvTkz1bz/k2/m0/MZT+FIYMpSfPXrYutLhiRB7r03oNU1pvCzao2oferfwPYKBRrtbaYhSsqG6qxiyK5HAAAAFQDim+TZZbe/hitGQV2iDbhtcQeTpQAAAIAOGNsqXOLq4aXX8npsxptEtF3p66PRdf07JiM/ZAbZmXW7rHcgaGD4uGd6KEelJ/wGlNWx3s0/uhRp8iA6IezRIh40fRs2lTfhYM7fhGBbayEY8W3AUp/ubepdS2iGblrkZMev0AgPMam+/dv1SHPJ3nrvxWVvYErXhJ9FELqJqwAAAIA9R3TuuB0Gsx0TSN7l+Vu/64ll6YJixoqOSG4YORfA53JGgfq7Kvmmrsc94qJjnfhYrPxIgYgbv7vu6ecbQAv+SWuV+EF27ZjvHP6PnbA6waqyFQXHnVbylq5UVx8tM6zcZ2/kFSPP5dJbl+ulPVdqlER/WYAn1SWl2nIG0Bimcg== root@sshproxy

View File

@ -0,0 +1,9 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQiEkna3O8UTXiyJLeDNHL3auILFKC3
qx/7oZHnAOojylp56XdJujFCIAEHt2D+RCGktsx2Gj6o7sYeYsMTAYX7AAAAuNIMKYjSDC
mIAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCISSdrc7xRNeLIk
t4M0cvdq4gsUoLerH/uhkecA6iPKWnnpd0m6MUIgAQe3YP5EIaS2zHYaPqjuxh5iwxMBhf
sAAAAgaHoa6s+bZ6lwNOCERevXikH8qEXswz1vKwRntqgJVwkAAAAddGlhZ29jYXJkb3Nv
QE1CUC1kZS1UaWFnby5sYW4BAgM=
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCISSdrc7xRNeLIkt4M0cvdq4gsUoLerH/uhkecA6iPKWnnpd0m6MUIgAQe3YP5EIaS2zHYaPqjuxh5iwxMBhfs= root@sshproxy

View File

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDazaVTGiAIJy5YKONBDYm1uFnPqRvt+lKNhog3yBNi1wAAAKBKlP+ZSpT/
mQAAAAtzc2gtZWQyNTUxOQAAACDazaVTGiAIJy5YKONBDYm1uFnPqRvt+lKNhog3yBNi1w
AAAEBv3sCrVqAhwxsnO4hIhfwQbaVTZ/KzRYoHPkzDYpZ9KdrNpVMaIAgnLlgo40ENibW4
Wc+pG+36Uo2GiDfIE2LXAAAAHXRpYWdvY2FyZG9zb0BNQlAtZGUtVGlhZ28ubGFu
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINrNpVMaIAgnLlgo40ENibW4Wc+pG+36Uo2GiDfIE2LX root@sshproxy

View File

@ -0,0 +1,27 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAlh2hVWlAwJQ1xPcGQiF9zntOg7aWTdjLo1f2YpdxGlJMNVhLPbyk
inRlneCOBAkcCHnl4z1djshKUfTt8OBpBIrtlkjaSbEzW4ouJkYdTp7iUT6MzO/VJ7Ns+f
bEOixn9PapZxY4AVf/BSUd6sCyGs9+Ma6MSxRVq7/9HW6lQXlbZebK1Nj0MgIxreb4yj5z
yIC2ekynZUTuIMYaa/qsu+Sx/WT0ByTJWVw7Bmjq4q5GiM37Jq1xacDXuJIr5PFn5CoKkw
kREuV6h7AroOGYee7aikBLdKhChCX+bzy8yStZl6rZjB0QDoX8izgX5To0L4eCoe/7k3r3
s/aXVLWtTwAAA9iMplygjKZcoAAAAAdzc2gtcnNhAAABAQCWHaFVaUDAlDXE9wZCIX3Oe0
6DtpZN2MujV/Zil3EaUkw1WEs9vKSKdGWd4I4ECRwIeeXjPV2OyEpR9O3w4GkEiu2WSNpJ
sTNbii4mRh1OnuJRPozM79Uns2z59sQ6LGf09qlnFjgBV/8FJR3qwLIaz34xroxLFFWrv/
0dbqVBeVtl5srU2PQyAjGt5vjKPnPIgLZ6TKdlRO4gxhpr+qy75LH9ZPQHJMlZXDsGaOri
rkaIzfsmrXFpwNe4kivk8WfkKgqTCRES5XqHsCug4Zh57tqKQEt0qEKEJf5vPLzJK1mXqt
mMHRAOhfyLOBflOjQvh4Kh7/uTevez9pdUta1PAAAAAwEAAQAAAQBtjpZuVXCym/haFcb1
gKfmiEuXCSzNB8onHk1tSwV6plGEJTpUhla3zZdUD8zV2Sgib4R9wg2D5V2ITu9Q+xbp/9
LB+c2GNtM6nbBssoM1G/QkQzhTYT35yZNhIt23by03tMWRvL+HC4fNY3dgGt5hfsmkzLUL
Hn4f6PB8CIKHM0J2wsU0+6NXjEz9JVUydnGcOCQsh2JTT/IFQR1XqR2nt3EK0vP6OMTuFc
CEXqw9a5NxR2cPTh2uCasepTlKtywlM/EuLXA4a67kc3ktR3SF4p9YnQNlzmapPuV+49M1
6xq//fT3Cp/ZcLhdDqiWrlfTHQfQ3KEohT9ZCBBULKWxAAAAgF/Lagq+gY496e5vNxjWed
yw73FrZbDLPXBcee5iaycpQcAPCOllQmO16+IPjyROhE9uOBfu46DdmFXk90C8ecznIvBG
fkS8Dfd35L0ebuzMkq2DDqBHBPcVAAiHk25la3aeYdMOOjy0HXYBOV2mDbjOxH1deuuRYe
AvxSLHrwxuAAAAgQDHk4JkLhAf3sajN1N6yDGyQDRnRm+GtgZFViB917njV/QvKO+xFVNi
2/UXDe+PrwK6s93RSsY0mDHvIaoYNnSr/UVleWkW8seeIepf1z0H/jaPLiXMiR1b2ULQgI
uNX0RW4xxvxPKaKxVdmdDxlabZFaAV2wklNL1AoyU5u/MyRQAAAIEAwI5hICRKiPggvRhl
Qd7rWLslauRJzoMxqCdSMsgXx/ZUVoA2igGJ++ObR3+aLxZ4EDM1dpBwvkomGAUQxqmXv2
v2F2Gskv+QhXcGxls+PYl61++3vDtFd/ku3haMhYBTqJEe0BZGX47vV7zAmViFB8pVESK5
I7GdHtAfXNMgZIMAAAAddGlhZ29jYXJkb3NvQE1CUC1kZS1UaWFnby5sYW4BAgMEBQY=
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCWHaFVaUDAlDXE9wZCIX3Oe06DtpZN2MujV/Zil3EaUkw1WEs9vKSKdGWd4I4ECRwIeeXjPV2OyEpR9O3w4GkEiu2WSNpJsTNbii4mRh1OnuJRPozM79Uns2z59sQ6LGf09qlnFjgBV/8FJR3qwLIaz34xroxLFFWrv/0dbqVBeVtl5srU2PQyAjGt5vjKPnPIgLZ6TKdlRO4gxhpr+qy75LH9ZPQHJMlZXDsGaOrirkaIzfsmrXFpwNe4kivk8WfkKgqTCRES5XqHsCug4Zh57tqKQEt0qEKEJf5vPLzJK1mXqtmMHRAOhfyLOBflOjQvh4Kh7/uTevez9pdUta1P root@sshproxy