em-wssh 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ef81d191ad0809a51504e1f789f5e40f6c882be
4
- data.tar.gz: da12d0d652fccf49292e6ee59917b607694d7af6
3
+ metadata.gz: bd71c6b444c01d0a53369ee3a19b4e249117fa87
4
+ data.tar.gz: a0e23c5ad1d09084ae419ff505358ca87094aad1
5
5
  SHA512:
6
- metadata.gz: 3ed9868c3ed61ce056cfdc261a391ce2f8b5607fb8ff8cc99efe94babf248e316698a988ef97f160e2cf7e1f0213503956e90d27f7f73702f3f181d90470e82f
7
- data.tar.gz: d60cee8efb8911ce45bfb880e036dff03ba8e65dd334e3c6ccf007de95248e8cd161c473384a6b18aff20b4ce31cad1429581079373e5cd7156a5df1eeefa839
6
+ metadata.gz: 3e807d7f77ffaed65ce0e065f2266735fe3290e2e01bef37e83ebcdaaa81c99bfb3ce599ab12f7751d11f726c3693b4023210b7ec78621e4f825e90b4013b6f5
7
+ data.tar.gz: a4576f6827cd4722b4a7fb64f54558386a4c4bab8ece93db875239ce4150ea086e9c23b3287b3b6171a078c57d58c5b57218773d561de51f5617b94a9a964085
data/README.md CHANGED
@@ -34,6 +34,23 @@ Single command `wssh` is exported. Sometimes it should be `bundle exec wssh`.
34
34
 
35
35
  To run WSSH server say `wssh server`.
36
36
 
