def aide(EMAIL): # File/Folder Integrity Monitoring With AID f_aide = '/etc/default/aide' if not main.answer('Do you want Monitoring File/Folder Integrity With AIDE?'): return main.run_cmd('apt install aide -y') main.output_message('Configuration File/Folder Monitoring tools..') main.change_lines( f_aide, _1=[ 'MAILTO=root', f'MAILTO={EMAIL}\n' ], _2=[ '#CRON_DAILY_RUN=yes', 'CRON_DAILY_RUN=yes\n' ] ) main.output_message( "Aide, for work, needs to create a new database and install it" "\nThis process requires time..." ) main.run_cmd_interactive("aideinit -y -f") main.run_cmd("cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db") main.run_cmd("update-aide.conf") main.run_cmd("cp /var/lib/aide/aide.conf.autogenerated /etc/aide/aide.conf")
def rkhunter(EMAIL): # Rootkit Detection With Rkhunter f_rkhunter_conf = '/etc/rkhunter.conf' if not General.answer('Do you want to install and configure rkhunter?'): return General.run_cmd('apt install rkhunter -y') General.error_message( '[ALERT] If this is the first installation select yes to the three questions you will be asked to answer ' ) time.sleep(5) General.output_message('Configuring rkhunter..') General.change_lines( f_rkhunter_conf, _1=['UPDATE_MIRRORS=0', 'UPDATE_MIRRORS=1\n'], _2=['MIRRORS_MODE=1', 'MIRRORS_MODE=0\n'], _3=['#MAIL-ON-WARNING=root', f'MAIL-ON-WARNING={EMAIL}\n'], _4=['#COPY_LOG_ON_ERROR=0', 'COPY_LOG_ON_ERROR=1\n'], _5=['#PKGMGR=NONE', 'PKGMGR=DPKG\n'], _6=['#PHALANX2_DIRTEST=0', 'PHALANX2_DIRTEST=1\n'], _7=['#USE_LOCKING=0', 'USE_LOCKING=1\n'], _8=[ '#SHOW_SUMMARY_WARNINGS_NUMBER=0', 'SHOW_SUMMARY_WARNINGS_NUMBER=1\n' ], _9=['WEB_CMD="/bin/false"', 'WEB_CMD=""\n']) subprocess.check_call(['dpkg-reconfigure', 'rkhunter'], stderr=subprocess.STDOUT) General.run_cmd('rkhunter --propupd', 'rkhunter --update') General.output_message('Rkhunter Done')
def maldet(EMAIL): # Anti-Virus Scanning With Maldet dir_path = '/usr/local/src/' if not General.answer( 'Do you want to use Maldet for Anti-virus Scanning?'): return General.run_cmd( f'wget -q http://www.rfxn.com/downloads/maldetect-current.tar.gz -P {dir_path}' ) General.output_message("Maldet Downloaded") tarfile = '/usr/local/src/maldetect-current.tar.gz' path = '/usr/local/src/' retcode = subprocess.call(['tar', '-xvf', tarfile, '-C', path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for elem in listdir(dir_path): if ('maldetect-' in elem) and (not 'maldetect-current' in elem): maldect_path = dir_path + elem file_path = f'{maldect_path}/files/conf.maldet' General.run_cmd(f'sh {maldect_path}/install.sh') General.output_message('Maldet Installed') General.output_message('Configuring Maldet..') General.change_lines( file_path, _1=['email_alert="0"', 'email_alert="1"\n'], _2=['email_addr="*****@*****.**"', f'email_alert="{EMAIL}"\n'], _3=['email_ignore_clean="1"', 'email_ignore_clean="0"\n']) break
def autamated_email_allerts(EMAIL_ENABLE, EMAIL): f_exm_conf = '/etc/exim4/update-exim4.conf.conf' file_path_1 = '/etc/exim4/passwd.client' f_exim_localmacros = '/etc/exim4/exim4.conf.localmacros' f_exim_template = '/etc/exim4/exim4.conf.template' f_exim_autogenerated = '/var/lib/exim4/config.autogenerated' # if not EMAIL_ENABLE: return General.run_cmd('apt install exim4 openssl ca-certificates -y') General.output_message('Editing configuration files..') General.run_cmd('chown root:Debian-exim /etc/exim4/passwd.client') General.run_cmd('chown 640 /etc/exim4/passwd.client') if not os.path.exists('/usr/share/doc/exim4-base/examples/exim-gencert'): General.run_cmd('bash /usr/share/doc/exim4-base/examples/exim-gencert') with open(f_exim_localmacros, 'w') as file: file.write(''' MAIN_TLS_ENABLE = 1 REMOTE_SMTP_SMARTHOST_HOSTS_REQUIRE_TLS = * TLS_ON_CONNECT_PORTS = 465 REQUIRE_PROTOCOL = smtps IGNORE_SMTP_LINE_LENGTH_LIMIT = true ''') General.run_cmd(f'wget -q https://raw.githubusercontent.com/TheeBlind/Exim4_Gmail_conf/master/nconf/config.autogenerated -O {f_exim_autogenerated}') General.run_cmd(f'wget -q https://raw.githubusercontent.com/TheeBlind/Exim4_Gmail_conf/master/nconf/exim4.conf.template -O {f_exim_template}') General.run_cmd(f'wget -q https://raw.githubusercontent.com/TheeBlind/Exim4_Gmail_conf/master/nconf/update-exim4.conf.conf -O {f_exm_conf}') local_ip = subprocess.check_output(['hostname', '-I'], stderr=subprocess.STDOUT) local_ip = str(local_ip).strip("b'n\\ ") General.change_lines(f_exim_template, _1=[ '* changeme Ffrs', f"* {local_ip}@{General.USER}.{General.HOSTNAME} Ffrs\n" ]) General.change_lines( f_exim_autogenerated, _1=[ '* chengeme Ffrs', f"* {local_ip}@{General.USER}.{General.HOSTNAME} Ffrs\n" ]) General.run_cmd('update-exim4.conf') General.run_cmd('systemctl restart exim4') General.run_cmd(f"""echo "Automatic SSlock notification test, if you received this mail, you can go ahead in the setup. if you have received this mail by mistake, please delete it." | mail -s 'Test!' {EMAIL}""") General.output_message('Mail setup Completed') response = General.answer(f'We have sent a test mail to the address {EMAIL}. Have you recived it?\nCheck the spam folder!') if not response: General.output_message( """ If you received no mail you can check the exim4 logs for investigate about the problem > sudo cat /var/log/exim4/mainlog > sudo cat /var/log/exim4/paniclog > check if something is blocking port 25 out """) time.sleep(5)
def psad(EMAIL): # iptables Intrusion Detection And Prevention with f_psad_conf = '/etc/psad/psad.conf' f_before_rules = '/etc/ufw/before.rules' f_before6_rules = '/etc/ufw/before6.rules' if not General.answer('Do you want to install and configure psad?'): return General.run_cmd('apt install psad -y') homenet = General.input_message('Type your Home net: ex(192.168.1.1/24) ') General.output_message('Configuring psad') General.change_lines( f_psad_conf, _1=[ 'EMAIL_ADDRESSES root@localhost;', f'EMAIL_ADDRESSES {EMAIL};\n' ], _2=[ 'HOSTNAME _CHANGEME_;', f'HOSTNAME {General.HOSTNAME};\n' ], _3=[ 'ENABLE_AUTO_IDS N;', 'ENABLE_AUTO_IDS Y;\n' ], _4=[ 'HOME_NET any;', f'HOME_NET {homenet};\n' ], _5=[ 'PORT_RANGE_SCAN_THRESHOLD 1;', 'PORT_RANGE_SCAN_THRESHOLD 2;\n' ], _6=[ 'ENABLE_MAC_ADDR_REPORTING N;', 'ENABLE_MAC_ADDR_REPORTING Y;\n' ], _7=[ 'ALERT_ALL Y;', 'ALERT_ALL N;\n' ]) General.output_message(f''' These changes have been made to the config '/etc/psad/psad.conf'. If u need more advanced options feel free to modify it. - EMAIL_ADDRESSES {EMAIL}; - HOSTNAME {General.HOSTNAME}; - ENABLE_AUTO_IDS Y; - HOME_NET {homenet}; - PORT_RANGE_SCAN_THRESHOLD 2; - ENABLE_MAC_ADDR_REPORTING Y; - ALERT_ALL N; ''') with open(f_before_rules, 'r') as file: lines = file.readlines() text = file.read() if not (General.check_lines_in_file( f_before_rules, '# log all traffic so psad can analyze', '-A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES]"', '-A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES]"' )): for index, line in enumerate(lines): if line.strip() == "COMMIT": lines.insert( index - 2, '# log all traffic so psad can analyze\n-A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES]"\n-A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES]"' ) break with open(f_before_rules, 'w') as file: for line in lines: file.write(line) with open(f_before6_rules, 'r') as file: lines = file.readlines() text = file.read() if not (General.check_lines_in_file( f_before_rules, '# log all traffic so psad can analyze', '-A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES]"', '-A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES]"' )): for index, line in enumerate(lines): if line.strip() == "COMMIT": lines.insert( index - 2, '# log all traffic so psad can analyze\n-A INPUT -j LOG --log-tcp-options --log-prefix "[IPTABLES]"\n-A FORWARD -j LOG --log-tcp-options --log-prefix "[IPTABLES]"' ) break with open(f_before6_rules, 'w') as file: for line in lines: file.write(line) General.warning_message('Restarting psad and Firewall..\n') General.warning_message('Maybe existing ssh connections will be stopped\n') General.run_cmd('ufw --force enable') General.run_cmd('ufw reload') subprocess.check_call(['psad', '-R'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) General.run_cmd('psad --sig-update') General.run_cmd('psad -H') General.output_message('Done')
def wpVirtualHost(EMAIL): #################################################### # SETUP APACHE VIRTUALHOST ( FOR MULTIPLE WEBSITES ) #################################################### General.output_message("-- Creation of apache2 Virtual Host --") DOMAIN_FULL = General.input_message("Insert a domain[ domain.(com/.it/.org/etc..) ] ") DOMAIN = '.'.join(DOMAIN_FULL.split(".")[:-1]) if len(DOMAIN_FULL.split(".")) > 2 else DOMAIN_FULL.split(".")[0] General.run_cmd(f"mkdir /var/www/{DOMAIN}") # create a directory for the virtual host General.run_cmd(f"chown -R $USER:$USER /var/www/{DOMAIN}") # assign the directory to $USER General.run_cmd(f"chmod -R 755 /var/www/{DOMAIN}") # be sure that permissions are correct f_apache_domain_conf = f'/etc/apache2/sites-available/{DOMAIN}.conf' with open(f_apache_domain_conf, 'w') as file: file.write( """ <VirtualHost *:80> ServerAdmin """ + EMAIL + """ ServerName """ + DOMAIN_FULL + """ ServerAlias www.""" + DOMAIN_FULL + """ DocumentRoot /var/www/""" + DOMAIN + """ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost> <Directory /var/www/""" + DOMAIN + """/> AllowOverride All </Directory> """) General.run_cmd(f"sudo a2ensite {DOMAIN}.conf") # Enable the new configuration ( you can enable as many different configs as you want ) General.run_cmd(f"sudo a2dissite 000-default.conf") # Disable the default configuration General.run_cmd("sudo a2enmod rewrite") General.run_cmd("sudo systemctl restart apache2") # restart apache # Test apache configs # sudo apachectl -t -D DUMP_VHOSTS # sudo apachectl configtest # Add SSL to apache virtualhost General.run_cmd("apt install certbot python3-certbot-apache -y") General.run_cmd("mkdir /var/lib/letsencrypt") General.run_cmd("chmod -R o+rx /var/lib/letsencrypt") General.output_message(""" -- Certbot configuration -- If questions are promp type the following answers: > Terms of service: A > Share mail address: N > redirect HTTP traffic to HTTPS: 2 > If the validation of the domain go wrong just exit the script and investigate on the problem. You can restart the script on the same domain and it will work correctly""") General.run_cmd_interactive(f"sudo certbot --apache -d {DOMAIN_FULL} -d www.{DOMAIN_FULL}") # runs interactive certbot configuration General.output_message('If certbot failed chek the apache2 logs ( Ubuntu: /var/log/apache2/error.log ) ') # wordpress virtualhost setup General.output_message("-- Configuring wordpress Db --") DB_NAME = General.input_message("Database Name ") DB_USER = General.input_message("Database User Name ") DB_USER_PASS = General.sensitive_input_message("Database User Password ") General.run_cmd( f""" mysql -e " CREATE DATABASE {DB_NAME} DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; " """, f""" mysql -e " CREATE USER '{DB_USER}'@'localhost' IDENTIFIED BY '{DB_USER_PASS}'; " """, f""" mysql -e " GRANT ALL PRIVILEGES ON {DB_NAME}.* TO '{DB_USER}'@'localhost'; " """, """ mysql -e " FLUSH PRIVILEGES; " """ ) if(not os.path.exists('/tmp/wordpress')): General.output_message("\n-- WORDPRESS DOWNLOAD --") General.run_cmd("wget -P /tmp 'https://wordpress.org/latest.tar.gz' ") General.run_cmd("tar -C /tmp -zxvf /tmp/latest.tar.gz") General.run_cmd("touch /tmp/wordpress/.htaccess") General.run_cmd("cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php") General.run_cmd("mkdir /tmp/wordpress/wp-content/upgrade") General.run_cmd(f"cp -a /tmp/wordpress/. /var/www/{DOMAIN}") General.run_cmd(f"chown -R www-data:www-data /var/www/{DOMAIN}") General.run_cmd("find /var/www/" + DOMAIN + "/ -type d -exec chmod 750 {} \;") General.run_cmd("find /var/www/" + DOMAIN + "/ -type f -exec chmod 640 {} \;") f_wp_domain_config = f"/var/www/{DOMAIN}/wp-config.php" General.run_cmd("apt install curl -y") OUTPUT_KEYS = General.output_run_cmd("curl -s https://api.wordpress.org/secret-key/1.1/salt/").decode( "utf-8").split("');") for key in OUTPUT_KEYS: if key.strip() == '': OUTPUT_KEYS.remove(key) OUTPUT_KEYS_DICT = dict() for index, key in enumerate(OUTPUT_KEYS): index += 1 OUTPUT_KEYS_DICT["_" + str(index)] = [ '( '.join(key.split(",")[0].split('(')).strip() + ',', key.strip() + "');\n" ] General.change_lines( f_wp_domain_config, **OUTPUT_KEYS_DICT ) General.change_lines( f_wp_domain_config, _1=["define( 'DB_NAME',", f"define( 'DB_NAME', '{DB_NAME}');"], _2=["define( 'DB_USER',", f"define( 'DB_USER', '{DB_USER}');"], _3=["define( 'DB_PASSWORD',", f"define( 'DB_PASSWORD', '{DB_USER_PASS}');"], ) General.insert_lines( f_wp_domain_config, "define( 'FS_METHOD', 'direct');", index_surplus=1, anchor_line=f"define( 'DB_PASSWORD', 'DB_USER_PASS');" ) General.output_message('-- INFO --') General.output_message(f" > Finish the WP GUI installation going to https://{DOMAIN}") General.output_message(" > We did a setup for SSL certificate with Let’s Encrypt CERTBOT, THis Certificate expire") General.output_message(" after 90 days, A Bot that automatic does the renewal is also installed, Check that ") General.output_message(" it is working by typing 'sudo systemctl status certbot.timer' ") General.output_message(" > For check that PHP was installed sucesfully and it is working correctly do this steps: ") General.output_message(f" - sudo nano /var/www/{DOMAIN}/info.php") General.output_message(" - Add the following line to the file '<?php phpinfo(); ?>' ") General.output_message(f" - Visit this link 'http://{DOMAIN_FULL}/info.php'") General.output_message(f" If a table with php information was displayed and nothing went wrong remove the file\n" f" previously created 'sudo rm /var/www/{DOMAIN}/info.php'") time.sleep(5)
def lynis(): if not General.answer( 'Do you want install Lynis for upgrade your security level?'): return General.run_cmd('git clone https://github.com/CISOfy/lynis /opt/lynis') General.output_message('Lynis installed in > /opt/lynis') General.output_message('Running lynis scan..') output = str( subprocess.check_output(['./lynis', 'audit', 'system', '-Q'], cwd="/opt/lynis", stderr=subprocess.STDOUT), 'utf-8') General.output_message('Scan completed') output = General.remove_ansi_escape(output) lynis_output_sections = dict() output = output.split('[+]') for section in output: if ('------------------------------------') in section: section = section.split('------------------------------------') if section[0].strip() == 'Plugins (phase 2)': section[1] = section[1].split( '================================================================================' ) lynis_output_sections[ section[0].strip()] = section[1][0].strip() section[1][1] = section[1][1].split('Suggestions') section[1][1][1] = section[1][1][1].split( '----------------------------') lynis_output_sections['Suggestions'] = section[1][1][1][1] else: lynis_output_sections[section[0].strip()] = section[1].strip() if ('Kernel Hardening' in lynis_output_sections): section = lynis_output_sections['Kernel Hardening'] section = section.split('-') for line in section: line = line.strip() if '[ DIFFERENT ]' in line: line = line.split() for word in line: if ')' in word: word = word.strip('()exp: ') if len(word) == 1: word = word[0] elif len(word) == 2: word = word[1] elif len(word) == 3: word = word[2] print(f'sysctl -w {line[0].strip()}={word}') General.run_cmd(f'sysctl -w {line[0].strip()}={word}') print('\n\n\n') if ('Suggestions' in lynis_output_sections): suggestions = lynis_output_sections['Suggestions'].split('*') suggestions_list = [] for suggestion in suggestions: suggestion = suggestion.split('\n') suggestions_list.append(suggestion[0].strip()) if 'Protect rescue.service by using sulogin [BOOT-5260]' in suggestions_list: task = 'BOOT-5260' General.output_message(f'Configuring {task}') with open('/usr/lib/systemd/system/rescue.service', 'r') as file: lines = file.readlines() for index, line in enumerate(lines): if 'ExecStart' in line.strip().split('='): lines[index] = 'ExecStart=-/usr/sbin/sulogin' with open('/usr/lib/systemd/system/rescue.service', 'w') as f_w: for line in lines: f_w.write(line) General.output_message(f'{task}') if 'Determine priority for available kernel update [KRNL-5788]' in suggestions_list: # https://phoenixnap.com/kb/how-to-update-kernel-ubuntu task = 'KRNL-5788' General.output_message(f'Configuring {task}') General.run_cmd('apt-get update') General.output_message('System updated') General.run_cmd('apt-get dist-upgrade') if 'Configure minimum password age in /etc/login.defs [AUTH-9286]' in suggestions_list or\ 'Configure maximum password age in /etc/login.defs [AUTH-9286]' in suggestions_list or \ 'Default umask in /etc/login.defs could be more strict like 027 [AUTH-9328]' in suggestions_list: task = 'AUTH-9286' General.change_lines( '/etc/login.defs', _1=['UMASK 022', 'UMASK 027\n'], _2=['PASS_MAX_DAYS 99999', 'PASS_MAX_DAYS 30\n'], _3=['PASS_MIN_DAYS 0', 'PASS_MIN_DAYS 1\n'], _4=['PASS_WARN_AGE 7', 'PASS_WARN_AGE 7\n'], ) General.output_message(f'Configuring {task}') else: General.error_message( 'Something went wrong, cannot analize lynis scan output')
def clamAv(EMAIL): # Anti-Virus Scanning With ClamAV f_clam_conf = '/etc/clamav/freshclam.conf' if not General.answer('Do you want to use ClamAV for Anti-virus Scanning with root permissions?'): return General.run_cmd('apt install clamav clamav-freshclam -y') General.change_lines( f_clam_conf, _1=[ '# Check for new database 24 times a day', '# Check for new database 1 times a day\n' ], _2=[ 'Checks 24', 'Checks 1\n' ] ) General.run_cmd('freshclam -v') user = '' while True: user = General.input_message( "Using clamscan as root is dangerous because if a file is in fact a virus there is risk that it could use the root privileges.\n" "You can create another user by writing [U] \nPlease chose a user that can run clamscan" ) if UsersConfig.user_exists(user): break if user.upper() == 'U': UsersConfig.create_users() else: General.error_message("User doesn't exists") General.output_message('For create a new user type [U]') dir_to_scan = '' all_directories_exists = False while not all_directories_exists: all_directories_exists = True dir_to_scan = General.input_message('Which direcotry should be scanned by clamAV? (ex. /var/www /var/vmail): ') for dir in dir_to_scan.split(): if not os.path.exists(dir): General.error_message(f"The directory {dir} doesn't exists, select another path") all_directories_exists = False General.output_message('Configuration Anti-virus Scanning tools..') f_clam_daily = f'/home/{user}/clamscan_daily.sh' with open(f_clam_daily, 'w') as file: file.write(f''' #!/bin/bash LOGFILE="/var/log/clamav/clamav-$(date +'%Y-%m-%d').log"; EMAIL_MSG="Please see the log file attached."; EMAIL_FROM="''' + EMAIL + '''"; DIRTOSCAN="''' + dir_to_scan + '''"; for S in ${DIRTOSCAN}; do DIRSIZE=$(du -sh "$S" 2>/dev/null | cut -f1); echo "Starting a daily scan of "$S" directory. Amount of data to be scanned is "$DIRSIZE"."; clamscan -ri "$S" >> "$LOGFILE"; # get the value of "Infected lines" MALWARE=$(tail "$LOGFILE"|grep Infected|cut -d" " -f3); # if the value is not equal to zero, send an email with the log file attached if [ "$MALWARE" -ne "0" ];then # using heirloom-mailx below echo "$EMAIL_MSG"|mail -A "$LOGFILE" -s "Malware Found" "$EMAIL_FROM"; fi done exit 0 ''') General.run_cmd('chmod 0755 /root/clamscan_daily.sh') General.run_cmd('ln /root/clamscan_daily.sh /etc/cron.daily/clamscan_daily') General.run_cmd('service clamav-freshclam start') if not General.answer("Do you want to run the virus scan on the given directories now? This can take some time"): return General.output_message('Running first scan for check if all is ok') subprocess.run(['clamscan', '-r', dir_to_scan])
def ssh(SSH_ENABLE, EMAIL_ENABLE, SSH_PORT, EMAIL): f_ssh_config = '/etc/ssh/sshd_config' if not SSH_ENABLE or not EMAIL_ENABLE: return General.run_cmd("apt install ssh -y") options_list = [ 'Port', 'ClientAliveCountMax', 'ClientAliveInterval', 'LoginGraceTime', 'MaxAuthTries', 'MaxSessions', 'MaxStartups', 'PasswordAuthentication', 'AllowGroups', 'PermitRootLogin' ] for option in options_list: status1 = General.check_lines_in_file(f_ssh_config, option) status2 = General.check_lines_in_file(f_ssh_config, '#' + option) if not status1 and not status2: with open(f_ssh_config, 'a') as file: file.write(option + '\n') generate_key = General.answer('Do you want enable ssh for some users?') if generate_key: generate_root_key = General.answer( f'Do you also want to enable ssh for Current user [{General.USER}] ? ' ) if generate_root_key: key_path = f'/{General.USER}/.ssh/id_rsa' if not os.path.exists(key_path): key_pass = General.input_message( 'Enter a passphrase for the key (empty for no passphrase):' ) General.run_cmd( f'ssh-keygen -C "{EMAIL}" -b 2048 -t rsa -f {key_path} -q -N "{key_pass}"' ) General.warning_message( f'[ALERT] your private key has been sent by email to {EMAIL}, please retrieve and save it, after destroy the email\n' ) time.sleep(3) General.run_cmd( f'echo " DANGER here you will find your private key attached for your device, destroy this mail and its contents instantly." | mail -s "Private key for: {General.USER} on {General.HOSTNAME}" {EMAIL} -A {key_path}' ) else: General.error_message( 'A key pair for the current user yet exists') else: General.change_lines( f_ssh_config, _19=['#PermitRootLogin yes', 'PermitRootLogin no\n'], _20=['PermitRootLogin yes', 'PermitRootLogin no\n']) General.run_cmd('groupadd sshusers') General.output_message("which user do you want to have ssh enabled?") while True: user = General.input_message("User to enable ssh [S to skip]: ") if user.upper() in ['S', 'SKIP']: break if not UsersConfig.user_exists(user): General.error_message("User doesn't exists") else: General.run_cmd(f'usermod -a -G sshusers {user}') generate_key_for_user = General.answer( f'[{user}] Do you want a ssh key pair for this user?') if generate_key_for_user: key_path = f'/home/{user}/.ssh/id_rsa' if not os.path.exists(f'/home/{user}/.ssh'): os.mkdir(f'/home/{user}/.ssh') if not os.path.exists(key_path): key_pass = General.input_message( 'Enter a passphrase for the key (empty for no passphrase):' ) General.run_cmd( f'ssh-keygen -C "{EMAIL}" -b 2048 -t rsa -f {key_path} -q -N "{key_pass}"' ) General.warning_message( f'[ALERT] your private key has been sent by email to {EMAIL}, please retrieve and save it, after destroy the email\n' ) time.sleep(3) General.run_cmd( f'echo " DANGER here you will find your private key attached for your device, destroy this mail and its contents instantly." | mail -s "Private key for: {user} on {General.HOSTNAME}" {EMAIL} -A {key_path}' ) else: General.error_message( f'A key pair for the user "{user}" yet exists') General.change_lines( f_ssh_config, _1=['Port', f"Port {SSH_PORT}\n"], _2=['#Port', f"Port {SSH_PORT}\n"], _3=['ClientAliveCountMax', "ClientAliveCountMax 0\n"], _4=['#ClientAliveCountMax', "ClientAliveCountMax 0\n"], _5=['ClientAliveInterval', "ClientAliveInterval 300\n"], _6=['#ClientAliveInterval', "ClientAliveInterval 300\n"], _7=['LoginGraceTime', "LoginGraceTime 30\n"], _8=['#LoginGraceTime', "LoginGraceTime 30\n"], _9=['MaxAuthTries', "MaxAuthTries 2\n"], _10=['#MaxAuthTries', "MaxAuthTries 2\n"], _11=['MaxSessions', "MaxSessions 2\n"], _12=['#MaxSessions', "MaxSessions 2\n"], _13=['MaxStartups', "MaxStartups 2\n"], _14=['#MaxStartups', "MaxStartups 2\n"], _15=['PasswordAuthentication', "PasswordAuthentication no\n"], _16=['#PasswordAuthentication', "PasswordAuthentication no\n"], _17=['AllowGroups', "AllowGroups sshusers\n"], _18=['#AllowGroups', "AllowGroups sshusers\n"], ) General.output_message('These changes have been made to the config') General.warning_message('/etc/ssh/sshd_config') General.output_message( f'''If u need more advanced options feel free to modify it. - Port {SSH_PORT}; - ClientAliveCountMax 0; - ClientAliveInterval 300; - LoginGraceTime 30; - MaxAuthTries 2; - MaxSessions 2; - MaxStartups 2; - PasswordAuthentication no; - AllowGroups sshusers; - PermitRootLogin no; ''') General.output_message( 'removing all Diffie-Hellman keys that are less than 3072 bits long' ) General.run_cmd( "awk '$5 >= 3071' /etc/ssh/moduli | sudo tee /etc/ssh/moduli.tmp") General.run_cmd('mv /etc/ssh/moduli.tmp /etc/ssh/moduli') response = General.answer('do you want to enable 2FA/MFA for SSH?') if response: General.warning_message( 'Note: a user will only need to enter their 2FA/MFA code if they are logging on with their password but not if they are using SSH public/private keys.\n' ) General.run_cmd('apt install libpam-google-authenticator -y') General.warning_message( '\n[ALERT] Notice this can not run as root, at the next machine restart RUN "google-authenticator"\n' ) time.sleep(5) General.output_message( 'Select default option (y in most cases) for all the questions it asks and remember to save the emergency scratch codes.' ) time.sleep(2) General.run_cmd( 'echo -e "\nauth required pam_google_authenticator.so nullok # added by $(whoami) on $(date +"%Y-%m-%d @ %H:%M:%S")" | tee -a /etc/pam.d/sshd' )