Skip to content

CP-308800: Dynamic control of firewalld service - part 1 #6629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: feature/dynamic-firewalld-control
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions ocaml/xapi/dbsync_slave.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)
Expand Down
135 changes: 135 additions & 0 deletions ocaml/xapi/firewall.ml
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions ocaml/xapi/firewall.mli
Original file line number Diff line number Diff line change
@@ -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)
39 changes: 29 additions & 10 deletions ocaml/xapi/xapi_globs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer if this was iptables by default, with the configuration file for XS9 overriding the value to firewalld for the time being. And changing the default once XS9 is forked.

Suggested change
let firewall_backend = ref "firewalld"
let firewall_backend = ref "iptables"


(* For firewalld, if dynamic control firewalld service. *)
let dynamic_control_firewalld_service = ref true

(* Fingerprint of default patch key *)
let citrix_patch_key =
"NERDNTUzMDMwRUMwNDFFNDI4N0M4OEVCRUFEMzlGOTJEOEE5REUyNg=="
Expand Down Expand Up @@ -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)
Expand All @@ -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 \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we would not accept arbitratry strings here and reject illegal strings already. But this is the easiest solution and we have to reject illegal backends later and report them as internal error - which is not entirely true.

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.
Expand Down Expand Up @@ -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
Expand Down
14 changes: 9 additions & 5 deletions ocaml/xapi/xapi_host.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)
Expand Down
Loading