37
+ You can set some options for server, ie:
38
+
39
+ Most useful option is `--base=path` (or `-b`). It set path, where server files stored.
40
+ By default this path is where gem installed. To use current directory say `wssh server -b.`
41
+
42
+ This `base` path is used to locate `hosts.yml` file, which maps host requested by user to real servers.
43
+ See [sample](hosts.yml). One can define direct map or regexp-style mapping (denoted by // with optional //i).
44
+ If multiple regexps match user host, the last one wins.
45
+ To disable connecting to host (or all hosts for regexp) map it to null or false.
46
+
47
+ The `base` also is root for log file and pid file, created by server.
48
+
49
+ Parameters `--listen=port` (`-l`) and `--all` (`-a`) define on what address server will listen.
50
+ By default it is `localhost:4567`.
51
+
52
+ Parameter `--daemon` will force server to go in background.
53
+
37
54
  ### nginx
38
55
 
39
56
  Directly exposing WSSH server to Internet is not a good idea.
@@ -77,6 +94,11 @@ x=Net::SSH.start 'sshd.local', 'root',
77
94
  puts x.exec! 'hostname'
78
95
  ```
79
96
 
97
+ Proxy allows the same command line parameters as server.
98
+ For proxy parameter `--ping` may be useful,
99
+ it forces proxy to periodically send Websocket ping packets to server
100
+ for nginx to not drop connection on timeout.
101
+
80
102
  ## API
81
103
 
82
104
  WSSH server, client or proxy can be start programmaticaly:
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "em-websocket" # server side
22
22
  spec.add_dependency "faye-websocket" # client side
23
+ spec.add_dependency "openssl-win-root" if Gem.win_platform?
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.3"
25
26
  spec.add_development_dependency "rake"
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Wssh
3
- VERSION = "0.6.0"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
@@ -1,4 +1,5 @@
1
1
  require_relative 'service'
2
+ require_relative 'uri'
2
3
 
3
4
  module EventMachine::Wssh
4
5
  module Connect
@@ -167,6 +168,7 @@ Simple HTTP CONNECT proxy to WSSH daemon
167
168
  end
168
169
 
169
170
  def self.listen!
171
+ options[:uri]=TLS.wrap options[:uri]
170
172
  conn=EM.start_server options[:host], options[:port], Http
171
173
  options[:onlisten].call Socket.unpack_sockaddr_in(EM.get_sockname conn)[0] if options[:onlisten]
172
174
  end
@@ -5,11 +5,9 @@ class Ephemeral
5
5
  extend Service
6
6
 
7
7
  @options={
8
- tlswrap: true,
9
8
  base: '.',
10
9
  pid: 'tmp/pids/ephemeral.pid',
11
10
  log: 'log/ephemeral.log',
12
- tls: 'log/tls.log',
13
11
  }
14
12
 
15
13
  %i(log options mkdir).each do |meth|
@@ -33,24 +31,7 @@ class Ephemeral
33
31
  sock.gets.to_i
34
32
  end
35
33
 
36
- def tlswrap uri
37
- return uri unless options[:tlswrap] and Gem.win_platform?
38
- require 'uri'
39
- z = URI uri
40
- return uri unless %w(wss https).include? z.scheme
41
- log "Running TLS Wrapper..."
42
- spawn 'node', '.', myport.to_s, z.host,
43
- chdir: __dir__,
44
- %i(out err)=>File.open(mkdir(:tls), 'a')
45
- z.scheme='ws'
46
- z.host='localhost'
47
- z.port=rport
48
- z.to_s
49
- end
50
-
51
34
  def allocate uri
52
- uri = tlswrap uri
53
-
54
35
  log "Running WSSH proxy..."
55
36
  spawn *%w(bundle exec wssh ephemeral), myport.to_s, uri,
56
37
  %i(out err)=>File.open(mkdir(:log), 'a')
@@ -6,7 +6,7 @@ module Service
6
6
 
7
7
  def log *msg
8
8
  msg.unshift "[#{Time.now}]"
9
- puts msg*' '
9
+ print msg*' '+"\n"
10
10
  end
11
11
 
12
12
  def helptions
@@ -0,0 +1,110 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'openssl/win/root' if Gem.win_platform?
4
+
5
+ require_relative 'service'
6
+
7
+ module EventMachine::Wssh
8
+ class TLS
9
+ extend Service
10
+
11
+ Chunk=0x10000
12
+
13
+ def self.run! host
14
+ @@host=host
15
+ s=TCPServer.new '127.0.0.1', 0
16
+ log "WSTunnel on #{s.addr} -> #{host}"
17
+ Thread.new do
18
+ new s.accept while true
19
+ end
20
+ s.addr[1]
21
+ end
22
+
23
+ def self.count
24
+ @n||=0
25
+ @n+=1
26
+ end
27
+
28
+ def initialize client
29
+ @count=self.class.count
30
+ @client=client
31
+ @t1=Thread.new{cloop!}
32
+ end
33
+
34
+ def log *msg
35
+ self.class.log "<&#{@count}>", *msg
36
+ end
37
+
38
+ def cloop!
39
+ begin
40
+ log "Connected from", @client.peeraddr
41
+ cloop
42
+ rescue=>e
43
+ log "Client error", e
44
+ ensure
45
+ log "Client disconnected"
46
+ @client.close
47
+ @t2.exit if @t2
48
+ end
49
+ end
50
+
51
+ def headerz
52
+ r=[]
53
+ until @client.eof
54
+ s=@client.gets.strip
55
+ break if 0==s.length
56
+ r << s
57
+ end
58
+ r
59
+ end
60
+
61
+ def headerz! headers
62
+ return headers if headers.length<1
63
+ verb=headers.shift
64
+ [verb]+
65
+ %w(Host Origin).map{|h| "#{h}: #{@@host}"}+
66
+ headers.reject{|h| /^(?:host|origin):/i.match h}
67
+ end
68
+
69
+ def connect!
70
+ srv=Socket.tcp @@host, 443
71
+ ctx=OpenSSL::SSL::SSLContext.new
72
+ ctx.set_params verify_mode: OpenSSL::SSL::VERIFY_PEER
73
+ srv=OpenSSL::SSL::SSLSocket.new srv, ctx
74
+ srv.hostname=@@host if srv.respond_to? :hostname=
75
+ srv.connect
76
+ srv
77
+ end
78
+
79
+ def cloop
80
+ h=headerz! headerz
81
+ if h.length<1
82
+ @client.write "HTTP/1.0 500 Invalid request\r\n"
83
+ return
84
+ end
85
+ @headers=h
86
+ @server=connect!
87
+ @t2=Thread.new{sloop!}
88
+ @server.write @client.readpartial Chunk until @client.eof
89
+ end
90
+
91
+ def sloop!
92
+ begin
93
+ log "Connected to server;", "Verify=#{@server.verify_result}"
94
+ sloop
95
+ rescue=>e
96
+ log "Server error", e
97
+ ensure
98
+ log "Server disconnected"
99
+ @server.close
100
+ @t1.exit
101
+ end
102
+ end
103
+
104
+ def sloop
105
+ @server.write @headers*"\r\n"+"\r\n"*2
106
+ @headers=nil
107
+ @client.write @server.readpartial Chunk until @server.eof
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,20 @@
1
+ require 'uri'
2
+
3
+ require_relative '../wssh'
4
+
5
+ module EventMachine::Wssh
6
+ class TLS
7
+
8
+ def self.wrap uri
9
+ return uri unless Gem.win_platform?
10
+ z = URI uri
11
+ return uri unless %w(wss https).include? z.scheme
12
+ require_relative 'tls'
13
+ z.port=TLS.run! z.host
14
+ z.scheme='ws'
15
+ z.host='localhost'
16
+ z.to_s
17
+ end
18
+
19
+ end
20
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-wssh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stas Ukolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-11 00:00:00.000000000 Z
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-websocket
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: openssl-win-root
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -90,9 +104,10 @@ files:
90
104
  - lib/em/wssh/ephemeral.rb
91
105
  - lib/em/wssh/exe.rb
92
106
  - lib/em/wssh/help.rb
93
- - lib/em/wssh/index.js
94
107
  - lib/em/wssh/server.rb
95
108
  - lib/em/wssh/service.rb
109
+ - lib/em/wssh/tls.rb
110
+ - lib/em/wssh/uri.rb
96
111
  - lib/em/wssh/version.rb
97
112
  - nginx/ssh
98
113
  homepage: https://github.com/ukoloff/em-wssh
@@ -1,212 +0,0 @@
1
- //
2
- // Simple stunnel for Websocket
3
- //
4
- net = require('net')
5
- ssl = require('tls')
6
-
7
- if(4!=process.argv.length) help()
8
-
9
- var Host = process.argv[3]
10
-
11
- net.createServer(Req)
12
- .listen(0, On)
13
-
14
- var
15
- myport,
16
- Count=0,
17
- _log=log
18
-
19
- function help()
20
- {
21
- path = require('path')
22
- log('Usage:', path.basename(__filename), 'port host')
23
- process.exit()
24
- }
25
-
26
- function On()
27
- {
28
- myport = this.address().port;
29
- log("Listening", this.address())
30
- net.connect(process.argv[2], 'localhost')
31
- .on('connect', pConnect)
32
- .on('end', pEnd)
33
- .on('error', pError)
34
- }
35
-
36
- function pConnect()
37
- {
38
- log('Connected to parent')
39
- this.write(''+myport+'\n')
40
- }
41
-
42
- function pEnd()
43
- {
44
- log('Parent closed')
45
- process.exit()
46
- }
47
-
48
- function pError(err)
49
- {
50
- log('Parent error', err)
51
- process.exit()
52
- }
53
-
54
- function Req(conn)
55
- {
56
- var
57
- No=++Count, hs=[], prolog, body, tls
58
-
59
- function log()
60
- {
61
- _log.apply(this, ['<'+No+'>'].concat([].slice.call(arguments)))
62
- }
63
-
64
- log("Client connected from", conn.remoteAddress+':'+conn.remotePort)
65
-
66
- conn
67
- .on('readable', cRead)
68
- .on('end', cClose)
69
- .on('error', cError)
70
-
71
- function cRead()
72
- {
73
- var x
74
- while(null!=(x=this.read()))
75
- body ? queue(x) : headers(x)
76
- }
77
-
78
- function cClose()
79
- {
80
- log('Client disconnected')
81
- bye()
82
- }
83
-
84
- function cError(e)
85
- {
86
- log('Client error', e)
87
- bye()
88
- }
89
-
90
- function bye()
91
- {
92
- conn.end()
93
- if(tls) tls.end()
94
- }
95
-
96
- function queue(data)
97
- {
98
- if(Array.isArray(body))
99
- body.push(data)
100
- else
101
- send(data)
102
- }
103
-
104
- function headers(data)
105
- {
106
- prolog = prolog ? Buffer.concat([prolog, data]) : data
107
- while(splitHdrs()){}
108
- }
109
-
110
- function splitHdrs()
111
- {
112
- for(var cr, L=prolog.length, i=0; i<L; i++)
113
- if(10==prolog[i])
114
- {
115
- L = prolog
116
- prolog = prolog.slice(i+1)
117
- header(L.slice(0, i-(cr ? 1 : 0)).toString())
118
- return true
119
- }
120
- else
121
- cr = 13==prolog[i]
122
- }
123
-
124
- function header(line)
125
- {
126
- if(!line.length)
127
- tlsConnect()
128
- else
129
- hs.push(line)
130
- }
131
-
132
- function tlsConnect()
133
- {
134
- body=[prolog]
135
- prolog=new Buffer(0)
136
- patchHeaders()
137
- tls = ssl.connect({
138
- servername: Host,
139
- host: Host,
140
- port: 443
141
- })
142
- tls
143
- .on('secureConnect', tConnect)
144
- .on('readable', tData)
145
- .on('end', tEnd)
146
- .on('error', tError)
147
- }
148
-
149
- function patchHeaders()
150
- {
151
- if(!hs.length) return
152
- var verb = hs.shift()
153
- hs=['Host', 'Origin']
154
- .map(hostHeader)
155
- .concat(hs.filter(filterHeader))
156
- hs.unshift(verb)
157
- }
158
-
159
- function send(data)
160
- {
161
- if(data.length)
162
- tls.write(data)
163
- }
164
-
165
- function tConnect()
166
- {
167
- log('TLS connected', 'Auth='+this.authorized,'CN='+this.getPeerCertificate().subject.CN)
168
- send(hs.join('\r\n')+'\r\n\r\n')
169
- body.forEach(send)
170
- body = true
171
- }
172
-
173
- function tData()
174
- {
175
- var x
176
- while(null!=(x=this.read()))
177
- if(x.length) conn.write(x)
178
- }
179
-
180
- function tError(err)
181
- {
182
- log('TLS error', err)
183
- bye()
184
- }
185
-
186
- function tEnd()
187
- {
188
- log('TLS closed')
189
- bye()
190
- }
191
- }
192
-
193
- function hostHeader(s)
194
- {
195
- return s+': '+Host
196
- }
197
-
198
- function filterHeader(s)
199
- {
200
- return !/^(?:host|origin):/i.test(s)
201
- }
202
-
203
- function log()
204
- {
205
- var
206
- d = new Date()
207
- console.info.apply(
208
- console,
209
- ['['+d.toISOString().replace('T', ' ').replace(/Z$/, '')
210
- +d.toTimeString().split(/\s+/)[1].replace(/^\w+/, ' ')+']']
211
- .concat([].slice.call(arguments)))
212
- }