em-wssh 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- }