daytona 0.199.0 → 0.200.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 +4 -4
- data/lib/daytona/common/socketio_client.rb +77 -6
- data/lib/daytona/sdk/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de70d9e56ce97468d9ef3bcf8c7dbe0b70f74ee7e64a7ac1d0e8642857a3c257
|
|
4
|
+
data.tar.gz: 3cec447b03a65f42bcd84c49e719cd8771a1404f496384afd5e797b429e47fb1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 91c94980be6003996bbbf4b447b170e9165cbc3a47e78c47f91886f28accd5f7b083b04903fc59dede9bbe56ccdcaf9957da73e07202e762ca928203274b2c8b
|
|
7
|
+
data.tar.gz: db39e610c03a011d4c2468475393ca7b7bd17cc1087dbb0edd46c9fc6959da18e0ca61d861485ca0b27faa8ccb4061f631ec44c97d90d8e6bea7db75317db4e3
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
require 'timeout'
|
|
7
7
|
|
|
8
|
+
require 'openssl'
|
|
8
9
|
require 'websocket-client-simple'
|
|
9
10
|
require 'json'
|
|
10
11
|
require 'uri'
|
|
@@ -52,6 +53,8 @@ module Daytona
|
|
|
52
53
|
@connected = false
|
|
53
54
|
@mutex = Mutex.new
|
|
54
55
|
@write_mutex = Mutex.new
|
|
56
|
+
@verify_mutex = Mutex.new
|
|
57
|
+
@tls_verified = nil
|
|
55
58
|
@health_thread = nil
|
|
56
59
|
@ping_interval = 25
|
|
57
60
|
@ping_timeout = 20
|
|
@@ -67,13 +70,24 @@ module Daytona
|
|
|
67
70
|
ws_url = build_ws_url
|
|
68
71
|
connected_queue = Queue.new
|
|
69
72
|
|
|
73
|
+
# Reset per-connection TLS state so a reconnect re-verifies its new socket
|
|
74
|
+
# instead of reusing a cached result from the previous connection.
|
|
75
|
+
@verify_mutex.synchronize { @tls_verified = nil }
|
|
76
|
+
|
|
70
77
|
# Capture self because websocket-client-simple uses instance_exec for callbacks
|
|
71
78
|
client = self
|
|
72
79
|
|
|
73
80
|
# Register callbacks inside the connect block so they are installed before the
|
|
74
81
|
# socket dials — otherwise the Engine.IO handshake can arrive before :message
|
|
75
82
|
# is bound, stalling the connection into a polling fallback/timeout.
|
|
76
|
-
|
|
83
|
+
#
|
|
84
|
+
# verify_mode is passed explicitly: websocket-client-simple builds its own
|
|
85
|
+
# SSLContext and defaults to no certificate verification (VERIFY_NONE).
|
|
86
|
+
@ws = WebSocket::Client::Simple.connect(ws_url, verify_mode: OpenSSL::SSL::VERIFY_PEER) do |ws|
|
|
87
|
+
# Capture the client before its reader thread starts so #verify_tls_peer!
|
|
88
|
+
# can reach the SSL socket even when a frame arrives before .connect returns.
|
|
89
|
+
@ws = ws
|
|
90
|
+
|
|
77
91
|
ws.on :message do |msg|
|
|
78
92
|
client.send(:handle_raw_message, msg.data.to_s, connected_queue)
|
|
79
93
|
end
|
|
@@ -104,15 +118,26 @@ module Daytona
|
|
|
104
118
|
end
|
|
105
119
|
end
|
|
106
120
|
|
|
107
|
-
# Wait for connection with timeout
|
|
108
121
|
result = nil
|
|
109
122
|
begin
|
|
110
|
-
Timeout.timeout(@connect_timeout)
|
|
123
|
+
Timeout.timeout(@connect_timeout) do
|
|
124
|
+
ensure_tls_verified!(connected_queue)
|
|
125
|
+
result = connected_queue.pop
|
|
126
|
+
if result == :open
|
|
127
|
+
send_connect_auth
|
|
128
|
+
result = connected_queue.pop
|
|
129
|
+
end
|
|
130
|
+
end
|
|
111
131
|
rescue Timeout::Error
|
|
112
132
|
close
|
|
113
133
|
raise "WebSocket connection timed out after #{@connect_timeout}s"
|
|
114
134
|
end
|
|
115
135
|
|
|
136
|
+
if result.is_a?(OpenSSL::SSL::SSLError)
|
|
137
|
+
close
|
|
138
|
+
raise result
|
|
139
|
+
end
|
|
140
|
+
|
|
116
141
|
if result != :connected
|
|
117
142
|
close
|
|
118
143
|
raise "WebSocket connection failed: #{result}"
|
|
@@ -141,6 +166,46 @@ module Daytona
|
|
|
141
166
|
|
|
142
167
|
private
|
|
143
168
|
|
|
169
|
+
# Verify the TLS peer's hostname exactly once, before any inbound frame is
|
|
170
|
+
# processed and before the auth frame is sent. websocket-client-simple
|
|
171
|
+
# verifies the certificate chain (VERIFY_PEER) during the handshake but never
|
|
172
|
+
# checks that the certificate matches the host, and its reader thread starts
|
|
173
|
+
# before #connect regains control — so a certificate that is valid for the
|
|
174
|
+
# wrong host could otherwise inject Socket.IO events (and receive the CONNECT
|
|
175
|
+
# auth frame) before verification runs. Gating both the reader path and the
|
|
176
|
+
# connect path guarantees no frame reaches the dispatcher, and no credentials
|
|
177
|
+
# are sent, until the peer is verified.
|
|
178
|
+
# @return [Boolean] true when verified (or plaintext ws://, no TLS peer to
|
|
179
|
+
# check); false on failure, after signalling the connecting thread.
|
|
180
|
+
def ensure_tls_verified!(connected_queue)
|
|
181
|
+
@verify_mutex.synchronize do
|
|
182
|
+
return @tls_verified unless @tls_verified.nil?
|
|
183
|
+
|
|
184
|
+
begin
|
|
185
|
+
verify_tls_peer!
|
|
186
|
+
@tls_verified = true
|
|
187
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
188
|
+
@tls_verified = false
|
|
189
|
+
@mutex.synchronize { @connected = false }
|
|
190
|
+
connected_queue&.push(e)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
@tls_verified
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def verify_tls_peer!
|
|
198
|
+
socket = @ws&.instance_variable_get(:@socket)
|
|
199
|
+
return unless socket.is_a?(OpenSSL::SSL::SSLSocket)
|
|
200
|
+
|
|
201
|
+
socket.post_connection_check(URI.parse(@api_url).host)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def send_connect_auth
|
|
205
|
+
auth = JSON.generate({ token: @token })
|
|
206
|
+
send_raw("#{EIO_MESSAGE}#{SIO_CONNECT}#{auth}")
|
|
207
|
+
end
|
|
208
|
+
|
|
144
209
|
def build_ws_url
|
|
145
210
|
parsed = URI.parse(@api_url)
|
|
146
211
|
ws_scheme = parsed.scheme == 'https' ? 'wss' : 'ws'
|
|
@@ -160,6 +225,11 @@ module Daytona
|
|
|
160
225
|
def handle_raw_message(raw, connected_queue)
|
|
161
226
|
return if raw.nil? || raw.empty?
|
|
162
227
|
|
|
228
|
+
# Drop every frame from a peer whose hostname is not yet verified. The
|
|
229
|
+
# reader thread can run before #connect verifies, so this is the gate that
|
|
230
|
+
# stops a wrong-host peer from injecting events or eliciting the auth frame.
|
|
231
|
+
return unless ensure_tls_verified!(connected_queue)
|
|
232
|
+
|
|
163
233
|
# Track all server activity for health monitoring.
|
|
164
234
|
# If the server stops sending ANY data (pings, events, etc.)
|
|
165
235
|
# the health monitor will detect the dead connection.
|
|
@@ -175,9 +245,10 @@ module Daytona
|
|
|
175
245
|
rescue JSON::ParserError
|
|
176
246
|
# Use default ping interval
|
|
177
247
|
end
|
|
178
|
-
#
|
|
179
|
-
|
|
180
|
-
|
|
248
|
+
# Signal the connecting thread instead of sending the CONNECT auth frame
|
|
249
|
+
# here: the token must not leave this process until the connecting thread
|
|
250
|
+
# has completed TLS peer verification (see #connect).
|
|
251
|
+
connected_queue&.push(:open)
|
|
181
252
|
|
|
182
253
|
when EIO_PING
|
|
183
254
|
# Server heartbeat — respond immediately with PONG
|
data/lib/daytona/sdk/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: daytona
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.200.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daytona Platforms Inc.
|
|
@@ -85,42 +85,42 @@ dependencies:
|
|
|
85
85
|
requirements:
|
|
86
86
|
- - '='
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: 0.
|
|
88
|
+
version: 0.200.0
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - '='
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 0.
|
|
95
|
+
version: 0.200.0
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
97
|
name: daytona_api_client
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - '='
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 0.
|
|
102
|
+
version: 0.200.0
|
|
103
103
|
type: :runtime
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - '='
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: 0.
|
|
109
|
+
version: 0.200.0
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: daytona_toolbox_api_client
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
|
114
114
|
- - '='
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: 0.
|
|
116
|
+
version: 0.200.0
|
|
117
117
|
type: :runtime
|
|
118
118
|
prerelease: false
|
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
120
|
requirements:
|
|
121
121
|
- - '='
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: 0.
|
|
123
|
+
version: 0.200.0
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
125
|
name: dotenv
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|