diff --git a/ocaml/xapi/dbsync_slave.ml b/ocaml/xapi/dbsync_slave.ml index ab8a6a3ef2..5dc2827065 100644 --- a/ocaml/xapi/dbsync_slave.ml +++ b/ocaml/xapi/dbsync_slave.ml @@ -134,16 +134,13 @@ let refresh_localhost_info ~__context info = ) else Db.Host.remove_from_other_config ~__context ~self:host ~key:Xapi_globs.host_no_local_storage ; - let script_output = - Helpers.call_script !Xapi_globs.firewall_port_config_script ["check"; "80"] + let module F = + ( val Firewall.firewall_provider !Xapi_globs.firewall_backend + : Firewall.FIREWALL + ) in - try - let network_state = Scanf.sscanf script_output "Port 80 open: %B" Fun.id in - Db.Host.set_https_only ~__context ~self:host ~value:network_state - with _ -> - Helpers.internal_error - "unexpected output from /etc/xapi.d/plugins/firewall-port: %s" - script_output + let enabled = F.is_firewall_service_enabled ~service:Firewall.Http in + Db.Host.set_https_only ~__context ~self:host ~value:(not enabled) (*************** update database tools ******************) (** Record host memory properties in database *) diff --git a/ocaml/xapi/firewall.ml b/ocaml/xapi/firewall.ml new file mode 100644 index 0000000000..d8791e59a9 --- /dev/null +++ b/ocaml/xapi/firewall.ml @@ -0,0 +1,135 @@ +(* + * Copyright (c) Cloud Software Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +module D = Debug.Make (struct let name = __MODULE__ end) + +open D + +type status = Enabled | Disabled + +type service_type = Dlm | Nbd | Ssh | Vxlan | Http | Xenha + +type service_info = {name: string; port: int; protocol: string} + +let status_to_string = function Enabled -> "enabled" | Disabled -> "disabled" + +let service_type_to_service_info = function + | Dlm -> + {name= "dlm"; port= !Xapi_globs.xapi_clusterd_port; protocol= "TCP"} + | Nbd -> + {name= "nbd"; port= 10809; protocol= "TCP"} + | Ssh -> + {name= "ssh"; port= 22; protocol= "TCP"} + | Vxlan -> + {name= "vxlan"; port= 4789; protocol= "UDP"} + | Http -> + {name= "xapi-insecure"; port= Constants.http_port; protocol= "TCP"} + | Xenha -> + {name= "xenha"; port= Xapi_globs.xha_udp_port; protocol= "UDP"} + +module type FIREWALL = sig + val update_firewall_status : service:service_type -> status:status -> unit + + val is_firewall_service_enabled : service:service_type -> bool +end + +module Firewalld : FIREWALL = struct + let update_firewall_status ~service ~status = + if !Xapi_globs.dynamic_control_firewalld_service then + let service_option = + match status with + | Enabled -> + "--add-service" + | Disabled -> + "--remove-service" + in + let service_info = service_type_to_service_info service in + try + Helpers.call_script !Xapi_globs.firewall_cmd + [service_option; service_info.name] + |> ignore + with e -> + error + "%s: Failed to update firewall service (%s) to status (%s) with \ + error: %s" + __FUNCTION__ service_info.name (status_to_string status) + (Printexc.to_string e) + + let is_firewall_service_enabled ~service = + let service_info = service_type_to_service_info service in + try + let output = + Helpers.call_script !Xapi_globs.firewall_cmd + ["--query-service"; service_info.name] + |> String.trim + |> String.lowercase_ascii + in + debug "%s: Check firewall service (%s) return: %s" __FUNCTION__ + service_info.name output ; + let status = Scanf.sscanf output "%s" Fun.id in + match status with "yes" -> true | _ -> false + with e -> + error "%s: Failed to check firewall service (%s) with error: %s" + __FUNCTION__ service_info.name (Printexc.to_string e) ; + false +end + +module Iptables : FIREWALL = struct + let update_firewall_status ~service ~status = + let op = match status with Enabled -> "open" | Disabled -> "close" in + let service_info = service_type_to_service_info service in + try + Helpers.call_script + !Xapi_globs.firewall_port_config_script + [op; string_of_int service_info.port; service_info.protocol] + |> ignore + with e -> + error + "%s: Failed to update firewall service (%s) to status (%s) with error: \ + %s" + __FUNCTION__ service_info.name (status_to_string status) + (Printexc.to_string e) + + let is_firewall_service_enabled ~service = + let service_info = service_type_to_service_info service in + try + let output = + Helpers.call_script + !Xapi_globs.firewall_port_config_script + ["check"; string_of_int service_info.port; service_info.protocol] + in + debug "%s: Check firewall service (%s) return: %s" __FUNCTION__ + service_info.name output ; + let enabled = + (* The firewall-port script returns true if port 80 is blocked and false + if it is not. *) + Scanf.sscanf output "Port %d open: %B" (fun _ is_blocked -> + not is_blocked + ) + in + enabled + with e -> + error "%s: Failed to check firewall service (%s) with error: %s" + __FUNCTION__ service_info.name (Printexc.to_string e) ; + false +end + +let firewall_provider (backend : string) : (module FIREWALL) = + match backend with + | "firewalld" -> + (module Firewalld) + | "iptables" -> + (module Iptables) + | _ -> + Helpers.internal_error "unknown firewall backend: %s" backend diff --git a/ocaml/xapi/firewall.mli b/ocaml/xapi/firewall.mli new file mode 100644 index 0000000000..af069e7bc3 --- /dev/null +++ b/ocaml/xapi/firewall.mli @@ -0,0 +1,31 @@ +(* + * Copyright (c) Cloud Software Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +type status = Enabled | Disabled + +type service_type = Dlm | Nbd | Ssh | Vxlan | Http | Xenha + +type service_info = {name: string; port: int; protocol: string} + +module type FIREWALL = sig + val update_firewall_status : service:service_type -> status:status -> unit + + val is_firewall_service_enabled : service:service_type -> bool +end + +module Firewalld : FIREWALL + +module Iptables : FIREWALL + +val firewall_provider : string -> (module FIREWALL) diff --git a/ocaml/xapi/xapi_globs.ml b/ocaml/xapi/xapi_globs.ml index 490045f871..aba7474e22 100644 --- a/ocaml/xapi/xapi_globs.ml +++ b/ocaml/xapi/xapi_globs.ml @@ -861,6 +861,10 @@ let nbd_firewall_config_script = let firewall_port_config_script = ref "/etc/xapi.d/plugins/firewall-port" +let firewall_cmd = ref "/usr/bin/firewall-cmd" + +let firewall_cmd_wrapper = ref "/usr/bin/firewall-cmd-wrapper" + let nbd_client_manager_script = ref "/opt/xensource/libexec/nbd_client_manager.py" @@ -1317,6 +1321,12 @@ let ssh_monitor_service = ref "xapi-ssh-monitor" let ssh_auto_mode_default = ref true +(* Firewall backend to use. iptables in XS 8, firewalld in XS 9. *) +let firewall_backend = ref "firewalld" + +(* For firewalld, if dynamic control firewalld service. *) +let dynamic_control_firewalld_service = ref true + (* Fingerprint of default patch key *) let citrix_patch_key = "NERDNTUzMDMwRUMwNDFFNDI4N0M4OEVCRUFEMzlGOTJEOEE5REUyNg==" @@ -1762,12 +1772,6 @@ let other_options = , (fun () -> string_of_bool !validate_reusable_pool_session) , "Enable validation of reusable pool sessions before use" ) - ; ( "ssh-auto-mode" - , Arg.Bool (fun b -> ssh_auto_mode_default := b) - , (fun () -> string_of_bool !ssh_auto_mode_default) - , "Defaults to true; overridden to false via \ - /etc/xapi.conf.d/ssh-auto-mode.conf(e.g., in XenServer 8)" - ) ; ( "vm-sysprep-enabled" , Arg.Set vm_sysprep_enabled , (fun () -> string_of_bool !vm_sysprep_enabled) @@ -1778,6 +1782,17 @@ let other_options = , (fun () -> string_of_float !vm_sysprep_wait) , "Time in seconds to wait for VM to recognise inserted CD" ) + ; ( "firewall-backend" + , Arg.Set_string firewall_backend + , (fun () -> !firewall_backend) + , "Firewall backend. iptables (in XS 8) or firewalld (in XS 9 or later XS \ + version)" + ) + ; ( "dynamic-control-firewalld-service" + , Arg.Bool (fun b -> dynamic_control_firewalld_service := b) + , (fun () -> string_of_bool !dynamic_control_firewalld_service) + , "Enable dynamic control firewalld service" + ) ] (* The options can be set with the variable xapiflags in /etc/sysconfig/xapi. @@ -1912,10 +1927,14 @@ module Resources = struct , "Executed after NBD-related networking changes to configure the \ firewall for NBD" ) - ; ( "firewall-port-config" - , firewall_port_config_script - , "Executed when starting/stopping xapi-clusterd to configure firewall \ - port" + ; ( "firewall-cmd" + , firewall_cmd + , "Executed when enable/disable a service on a firewalld zone" + ) + ; ( "firewall-cmd-wrapper" + , firewall_cmd_wrapper + , "Executed when enable/disable a service on a firewalld zone and \ + interface" ) ; ( "nbd_client_manager" , nbd_client_manager_script diff --git a/ocaml/xapi/xapi_host.ml b/ocaml/xapi/xapi_host.ml index 1bf3e4d9b6..70bcbeaf0f 100644 --- a/ocaml/xapi/xapi_host.ml +++ b/ocaml/xapi/xapi_host.ml @@ -3116,13 +3116,17 @@ let cc_prep () = true let set_https_only ~__context ~self ~value = - let state = match value with true -> "close" | false -> "open" in match cc_prep () with | false -> - ignore - @@ Helpers.call_script - !Xapi_globs.firewall_port_config_script - [state; "80"] ; + let status = + match value with true -> Firewall.Disabled | false -> Firewall.Enabled + in + let module F = + ( val Firewall.firewall_provider !Xapi_globs.firewall_backend + : Firewall.FIREWALL + ) + in + F.update_firewall_status ~service:Firewall.Http ~status ; Db.Host.set_https_only ~__context ~self ~value | true when value = Db.Host.get_https_only ~__context ~self -> (* the new value is the same as the old value *)