Skip to content

Commit 95fe296

Browse files
CP-54215: Limit to one VNC console session per VM/host
When the `limit_console_sessions` is true. - Enforced a single active VNC console connection per VM/host - Disable connection to websocket Signed-off-by: Stephen Cheng <[email protected]>
1 parent 22336da commit 95fe296

File tree

1 file changed

+80
-18
lines changed

1 file changed

+80
-18
lines changed

ocaml/xapi/console.ml

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,51 @@ type address =
3434

3535
(* console is listening on a Unix domain socket *)
3636

37+
(* This module enforces connection limits for VM consoles.
38+
Depending on configuration, only one active connection per VM (including Dom0) is allowed,
39+
or that connections are unlimited. *)
40+
module Connection_limit = struct
41+
module VMSet = Set.Make (String)
42+
43+
let active_connections : VMSet.t ref = ref VMSet.empty
44+
45+
let mutex = Mutex.create ()
46+
47+
let add vm_id =
48+
Mutex.lock mutex ;
49+
active_connections := VMSet.add vm_id !active_connections ;
50+
Mutex.unlock mutex
51+
52+
let remove vm_id =
53+
Mutex.lock mutex ;
54+
active_connections := VMSet.remove vm_id !active_connections ;
55+
Mutex.unlock mutex
56+
57+
let exists vm_id =
58+
Mutex.lock mutex ;
59+
let present = VMSet.mem vm_id !active_connections in
60+
Mutex.unlock mutex ; present
61+
62+
let can_add vm_id limit_console_sessions =
63+
if not limit_console_sessions then
64+
true
65+
else
66+
not (exists vm_id)
67+
end
68+
69+
let connection_limit_reached __context vm_id =
70+
let pool = Helpers.get_pool ~__context in
71+
let limit_console_sessions =
72+
Db.Pool.get_limit_console_sessions ~__context ~self:pool
73+
in
74+
if not (Connection_limit.can_add vm_id limit_console_sessions) then (
75+
debug
76+
"Console session limit reached: only one active session allowed for VM %s"
77+
vm_id ;
78+
true
79+
) else
80+
false
81+
3782
let string_of_address = function
3883
| Port x ->
3984
"localhost:" ^ string_of_int x
@@ -84,26 +129,43 @@ let address_of_console __context console : address option =
84129
) ;
85130
address_option
86131

87-
let real_proxy __context _ _ vnc_port s =
132+
let real_proxy __context console _ _ vnc_port s =
88133
try
89-
Http_svr.headers s (Http.http_200_ok ()) ;
90-
let vnc_sock =
91-
match vnc_port with
92-
| Port x ->
93-
Unixext.open_connection_fd "127.0.0.1" x
94-
| Path x ->
95-
Unixext.open_connection_unix_fd x
96-
in
97-
(* Unixext.proxy closes fds itself so we must dup here *)
98-
let s' = Unix.dup s in
99-
debug "Connected; running proxy (between fds: %d and %d)"
100-
(Unixext.int_of_file_descr vnc_sock)
101-
(Unixext.int_of_file_descr s') ;
102-
Unixext.proxy vnc_sock s' ;
103-
debug "Proxy exited"
134+
let vm = Db.Console.get_VM ~__context ~self:console in
135+
let vm_id = Ref.string_of vm in
136+
if connection_limit_reached __context vm_id then
137+
Http_svr.headers s (Http.http_503_service_unavailable ())
138+
else (
139+
Http_svr.headers s (Http.http_200_ok ()) ;
140+
let vnc_sock =
141+
match vnc_port with
142+
| Port x ->
143+
Unixext.open_connection_fd "127.0.0.1" x
144+
| Path x ->
145+
Unixext.open_connection_unix_fd x
146+
in
147+
(* Unixext.proxy closes fds itself so we must dup here *)
148+
let s' = Unix.dup s in
149+
debug "Connected; running proxy (between fds: %d and %d)"
150+
(Unixext.int_of_file_descr vnc_sock)
151+
(Unixext.int_of_file_descr s') ;
152+
Connection_limit.add vm_id ;
153+
Unixext.proxy vnc_sock s' ;
154+
Connection_limit.remove vm_id ;
155+
debug "Proxy exited"
156+
)
104157
with exn -> debug "error: %s" (ExnHelper.string_of_exn exn)
105158

106-
let ws_proxy __context req protocol address s =
159+
let ws_proxy __context _ req protocol address s =
160+
let pool = Helpers.get_pool ~__context in
161+
let limit_console_sessions =
162+
Db.Pool.get_limit_console_sessions ~__context ~self:pool
163+
in
164+
(* Disable connection via websocket if the limit is set *)
165+
if limit_console_sessions = true then (
166+
Http_svr.headers s (Http.http_503_service_unavailable ()) ;
167+
()
168+
) ;
107169
let addr = match address with Port p -> string_of_int p | Path p -> p in
108170
let protocol =
109171
match protocol with `rfb -> "rfb" | `vt100 -> "vt100" | `rdp -> "rdp"
@@ -247,7 +309,7 @@ let handler proxy_fn (req : Request.t) s _ =
247309
check_vm_is_running_here __context console ;
248310
match address_of_console __context console with
249311
| Some vnc_port ->
250-
proxy_fn __context req protocol vnc_port s
312+
proxy_fn __context console req protocol vnc_port s
251313
| None ->
252314
Http_svr.headers s (Http.http_404_missing ())
253315
)

0 commit comments

Comments
 (0)