scap-security-guide/Library/scap-common/lib.sh

262 lines
8.2 KiB
Bash

#!/bin/bash
# vim: dict+=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# library-prefix = scapCommon
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Variables
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
true <<'=cut'
=pop
=item scapCommonDISTRO
Name of distribution which is set based on the host system. Variable is set by
scapCommonGetDistroName function.
=item scapCommonDS
Full path to datastream file from scap-security-guide package based
on the host system. Variable is set by scapCommonGetDSPath function.
=back
=cut
scapCommonDISTRO=""
scapCommonDS=""
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Functions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
scapCommonLibraryLoaded() {
return 0
}
true <<'=cut'
=pod
=head2 scapCommonGetDistroName
Prints a name of the host distro.
Supported distros:
- Fedora
- RHEL
- CentOS Stream
Usage:
DISTRO=$(scapCommonGetDistroName)
=cut
function scapCommonGetDistroName() {
if rlIsRHEL; then
echo "rhel$(rlGetDistroRelease)"
elif rlIsCentOS; then
if rlIsCentOS "<=8"; then
echo "centos$(rlGetDistroRelease)"
elif rlIsCentOS ">=9"; then
echo "cs$(rlGetDistroRelease)"
fi
else
echo "fedora"
fi
}
scapCommonDISTRO=$(scapCommonGetDistroName)
true <<'=cut'
=pod
=head2 scapCommonGetDSPath
Prints a path to a datastream file from the scap-security-guide package based
on the host distro. Function accepts one optional argument DS_PREFIX_PATH -
a prefix directory path where a datastream for the host distro is located and
has a file name of "ssg-$(scapCommonGetDistroName)-ds.xml".
Supported distros:
- Fedora
- RHEL
- CentOS Stream
Usage:
DS=$(scapCommonGetDSPath)
or
DS=$(scapCommonGetDSPath <DS_PREFIX_PATH>)
=cut
function scapCommonGetDSPath() {
local ds_prefix_path="$1"
if [ -z "$ds_prefix_path" ]; then
rpm -ql scap-security-guide | grep "ssg-$(scapCommonGetDistroName)-ds.xml"
else
echo "${ds_prefix_path}/ssg-$(scapCommonGetDistroName)-ds.xml"
fi
}
scapCommonDS=$(scapCommonGetDSPath)
true <<'=cut'
=pod
=head2 scapCommonIsProfileInDS
Tries to find a PROFILE in DS.
Returns 0, if PROFILE is in DS. Otherwise returns 1.
Usage:
scapCommonIsProfileInDS <DS> <PROFILE> || rlLog "Profile is not in datastream"
=cut
function scapCommonIsProfileInDS() {
local datastream="$1"
local profile="$2"
if [[ $profile != xccdf_org.ssgproject.content_profile_* ]]; then
profile="xccdf_org.ssgproject.content_profile_"$profile
fi
local query="string(//*[local-name()=\"Profile\"][@id=\"$profile\"]/*)"
local result="$(xmllint --xpath "$query" $datastream)"
if [ -z "$result" ]; then
rlLog "scapCommonIsProfileInDS: $profile profile is not in $datastream"
return 1
fi
rlLog "scapCommonIsProfileInDS: $profile profile is in $datastream"
return 0
}
true <<'=cut'
=pod
=head2 scapCommonIsRuleInProfile
Tries to find RULES in a PROFILE from DS. If PROFILE is 'all', then
it will try to find the rules in datastream regardless of the profile.
Returns 0, if all RULES are in PROFILE. Otherwise (rules not in profile,
or profile not in ds) returns 1.
Usage:
scapCommonIsRuleInProfile <DS> <RULES> <PROFILE> || rlLog "Rules not in profile"
Note: Rule prefix can be used, e.g. "dconf_" - will be checked
by "grep "xccdf_org.ssgproject.content_rule_dconf_" ds" or
"xmllint --xpath ...[starts-with(@idref, rule)].. ds"
=cut
function scapCommonIsRuleInProfile() {
local datastream="$1"
local rules="$2"
local profile="$3"
local ret_val=0
if [[ $profile == all ]]; then
for rule in ${rules}; do
if [[ $rule != xccdf_org.ssgproject.content_rule_* ]]; then
rule="xccdf_org.ssgproject.content_rule_"$rule
fi
if ! grep -q "$rule" "$datastream"; then
rlLog "scapCommonIsRuleInProfile: $rule rule is not in $datastream"
ret_val=1
else
rlLog "scapCommonIsRuleInProfile: $rule is in $profile profile from $datastream"
fi
done
return $ret_val
fi
if [[ $profile != xccdf_org.ssgproject.content_profile_* ]]; then
profile="xccdf_org.ssgproject.content_profile_"$profile
fi
scapCommonIsProfileInDS "$datastream" "$profile" || return 1
local query=""
for rule in ${rules}; do
if [[ $rule != xccdf_org.ssgproject.content_rule_* ]]; then
rule="xccdf_org.ssgproject.content_rule_"$rule
fi
query="count(//*[local-name()=\"Profile\"][@id=\"$profile\"]/*[local-name()=\"select\"][starts-with(@idref, \"$rule\")][@selected=\"true\"])"
local result="$(xmllint --xpath "$query" $datastream)"
if [ $result -eq 0 ]; then
rlLog "scapCommonIsRuleInProfile: $rule is not in $profile profile from $datastream"
ret_val=1
else
rlLog "scapCommonIsRuleInProfile: $rule is in $profile profile from $datastream"
fi
done
return $ret_val
}
true <<'=cut'
=pod
=head2 scapCommonInstallAnsible
Installs Ansible packages needed by Ansible remediations shipped in the
scap-security-guide package and returns 0 if installation is successful,
otherwise returns 1.
Usage:
scapCommonInstallAnsible || rlDie "Failed to install Ansible"
Note: Function utilizes Cleanup library and in the test using this function
it is expected that CleanupDo function is called in its cleanup phase.
=cut
function scapCommonInstallAnsible() {
if rlIsFedora; then
rlRun "dnf install -y ansible" || return 1
rlAssertRpm ansible
elif rlIsRHEL 7 || rlIsCentOS 7; then
if ! rpm -q epel-release &>/dev/null; then
rlRun "yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm"
CleanupRegister "rlRun 'yum remove -y epel-release'"
fi
rlRun "yum install --enablerepo=epel -y ansible" || return 1
CleanupRegister "rlRun 'yum remove -y ansible'"
rlAssertRpm ansible
else
rlRun "dnf install -y ansible-core" || return 1
CleanupRegister "rlRun 'yum remove -y ansible-core'"
rlAssertRpm ansible-core
rlRun "dnf install -y rhc-worker-playbook" 0-255
if [ $? -ne 0 ]; then
rlLog "rhc-worker-playbook RPM not found, installing required collections from Ansible Galaxy"
# ansible-galaxy does not support removal of collections through CLI.
CleanupRegister "rlRun 'rm -rf /root/.ansible/collections/ansible_collections/community/general'"
CleanupRegister "rlRun 'rm -rf /root/.ansible/collections/ansible_collections/ansible/posix/'"
for i in $(seq 1 10); do
local rv=0
rlLog "Install required collections from Ansible Galaxy (attempt: $i)"
rlRun "ansible-galaxy collection install community.general" 0-255 || rv=1
rlRun "ansible-galaxy collection install ansible.posix" 0-255 || rv=1
if [ $rv -eq 0 ]; then
break
else
if [ $i -eq 10 ]; then
rlFail "Failed to install required collections from Ansible Galaxy (attempt: $i)"
return 1
fi
rlLogWarning "Failed to install required collections from Ansible Galaxy (attempt: $i)"
rlLog "Waiting 1m before another attempt.."
sleep 1m
fi
done
rlRun "ansible-galaxy collection list -v" 0 "List installed Ansible collections"
else
CleanupRegister "rlRun 'yum remove -y rhc-worker-playbook'"
rlAssertRpm rhc-worker-playbook
rlRun "export ANSIBLE_COLLECTIONS_PATH=/usr/share/rhc-worker-playbook/ansible/collections/ansible_collections/" \
0 "Export path to Ansible collections from rhc-worker-playbook RPM"
fi
fi
return 0
}