Skip to content

Commit

Permalink
Moved firewall selection to a specific class
Browse files Browse the repository at this point in the history
  • Loading branch information
teclator committed Sep 25, 2018
1 parent 02a38ed commit 80b2fb7
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 126 deletions.
3 changes: 2 additions & 1 deletion library/network/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ agent_SCRIPTS = \

ylibdir = @ylibdir@/network
ylib_DATA = \
lib/network/firewall_chooser.rb \
lib/network/firewalld.rb \
lib/network/susefirewall.rb \
lib/network/susefirewall2.rb \
Expand All @@ -36,4 +37,4 @@ ylib_DATA = \

EXTRA_DIST = $(module_DATA) $(scrconf_DATA) $(agent_SCRIPTS) $(ylib_DATA)

include $(top_srcdir)/Makefile.am.common
include $(top_srcdir)/Makefile.am.common
137 changes: 137 additions & 0 deletions library/network/src/lib/network/firewall_chooser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# encoding: utf-8

# Copyright (c) [2018] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# 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 General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
require "yast"
require "network/susefirewalld"
require "network/susefirewall2"

Yast.import "Service"
Yast.import "PackageSystem"
Yast.import "Mode"

module Yast
class SuSEFirewallMultipleBackends < StandardError
DEFAULT_MESSAGE = "Multiple firewall backends are running. " \
"One needs to be shutdown to continue.".freeze
def initialize(message=DEFAULT_MESSAGE)
super message
end
end

# Factory for construction of appropriate firewall object based on
# desired backend.
class FirewallChooser
include Yast::Logger

# Use same hash for package names and services
FIREWALL_BACKENDS = {
sf2: "SuSEfirewall2",
fwd: "firewalld"
}.freeze

# Check if backend is installed on the system.
#
# @param backend_sym [Symbol] Firewall backend
# @return [Boolean] True if backend is installed.
def self.backend_available?(backend_sym)
# SF2 has it's own method of checking if it's installed. This is
# used internally by SF2. Here, we simply care if the package
# is present on the system.
PackageSystem.Installed(FIREWALL_BACKENDS[backend_sym])
end

# Obtain backends which are installed on the system
#
# @return [Array<Symbol>] List of installed backends.
def self.installed_backends
FIREWALL_BACKENDS.select { |b, _p| backend_available?(b) }.keys
end

# Obtain list of enabled backends.
#
# @return [Array<Symbol>] List of enabled backends.
def self.enabled_backends
installed_backends.select { |b| Service.Enabled(FIREWALL_BACKENDS[b]) }
end

# Obtain running backend on the system.
#
# @note In theory this should only return an Array with only one element in it
# since FirewallD and SF2 systemd service files conflict with each other.
#
# @return [Array<Symbol>] List of running backends.
def self.running_backends
installed_backends.select { |b| Service.Active(FIREWALL_BACKENDS[b]) }
end

# Return an instance of the firewall given or detected
#
# @return [SuSEFirewall2Class, SuSEFirewalldClass]
def self.choose(backend_sym = nil)
backend = backend_sym || detect
# For the old testsuite, always generate SF2 instance. FirewallD tests
# will be committed later on but they will only affect the new
# testsuite

# If backend is specificed, go ahead and create an instance. Otherwise, try
# to detect which backend is enabled and create the appropriate instance.
backend = detect unless backend

backend == :sf2 ? SuSEFirewall2Class.new : SuSEFirewalldClass.new
end

# Determine which firewall should be selected as the backend depending on
# which one is enabled, running and/or installed. SuSEfirewall2 is the
# predefined one in case there is no way to decide.
#
# @raise [SuSEFirewallMultipleBackends] if firewalld and SuSEfirewalld2 are
# running
# @return [Symbol] the backend that should be used
def self.detect
# Old testsuite
backend = :sf2 if Mode.testsuite

# Only one running backend is permitted.
raise SuSEFirewallMultipleBackends if running_backends.size > 1

# If both firewalls are enabled, then make SF2 the default one and
# emit a warning
if running_backends.empty? && enabled_backends.size > 1
Builtins.y2warning("Both SuSEfirewall2 and firewalld services are enabled. " \
"Defaulting to SuSEfirewall2")
enabled_backends[0] = :sf2
end

# Set a good default. The running one takes precedence over the enabled one.
selected_backend = running_backends[0] ? running_backends[0] : enabled_backends[0]
# Fallback to the first installed backend or to SuSEfirewall2 if not
selected_backend = (installed_backends.first || :sf2) if selected_backend.to_s.empty?

return selected_backend

rescue SuSEFirewallMultipleBackends
# This should never happen since FirewallD and SF2 systemd services
# conflict with each other
Builtins.y2error("Multiple firewall backends are running. One needs to be shutdown to continue.")
# Re-raise it
raise SuSEFirewallMultipleBackends
end
end
end
125 changes: 2 additions & 123 deletions library/network/src/modules/SuSEFirewall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,131 +29,10 @@
#
# Module for handling SuSEfirewall2 or FirewallD
require "yast"
require "network/susefirewalld"
require "network/firewall_chooser"
require "network/susefirewall2"

