Under Construction
Step 6: Implementing Undo
There are three ways to implement the undo functionality:
- Undo is not supported and therefore not implemented in the script. (Some standard scripts such as validate_check, rmdotfrmpathnroot, and others also do not support undo.)
- The script for setting and checking security settings creates a script that implements the undo function. (Some standard scripts, such as loginherald, rmrhostsnetrc, and others, do this.)
- The script for setting and checking security settings implements undo itself. (Some standard scripts such as chusrattr, nfsopts, and others implement undo themselves.)
In many cases, setting a security setting involves assigning a value to a variable or attribute in a configuration file. The corresponding implementation scripts then typically expect the value to be set (AIXPertArgs) as an argument when the script is called. If you query the original value before making the change, an undo is achieved by simply starting the script with the original value as an argument. Therefore, there is often no need to program any additional functionality.
Since our previous script does not support arguments, we will first show option 2.
Regardless of which of the two options is to be implemented, the script must add an entry for undo to the /etc/security/aixpert/core/undo.xml file when setting the security setting. The entry has almost the same format as entries in /etc/security/aixpert/core/appliedaixpert.xml. The entry requires a name, which is always passed by aixpert via the AIXPERT_NAME variable.
All standard scripts that support undo always expect the name of a rule as the last argument. Since we don’t have any arguments yet, we’ll expand our script and add a comment at the beginning about the command-line arguments, output, and description of the script:
# Command Line Arguments : This script expects exactly one command line argument.
# This argument should be the rulename.
# Syntac - local_chsshdconf rulename
#
# OUTPUT : None
#
# Description : This script sets the value of PermitRootLogin in /etc/ssh/sshd_config
# to 'no'.
# It dynamically generates the undo rules and scripts.
# This script should be run with superuser privileges.
#
The argument validation and usage notification must also be modified accordingly:
if [ $# -ne 1 ]
then
echo "Usage : local_chsshdconf rulename\n"
exit 1
fi
Since the script is called via an AIXPertEntry of the XML file custom/sshd.xml, we must also extend this XML entry accordingly with the argument:
/etc/security/aixpert # cat custom/sshd.xml
<?xml version="1.0" encoding="UTF-8"?>
<AIXPertSecurityHardening>
<AIXPertEntry name="test_permitrootlogin" function="permitrootlogin">
<AIXPertRuleType type="TEST"/>
<AIXPertDescription>Ensure that PermitRootLogin is False.</AIXPertDescription>
<AIXPertPrereqList>openssh.base.server</AIXPertPrereqList>
<AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCommand>
<AIXPertArgs>test_permitrootlogin</AIXPertArgs>
<AIXPertGroup>SSH policy rules</AIXPertGroup>
</AIXPertEntry>
</AIXPertSecurityHardening>
/etc/security/aixpert #
The name of the AIXPertEntry is used as an argument for the script call. This name is then available in the script via the variable “$1“.
Whether or not an undo entry should be generated is implemented in all standard scripts by setting the variable named undo. At the beginning of the script, the value of this variable is set to “0” (no undo entry generated):
# A value of 0 indicates that Undo rule need not be created.
# This variable will be set later in the script if there is anything to be undone
undo=0
If the security setting is checked (AIXPERT_CHECK=1), no undo entry is generated and the undo variable retains the value “0“. If the security setting is set, the undo variable is set to the value “1“:
# Generate a dynamic undo rule
undo=1
At the end of the script (after the sections for checking and setting the security settings), the undo variable is checked. If it has the value “1“, an XML entry for the undo is added to /etc/security/aixpert/core/undo.xml. If the script implements option 2 above, an additional undo script is created in the /etc/security/aixpert/undo directory. The XML entry then references this generated script.
if [ $undo -eq 1 ]
then
time=`date +%s`
# Create Undo script $SCPTDIR/Undo$1$time
…
fi
Since the undo function in AIX Security Expert is multi-level, a unique timestamp is generated using “date +%s“. The SCPTDIR variable has the value /etc/security/aixpert/undo (this is specified in /etc/security/aixpert/bin/initialize_variables). The variable “$1” contains the rule name, which is passed as the last argument when the script is called.
Changing PermitRootLogin to “yes” is not entirely elegant with the following two-liner:
sed -e 's/\(PermitRootLogin\) \(no\)/\1 yes/' /etc/ssh/sshd_config >/etc/ssh/sshd_config.new
mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config
Note: We will implement the (better) option 3 later and therefore will not try to find a good solution here.
As with the script for the security settings, local_chsshdconf, the entire process should be logged. Therefore, we’ll copy a few lines from the standard scripts. The following script should then be automatically generated in /etc/security/aixpert/undo:
#!/usr/bin/ksh
export PATH=/usr/bin:/usr/sbin:$PATH
exec 1>>$LOG
exec 2>&1
set -x
date
echo $0
sed -e 's/\(PermitRootLogin\) \(no\)/\1 yes/' /etc/ssh/sshd_config >/etc/ssh/sshd_config.new
mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config
To do this, we add the following lines to our local_chsshdconf script:
echo "#!/usr/bin/ksh\nexport PATH=/usr/bin:/usr/sbin:\$PATH\n" >$SCPTDIR/Undo$2$time
echo "exec 1>>\$LOG\nexec 2>&1\nset -x\ndate\necho" '$0' >>$SCPTDIR/Undo$2$time
echo "sed -e 's/\\(PermitRootLogin\\) \\(no\\)/\\1 yes/' /etc/ssh/sshd_config >/etc/ssh/sshd_config.new\n" >$SCPTDIR/Undo$2$time
echo "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config\n" >>$SCPTDIR/Undo$2$time
chmod ug+x $SCPTDIR/Undo$2$time
Next, an entry must be created in /etc/security/aixpert/core/undo.xml. The file path is stored in the variable UNDOXML. If our script is the first script called in the application, then the undo.xml file does not yet exist. This must be checked, and if so, a minimal version of the file must be created. To do this, the standard scripts use the following lines:
# Check if UNDOXML file has <AIXPertUndo> tag or not
empty=`grep "^<AIXPertUndo>" $UNDOXML`
if [ "$empty" = "" ]
then
echo "\n<AIXPertUndo>\n</AIXPertUndo>" >> $UNDOXML
fi
Rules entered in /etc/security/aixpert/core/appliedaixpert.xml and /etc/security/aixpert/core/undo.xml have slightly different rule names than those used in the original XML file. When applying a profile or security level to a system, the AIX Security Expert generates a unique 8-digit hexadecimal ID, which is appended to the original rule name, separated by an underscore. For example, test_permitrootlogin then becomes test_permitrootlogin_C515EA26. This extended rule name is stored in the variable AIXPERT_NAME. The rule description (AIXPertDescription) is stored in AIXPERT_DESC, and information for language-specific messages is stored in AIXPERT_DESC_CATMSGINFO. These three variables are passed to the script via the environment. Typically, these 3 variables are then assigned to new variables as follows:
# Get the rulename and put it in undo rule
name=`echo $AIXPERT_NAME`
# Get the rule description & pass it to undo rule
desc=`echo $AIXPERT_DESC`
desc_catmsginfo=`echo $AIXPERT_DESC_CATMSGINFO`
undomsg=`/usr/bin/dspmsg -s 49 aixpert.cat 1 "Undo action for: "`
Now you can add the necessary XML entry to /etc/security/aixpert/core/undo.xml. The entry should look something like this:
<AIXPertEntry name="test_permitrootlogin_C515EA26">
<AIXPertRuleType type="Undo"/>
<AIXPertDescription catalog="aixpert.cat" setNum="101" msgNum="15">Undo action for: " Ensure that PermitRootLogin is False."</AIXPertDescription>
<AIXPertPrereqList/>
<AIXPertCommand>/etc/security/aixpert/bin/local_chsshdconf</AIXPertCommand>
<AIXPertArgs> </AIXPertArgs>
<AIXPertGroup>SSH policy rules</AIXPertGroup>
</AIXPertEntry>
The standard scripts add the entry to the XML file using the following awk command:
# Add an undo XML rule to the file $UNDOXML
awk -v rulepart1="\t<AIXPertEntry name=\"$name\">\n\
\t\t<AIXPertRuleType type=\"Undo\"/>\n"\
-v rulepart2="\t\t<AIXPertDescription ${desc_catmsginfo}>$undomsg \"$desc\"</AIXPertDescription>\n\
\t\t<AIXPertPrereqList/>\n"\
-v rulepart3="\t\t<AIXPertCommand>$SCPTDIR/Undo$2$time</AIXPertCommand>\n\
\t\t<AIXPertArgs/>\n\t\t<AIXPertGroup>Miscellaneous Rules</AIXPertGroup>\n\
\t</AIXPertEntry>" '{if(match($0,"^[\t ]*</AIXPertUndo>")==0) print $0;
else print rulepart1 rulepart2 rulepart3 "\n" $0}' $UNDOXML >$TMP$PID
if [ $? -eq 0 ] ; then
mv $TMP$PID $UNDOXML
fi
The two variables TMP and PID are typically set at the beginning of the script, immediately after the file /etc/security/aixpert/bin/initialize_variables has been read:
TMP=/etc/security/aixpert/tmp/permitrootlogin
PID=$$
Overall, this results in the following version of the script:
/etc/security/aixpert # cat bin/local_chsshdconf
#! /bin/ksh
#
# Command Line Arguments : This script expects exactly one command line argument.
# This argument should be the rulename.
# Syntac - local_chsshdconf rulename
#
# OUTPUT : None
#
# Description : This script sets the value of PermitRootLogin in /etc/ssh/sshd_config
# to 'no'.
# It dynamically generates the undo rules and scripts.
# This script should be run with superuser privileges.
#
export PATH=/usr/bin:/usr/sbin:$PATH
# Initialize variables AIXPERT_FIFO, LOG, REPORT, SCPTDIR and UNDOXML
. /etc/security/aixpert/bin/initialize_variables
TMP=/etc/security/aixpert/tmp/permitrootlogin
PID=$$
# Log output and errors to /etc/security/aixpert/log/aixpert.log
exec 1>>$LOG
exec 2>&1
# echo all the commands and the current time and date to the AIXpert log
set -x
date
echo $0 $@ AIXPERT_CHECK_REPORT=$AIXPERT_CHECK_REPORT BASE_REPT=$BASE_REPT DETAILED_REPT=$DETAILED_REPT
if [ $# -ne 1 ]
then
echo "Usage : local_chsshdconf rulename\n"
exit 1
fi
# A value of 0 indicates that Undo rule need not be created.
# This variable will be set later in the script if there is anything to be undone
undo=0
# Check whether AIXPERT_CHECK_REPORT environment variable is set or not.
report=`echo $AIXPERT_CHECK_REPORT`
BrType=`echo $BASE_REPT`
DrType=`echo $DETAILED_REPT`
if [ "$report" = "1" ]
then
ret=1
permit=$( awk '$1 ~ /^PermitRootLogin$/ { print $2; }' /etc/ssh/sshd_config 2>/dev/null | tail -n 1 )
case "${permit}" in
[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]) ret=0 ;;
esac
if [ $ret = 1 ]
then
if [ "$BrType" = "1" ]
then
echo "Base Compliance Report\n";
printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" PermitRootLogin no $permit >>$TMPREPT
elif [ "$DrType" = "1" ]
then
echo "Description Report\n";
printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" PermitRootLogin no $permit >>$TMPREPT
else
printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" PermitRootLogin no $permit >>$REPORT
printf "local_chsshdconf: sshd attribute %s should have value %s, but it is %s now\n" PermitRootLogin no $permit >>$AIXPERT_FIFO
fi
fi
exit $ret
fi
# Generate a dynamic undo rule
undo=1
firstOccurrence=true
while read -r line
do
case "$line" in
PermitRootLogin*)
if $firstOccurrence
then
print "PermitRootLogin no"
firstOccurrence=false
fi
;;
*)
print $line ;;
esac
done </etc/ssh/sshd_config >/etc/ssh/sshd_config.new
$firstOccurrence && print "PermitRootLogin no" >>/etc/ssh/sshd_config.new
cp /etc/ssh/sshd_config.new /etc/ssh/sshd_config
rm /etc/ssh/sshd_config.new
if [ $undo -eq 1 ]
then
time=`date +%s`
# Create Undo script $SCPTDIR/Undo$1$time
echo "#!/usr/bin/ksh\nexport PATH=/usr/bin:/usr/sbin:\$PATH\n" >$SCPTDIR/Undo$2$time
echo "exec 1>>$LOG\nexec 2>&1\nset -x\ndate\necho" '$0' >>$SCPTDIR/Undo$2$time
echo "sed -e 's/\\(PermitRootLogin\\) \\(no\\)/\\1 yes/' /etc/ssh/sshd_config >/etc/ssh/sshd_config.new\n" >>$SCPTDIR/Undo$2$time
echo "mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config\n" >>$SCPTDIR/Undo$2$time
chmod ug+x $SCPTDIR/Undo$2$time
# Check if UNDOXML file has <AIXPertUndo> tag or not
empty=`grep "^<AIXPertUndo>" $UNDOXML`
if [ "$empty" = "" ]
then
echo "\n<AIXPertUndo>\n</AIXPertUndo>" >> $UNDOXML
fi
# Get the rulename and put it in undo rule
name=`echo $AIXPERT_NAME`
# Get the rule description & pass it to undo rule
desc=`echo $AIXPERT_DESC`
desc_catmsginfo=`echo $AIXPERT_DESC_CATMSGINFO`
undomsg=`/usr/bin/dspmsg -s 49 aixpert.cat 1 "Undo action for: "`
# Add an undo XML rule to the file $UNDOXML
awk -v rulepart1="\t<AIXPertEntry name=\"$name\">\n\
\t\t<AIXPertRuleType type=\"Undo\"/>\n"\
-v rulepart2="\t\t<AIXPertDescription ${desc_catmsginfo}>$undomsg \"$desc\"</AIXPertDescription>\n\
\t\t<AIXPertPrereqList/>\n"\
-v rulepart3="\t\t<AIXPertCommand>$SCPTDIR/Undo$2$time</AIXPertCommand>\n\
\t\t<AIXPertArgs/>\n\t\t<AIXPertGroup>Miscellaneous Rules</AIXPertGroup>\n\
\t</AIXPertEntry>" '{if(match($0,"^[\t ]*</AIXPertUndo>")==0) print $0;
else print rulepart1 rulepart2 rulepart3 "\n" $0}' $UNDOXML >$TMP$PID
if [ $? -eq 0 ] ; then
mv $TMP$PID $UNDOXML
fi
fi
exit 0
/etc/security/aixpert #
Before we try the script, we remove all currently set rules:
/etc/security/aixpert # aixpert -u
/etc/security/aixpert # aixpert -u
There are no security rules to undo in file /etc/security/aixpert/core/appliedaixpert.xml
/etc/security/aixpert # rm check_report.txt core/appliedaixpert.* core/undo.* log/* tmp/* undo/*
rm: cannot remove directory undo/data
/etc/security/aixpert #
Now we first apply our XML profile custom/sshd.xml:
/etc/security/aixpert # aixpert -f custom/sshd.xml
Processedrules=1 Passedrules=1 PrereqFailedrules=0 Failedrules=0 Level=TEST
Input file=custom/sshd.xml
/etc/security/aixpert #
We first check the undo.xml file:
/etc/security/aixpert # cat /etc/security/aixpert/core/undo.xml
<AIXPertUndo>
<AIXPertEntry name="test_permitrootlogin_4E952A4B">
<AIXPertRuleType type="Undo"/>
<AIXPertDescription catalog="" setNum="" msgNum="">Undo action for: "Ensure that PermitRootLogin is False."</AIXPertDescription>
<AIXPertPrereqList/>
<AIXPertCommand>/etc/security/aixpert/undo/Undo1670354323</AIXPertCommand>
<AIXPertArgs/>
<AIXPertGroup>Miscellaneous Rules</AIXPertGroup>
</AIXPertEntry>
</AIXPertUndo>
/etc/security/aixpert #
This has the expected contents. The stored AIXPertCommand is /etc/security/aixpert/undo/Undo1670354323. This should be the undo script generated by local_chsshdconf. We check the contents of the script:
/etc/security/aixpert # cat /etc/security/aixpert/undo/Undo1670354323
#!/usr/bin/ksh
export PATH=/usr/bin:/usr/sbin:$PATH
exec 1>>/etc/security/aixpert/log/aixpert.log
exec 2>&1
set -x
date
echo $0
sed -e 's/\(PermitRootLogin\) \(no\)/\1 yes/' /etc/ssh/sshd_config >/etc/ssh/sshd_config.new
mv /etc/ssh/sshd_config.new /etc/ssh/sshd_config
/etc/security/aixpert #
This is the expected generated content!
Finally, we check the value of PermitRootLogin:
# grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin no
#
This is the value desired by the security setting!
Next we perform an undo:
# aixpert -u
#
The test_permitrootlogin rule should have been removed from both the appliedaixpert.xml and undo.xml files. We’ll check this:
# cat /etc/security/aixpert/core/appliedaixpert.xml
<?xml version="1.0"?>
<AIXPertSecurityHardening>
</AIXPertSecurityHardening>
# cat /etc/security/aixpert/core/undo.xml
<AIXPertUndo>
</AIXPertUndo>
#
The rule has been removed from both files! Finally, we check the value of PermitRootLogin. The undo should reset the value to yes:
# grep ^PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin yes
#
The undo function worked as intended. However, before it could be used in practice, the script should be enhanced with better error detection and handling.