def configure_sshd(): """5.2 SSH Server Configuration""" # 5.2.1 Ensure permissions on /etc/ssh/sshd_config are configured exec_shell([ 'chown root:root /etc/ssh/sshd_config', 'chmod og-rwx /etc/ssh/sshd_config' ]) # 5.2.2 - 5.2.16 PropertyFile('/etc/ssh/sshd_config', ' ').override({ 'Protocol': '2', 'LogLevel': 'INFO', 'X11Forwarding': 'no', 'MaxAuthTries': '4', 'IgnoreRhosts': 'yes', 'HostbasedAuthentication': 'no', 'PermitRootLogin': '******', 'PermitEmptyPasswords': 'no', 'PermitUserEnvironment': 'no', 'Ciphers': 'aes256-ctr,aes192-ctr,aes128-ctr', 'MACs': '[email protected],[email protected],[email protected],hmac-sha2-512,hmac-sha2-256,[email protected]', 'ClientAliveInterval': '300', 'ClientAliveCountMax': '0', 'LoginGraceTime': '60', 'AllowUsers': 'ec2-user', 'Banner': '/etc/issue.net' }).write()
def configure_mta(): """2.2.15 Ensure mail transfer agent is configured for local - only mode""" exec_shell(['mkdir -p /etc/postfix', 'touch /etc/postfix/main.cf']) PropertyFile('/etc/postfix/main.cf', ' = ').override({ 'inet_interfaces': 'localhost' }).write()
def configure_cron(): """5.1 Configure cron""" # 5.1.1 Ensure cron daemon is enabled Service('crond').enable() # 5.1.2 - 5.1.8 exec_shell([ 'chown root:root /etc/crontab', 'chmod og-rwx /etc/crontab', 'chown root:root /etc/cron.hourly', 'chmod og-rwx /etc/cron.hourly', 'chown root:root /etc/cron.daily', 'chmod og-rwx /etc/cron.daily', 'chown root:root /etc/cron.weekly', 'chmod og-rwx /etc/cron.weekly', 'chown root:root /etc/cron.monthly', 'chmod og-rwx /etc/cron.monthly', 'chown root:root /etc/cron.d', 'chmod og-rwx /etc/cron.d', 'rm -f /etc/cron.deny', 'rm -f /etc/at.deny', 'touch /etc/cron.allow', 'touch /etc/at.allow', 'chmod og-rwx /etc/cron.allow', 'chmod og-rwx /etc/at.allow', 'chown root:root /etc/cron.allow', 'chown root:root /etc/at.allow' ])
def configure_password_parmas(): """5.4.1 Set Shadow Password Suite Parameters""" PropertyFile('/etc/login.defs', '\t').override({ 'PASS_MAX_DAYS': '90', 'PASS_MIN_DAYS': '7', 'PASS_WARN_AGE': '7' }).write() exec_shell(['useradd -D -f 30'])
def configure_umask(): """5.4.3, 5.4.4""" umask_reg = r'^(\s*)umask\s+[0-7]+(\s*)$' bashrc = exec_shell([ 'cat /etc/bashrc | sed -E "s/{}/\\1umask 027\\2/g"'.format(umask_reg) ]) File('/etc/bashrc').write(bashrc) profile = exec_shell([ 'cat /etc/profile | sed -E "s/{}/\\1umask 027\\2/g"'.format(umask_reg) ]) File('/etc/profile').write(profile)
def secure_boot_settings(): """1.4 Secure Boot Settings""" if os.path.isfile('/boot/grub/menu.lst'): exec_shell([ 'chown root:root /boot/grub/menu.lst', 'chmod og-rwx /boot/grub/menu.lst' ]) PropertyFile('/etc/sysconfig/init', '=').override({ 'SINGLE': '/sbin/sulogin', 'PROMPT': 'no' }).write()
def configure_iptables(): """3.6 Firewall Configuration""" Package('iptables').install() exec_shell([ 'iptables -F', 'iptables -P INPUT DROP', 'iptables -P OUTPUT DROP', 'iptables -P FORWARD DROP', 'iptables -A INPUT -i lo -j ACCEPT', 'iptables -A OUTPUT -o lo -j ACCEPT', 'iptables -A INPUT -s 127.0.0.0/8 -j DROP', 'iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT', 'iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT', 'iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT', 'iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT', 'iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT', 'iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT', 'iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT', 'iptables-save' ])
def ensure_sticky_bit(): """1.1.18 Ensure sticky bit is set on all world - writable directories""" try: return exec_shell([ 'df --local -P | awk {\'if (NR!=1) print $6\'} | xargs -I \'{}\' find \'{}\' -xdev -type d -perm -0002 2>/dev/null | xargs chmod a+t' ]) except CalledProcessError: return 1
def configure_chrony(upstream): """2.2.1 Time Synchronization""" # 2.2.1.1 Ensure time synchronization is in use Package('ntp').remove() Package('chrony').install() # 2.2.1.3 Ensure chrony is configured PropertyFile('/etc/chrony.conf', ' ').override({ 'server': upstream }).write() PropertyFile('/etc/sysconfig/chronyd', '=').override({ 'OPTIONS': '"-u chrony"' }).write() exec_shell([ 'chkconfig chronyd on', ])
def enable_aide(): """1.3 Filesystem Integrity Checking""" cron_job = '0 5 * * * /usr/sbin/aide --check' Package('aide').install() return exec_shell([ 'aide --init', 'mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz', '(crontab -u root -l 2>/dev/null | grep -v /usr/sbin/aide; echo "{}") | crontab -'.format(cron_job) ])
def configure_tcp_wrappers(hosts): """3.4 TCP Wrappers""" # 3.4.1 Ensure TCP Wrappers is installed Package('tcp_wrappers').install() if hosts: # 3.4.2 Ensure /etc/hosts.allow is configured allowed_hosts = ','.join(hosts) exec_shell('echo "ALL: {}" > /etc/hosts.allow'.format(allowed_hosts)) # 3.4.3 Ensure /etc/hosts.deny is configured exec_shell('echo "ALL: ALL" > /etc/hosts.deny') # 3.4.4 Ensure permissions on /etc/hosts.allow are configured exec_shell( ['chown root:root /etc/hosts.allow', 'chmod 644 /etc/hosts.allow']) # 3.4.5 Ensure permissions on /etc/hosts.deny are configured exec_shell( ['chown root:root /etc/hosts.deny', 'chmod 644 /etc/hosts.deny'])
def configure_mac(): """1.6. Mandatory Access Control""" Package('selinux-policy').install() Package('selinux-policy-targeted').install() Package('policycoreutils-python').install() kernel=exec_shell([ 'cat /boot/grub/menu.lst | grep ^kernel' ]) # add selinux=1 if not 'selinux' in kernel: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/^(kernel.*)$/\\1 selinux=1/"' ]) else: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/(selinux)=0/\\1=1/g"' ]) File('/boot/grub/menu.lst').write(boot) # add security=selinux if not 'security' in kernel: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/^(kernel.*)$/\\1 security=selinux/"' ]) else: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/^(kernel.*security=)[^ ]*(.*)/\\1selinux\\2/g"' ]) File('/boot/grub/menu.lst').write(boot) boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/(enforcing)=0/\\1=1/g"' ]) File('/boot/grub/menu.lst').write(boot) exec_shell([ 'echo "SELINUX=enforcing\nSELINUXTYPE=targeted" > /etc/selinux/config', 'chown root:root /etc/selinux/config', 'chmod 0600 /etc/selinux/config', 'touch /.autorelabel' ])
def setup_env(self): # Create experiments directory if it doesn't exist if not os.path.isdir(os.path.join(self.rootdir, exp_common.EXP_DIR)): os.makedirs(os.path.join(self.rootdir, exp_common.EXP_DIR)) # Make the results directory for this experiment if not os.path.isdir(self.exp_results): os.makedirs(self.exp_results) # Save the description and info save_descr(os.path.join(self.exp_results, exp_common.DESCR_FILE), self.info); # Make the experiment directories and checkout code. Do it # here so that you fail in the root node of the cluster, if # you fail if os.path.isdir(self.expdir): shutil.rmtree(self.expdir) try: os.mkdir(self.expdir) except OSError: print 'Experimental directory could not be created or already exists.' print 'Aborting.' exit(1) if self.subdir_only: checkout_dir = working_dir else: checkout_dir = '.' # checkout the appropriate commit # can do this with git --work-tree=... checkout commit -- ., but # cannot do concurrently, so use git archive... # ... whose behavior seems to depend on current directory rootdir=util.abs_root_path() os.chdir(rootdir) sts = util.exec_shell('git archive {} {} | tar xC {}' .format(self.info['commit'], checkout_dir, self.expdir)) if sts != 0: print 'Attempt to checkout experimental code failed' exit(1)
def configure_warning_banners(): """1.7 Warning Banners""" # 1.7.1 Command Line Warning Banners exec_shell([ 'update-motd --disable', 'chown root:root /etc/motd', 'chmod 644 /etc/motd' ]) File('/etc/motd').write(get_string_asset('/etc/motd')) exec_shell(['chown root:root /etc/issue', 'chmod 644 /etc/issue']) File('/etc/issue').write('Authorized uses only. All activity may be monitored and reported.\n') exec_shell(['chown root:root /etc/issue.net', 'chmod 644 /etc/issue.net']) File('/etc/issue.net').write('Authorized uses only. All activity may be monitored and reported.\n')
def configure_pam(): """5.3 Configure PAM""" def convert_password(line): if password_unix_re.match(line): if 'remember=5' not in line: line += ' remember=5' if 'sha512' not in line: line += ' sha512' return line password_unix_re = re.compile(r'^password\s+sufficient\s+pam_unix.so') password_auth_content = get_string_asset('/etc/pam.d/password-auth') password_auth_content += exec_shell([ 'cat /etc/pam.d/password-auth | grep -v "^auth"' ]) password_auth_content = '\n'.join([ convert_password(line) for line in password_auth_content.splitlines() ]) with open('/etc/pam.d/password-auth-local', 'w') as f: f.write(password_auth_content) exec_shell(['ln -sf /etc/pam.d/password-auth-local /etc/pam.d/password-auth']) system_auth_content = get_string_asset('/etc/pam.d/system-auth') system_auth_content += exec_shell([ 'cat /etc/pam.d/system-auth | grep -v "^auth"' ]) system_auth_content = '\n'.join([ convert_password(line) for line in system_auth_content.splitlines() ]) with open('/etc/pam.d/system-auth-local', 'w') as f: f.write(system_auth_content) exec_shell( ['ln -sf /etc/pam.d/system-auth-local /etc/pam.d/system-auth']) PropertyFile('/etc/security/pwquality.conf', '=').override({ 'minlen': '14', 'dcredit': '-1', 'ucredit': '-1', 'ocredit': '-1', 'lcredit': '-1' }).write()
def configure_su(): """5.5 Ensure access to the su command is restricted""" File('/etc/pam.d/su').write(get_string_asset('/etc/pam.d/su')) exec_shell('usermod -aG wheel root')
def configure_auditd(): """4.1.1 Configure Data Retention""" PropertyFile('/etc/audit/auditd.conf', ' = ').override({ 'max_log_file': '8', 'space_left_action': 'email', 'action_mail_acct': 'root', 'admin_space_left_action': 'halt', 'max_log_file_action': 'keep_logs' }).write() kernel=exec_shell([ 'cat /boot/grub/menu.lst | grep ^kernel' ]) # add audit=1 if not 'audit' in kernel: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/^(kernel.*)$/\\1 audit=1/"' ]) else: boot = exec_shell([ 'cat /boot/grub/menu.lst | sed -E "s/(audit)=0/\\1=1/g"' ]) File('/boot/grub/menu.lst').write(boot) audit_rules = """-D -b 320 -a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change -a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change -a always,exit -F arch=b64 -S clock_settime -k time-change -a always,exit -F arch=b32 -S clock_settime -k time-change -w /etc/localtime -p wa -k time-change -w /etc/group -p wa -k identity -w /etc/passwd -p wa -k identity -w /etc/gshadow -p wa -k identity -w /etc/shadow -p wa -k identity -w /etc/security/opasswd -p wa -k identity -a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale -a always,exit -F arch=b32 -S sethostname -S setdomainname -k system-locale -w /etc/issue -p wa -k system-locale -w /etc/issue.net -p wa -k system-locale -w /etc/hosts -p wa -k system-locale -w /etc/sysconfig/network -p wa -k system-locale -w /etc/sysconfig/network-scripts/ -p wa -k system-locale -w /etc/selinux/ -p wa -k MAC-policy -w /usr/share/selinux/ -p wa -k MAC-policy -w /var/log/lastlog -p wa -k logins -w /var/run/faillock/ -p wa -k logins -w /var/run/utmp -p wa -k session -w /var/log/wtmp -p wa -k logins -w /var/log/btmp -p wa -k logins -a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod -a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access -a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access -a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access -a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access -a always,exit -F arch=b64 -S mount -F auid>=500 -F auid!=4294967295 -k mounts -a always,exit -F arch=b32 -S mount -F auid>=500 -F auid!=4294967295 -k mounts -a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete -a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete -w /etc/sudoers -p wa -k scope -w /etc/sudoers.d/ -p wa -k scope -w /var/log/sudo.log -p wa -k actions -w /sbin/insmod -p x -k modules -w /sbin/rmmod -p x -k modules -w /sbin/modprobe -p x -k modules -a always,exit -F arch=b64 -S init_module -S delete_module -k modules -e 2 """ bashrc = exec_shell([ 'echo "{}" > /etc/audit/audit.rules'.format(audit_rules) ])
def repo_gpg_settings(): exec_shell([ 'sed -i "s/gpgcheck=0$/gpgcheck=1/" /etc/yum.conf /etc/yum.repos.d/*' ])
def configure_log_file_permissions(): """4.2.4 Ensure permissions on all logfiles are configured""" exec_shell([r'find /var/log -type f -exec chmod g-wx,o-rwx {} +'])
def configure_system_file_permission(): """6.1 System File Permissions""" exec_shell([ 'chmod 000 /etc/shadow /etc/shadow- /etc/gshadow /etc/gshadow-', 'chmod 644 /etc/passwd /etc/passwd- /etc/group /etc/group-' ])
def run_command(args): cmd = read_command_args(args) sys.exit(util.exec_shell(cmd))