module Yast
class SuSEFirewallMultipleBackends < StandardError
def initialize(message)
super message
end
end

# Factory for construction of appropriate firewall object based on
# desired backend.
class FirewallClass < Module
include Yast::Logger
Yast.import "PackageSystem"

# Use same hash for package names and services
FIREWALL_BACKENDS = {
sf2: "SuSEfirewall2",
fwd: "firewalld"
}.freeze

# Check if backend is installed on the system.
#
# @param backend_sym [Symbol] Firewall backend
# @return [Boolean] True if backend is installed.
def self.backend_available?(backend_sym)
# SF2 has it's own method of checking if it's installed. This is
# used internally by SF2. Here, we simply care if the package
# is present on the system.
PackageSystem.Installed(FIREWALL_BACKENDS[backend_sym])
end

# Obtain backends which are installed on the system
#
# @return [Array<Symbol>] List of installed backends.
def self.installed_backends
backends = []

FIREWALL_BACKENDS.each_key { |k| backends << k if backend_available?(k) }

backends
end

# Obtain list of enabled backends.
#
# @return [Array<Symbol>] List of enabled backends.
def self.enabled_backends
Yast.import "Service"

backends = []

installed_backends.each do |b|
backends << b if Service.Enabled(FIREWALL_BACKENDS[b])
end

backends
end

# Obtain running backend on the system.
#
# @return [Array<Symbol>] List of running backends.
def self.running_backends
Yast.import "Service"

backends = []

installed_backends.each { |b| backends << b if Service.Active(FIREWALL_BACKENDS[b]) }

# In theory this should only return an Array with only one element in it
# since FirewallD and SF2 systemd service files conflict with each other.
backends
end

def self.create(backend_sym = nil)
Yast.import "Mode"

# Old testsuite
if Mode.testsuite
# For the old testsuite, always generate SF2 instance. FirewallD tests
# will be committed later on but they will only affect the new
# testsuite
SuSEFirewall2Class.new

# If backend is specificed, go ahead and create an instance. Otherwise, try
# to detect which backend is enabled and create the appropriate instance.
elsif backend_sym == :sf2
SuSEFirewall2Class.new
elsif backend_sym == :fwd
SuSEFirewalldClass.new
else
begin
# Only one running backend is permitted.
raise SuSEFirewallMultipleBackends if running_backends.size > 1

# If both firewalls are enabled, then make SF2 the default one and
# emit a warning
if running_backends.empty? && enabled_backends.size > 1
Builtins.y2warning("Both SuSEfirewall2 and firewalld services are enabled. " \
"Defaulting to SuSEfirewall2")
enabled_backends[0] = :sf2
end

# Set a good default. The running one takes precedence over the enabled one.
selected_backend = running_backends[0] ? running_backends[0] : enabled_backends[0]
selected_backend = :sf2 if selected_backend.to_s.empty? # SF2 still the default

if selected_backend == :fwd
# SuSEFirewalld instance is only generated if firewalld is running.
SuSEFirewalldClass.new
else
# All other cases, we generate SF2 instance. This is still our default afterall.
SuSEFirewall2Class.new
end
rescue SuSEFirewallMultipleBackends
# This should never happen since FirewallD and SF2 systemd services
# conflict with each other
Builtins.y2error("Multiple firewall backends are running. One needs to be shutdown to continue.")
# Re-raise it
raise SuSEFirewallMultipleBackends
end
end
end
end

SuSEFirewall = FirewallClass.create
SuSEFirewall = FirewallChooser.choose
SuSEFirewall.main if SuSEFirewall.is_a?(SuSEFirewall2Class)
end
3 changes: 2 additions & 1 deletion library/network/test/susefirewall_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env rspec

require_relative "test_helper"
require "network/firewall_chooser"

Yast.import "Mode"
Yast.import "PackageSystem"
Expand All @@ -13,7 +14,7 @@ def reset_SuSEFirewallIsInstalled_cache
end

# Instantiate an SF2 object
FakeFirewall = Yast::FirewallClass.create(:sf2)
FakeFirewall = Yast::FirewallChooser.choose(:sf2)
FakeFirewall.main

describe FakeFirewall do
Expand Down
3 changes: 2 additions & 1 deletion library/network/test/susefirewalld_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative "test_helper"
require "network/firewalld"
require "network/firewall_chooser"

Yast.import "Mode"
Yast.import "PackageSystem"
Expand All @@ -24,7 +25,7 @@ def reset_FirewallDIsInstalled_cache
need_API_mock = true

# Re-instansiate our objects
FakeFirewallD = Yast::FirewallClass.create(:fwd)
FakeFirewallD = Yast::FirewallChooser.choose(:fwd)

describe FakeFirewallD do

Expand Down

0 comments on commit 80b2fb7

Please sign in to comment.