Skip to content

Commit b0a7341

Browse files
lindigChristian Lindig
authored andcommitted
CP-41796 Close Port 80 (Encrypt Data Transfer During VM Migrations)
Add modified copies of * open_uri * xcp_client to handle https URIs. This can't be done in the original module in xcp-idl as it would introduce a circular dependency. Signed-off-by: Christian Lindig <christian.lindig@cloud.com>
1 parent 79a6d77 commit b0a7341

File tree

3 files changed

+213
-53
lines changed

3 files changed

+213
-53
lines changed

lib/open_uri_patched.ml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
(* This code is usually in xcp-idl but we introduced a local copy here
3+
to support https, which has a dependency on stunnel and would create
4+
a circular dependency. *)
5+
6+
let https_port = 443
7+
8+
let with_open_uri uri f =
9+
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
10+
match Uri.scheme uri with
11+
| Some "http" -> (
12+
match (Uri.host uri, Uri.port uri) with
13+
| Some host, Some port ->
14+
Open_uri.open_tcp f host port
15+
| Some host, None ->
16+
Open_uri.open_tcp f host 80
17+
| _, _ ->
18+
failwith
19+
(Printf.sprintf "Failed to parse host and port from URI: %s"
20+
(Uri.to_string uri)
21+
)
22+
)
23+
| Some "https" -> (
24+
match (Uri.host uri, Uri.port uri) with
25+
| Some host, Some port ->
26+
Stunnel.with_connect host port (fun s ->
27+
f Safe_resources.Unixfd.(!(s.Stunnel.fd))
28+
)
29+
| Some host, None ->
30+
Stunnel.with_connect host https_port (fun s ->
31+
f Safe_resources.Unixfd.(!(s.Stunnel.fd))
32+
)
33+
| _, _ ->
34+
failwith
35+
(Printf.sprintf "Failed to parse host and port from URI: %s"
36+
(Uri.to_string uri)
37+
)
38+
)
39+
| Some "file" ->
40+
let filename = Uri.path_and_query uri in
41+
let sockaddr = Unix.ADDR_UNIX filename in
42+
let s = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
43+
finally
44+
(fun () -> Unix.connect s sockaddr ; Open_uri.handle_socket f s)
45+
(fun () -> Unix.close s)
46+
| Some x ->
47+
failwith (Printf.sprintf "Unsupported URI scheme: %s" x)
48+
| None ->
49+
failwith (Printf.sprintf "Failed to parse URI: %s" (Uri.to_string uri))
50+
51+

