Skip to content

Commit

Permalink
WIP: another approach
Browse files Browse the repository at this point in the history
  • Loading branch information
dgdavid committed Feb 2, 2021
1 parent aa7fc01 commit ecf219a
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 65 deletions.
125 changes: 70 additions & 55 deletions src/lib/security/selinux_config.rb
Expand Up @@ -64,11 +64,8 @@ def running_mode
:disabled
end

# Returns available SELinux modes
#
# @return [Hash{Symbol=><String, Symbol>}] collection holding available modes ids and names
def available_modes
MODES.map { |id, mode| { id: id, name: _(mode[:name]) } }
def modes
Mode.all
end

# Set the mode to given value
Expand All @@ -91,7 +88,7 @@ def mode=(id)
#
# @return [Boolean] false if there area not mode options or nothing changed; true otherwise
def save
mode_options = find_mode_options(mode)
mode_options = Mode.find(mode)&.options

unless mode_options
log.info("Unknown `#{mode}` SELinux mode")
Expand All @@ -109,54 +106,6 @@ def save
GETENFORCE_PATH = "/usr/sbin/getenforce".freeze
private_constant :GETENFORCE_PATH

# Known keys for setting a SELinux mode via kernel command line
MODE_KEYS = ["security", "selinux", "enforcing"].freeze
private_constant :MODE_KEYS

# Known SELinux modes
#
# This is _the main_ or _base_ configuration for known SELinux modes. However, note that, for
# example, a permissive mode could be set by just setting the "security" module; i.e.,
# "security=selinux" means "enable SELinux using the permissive mode". Or even setting
# the enforcing param to a value equal or less than 0; i.e., "security=selinux enforcing=0".
#
# Additionally, removing the "security" from the kernel params does not mean to use none
# security module. Instead, it just fallback to the kernel configuration at the compile time,
# which in SUSE is to use AppArmor according to the CONFIG_LSM variable. So, it could be said
# that dropping all mode param to disabling SELinux is safe enough.
#
# To know more, please visit the LSM Usage documentation at
# https://www.kernel.org/doc/html/latest/admin-guide/LSM/index.html
# and/or grep for CONFIG_LSM in /boot/config-*
MODES = {
disabled: {
# TRANSLATORS: the name for the disabled SELinux mode
name: N_("Disabled"),
options: { "security" => :missing, "selinux" => :missing, "enforcing" => :missing },
},
permissive: {
# TRANSLATORS: the name for the permissive SELinux mode
name: N_("Permissive"),
options: { "security" => "selinux", "selinux" => "1", "enforcing" => :missing }
},
enforcing: {
# TRANSLATORS: the name for the enforcing SELinux mode
name: N_("Enforcing"),
options: { "security" => "selinux", "selinux" => "1", "enforcing" => "1" }
}
}.freeze
private_constant :MODES

# Returns the options for the requested mode, if exists
#
# @param mode_id [String, Symbol] the mode identifier
# @return [Hash, nil] options for matched mode or nil if none
def find_mode_options(mode_id)
id = mode_id.to_sym

MODES[id] && MODES[id][:options]
end

# Proposes a mode based on `selinux_mode` value set in the control file
def propose_mode
mode_key = Yast::ProductFeatures.GetFeature("globals", "selinux_mode").to_sym
Expand Down Expand Up @@ -190,7 +139,7 @@ def configured_mode
#
# @return [Symbol] the mode identifier
def mode_from_kernel_params
options = Hash[*MODE_KEYS.flat_map { |key| [key, read_param(key)] }]
options = Hash[*Mode.keys.flat_map { |key| [key, read_param(key)] }]
options.filter { |_, value| value != :missing }
end

Expand All @@ -200,5 +149,71 @@ def mode_from_kernel_params
def read_param(key)
Yast::Bootloader.kernel_param(:common, key.to_s)
end

# Model that represents a SELinux mode
class Mode
extend Yast::I18n
include Yast::I18n

attr_reader :id, :name, :options

# Constructor
#
# @param id [String, Symbol] if of the mode
# @param name [String] the mode name, a string marked for translation
# @param enable [Boolean] wether the mode will enable SELinux
# @param enforcing [Boolean] if SELinux should be run enforcing or not
def initialize(id, name, enable, enforcing)
textdomain "security"

@id = id.to_sym
@name = _(name)
@options = {
"security" => enable ? "selinux" : :missing,
"selinux" => enable ? "1" : :missing,
"enforcing" => enforcing ? "1" : :missing
}
end

def self.all
ALL.dup
end

def self.keys
OPTIONS_KEYS
end

def self.find(id)
ALL.find { |mode| mode.id == id.to_sym }
end

private

# All known SELinux modes
#
# This is _the main_ or _base_ configuration for known SELinux modes. However, note that, for
# example, a permissive mode could be set by just setting the "security" module; i.e.,
# "security=selinux" means "enable SELinux using the permissive mode". Or even setting
# the enforcing param to a value equal or less than 0; i.e., "security=selinux enforcing=0".
#
# Additionally, removing the "security" from the kernel params does not mean to use none
# security module. Instead, it just fallback to the kernel configuration at the compile time,
# which in SUSE is to use AppArmor according to the CONFIG_LSM variable. So, it could be said
# that dropping all mode param to disabling SELinux is safe enough.
#
# To know more, please visit the LSM Usage documentation at
# https://www.kernel.org/doc/html/latest/admin-guide/LSM/index.html
# and/or grep for CONFIG_LSM in /boot/config-*
ALL = [
new(:disabled, N_("Disabled"), false, false),
new(:permissive, N_("Permissive"), true, false),
new(:enforcing, N_("Enforcing"), true, true),
].freeze
private_constant :ALL

# Known keys for setting a SELinux mode via kernel command line
OPTIONS_KEYS = ["security", "selinux", "enforcing"]
private_constant :OPTIONS_KEYS
end
end
end
25 changes: 15 additions & 10 deletions test/security/selinux_config_test.rb
Expand Up @@ -78,6 +78,21 @@
include_examples "initial mode"
end

describe "#modes" do
it "returns a collection of known SELinux modes" do
expect(subject.modes).to all(be_a(Security::SelinuxConfig::Mode))
end

it "contains known mode ids" do
expect(subject.modes.map(&:id)).to eq([:disabled, :permissive, :enforcing])
end

it "contains known mode names" do
expect(subject.modes.map(&:name)).to eq(["Disabled", "Permissive", "Enforcing"])
end
end


describe "#mode" do
context "when mode has not been changed yet" do
include_examples "initial mode"
Expand Down Expand Up @@ -123,16 +138,6 @@
end
end
end

describe "#available_modes" do
it "returns all known SELinux modes" do
modes = subject.available_modes
mode_ids = modes.map { |m| m[:id] }

expect(mode_ids).to eq([:disabled, :permissive, :enforcing])
end
end

describe "#running_mode=" do
let(:getenforce_cmd) { ["/usr/sbin/getenforce", stdout: :capture] }
let(:cheetah_error) { Cheetah::ExecutionFailed.new([], "", nil, nil) }
Expand Down

0 comments on commit ecf219a

Please sign in to comment.