diff --git a/README.md b/README.md index 1eded46..4cb8c05 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,45 @@ # Overview -This is a collection of bash/shell scripts to assist in linux server administration -designed to be run by a Unix shell, a command-line interpreter to assist in -automating, management and reporting tasks. +Script to provide html reporting of a system status. +Provides an offline/regular report to lessen the need to install resource heavy software like webadmin on a server -# Prerequisites -

-**Bash (the Bourne Again SHell) - version 5.1+** - -**Linux/Unix System** - -> [!IMPORTANT] -> Other versions of bash are not tested/support, for testing you can utilise [Shell Check](https://www.shellcheck.net/) -> -> The scripts have not been designed to work with non-unix/linux systems +The main focus of the script is to be secure, simple and focus on using less resources as possible +while still providing a good amount of information for server administration -**The scripts are very basic and easy to use/customise** +The scripts were previous written in bash/shell script, this is still available in the inital release of this project. -

-## **SURE - System Utilization and Risk Evaluation** -

- -### :basecamp: System Audit Report - - -A script to provide a basic audit of the system. Can be run from crontab to provide a daily/weekly report as needed. -Provides a basic snapshot of the server with the following information - - - -:star: Current Users -:star: Running Procs -:star: Top 10 Memory Usage -:star: Top 10 CPU Usage -:star: Open Files - -

- -### :basecamp: System Security Report - +# Prerequisites - -A script to provide a basic security report of the system. Can be run from crontab to provide daily/weekly report as needed. -Provides a basic snapshot of the following information from the server - - +Requires a Linux/Unix System and Python 3, please see the `COMMANDS` section of the script for commands utilising in the reporting. - -:star: failog report -:star: No Owner Files -:star: Set GID Files -:star: Set UID Files -:star: World Writeable Files -:star: Open Ports -:star: Current Services - +# Configuration +> LOG_FILE = "" +This should be the directory and file name for the log output and errors -

-## **ALERT - Auth Log and Event Reporting Tool** -

- -### :basecampy: Auth Log Report - +> OUTPUT_HTML = "" +Directory and file name for the HTML output to be generated too - -A script to generate a HTML report of the current server auth requests for the past 3 hours -Provides a regular report for the each 3 hours of the `auth.log` on the server to audit server -connection attempts and logins. -Can be run via the crontab to produce the html report -:star: a script snipplet is available to provide an admin with a copy of the `auth.log` from the -log file directory - -

- -### :basecampy: System Status Report - +> LAST_RUN_FILE = "" +The directory and lock file name to use for tracking the last run time - -A script to generate a HTML report of the server stats for the administrator or operators -The html report generates includes the following information and can be run via the crontab - +> MAIL_CONFIG = "" +> +This is configuration for the mail settings +> +`Enabled` +TRUE would enable to use of mailing the output. +FALSE would disable the option of mailing the outputted HTML file. +> +`recipient` +This should be the email to send the html out/stats file to. +> +`Subject` +This is the subject line of the email/stats email if enabled. +> +`mail_command` +You can set the default mail command here. - -:star: Current Open Ports -:star: Current Connections -:star: System Log Entries from the past 2 hours -:star: System Critical Entry's (Priority 2) -:star: System Alert Entry's (Priority 1) -:star: System Emergency Entry's (Priority 0) - +> [!IMPORTANT] +> It is not recommend to change the `COMMANDS` section unless you are able to configure the out correctly. +> We do not provide support for customising this section of the script diff --git a/authlog_report.sh b/authlog_report.sh deleted file mode 100644 index 74b7f4c..0000000 --- a/authlog_report.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/bash - -# Settings/Variable -LOG_FILE="/home//auth.log" # Path/Directory to your log file 'authlog' file -OUTPUT_HTML="/home//authlog.html" # Path/Directory and File name for the html report - -# Get the current date and time -CURRENT_DATE=$(date +"%b %d %H:%M:%S") - -# Get the date and time 3 hours ago -PAST_DATE=$(date --date="3 hours ago" +"%b %d %H:%M:%S") - -# Defines the name of the server -MY_NAME=$(hostname) - -# Start the HTML file with basic structure and styles -# Style color is RED -cat < "$OUTPUT_HTML" - - - - - - Auth Log Report - - - -

Authentication Report for $MY_NAME

- - - - - - - -EOF - -# Process the log file to extract entries from the past 3 hours -awk -v past_date="$PAST_DATE" -v current_date="$CURRENT_DATE" ' -{ - log_time = sprintf("%s %s %s", $1, $2, $3); - if (log_time >= past_date && log_time <= current_date) { - print $0; - } -}' "$LOG_FILE" | grep -E "Accepted|Failed password|session opened" | while read -r line; do - # Extract timestamp - timestamp=$(echo "$line" | awk '{print $1, $2, $3}') - - # Extract username correctly (handles both "invalid user" and normal cases) - # or applies 'N/A' if unknown or not found - username=$(echo "$line" | grep -oP "for (invalid user |user )\K\w+" || echo "N/A") - - # Extract IP address - ip=$(echo "$line" | grep -oP "(\d{1,3}\.){3}\d{1,3}") - - # If the IP is blank, skip this entry - # prevents a lot of false entries - if [[ -z "$ip" ]]; then - continue - fi - - # Append the details as a table row to the HTML file - echo " " >> "$OUTPUT_HTML" - echo " " >> "$OUTPUT_HTML" - echo " " >> "$OUTPUT_HTML" - echo " " >> "$OUTPUT_HTML" - echo " " >> "$OUTPUT_HTML" -done - -# Close the HTML output -cat <> "$OUTPUT_HTML" -
Connection Attempts
TimestampUsernameIP Address
$timestamp$username$ip
- - -EOF - -# Print success message -# -- if running from crontab -# Please comment this out by putting -# '#' before the word 'echo' -echo "HTML report generated: $OUTPUT_HTML" \ No newline at end of file diff --git a/sysstatus_report.py b/sysstatus_report.py new file mode 100644 index 0000000..8f36fdf --- /dev/null +++ b/sysstatus_report.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 + +import os +import subprocess +from datetime import datetime +import logging +import fcntl +from jinja2 import Template + +# Configurable Options +LOG_FILE = "/var/log/sysstatus_report.log" # Log file for output and errors +OUTPUT_HTML = "/var/log/sys_status_report.html" # Output file for the HTML report +LOCK_FILE = "/var/lock/sysstatus_report.lock" # Lock file to prevent concurrent execution +MAIL_CONFIG = { + 'enabled': True, # Enable/disable email sending + 'recipient': 'reports@myserver.com', # Recipient email address + 'subject': 'System Status Report', # Email subject + 'mail_command': '/usr/bin/mail -a "Content-type: text/html" -s "{subject}" {recipient} < {html_file}' # Command to send mail +} + +# Commands to gather system data +COMMANDS = { + 'failed_services': 'systemctl --type=service --state=failed', + 'package_updates': 'apt list --upgradeable 2>/dev/null', + 'top_cpu_processes': 'ps auxf | sort -nr -k 3 | head -10', + 'top_mem_processes': 'ps auxf | sort -nr -k 4 | head -10', + 'no_owner_files': 'find / -xdev \( -nouser -o -nogroup \) -print', + 'setuid_setgid_files': 'find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -print', + 'world_writable_files': 'find / -xdev -type f -perm -o+w -print', + 'disk_usage': 'df -h', + 'iostat': 'iostat -x 1 5', + 'uptime': 'uptime', + 'network_stats_tcp': 'netstat -at', + 'network_stats_udp': 'netstat -au', + 'memory_usage': 'free -h', + 'load_average': 'uptime | awk \'{print "Load average: " $10 " " $11 " " $12}\'', + 'auth_log': 'grep "Failed password" /var/log/auth.log', # Check for failed logins + 'journal_crit': 'journalctl -p 0 -n 15 --no-pager', # Priority 0 (Emergency) + 'journal_alert': 'journalctl -p 1 -n 15 --no-pager', # Priority 1 (Alert) + 'journal_crit': 'journalctl -p 2 -n 15 --no-pager', # Priority 2 (Critical) + 'journal_error': 'journalctl -p 3 -n 15 --no-pager', # Priority 3 (Error) + 'journal_warning': 'journalctl -p 4 -n 15 --no-pager', # Priority 4 (Warning) +} + +# HTML Template for the report +HTML_TEMPLATE = """ + + + + System Status Report + + + +

System Status Report for {{ hostname }}

+

Report generated on {{ current_time }}

+ + {% for section_title, section_data in sections.items() %} +

{{ section_title }}

+ + +
{{ section_data }}
+ {% endfor %} + + +""" + +def setup_logging(): + logging.basicConfig( + filename=LOG_FILE, + level=logging.INFO, + format='%(asctime)s %(levelname)s:%(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + console = logging.StreamHandler() + console.setLevel(logging.ERROR) + formatter = logging.Formatter('%(asctime)s %(levelname)s:%(message)s') + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + +def run_command(command): + """Run a shell command and return the output.""" + try: + result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=300) + if result.returncode != 0: + logging.error(f"Command failed: {command}\nError: {result.stderr.strip()}") + return f"Error: {result.stderr.strip()}" + return result.stdout.strip() + except subprocess.TimeoutExpired: + logging.error(f"Command timed out: {command}") + return "Error: Command timed out." + +def gather_system_data(): + """Gather system data and return it as a dictionary.""" + data = {} + data['hostname'] = run_command("hostname") + data['current_time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + sections = {} + for section_name, command in COMMANDS.items(): + sections[section_name.replace("_", " ").title()] = run_command(command) + + return { + 'hostname': data['hostname'], + 'current_time': data['current_time'], + 'sections': sections + } + +def generate_html_report(data): + """Generate the HTML report using Jinja2.""" + template = Template(HTML_TEMPLATE) + html_content = template.render(data) + + try: + with open(OUTPUT_HTML, 'w') as f: + f.write(html_content) + logging.info(f"Report generated: {OUTPUT_HTML}") + except Exception as e: + logging.error(f"Failed to write HTML report: {e}") + +def mail_report(): + """Mail the generated HTML report.""" + if MAIL_CONFIG['enabled']: + mail_command = MAIL_CONFIG['mail_command'].format( + subject=MAIL_CONFIG['subject'], + recipient=MAIL_CONFIG['recipient'], + html_file=OUTPUT_HTML + ) + try: + result = subprocess.run(mail_command, shell=True, capture_output=True, text=True) + if result.returncode != 0: + logging.error(f"Mail command failed: {result.stderr.strip()}") + else: + logging.info(f"Mail sent to {MAIL_CONFIG['recipient']}") + except Exception as e: + logging.error(f"Failed to send email: {e}") + +def acquire_lock(lock_file): + """Acquire a file lock to prevent concurrent executions.""" + try: + lock_fd = open(lock_file, 'w') + try: + fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + return lock_fd + except IOError: + logging.error("Another instance of the script is running.") + sys.exit(1) + except IOError as e: + logging.error(f"Error opening lock file: {e}") + sys.exit(1) + +def release_lock(lock_fd): + """Release the lock and close the file descriptor.""" + try: + fcntl.flock(lock_fd, fcntl.LOCK_UN) + lock_fd.close() + except IOError as e: + logging.error(f"Error releasing lock: {e}") + +def main(): + setup_logging() + + # Acquire lock to prevent concurrent runs + lock_fd = acquire_lock(LOCK_FILE) + + try: + # Gather system data + data = gather_system_data() + + # Generate HTML report + generate_html_report(data) + + # Mail the report + mail_report() + + finally: + release_lock(lock_fd) + +if __name__ == "__main__": + main() diff --git a/sysstatus_report.sh b/sysstatus_report.sh deleted file mode 100644 index 1c20817..0000000 --- a/sysstatus_report.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/bash - -# Log file to analyze (replace with actual path if needed) -LOG_FILE="/home//auth.log" - -# The filename to use for the html report -OUTPUT_HTML="report.html" - -# Get today's date in the format of the log system log file -TODAYS_DATE=$(date +"%b %d") - -# Get the current time -CURRENT_TIME=$(date +"%H:%M") - -# Get the time two hours ago -TWO_HOURS_AGO=$(date -d '2 hours ago' +"%H:%M") - -# delete the old file as we will update -if [ -f $OUTPUT_HTML ]; then - rm $OUTPUT_HTML -fi - -# Updated HTML Code for better email format -# The Color Scheme is currently BLUE - -# Create or overwrite the HTML report file -echo "" > $OUTPUT_HTML -echo "System Report $(hostname)" >> $OUTPUT_HTML - -# Add inline CSS for email-friendly formatting -echo "" >> $OUTPUT_HTML -echo "

System Report

" >> $OUTPUT_HTML - -# Netstat TCP Connections -echo "

Netstat TCP Connections (-at)

" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML -netstat -at | tail -n +3 | while read proto local_addr foreign_addr state; do - echo "" >> $OUTPUT_HTML -done -echo "
ProtoLocal AddressForeign AddressState
$proto$local_addr$foreign_addr$state
" >> $OUTPUT_HTML - -# Netstat UDP Connections -echo "

Netstat UDP Connections (-au)

" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML -netstat -au | tail -n +3 | while read proto local_addr foreign_addr; do - echo "" >> $OUTPUT_HTML -done -echo "
ProtoLocal AddressForeign Address
$proto$local_addr$foreign_addr
" >> $OUTPUT_HTML - -# Table of log entries by today's date only -echo "

Log Entries (Past 2 hours)

" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML -echo "" >> $OUTPUT_HTML - -# Filter logs by today's date and the last two hours -grep "$TODAYS_DATE" $LOG_FILE | awk -v current_time="$CURRENT_TIME" -v two_hours_ago="$TWO_HOURS_AGO" ' -{ - # Extract the time (hour:minute) from the log line (4th field) - log_time = substr($3, 1, 5) - - # Check if the log time is between two_hours_ago and current_time - if (log_time >= two_hours_ago && log_time <= current_time) { - # Print log entry with date and message (cut off the first 5 fields for the message) - print "" - } -}' >> $OUTPUT_HTML - -echo "
DateMessage
" $1, $2, $3 "" substr($0, index($0,$6)) "
" >> $OUTPUT_HTML - -# Function to add journalctl output, check if "-- No entries --" is returned -add_journalctl_section() { - PRIORITY=$1 - TITLE=$2 - - echo "

Journalctl Output (Priority $PRIORITY - $TITLE)

" >> $OUTPUT_HTML - echo "" >> $OUTPUT_HTML - echo "" >> $OUTPUT_HTML - - # Capture the journalctl output - JOURNAL_OUTPUT=$(journalctl -p $PRIORITY -n 5 --no-pager) - - if [[ "$JOURNAL_OUTPUT" == *"-- No entries --"* ]]; then - echo "" >> $OUTPUT_HTML - else - echo "$JOURNAL_OUTPUT" | while read date time message; do - echo "" >> $OUTPUT_HTML - done - fi - - echo "
DateTimeMessage
No current issues logged
$date$time$message
" >> $OUTPUT_HTML -} - -# Add sections for different journal priorities -add_journalctl_section 2 "Critical" -add_journalctl_section 1 "Alert" -add_journalctl_section 0 "Emergency" - -# Close HTML tags -echo "" >> $OUTPUT_HTML - -# we are going to be running from crontab so don't confirm report generated -# Print completion message -# if you are running this from a crontab, you should -# comment this out with '#' -echo "Report generated: $OUTPUT_HTML" diff --git a/system_audit.sh b/system_audit.sh deleted file mode 100644 index f832188..0000000 --- a/system_audit.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/bash - -# File name to use for the -# audit report -REPORTNAME="sysaudit.log" - -# Check if the file already -# exists, if so delete it -if [ -f $REPORTNAME ]; then - rm "$REPORTNAME" -fi - -# Generate the report -echo "================= SYSTEM AUDIT" | tee $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- Current Users: " | tee -a $REPORTNAME -w -f | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- Running Proc: " | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -ps -eo euser,ruser,suser,fuser,f,comm,label | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- TOP 10 Memory Usage: " | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -ps -auxf | sort -nr -k 4 | head -10 | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- TOP 10 CPU Usage: " | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -ps -auxf | sort -nr -k 3 | head -10 | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- OPEN Files (Sockets ..etc): " | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -lsof | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "================= END OF SYSTEM AUDIT" | tee -a $REPORTNAME - -# END \ No newline at end of file diff --git a/system_security.sh b/system_security.sh deleted file mode 100644 index ff888d2..0000000 --- a/system_security.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/bash - -# File name to use for the -# security report -REPORTNAME="securityreport.log" - -# If the file exists delete it -if [ -f $REPORTNAME ]; then - rm "$REPORTNAME" -fi - -# Generate the report output -echo "================= SYSTEM SECURITY REPORT" | tee $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- FAILLOG:" | tee -a $REPORTNAME -faillog -a | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- CURRENT NO OWNER FILES: " | tee -a $REPORTNAME -find / -xdev \( -nouser -o -nogroup \) -print | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- SECURITY CHECK FOR PASSWORD AND SHADOW:" | tee -a $REPORTNAME -awk -F: '($3 == "0") {print}' /etc/passwd | tee -a $REPORTNAME -awk -F: '($2 == "") {print}' /etc/shadow | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- CURRENT SERVICES STATUS:" | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -systemctl list-unit-files --type=service | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- SET GID FILES:" | tee -a $REPORTNAME -find / -perm 2000 | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- SET UID FILES:" | tee -a $REPORTNAME -find / -perm 4000 | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- WORLD WRITEABLE FILES:" | tee -a $REPORTNAME -find / -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -echo "- CURRENT OPEN PORTS -" | tee -a $REPORTNAME -echo " " | tee -a $REPORTNAME -ss -tual | tee -a $REPORTNAME -echo "================= END OF SECURITY REPORT" | tee -a $REPORTNAME - -# END \ No newline at end of file diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 34e9c83..0000000 --- a/tools/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Description - -Collection of Scripts to assist in using the ALERT and SURE shell scripts - -Please review the 'How To' documents for each script. - -## clone_syslog.sh - -Provides a simply shell script to clone a system file to a user's home directory. -This will update the file permissons to the user and group as required. - -### Settings - -The Name of the system log or file to be copied -> SOURCE_NAME= - -The full path to the system log or file including directory -> SOURCE_FILE= - -The directory of the user to copy the file to, the destination -> DEST_DIR= - -The username to change the file permissons to -> USER= - -The group to change the file permissons to -> GROUP= - diff --git a/tools/clone_syslog.sh b/tools/clone_syslog.sh deleted file mode 100644 index 9e26bfe..0000000 --- a/tools/clone_syslog.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/bash - -# Description - -# -# This script is designed to automate -# copying a system file to a user directory -# defined below, it will update the -# file permissons to the defined user and group - -# Settings - - -# File name to be copied -SOURCE_NAME="auth.log" - -# The system file/log name to be copied -# Please include the full path (directory) -SOURCE_FILE="/var/log/auth.log" - -# Destination -# This should be the directory -# you want to copy the file to -# The home directory of the target user -DEST_DIR="/home//" - -# The username and group of -# This will change the file permissons -# for the user in their home directory -USER="" -GROUP="" - -# Advise we are coping the file -# - Please comment this out if running -# from crontab -echo "Copying $SOURCE_FILE to $DEST_DIR..." - -# Copy the file to the destination directory -cp "$SOURCE_FILE" "$DEST_DIR" - -# Change ownership of the copied file -DEST_FILE="$DEST_DIR/$SOURCE_NAME" - -# Confirm the action -# - Please comment this out if running -# from crontab with '#' -echo "Changing ownership of $DEST_FILE to $USER:$GROUP..." - -# update the file permissons -chown "$USER:$GROUP" "$DEST_FILE" - -# Confirm what we have done -# - Please comment this out if running -# from crontab with '#' -echo "File copied and ownership changed successfully." - -# END \ No newline at end of file