Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ncharles committed Aug 28, 2018
1 parent 2985042 commit 0b4c407
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 47 deletions.
232 changes: 232 additions & 0 deletions techniques/system/common/1.0/hooks.st
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,235 @@ bundle agent runhook_repoGpgKeyManagementGetKeys(json) {
contain => outputable,
module => "true";
}


########################################################
## SSH authorised keys specific hooks
########################################################


# Create the temporary ssh key files for all users, and create classes for all users that need their keys enforce
# in the format ssh_key_distribution_<username>_to_flush , and check if user is present
# with class ssh_key_distribution_user_<username>_exists
# It also computes, for each user,
# - the homedir homedir[<username>]
# - the gid gid[<username>]
# And create empty temp key files in /var/rudder/tmp/check_ssh_key_distribution/
bundle agent runhook_sshKeyDistribution_pre_hook(json) {
vars:
"definitions" data => parsejson("${json}");

"reporting" data => mergedata("definitions[reports]");

"reportkeys" slist => getindices("reporting");


# Here we find all the bundles that are tagged as check_ssh_key_distribution_technique
# we need to remove the default: from the variable name, as it causes issues if used in variable name definition
# Note: I cannot remove the defaults: from the bundlesmatching directly
"raw_bundles" slist => bundlesmatching("default:check_ssh_key_distribution_.*", "check_ssh_key_distribution_technique");
"bundles" slist => maplist(regex_replace("${this}", "default:", "", ""), "@{raw_bundles}");


# get all indices for each of the bundle
"array[${bundles}]" slist => getindices("${bundles}.sshkey_distribution_name");

# get the list of all users to manage in list userlist
# first, we put them in array (and ${bundles}_${array[${bundles}]} is canonified, so a valid name)
"data_userlist[${bundles}_${array[${bundles}]}]" string => "${${bundles}.sshkey_distribution_name[${array[${bundles}]}]}";

"userlist" slist => getvalues("data_userlist");


# Path to the temporary files created for the checking the files
"temp_ssh_key_path" string => "/var/rudder/tmp/check_ssh_key_distribution/";

# get the home dir for each users
# Only Linuxes (not Slackware), Solaris and FreeBSD support PAM/getent
pass1.(linux.!slackware)|solaris|freebsd::

"userdata_${userlist}"
string => execresult("/usr/bin/getent passwd ${userlist}", "noshell");

# On systems without PAM, directly read entries from /etc/passwd instead (compatibility)
pass1.!((linux.!slackware)|solaris|freebsd)::

"userdata_${userlist}"
string => execresult("/usr/bin/grep ^${userlist}: /etc/passwd", "noshell");

pass2::
"no_${userlist}"
int => parsestringarray("userarray_${userlist}", "${userdata_${userlist}}", "", ":", "1000", "200000" ),
ifvarclass => "ssh_key_distribution_user_${userlist}_exists";

"homedir[${userlist}]"
string => "${userarray_${userlist}[${userlist}][5]}",
ifvarclass => "ssh_key_distribution_user_${userlist}_exists";

"gid[${userlist}]"
string => "${userarray_${userlist}[${userlist}][3]}",
ifvarclass => "ssh_key_distribution_user_${userlist}_exists";

classes:
# define if there is at least one enforce
"is_enforce" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce");

# For reporting, detect each enforce/audit - and publish it in the global namespace, so that we get it back in ssh_key_distribution
"is_enforce_${reporting[${reportkeys}][id]}" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce"),
scope => "namespace";

pass1::
# check if user is present on the system
"ssh_key_distribution_user_${userlist}_exists" expression => userexists("${userlist}"),
scope => "namespace";

any::
"pass2" expression => "pass1";
"pass1" expression => "any";


files:
pass2::
"${temp_ssh_key_path}/${userlist}.authorized_keys.tmp"
create => "true",
edit_line => delete_lines_matching(".*"),
edit_defaults => empty,
ifvarclass => "ssh_key_distribution_user_${userlist}_exists";




reports:
pass2::
" ssh_key_distribution_user_${userlist}_exists" ifvarclass => "ssh_key_distribution_user_${userlist}_exists";

}







