Skip to content

Commit

Permalink
Add manager and scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Aug 29, 2022
1 parent 1e21fdb commit e44725b
Show file tree
Hide file tree
Showing 13 changed files with 552 additions and 218 deletions.
74 changes: 33 additions & 41 deletions src/lib/y2security/clients/security_policy_proposal.rb
Expand Up @@ -18,32 +18,10 @@
# find current contact information at www.suse.com.
#
require "installation/proposal_client"
require "y2security/security_policies/policy"
require "y2security/security_policies/manager"

module Y2Security
module Clients
# Helper class to keep the list of issues
class IssuesCollection
def initialize
@issues = {}
end

def update(policy, issues)
@issues[policy.id] = issues
end

def by_policy(policy)
@issues[policy.id]
end

def all
@issues.values.flatten
end

def clear
@issues.clear
end
end
# Proposal client to enable/disable security policies
class SecurityPolicyProposal < ::Installation::ProposalClient
include Yast::I18n
Expand All @@ -55,6 +33,8 @@ class << self
def issues
@issues ||= IssuesCollection.new
end

attr_writer :issues
end

def initialize
Expand Down Expand Up @@ -94,10 +74,10 @@ def ask_user(param)
action, id = parse_link(param["chosen_id"])
case action
when "disable"
find_policy(id).disable
disable_policy(id.to_sym)
refresh_packages
when "enable"
find_policy(id).enable
enable_policy(id.to_sym)
refresh_packages
when "fix"
fix_issue(id.to_i)
Expand Down Expand Up @@ -127,28 +107,47 @@ def action_link(action, id)
"#{LINK_DIALOG}--#{action}:#{id}"
end

def find_policy(id)
Y2Security::SecurityPolicies::Policy.find(id.to_sym)
def policies_manager
Y2Security::SecurityPolicies::Manager.instance
end

# All policies
#
# @return [Array<Y2Security::SecurityPolicies::Policy>]
def policies
Y2Security::SecurityPolicies::Policy.all
policies_manager.policies
end

# Enables the policy with the given id
#
# @param id [Symbol] Policy id
def enable_policy(id)
policy = policies_manager.find_policy(id)
policies_manager.enable_policy(policy) if policy
end

# Disables the policy with the given id
#
# @param id [Symbol] Policy id
def disable_policy(id)
policy = policies_manager.find_policy(id)
policies_manager.disable_policy(policy) if policy
end

def warning_message
return nil if policies.none?(&:enabled?) || all_issues.empty?
return nil if policies_manager.enabled_policies.none? || all_issues.empty?

_("The system does not comply with the security policy.")
end