lib/xcp_client_patched.ml

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
(*
2+
* Copyright (C) Citrix Systems Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; version 2.1 only. with the special
7+
* exception on linking described in file LICENSE.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*)
14+
15+
(* Generic RPC marshalling functions for XCP services *)
16+
17+
module Request = Cohttp.Request.Make (Cohttp_posix_io.Buffered_IO)
18+
module Response = Cohttp.Response.Make (Cohttp_posix_io.Buffered_IO)
19+
20+
let get_user_agent () = Sys.argv.(0)
21+
22+
let switch_path = ref "/var/run/message-switch/sock"
23+
24+
let use_switch = ref true
25+
26+
let get_ok = function
27+
| `Ok x ->
28+
x
29+
| `Error e ->
30+
let b = Buffer.create 16 in
31+
let fmt = Format.formatter_of_buffer b in
32+
Message_switch_unix.Protocol_unix.Client.pp_error fmt e ;
33+
Format.pp_print_flush fmt () ;
34+
failwith (Buffer.contents b)
35+
36+
let switch_rpc ?timeout queue_name string_of_call response_of_string =
37+
let t =
38+
get_ok
39+
(Message_switch_unix.Protocol_unix.Client.connect ~switch:!switch_path ())
40+
in
41+
fun call ->
42+
response_of_string
43+
(get_ok
44+
(Message_switch_unix.Protocol_unix.Client.rpc ~t ?timeout
45+
~queue:queue_name ~body:(string_of_call call) ()))
46+
47+
let split_colon str =
48+
try
49+
let x = String.index str ':' in
50+
let uname = String.sub str 0 x in
51+
let passwd = String.sub str (x + 1) (String.length str - x - 1) in
52+
[uname; passwd]
53+
with Not_found -> [str]
54+
55+
(* Use HTTP to frame RPC messages *)
56+
[@@@ocaml.warning "-27"]
57+
58+
let http_rpc string_of_call response_of_string ?(srcstr = "unset")
59+
?(dststr = "unset") url call =
60+
let uri = Uri.of_string (url ()) in
61+
let req = string_of_call call in
62+
let headers =
63+
Cohttp.Header.of_list
64+
[
65+
("User-agent", get_user_agent ())
66+
; ("content-length", string_of_int (String.length req))
67+
]
68+
in
69+
(* If we have a username:password@ then use basic authentication *)
70+
let userinfo = Uri.userinfo uri in
71+
let headers =
72+
match userinfo with
73+
| Some x -> (
74+
match split_colon x with
75+
| [username; password] ->
76+
Cohttp.Header.add_authorization headers (`Basic (username, password))
77+
| _ ->
78+
headers
79+
)
80+
| None ->
81+
headers
82+
in
83+
let http_req =
84+
Cohttp.Request.make ~meth:`POST ~version:`HTTP_1_1 ~headers uri
85+
in
86+
Open_uri_patched.with_open_uri uri (fun fd ->
87+
let ic = Unix.in_channel_of_descr fd in
88+
let oc = Unix.out_channel_of_descr fd in
89+
Request.write (fun writer -> Request.write_body writer req) http_req oc ;
90+
match Response.read ic with
91+
| `Eof ->
92+
failwith
93+
(Printf.sprintf "Failed to read HTTP response from: %s" (url ()))
94+
| `Invalid x ->
95+
failwith
96+
(Printf.sprintf "Failed to read HTTP response from: %s (got '%s')"
97+
(url ()) x)
98+
| `Ok response -> (
99+
let body = Buffer.create 16 in
100+
let reader = Response.make_body_reader response ic in
101+
let rec loop () =
102+
match Response.read_body_chunk reader with
103+
| Cohttp.Transfer.Chunk x ->
104+
Buffer.add_string body x ; loop ()
105+
| Cohttp.Transfer.Final_chunk x ->
106+
Buffer.add_string body x
107+
| Cohttp.Transfer.Done ->
108+
()
109+
in
110+
loop () ;
111+
let body = Buffer.contents body |> response_of_string in
112+
match Cohttp.Response.status response with
113+
| `OK ->
114+
body
115+
| bad ->
116+
failwith
117+
(Printf.sprintf "Unexpected HTTP response code: %s"
118+
(Cohttp.Code.string_of_status bad))
119+
))
120+
121+
let xml_http_rpc = http_rpc Xmlrpc.string_of_call Xmlrpc.response_of_string
122+
123+
let json_switch_rpc ?timeout queue_name =
124+
switch_rpc ?timeout queue_name Jsonrpc.string_of_call
125+
Jsonrpc.response_of_string
126+
127+
let () =
128+
Printexc.register_printer (function
129+
| Xmlm.Error ((line, col), error) ->
130+
Some
131+
(Printf.sprintf "Xmlm.Error(%d:%d, \"%s\")" line col
132+
(Xmlm.error_message error))
133+
| _ ->
134+
None)
135+
136+
(* Use a binary 16-byte length to frame RPC messages *)
137+
let binary_rpc string_of_call response_of_string ?(srcstr = "unset")
138+
?(dststr = "unset") url (call : Rpc.call) : Rpc.response =
139+
let uri = Uri.of_string (url ()) in
140+
Open_uri.with_open_uri uri (fun fd ->
141+
let ic = Unix.in_channel_of_descr fd in
142+
let oc = Unix.out_channel_of_descr fd in
143+
let msg_buf = string_of_call call in
144+
let len = Printf.sprintf "%016d" (String.length msg_buf) in
145+
output_string oc len ;
146+
output_string oc msg_buf ;
147+
flush oc ;
148+
let len_buf = Bytes.make 16 '\000' in
149+
really_input ic len_buf 0 16 ;
150+
let len = int_of_string (Bytes.unsafe_to_string len_buf) in
151+
let msg_buf = Bytes.make len '\000' in
152+
really_input ic msg_buf 0 len ;
153+
let (response : Rpc.response) =
154+
response_of_string (Bytes.unsafe_to_string msg_buf)
155+
in
156+
response)
157+
158+
let json_binary_rpc =
159+
binary_rpc Jsonrpc.string_of_call Jsonrpc.response_of_string

lib/xenops_server.ml

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -21,56 +21,6 @@ module D = Debug.Make (struct let name = "xenops_server" end)
2121

2222
open D
2323

24-
(* This code is usually in xcp-idl but we introduced a local copy here
25-
to support https, which has a dependency on stunnel and would create
26-
a circular dependency. *)
27-
28-
let https_port = 443
29-
30-
let with_open_uri' uri f =
31-
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
32-
match Uri.scheme uri with
33-
| Some "http" -> (
34-
match (Uri.host uri, Uri.port uri) with
35-
| Some host, Some port ->
36-
Open_uri.open_tcp f host port
37-
| Some host, None ->
38-
Open_uri.open_tcp f host 80
39-
| _, _ ->
40-
failwith
41-
(Printf.sprintf "Failed to parse host and port from URI: %s"
42-
(Uri.to_string uri)
43-
)
44-
)
45-
| Some "https" -> (
46-
match (Uri.host uri, Uri.port uri) with
47-
| Some host, Some port ->
48-
Stunnel.with_connect host port (fun s ->
49-
f Safe_resources.Unixfd.(!(s.Stunnel.fd))
50-
)
51-
| Some host, None ->
52-
Stunnel.with_connect host https_port (fun s ->
53-
f Safe_resources.Unixfd.(!(s.Stunnel.fd))
54-
)
55-
| _, _ ->
56-
failwith
57-
(Printf.sprintf "Failed to parse host and port from URI: %s"
58-
(Uri.to_string uri)
59-
)
60-
)
61-
| Some "file" ->
62-
let filename = Uri.path_and_query uri in
63-
let sockaddr = Unix.ADDR_UNIX filename in
64-
let s = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
65-
finally
66-
(fun () -> Unix.connect s sockaddr ; Open_uri.handle_socket f s)
67-
(fun () -> Unix.close s)
68-
| Some x ->
69-
failwith (Printf.sprintf "Unsupported URI scheme: %s" x)
70-
| None ->
71-
failwith (Printf.sprintf "Failed to parse URI: %s" (Uri.to_string uri))
72-
73-
7424
let rpc_of ty x = Rpcmarshal.marshal ty.Rpc.Types.ty x
7525

7626
let finally = Xapi_stdext_pervasives.Pervasiveext.finally
@@ -2399,7 +2349,7 @@ and perform_exn ?subtask ?result (op : operation) (t : Xenops_task.task_handle)
23992349
let module Remote =
24002350
Xenops_interface.XenopsAPI (Idl.Exn.GenClient (struct
24012351
let rpc =
2402-
Xcp_client.xml_http_rpc ~srcstr:"xenops" ~dststr:"dst_xenops"
2352+
Xcp_client_patched.xml_http_rpc ~srcstr:"xenops" ~dststr:"dst_xenops"
24032353
(fun () -> vmm.vmm_url
24042354
)
24052355
end)) in
@@ -2446,7 +2396,7 @@ and perform_exn ?subtask ?result (op : operation) (t : Xenops_task.task_handle)
24462396
memory on the receiver *)
24472397
let state = B.VM.get_state vm in
24482398
info "VM %s has memory_limit = %Ld" id state.Vm.memory_limit ;
2449-
with_open_uri' memory_url (fun mem_fd ->
2399+
Open_uri_patched.with_open_uri memory_url (fun mem_fd ->
24502400
let module Handshake = Xenops_migrate.Handshake in
24512401
let do_request fd extra_cookies url =
24522402
let https = Uri.scheme url = Some "https" in
@@ -2529,7 +2479,7 @@ and perform_exn ?subtask ?result (op : operation) (t : Xenops_task.task_handle)
25292479
make_url "/migrate-vgpu/"
25302480
(VGPU_DB.string_of_id (new_dest_id, dev_id))
25312481
in
2532-
with_open_uri' vgpu_url (fun vgpu_fd ->
2482+
Open_uri_patched.with_open_uri vgpu_url (fun vgpu_fd ->
25332483
do_request vgpu_fd [(cookie_vgpu_migration, "")] vgpu_url ;
25342484
Handshake.recv_success vgpu_fd ;
25352485
debug "VM.migrate: Synchronisation point 1-vgpu" ;

0 commit comments

Comments
 (0)