diff --git a/ocaml/idl/datamodel_errors.ml b/ocaml/idl/datamodel_errors.ml index c6bc9ed01d3..1c4f19dacff 100644 --- a/ocaml/idl/datamodel_errors.ml +++ b/ocaml/idl/datamodel_errors.ml @@ -1903,8 +1903,6 @@ let _ = ~doc:"The base url in the repository is invalid." () ; error Api_errors.invalid_gpgkey_path ["gpgkey_path"] ~doc:"The GPG public key file name in the repository is invalid." () ; - error Api_errors.cdn_token_invalid ["message"] - ~doc:"The provided CDN token or token_id is empty." () ; error Api_errors.repository_already_exists ["ref"] ~doc:"The repository already exists." () ; error Api_errors.bundle_repository_already_exists ["ref"] diff --git a/ocaml/xapi-consts/api_errors.ml b/ocaml/xapi-consts/api_errors.ml index f76abc6c888..04912fb4932 100644 --- a/ocaml/xapi-consts/api_errors.ml +++ b/ocaml/xapi-consts/api_errors.ml @@ -1314,8 +1314,6 @@ let invalid_base_url = add_error "INVALID_BASE_URL" let invalid_gpgkey_path = add_error "INVALID_GPGKEY_PATH" -let cdn_token_invalid = add_error "CDN_TOKEN_INVALID" - let repository_already_exists = add_error "REPOSITORY_ALREADY_EXISTS" let bundle_repository_already_exists = diff --git a/ocaml/xapi/repository.ml b/ocaml/xapi/repository.ml index 305960fd56a..8d62a27a84e 100644 --- a/ocaml/xapi/repository.ml +++ b/ocaml/xapi/repository.ml @@ -149,67 +149,76 @@ let get_proxy_params ~__context repo_name = | _ -> ("", "", "") -type client_auth_conf = - | CdnTokenAuthConf (* remote *) of {token: string; token_id: string} - | NoAuth (* bundle *) - | PoolExtHostAuthConf (* remote_pool *) of { - cert: string - ; remote_addr: string - ; username: string - ; password: string - } - -type server_auth_conf = - | DefaultAuth (* remote *) - | NoAuth (* bundle *) - | ServerAuthConf (* remote_pool *) of { - cert: string - ; remote_addr: string - ; remote_port: int - } - let sync ~__context ~self ~token ~token_id ~username ~password = try let repo_name = get_remote_repository_name ~__context ~self in remove_repo_conf_file repo_name ; let origin = Db.Repository.get_origin ~__context ~self in - let local_host = "127.0.0.1" in - let local_port = !Xapi_globs.local_yum_repo_port in let ( binary_url , source_url , repo_gpgcheck - , client_auth_config - , server_auth_config ) = + , use_proxy + , client_auth + , server_auth ) = match origin with | `remote -> + let plugin = "accesstoken" in ( Db.Repository.get_binary_url ~__context ~self , Some (Db.Repository.get_source_url ~__context ~self) , true - , CdnTokenAuthConf {token; token_id} + , true + , CdnTokenAuth {token_id; token; plugin} , DefaultAuth ) | `bundle -> let uri = Uri.make ~scheme:"file" ~path:!Xapi_globs.bundle_repository_dir () in - (Uri.to_string uri, None, true, NoAuth, NoAuth) + (Uri.to_string uri, None, true, false, NoAuth, NoAuth) | `remote_pool -> - let uri = - Uri.make ~scheme:"http" ~host:local_host - ~port:!Xapi_globs.local_yum_repo_port - ~path:Constants.get_enabled_repository_uri () - in let cert = Db.Repository.get_certificate ~__context ~self in + let repo_binary_url = Db.Repository.get_binary_url ~__context ~self in let remote_addr = - Db.Repository.get_binary_url ~__context ~self - |> Repository_helpers.get_remote_pool_coordinator_ip + repo_binary_url |> Repository_helpers.get_remote_pool_coordinator_ip + in + let verified_rpc = + try + Helpers.make_external_host_verified_rpc ~__context remote_addr + cert + with Xmlrpc_client.Connection_reset -> + raise + (Api_errors.Server_error + ( Api_errors + .update_syncing_remote_pool_coordinator_connection_failed + , [] + ) + ) + in + let session_id = + try + Client.Client.Session.login_with_password ~rpc:verified_rpc + ~uname:username ~pwd:password + ~version:Datamodel_common.api_version_string + ~originator:Xapi_version.xapi_user_agent + with + | Http_client.Http_request_rejected _ | Http_client.Http_error _ -> + raise + (Api_errors.Server_error + ( Api_errors + .update_syncing_remote_pool_coordinator_service_failed + , [] + ) + ) in - ( Uri.to_string uri + let xapi_token = session_id |> Ref.string_of in + let plugin = "xapitoken" in + ( repo_binary_url , None , false - , PoolExtHostAuthConf {cert; remote_addr; username; password} - , ServerAuthConf + , true + , PoolExtHostAuth {xapi_token; plugin} + , StunnelClientProxyAuth {cert; remote_addr; remote_port= Constants.default_ssl_port} ) in @@ -220,46 +229,18 @@ let sync ~__context ~self ~token ~token_id ~username ~password = | s -> s in - let write_initial_yum_config () = + let write_initial_yum_config ~binary_url = write_yum_config ~source_url ~binary_url ~repo_gpgcheck ~gpgkey_path ~repo_name in - write_initial_yum_config () ; - clean_yum_cache repo_name ; - (* Remove imported YUM repository GPG key *) - if Pkgs.manager = Yum then - Xapi_stdext_unix.Unixext.rm_rec (get_repo_config repo_name "gpgdir") ; Xapi_stdext_pervasives.Pervasiveext.finally (fun () -> - let config_repo yum_conf = - match yum_conf with - | Some (token_path, yum_plugin) -> - (* Configure proxy and token *) - let token_param = - match token_path with - | "" -> - "" - | p -> - Printf.sprintf "--setopt=%s.%s=%s" repo_name yum_plugin - (Uri.make ~scheme:"file" ~path:p () |> Uri.to_string) - in - let proxy_url_param, proxy_username_param, proxy_password_param = - get_proxy_params ~__context repo_name - in - let Pkg_mgr.{cmd; params} = - [ - "--save" - ; proxy_url_param - ; proxy_username_param - ; proxy_password_param - ; token_param - ] - |> fun config -> Pkgs.config_repo ~repo_name ~config - in - ignore - (Helpers.call_script ~log_output:Helpers.On_failure cmd params) - | None -> - () + let config_repo params = + let Pkg_mgr.{cmd; params} = + "--save" :: params |> fun config -> + Pkgs.config_repo ~repo_name ~config + in + ignore (Helpers.call_script ~log_output:Helpers.On_failure cmd params) in let make_cache () = @@ -276,73 +257,50 @@ let sync ~__context ~self ~token ~token_id ~username ~password = ignore (Helpers.call_script cmd params) in - let server_auth conf f = - match conf with - | DefaultAuth | NoAuth -> - f () - | ServerAuthConf {cert; remote_addr; remote_port} -> - let ( let@ ) f x = f x in - let@ temp_file = - Helpers.with_temp_file_of_content "external-host-cert-" ".pem" - cert + with_sync_client_auth client_auth @@ fun client_auth -> + with_sync_server_auth server_auth @@ fun binary_url' -> + write_initial_yum_config + ~binary_url:(Option.value binary_url' ~default:binary_url) ; + clean_yum_cache repo_name ; + (* Remove imported YUM repository GPG key *) + if Pkgs.manager = Yum then + Xapi_stdext_unix.Unixext.rm_rec (get_repo_config repo_name "gpgdir") ; + let auth_params = + match client_auth with + | Some (auth_file, plugin) -> + let token_param = + Printf.sprintf "--setopt=%s.%s=%s" repo_name plugin + (Uri.make ~scheme:"file" ~path:auth_file () |> Uri.to_string) in - Stunnel.with_client_proxy - ~verify_cert:(Stunnel_client.external_host temp_file) - ~remote_host:remote_addr ~remote_port ~local_host ~local_port - @@ fun () -> f () + [token_param] + | None -> + [] in - - let auth = - match client_auth_config with - | CdnTokenAuthConf {token; token_id} -> - Some (CdnTokenAuth (token_id, token), "accesstoken") - | PoolExtHostAuthConf {cert; remote_addr; username; password} -> - let verified_rpc = - try - Helpers.make_external_host_verified_rpc ~__context remote_addr - cert - with Xmlrpc_client.Connection_reset -> - raise - (Api_errors.Server_error - ( Api_errors - .update_syncing_remote_pool_coordinator_connection_failed - , [] - ) - ) - in - let session_id = - try - Client.Client.Session.login_with_password ~rpc:verified_rpc - ~uname:username ~pwd:password - ~version:Datamodel_common.api_version_string - ~originator:Xapi_version.xapi_user_agent - with - | Http_client.Http_request_rejected _ | Http_client.Http_error _ - -> - raise - (Api_errors.Server_error - ( Api_errors - .update_syncing_remote_pool_coordinator_service_failed - , [] - ) - ) + let proxy_params = + match use_proxy with + | true -> + let proxy_url_param, proxy_username_param, proxy_password_param = + get_proxy_params ~__context repo_name in - let xapi_token = session_id |> Ref.string_of in - Some (ExtHostAuth xapi_token, "xapitoken") - | NoAuth -> - None + [proxy_url_param; proxy_username_param; proxy_password_param] + | false -> + [] in - - with_sync_client_auth auth @@ fun yum_conf -> - config_repo yum_conf ; - server_auth server_auth_config @@ fun () -> make_cache () ; sync_repo () + config_repo (auth_params @ proxy_params) ; + make_cache () ; + sync_repo () ) (fun () -> (* Rewrite repo conf file as initial content to remove credential * related info, I.E. proxy username/password and temporary token file * path. + * One thing to note: for remote_repo, the binary_url used to + * re-initial yum repo is the url configed in the remote_pool repo, + * which is not the correct one for stunnel client proxy, while as we + * will always write_initial_yum_config every time before syncing repo, + * this should be ok. *) - write_initial_yum_config () + write_initial_yum_config ~binary_url ) ; (* The custom yum-utils will fully download repository metadata including * the repo gpg signature. diff --git a/ocaml/xapi/repository_helpers.ml b/ocaml/xapi/repository_helpers.ml index 05e052cf1a4..ea7495b32b1 100644 --- a/ocaml/xapi/repository_helpers.ml +++ b/ocaml/xapi/repository_helpers.ml @@ -1288,43 +1288,70 @@ let get_single_enabled_update_repository ~__context = in get_singleton enabled_update_repositories -type client_auth = ExtHostAuth of string | CdnTokenAuth of string * string +type client_auth = + | CdnTokenAuth (* remote *) of { + token_id: string + ; token: string + ; plugin: string + } + | NoAuth (* bundle *) + | PoolExtHostAuth (* remote_pool *) of {xapi_token: string; plugin: string} let with_sync_client_auth auth f = + let go_with_client_plugin cred plugin = + let ( let@ ) g x = g x in + let@ temp_file = + Helpers.with_temp_file_of_content ~mode:[Open_text] "token-" ".json" cred + in + f (Some (temp_file, plugin)) + in match auth with - | Some (client_auth, yum_plugin) -> ( - let secret = - match client_auth with - | ExtHostAuth session -> - Some (`Assoc [("xapitoken", `String session)]) - | CdnTokenAuth (token_id, token) when token_id = "" && token = "" -> - None - | CdnTokenAuth (token_id, token) when token_id <> "" && token <> "" -> - Some - (`Assoc [("token", `String token); ("token_id", `String token_id)]) - | CdnTokenAuth (token_id, _) -> - let msg = - if token_id = "" then - Printf.sprintf "%s: The token_id is empty" __LOC__ - else - Printf.sprintf "%s: The token is empty" __LOC__ - in - raise Api_errors.(Server_error (cdn_token_invalid, [msg])) + | CdnTokenAuth {token_id; token; _} when token_id = "" && token = "" -> + f None + | CdnTokenAuth {token_id; token; plugin} -> + let cred = + `Assoc [("token", `String token); ("token_id", `String token_id)] + |> Yojson.Basic.to_string in - match secret with - | None -> - f (Some ("", yum_plugin)) - | Some s -> - let ( let@ ) f x = f x in - let@ temp_file = - Helpers.with_temp_file_of_content ~mode:[Open_text] "token-" ".json" - (Yojson.Basic.to_string s) - in - f (Some (temp_file, yum_plugin)) - ) - | None -> + go_with_client_plugin cred plugin + | PoolExtHostAuth {xapi_token; plugin} -> + let cred = + `Assoc [("xapitoken", `String xapi_token)] |> Yojson.Basic.to_string + in + go_with_client_plugin cred plugin + | NoAuth -> f None +type server_auth = + | DefaultAuth (* remote *) + | NoAuth (* bundle *) + | StunnelClientProxyAuth (* remote_pool *) of { + cert: string + ; remote_addr: string + ; remote_port: int + } + +let with_sync_server_auth auth f = + match auth with + | DefaultAuth | NoAuth -> + f None + | StunnelClientProxyAuth {cert; remote_addr; remote_port} -> + let local_host = "127.0.0.1" in + let local_port = !Xapi_globs.local_yum_repo_port in + let ( let@ ) f x = f x in + let@ temp_file = + Helpers.with_temp_file_of_content "external-host-cert-" ".pem" cert + in + let binary_url = + Uri.make ~scheme:"http" ~host:local_host ~port:local_port + ~path:Constants.get_enabled_repository_uri () + |> Uri.to_string + in + Stunnel.with_client_proxy + ~verify_cert:(Stunnel_client.external_host temp_file) + ~remote_host:remote_addr ~remote_port ~local_host ~local_port + @@ fun () -> f (Some binary_url) + let prune_updateinfo_for_livepatches latest_lps updateinfo = let livepatches = let open LivePatch in