# Compare the ssh key file with the temporary one
# We also need to know if any of the user is in enforce
bundle agent runhook_sshKeyDistribution_post_hook(json) {
vars:
"definitions" data => parsejson("${json}");

"reporting" data => mergedata("definitions[reports]");

"reportkeys" slist => getindices("reporting");


# get size of generated tmp files
"tmp_ssh_file_size_${runhook_sshKeyDistribution_pre_hook.userlist}" int => filesize("${runhook_sshKeyDistribution_pre_hook.temp_ssh_key_path}/${runhook_sshKeyDistribution_pre_hook.userlist}.authorized_keys.tmp"),
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";


# get the size of the ssh key
"ssh_file_size_${runhook_sshKeyDistribution_pre_hook.userlist}" int => filesize("${runhook_sshKeyDistribution_pre_hook.homedir[${sshkey_distribution_name[${sshkey_distribution_index}]}]}/.ssh/authorized_keys"),
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";


# DETECT IF THE USER IS IN AUDIT OR IN ENFORCE
# Here we find all the bundles that are tagged as check_ssh_key_distribution_technique
"bundles" slist => bundlesmatching("default:check_ssh_key_distribution_.*", "check_ssh_key_distribution_technique");

# get all indices for each of the bundle
"array[${bundles}]" slist => getindices("${bundles}.sshkey_distribution_name");
"audit_mode[${bundles}]" string => "${${bundles}.is_audit_mode}";



classes:
# check if files have the same size
"same_size_for_${runhook_sshKeyDistribution_pre_hook.userlist}" expression => strcmp("${tmp_ssh_file_size_${runhook_sshKeyDistribution_pre_hook.userlist}}", "${ssh_file_size_${runhook_sshKeyDistribution_pre_hook.userlist}}");


# Identical files are if:
# No error/repair for a user + identical file size
"no_change_on_ssh_file_for_${runhook_sshKeyDistribution_pre_hook.userlist}" expression => "same_size_for_${runhook_sshKeyDistribution_pre_hook.userlist}.!(check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_repaired|check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_error)",
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";

# file differents and No error/repair for a user -> maybe need to flush
"same_keys_different_size_ssh_file_for_${runhook_sshKeyDistribution_pre_hook.userlist}" expression => "!same_size_for_${runhook_sshKeyDistribution_pre_hook.userlist}.!(check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_repaired|check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_error)",
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";

# same sizes, but repaired -> could be that we repaired, and no need to flush - this will be settled when the copy of file will be done
"same_size_but_repaired_keys_${runhook_sshKeyDistribution_pre_hook.userlist}" expression => "same_size_for_${runhook_sshKeyDistribution_pre_hook.userlist}.(check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_repaired|check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_error)",
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";

# different size and repaired/error -> yeah, we repaired
"ssh_keys_repaired_${runhook_sshKeyDistribution_pre_hook.userlist}" expression => "!same_size_for_${runhook_sshKeyDistribution_pre_hook.userlist}.(check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_repaired|check_ssh_key_distribution_user_key_${runhook_sshKeyDistribution_pre_hook.userlist}_error)",
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";


# define if there is at least one enforce
"is_enforce" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce");

# For reporting, detect each enforce/audit
"is_enforce_${reportkeys}" expression => strcmp("${reporting[${reportkeys}][mode]}", "enforce");


# define for each bundle a class if in enforce
"enforce_mode[${bundles}]" expression => strcmp("${audit_mode[${bundles}]}", "false");

# define the class "enforce_ssh_key_distribution_<username>_to_flush" for bundle not in audit mode, where we want to flush
"enforce_ssh_key_distribution_${${bundles}.sshkey_distribution_name[${array[${bundles}]}]}_to_flush" expression => strcmp("${${bundles}.sshkey_distribution_edit_type[${array[${bundles}]}]}", "true"),
ifvarclass => "enforce_mode[${bundles}]";

# audit is for all user that are to flush, but not the class enforce_ssh_key_distribution_<username>_to_flush set
"audit_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush" expression => "any",
ifvarclass => "!enforce_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush.ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush";




any::
"pass2" expression => "pass1";
"pass1" expression => "any";


files:
# replace file if size if same_keys_different_size_ssh_file_for_<user> or ssh_keys_repaired_<user> or same_size_but_repaired_keys_<user> and ssh_key_distribution_<username>_to_flush, and in ENFORCE (we need to know when enforce)
# do we also need to have the report id ? maybe ...
"${runhook_sshKeyDistribution_pre_hook.homedir[${runhook_sshKeyDistribution_pre_hook.userlist}]}/.ssh/authorized_keys"
copy_from => copy_digest("${runhook_sshKeyDistribution_pre_hook.temp_ssh_key_path}/${runhook_sshKeyDistribution_pre_hook.userlist}.authorized_keys.tmp"),
classes => classes_generic("FIND_A_NAME_${runhook_sshKeyDistribution_pre_hook.userlist}"),
ifvarclass => "enforce_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush.(same_keys_different_size_ssh_file_for_${runhook_sshKeyDistribution_pre_hook.userlist}|ssh_keys_repaired_${runhook_sshKeyDistribution_pre_hook.userlist}|same_size_but_repaired_keys_${runhook_sshKeyDistribution_pre_hook.userlist})";


# auditing file replacement
"${runhook_sshKeyDistribution_pre_hook.homedir[${runhook_sshKeyDistribution_pre_hook.userlist}]}/.ssh/authorized_keys"
copy_from => copy_digest("${runhook_sshKeyDistribution_pre_hook.temp_ssh_key_path}/${runhook_sshKeyDistribution_pre_hook.userlist}.authorized_keys.tmp"),
action => WarnOnly,
classes => classes_generic("FIND_A_NAME_${runhook_sshKeyDistribution_pre_hook.userlist}"),
ifvarclass => "audit_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush.(same_keys_different_size_ssh_file_for_${runhook_sshKeyDistribution_pre_hook.userlist}|ssh_keys_repaired_${runhook_sshKeyDistribution_pre_hook.userlist}|same_size_but_repaired_keys_${runhook_sshKeyDistribution_pre_hook.userlist})";


# finally, delete the files
# "${runhook_sshKeyDistribution_pre_hook.temp_ssh_key_path}/${runhook_sshKeyDistribution_pre_hook.userlist}.authorized_keys.tmp"
# delete => tidy;


reports:
"reporting is ${reporting}";
"reportkeys is ${reportkeys}";

" flushing file for user ${runhook_sshKeyDistribution_pre_hook.userlist} "
ifvarclass => "enforce_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush";

" auditing flushing file for user ${runhook_sshKeyDistribution_pre_hook.userlist} "
ifvarclass => "audit_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush";


" neither flushing nor enforcing for ${runhook_sshKeyDistribution_pre_hook.userlist} "
ifvarclass => "!enforce_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush.!audit_ssh_key_distribution_${runhook_sshKeyDistribution_pre_hook.userlist}_to_flush";


"user exists, of course ${runhook_sshKeyDistribution_pre_hook.userlist} "
ifvarclass => "ssh_key_distribution_user_${runhook_sshKeyDistribution_pre_hook.userlist}_exists";


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<OS version=">= 4 (Nahant)">RHEL / CentOS</OS>
<OS version=">= 10 SP1 (Agama Lizard)">SuSE LES / DES / OpenSuSE</OS>
<OS version=">= 5.3">AIX</OS>
<AGENT version=">= 3.4.4">cfengine-community</AGENT>
<AGENT version=">= 3.7">cfengine-community</AGENT>
</COMPATIBLE>

<MULTIINSTANCE>true</MULTIINSTANCE>
<POLICYGENERATION>separated</POLICYGENERATION>

<BUNDLES>
<NAME>check_ssh_key_distribution</NAME>
<NAME>check_ssh_key_distribution_RudderUniqueID</NAME>
</BUNDLES>

<TMLS>
<TML name="sshKeyDistribution"/>
</TMLS>

<RUNHOOKS>
<PRE bundle="runhook_sshKeyDistribution_pre_hook">
<REPORT name="PLACEHOLDER"/>
</PRE>
<POST bundle="runhook_sshKeyDistribution_post_hook">
<REPORT name="Flush SSH Key"/>
<PARAMETER name="Username" value="SSH_KEY_DISTRIBUTION_NAME"/>
</POST>
</RUNHOOKS>

<!-- Policy Instance Settings -->
<TRACKINGVARIABLE>
<SAMESIZEAS>SSH_KEY_DISTRIBUTION_NAME</SAMESIZEAS>
Expand Down
Loading

0 comments on commit 0b4c407

Please sign in to comment.