def policy_link(policy)
if policy.enabled?
if policies_manager.enabled_policy?(policy)
format(
# TRANSLATORS: 'policy' is a security policy name; 'link' is just an HTML-like link
_("%{policy} is enabled (<a href=\"%{link}\">disable</a>)"),
policy: policy.name,
link: action_link("disable", policy.id)
) + issues_list(policy_issues(policy))
) + issues_list(issues.by_policy(policy))
else
format(
# TRANSLATORS: 'policy' is a security policy name; 'link' is just an HTML-like link
Expand All @@ -168,17 +167,14 @@ def warning_level
end

def check_security_policies
issues.clear
enabled_policies = policies.select(&:enabled?)
enabled_policies.each do |policy|
issues.update(policy, policy.validate)
end
self.class.issues = policies_manager.issues
end

# Adds or removes the packages needed by the policy to or from the Packages Proposal
def refresh_packages
policies.each do |policy|
method = policy.enabled? ? "AddResolvables" : "RemoveResolvables"
enabled = policies_manager.enabled_policy?(policy)
method = enabled ? "AddResolvables" : "RemoveResolvables"

Yast::PackagesProposal.public_send(method, "security", :package, policy.packages)
end
Expand All @@ -205,10 +201,6 @@ def issues_list(issues)
Yast::HTML.List(items)
end

def policy_issues(policy)
issues.by_policy(policy)
end

def issues
self.class.issues
end
Expand Down
6 changes: 3 additions & 3 deletions src/lib/y2security/security_policies.rb
Expand Up @@ -17,6 +17,6 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "y2security/security_policies/policy"
require "y2security/security_policies/validator"
require "y2security/security_policies/disa_stig_validator"
require "y2security/security_policies/manager"
require "y2security/security_policies/scopes"
require "y2security/security_policies/disa_stig_policy"
Expand Up @@ -18,59 +18,60 @@
# find current contact information at www.suse.com.

require "yast"
require "y2security/security_policies/validator"
require "y2security/security_policies/issue"
require "y2security/security_policies/policy"
require "y2security/security_policies/action"
require "y2issues/list"
require "y2security/security_policies/scopes"
require "y2security/security_policies/issue"
require "y2network/startmode"
require "y2network/connection_config/wireless"
require "bootloader/bootloader_factory"
require "bootloader/grub2base"

Yast.import "Lan"

module Y2Security
module SecurityPolicies
# Validator for the STIG security policy
class DisaStigValidator < Validator
# DISA STIG Security Policy
class DisaStigPolicy < Policy
include Yast::I18n

KNOWN_SCOPES = [:bootloader, :firewall, :network, :storage].freeze
private_constant :KNOWN_SCOPES

# @see Policy
def initialize
textdomain "security"
end

# Returns the issues found for the given scope
#
# @return [Y2Issues::List] List of found issues
def validate(*scopes)
scopes_to_validate = scopes.empty? ? KNOWN_SCOPES : KNOWN_SCOPES & scopes
scopes_to_validate.reduce([]) do |all, scope|
all + send("#{scope}_issues")
end
super(:disa_stig, _("Defense Information Systems Agency STIG"), ["scap-security-guide"])
end

private

# @see Policy
def issues_for(scope)
case scope
when Scopes::Network
network_issues(scope.config)
when Scopes::Storage
storage_issues(scope.devicegraph)
when Scopes::Firewall
firewall_issues(scope.security_settings)
when Scopes::Bootloader
bootloader_issues(scope.bootloader)
else
[]
end
end

# Returns the issues in the network configuration
#
# * Wireless devices are not supported
#
# @return [Array<Y2Issues::Issue>]
def network_issues
conns = find_wireless_connections
# @return [Array<Issue>]
def network_issues(config)
conns = find_wireless_connections(config)
return [] if conns.empty?

conns.each_with_object([]) do |conn, all|
message = format(
_("Wireless connections are not allowed: %s"), conn.name
)
message = format(_("Wireless connections are not allowed: %s"), conn.name)
action = Action.new(_(format("disable %s device", conn.name))) do
yast_config = Yast::Lan.yast_config
conn = yast_config.connections.by_name(conn.name)
conn = config.connections.by_name(conn.name)
conn.startmode = Y2Network::Startmode.create("off")
yast_config.add_or_update_connection_config(conn)
config.add_or_update_connection_config(conn)
end
all << Issue.new(message, action)
end
Expand All @@ -79,10 +80,10 @@ def network_issues
# Returns wireless connections which are not disabled
#
# @return [Array<Y2Network::ConnectionConfig::Wireless]
def find_wireless_connections
return [] if Yast::Lan.yast_config.nil?
def find_wireless_connections(config)
return [] if config.nil?

Yast::Lan.yast_config.connections.select do |conn|
config.connections.select do |conn|
conn.is_a?(Y2Network::ConnectionConfig::Wireless) &&
conn.startmode&.name != "off"
end
Expand All @@ -96,10 +97,9 @@ def find_wireless_connections
#
# * Full disk encryption is required
#
# @return [Array<Y2Issues::Issue>]
def storage_issues
staging = Y2Storage::StorageManager.instance.staging
plain_filesystems = staging.filesystems.select do |fs|
# @return [Array<Issue>]
def storage_issues(devicegraph)
plain_filesystems = devicegraph.filesystems.select do |fs|
mp = fs.mount_point
next if mp.nil? || PLAIN_MOUNT_POINTS.include?(mp.path)

Expand Down Expand Up @@ -135,9 +135,9 @@ def plain_filesystem?(filesystem)
#
# * Firewall must be enabled
#
# @return [Array<Y2Issues::Issue>]
def firewall_issues
return [] if !!security_settings.enable_firewall
# @return [Array<Issue>]
def firewall_issues(security_settings)
return [] if !!security_settings&.enable_firewall

[
Issue.new(
Expand All @@ -149,26 +149,13 @@ def firewall_issues
]
end

# Convenience method to obtain an Installation::SecuritySettings instance
#
# @return [Installation::SecuritySettings]
def security_settings
# FIXME: avoid a singular dependency with yast2-installation
require "installation/security_settings"
::Installation::SecuritySettings.instance
end

def bootloader
::Bootloader::BootloaderFactory.current
end

# Returns the issues in the bootloader proposal
#
# * Bootloader password must be set
# * Bootloader menu editing must be set as restricted
#
# @return [Array<Y2Issues::Issue>]
def bootloader_issues
# @return [Array<Issue>]
def bootloader_issues(bootloader)
issues = []
# When there is no Bootloader selected then the user will be in charge of configuring it
# himself therefore we will not add any issue there. (e.g. Bootloader::NoneBootloader)
Expand Down
37 changes: 37 additions & 0 deletions src/lib/y2security/security_policies/issue.rb
Expand Up @@ -19,6 +19,43 @@

module Y2Security
module SecurityPolicies
# Helper class to keep the list of issues for each policy
class IssuesCollection
def initialize
@issues = {}
end

# Updates the issues of a policy
#
# @param policy [Policy]
# @param issues [Array<Issue>]
def update(policy, issues)
@issues[policy] = issues
end

# Issues for the given policy
#
# @param policy [Policy]
# @return [Array<Issue>]
def by_policy(policy)
@issues[policy] || []
end

# All issues
#
# @return [Array<Issue>]
def all
@issues.values.flatten
end

# Hash representation of the collection
#
# @return [Hash]
def to_h
@issues.dup
end
end

# Represents an issue related to a security policy
#
# An issue can have an associated action to remedy the issue.
Expand Down

0 comments on commit e44725b

Please sign in to comment.