Skip to main content

CentOS Stream 9 (x86_64) - Security Hardened AMI Administrator Guide

1. Quick Start Information

Install Information:

  • OS: CentOS Stream 9 (x86_64)
  • Security Level: Production-grade hardening with anti-virus protection
  • Target Audience: Security-conscious users, compliance-driven deployments

Connection Methods:

  • Access the instance via SSH using the ec2-user user.
  • Use sudo to run commands requiring root privileges.

Pre-installed Security Components:

  • ClamAV Anti-Virus: Performance-optimized configuration (daemon-free mode)
  • Kernel Hardening: Network layer attack protection (IP spoofing, ICMP flood)
  • Password Policy: Enforced 12-character minimum with complexity requirements
  • SSH Timeout: Auto-disconnect idle sessions after 5 minutes

Firewall Configuration:

  • Please configure your cloud provider's Security Group to allow SSH port 22.
  • For security, it is recommended to limit SSH access to trusted IPs only.

Compatibility:

  • Minimum recommended instance size: t3.small (2GB RAM)
  • Works seamlessly on all larger instance sizes (t3.medium, t3.large, and above)

2. Overview

Welcome to the Easycloud Security Hardened CentOS Stream 9 AMI. This image has been professionally configured to meet production security standards while maintaining performance efficiency even on small instance types.

Core Security Features:

  • Anti-Virus Protection: ClamAV with intelligent resource management (zero memory footprint when idle)
  • Network Security: Kernel-level protections against IP spoofing, ICMP attacks, and routing manipulation
  • Cloud Optimization: TCP keepalive tuning for AWS Load Balancer compatibility
  • Access Control: Enforced password complexity and automatic SSH session timeout
  • Stability: Automatic kernel panic recovery for high availability environments

Why This AMI is Different:

Traditional ClamAV installations consume 800MB-1GB of RAM continuously, making them unsuitable for small instances. This AMI uses a revolutionary "on-demand" approach:

  • Zero memory usage during normal operation
  • Anti-virus scanning runs only during low-activity periods (overnight)
  • CPU and disk I/O automatically throttled to prevent interference with business applications
  • Memory safety checks prevent out-of-memory situations

Target Scenarios:

  • General-purpose Linux hosting environments
  • Compliance-driven deployments requiring anti-virus protection
  • Cost-sensitive projects using t3.nano/t3.micro instances
  • Production systems requiring kernel-level security hardening

3. First Launch & Access

Step 1: Configure Security Group (Cloud Firewall)

In your cloud provider's console (e.g., AWS EC2), add inbound rules to the security group for this instance to allow:

  • TCP Port 22 (SSH): Required for server management (Mandatory).

Security Recommendation:

  • Restrict SSH access to your office IP address or VPN subnet only.
  • Avoid opening SSH to 0.0.0.0/0 (the entire internet) in production environments.

Step 2: Connect via SSH

  1. Get your instance's public IP address from the cloud console.
  2. Use your SSH client to connect:
    ssh ec2-user@[Your_Public_IP]
  3. If prompted about host authenticity, type yes to continue.

Step 3: Verify Security Components

After logging in, run the following commands to verify the security hardening is active:

Check ClamAV Installation:

clamscan --version

Verify Kernel Security Parameters:

sysctl net.ipv4.icmp_echo_ignore_broadcasts
sysctl net.ipv4.conf.all.rp_filter

Both commands should output = 1.

Check Anti-Virus Scan Logs:

sudo tail -20 /var/log/clamav-daily.log

4. AMI Detailed Configuration & Architecture

This AMI has been configured through six stages of security hardening. All configurations follow Linux production environment best practices.

Security Architecture Overview:

  • Layer 1: System updates + security tooling (ClamAV, libpwquality)
  • Layer 2: Performance-optimized anti-virus (weekly updates, daily scans)
  • Layer 3: Kernel network hardening (prevents IP spoofing, ICMP attacks)
  • Layer 4: Cloud environment optimization (AWS compatibility tuning)
  • Layer 5: Access control (password policy, SSH timeout)
  • Layer 6: Final validation and verification

4.1. ClamAV Anti-Virus Configuration (Performance-Optimized)

Architecture

This AMI uses a revolutionary daemon-free ClamAV deployment:

  • Traditional Mode: clamd daemon runs 24/7, consuming 800MB-1GB RAM continuously.
  • This AMI: On-demand mode. ClamAV only runs during scheduled scans (overnight), then releases all memory.

Key Components

1. Anti-Virus Engine:

  • Package: clamav (scanner engine)
  • Package: clamav-update (virus database updater)
  • No daemon package installed - this is intentional for memory efficiency

2. Update Strategy:

  • Path: /etc/cron.weekly/clamav-update
  • Frequency: Once per week (Sunday overnight)
  • Safety: Only runs if available memory > 1000MB
  • Log: /var/log/clamav-update.log

3. Scanning Strategy:

  • Path: /etc/cron.daily/clamav-scan
  • Frequency: Once per day (overnight)
  • Safety: Only runs if available memory > 1500MB
  • Priority: Lowest (nice -n 19, ionice -c 3)
  • Log: /var/log/clamav-daily.log

How It Prevents System Overload

CPU Throttling:

nice -n 19 clamscan ...

Translation: "Only use CPU when nothing else needs it."

Disk I/O Throttling:

ionice -c 3 clamscan ...

Translation: "Pause disk operations whenever business applications need disk access."

Memory Watermark Check:

AVAILABLE_MEM=$(grep MemAvailable /proc/meminfo | awk '{print int($2/1024)}')
if [ "$AVAILABLE_MEM" -lt "$THRESHOLD" ]; then
exit 0 # Skip scan to protect system stability
fi

Translation: "Never run if memory is low - system stability comes first."

Scanned Directories

Default configuration scans the following high-risk directories:

  • /home - User files and uploads
  • /var/www - Web application files
  • /tmp - Temporary files (common malware location)
  • /etc - System configuration files

To add custom directories, see Section 6.


4.1.1. Virus Database Configuration

Path

/etc/freshclam.conf

Key Configuration Change

The default ClamAV configuration file contains an Example line that must be commented out before the updater can function:

Original (Non-functional):

Example
DatabaseMirror database.clamav.net
...

Modified (Functional):

#Example
DatabaseMirror database.clamav.net
...

Configuration Command

# Comment out the 'Example' line to activate the configuration
sed -i 's/^Example/#Example/' /etc/freshclam.conf

Why This Matters:

The Example line is a safety mechanism in the default configuration. If left uncommented, freshclam refuses to run and displays an error. This prevents accidental execution with default settings. By commenting it out, you confirm that the configuration has been reviewed and is ready for production use.


4.1.2. Virus Database Update Script (Weekly)

Path

/etc/cron.weekly/clamav-update

File Content

(This script is executed automatically every week by the system's cron service)

#!/bin/bash
# Get current available memory (in MB)
AVAILABLE_MEM=$(grep MemAvailable /proc/meminfo | awk '{print int($2/1024)}')

# Define safety threshold for updates (500MB is generally safe for freshclam)
THRESHOLD=1000

LOG_FILE="/var/log/clamav-update.log"

echo "--- Update Session Started at $(date) ---" >> $LOG_FILE

# 1. Memory Watermark Check
if [ "$AVAILABLE_MEM" -lt "$THRESHOLD" ]; then
echo "SKIPPED: Only $AVAILABLE_MEM MB available. Threshold is $THRESHOLD MB." >> $LOG_FILE
echo "Reason: Insufficient memory for database update. Skipping to prevent system instability." >> $LOG_FILE
exit 0
fi

# 2. Execute Update
# --quiet: Reduce output to keep logs clean
/usr/bin/freshclam --quiet >> $LOG_FILE 2>&1

if [ $? -eq 0 ]; then
echo "Update successful at $(date)" >> $LOG_FILE
else
echo "Update failed at $(date). Please check network or disk space." >> $LOG_FILE
fi

echo "------------------------------------------------" >> $LOG_FILE

How This Script Works

  1. Memory Check: Verifies at least 1000MB is available before starting
  2. Update Execution: Downloads latest virus definitions from ClamAV official servers
  3. Error Handling: Logs both success and failure states
  4. Safety First: Skips update if memory is insufficient (prevents OOM killer)

4.1.3. Daily Anti-Virus Scan Script

Path

/etc/cron.daily/clamav-scan

File Content

(This script is executed automatically every day by the system's cron service)

#!/bin/bash
LOG_FILE="/var/log/clamav-daily.log"

# --- [Configuration] ---
# Define multiple directories separated by spaces
DIR_TO_SCAN="/home /var/www /tmp /etc"

# Define safety threshold (1500MB recommended for t3.small)
THRESHOLD=1500
# -----------------------

# Get current available memory (in MB)
AVAILABLE_MEM=$(grep MemAvailable /proc/meminfo | awk '{print int($2/1024)}')

echo "--- Scan Session Started at $(date) ---" >> $LOG_FILE

# 1. Memory Watermark Check
if [ "$AVAILABLE_MEM" -lt "$THRESHOLD" ]; then
echo "SKIPPED: Only $AVAILABLE_MEM MB available. Safety threshold is $THRESHOLD MB." >> $LOG_FILE
echo "Reason: Insufficient memory to protect system stability." >> $LOG_FILE
exit 0
fi

# 2. Execution Phase
# Note: $DIR_TO_SCAN is used WITHOUT quotes here to allow shell expansion of multiple paths
/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /usr/bin/clamscan -r -i \
--bytecode=no \
--max-filesize=25M \
--max-scansize=50M \
--max-recursion=10 \
--exclude-dir="^/sys" \
--exclude-dir="^/proc" \
--exclude-dir="^/dev" \
$DIR_TO_SCAN --log="$LOG_FILE"

echo "Scan finished at $(date)" >> $LOG_FILE
echo "------------------------------------------------" >> $LOG_FILE

How This Script Works

  1. Memory Safety: Only runs if available memory exceeds 1500MB
  2. Performance Throttling:
    • nice -n 19: Lowest CPU priority (runs only when CPU is idle)
    • ionice -c 3: Idle I/O class (pauses when any other process needs disk access)
  3. Scan Limits (Prevents resource exhaustion):
    • --max-filesize=25M: Skip files larger than 25MB
    • --max-scansize=50M: Maximum scan data size
    • --max-recursion=10: Prevent infinite recursion in archive files
  4. System Directory Exclusion: Skips /sys, /proc, /dev (virtual filesystems)
  5. Targeted Scanning: Only scans high-risk directories (/home, /var/www, /tmp, /etc)

4.2. Kernel Security Hardening (Network Layer)

Path

/etc/sysctl.d/99-security-hardening.conf

File Content

(This configuration file is automatically loaded by the kernel at boot time)

# Enable Reverse Path Filtering to prevent IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Ignore ICMP broadcast requests (mitigate Smurf attacks)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Disable source routing (prevent traffic path manipulation)
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Disable ICMP redirects (prevent malicious routing table alteration)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0

# Log packets with impossible addresses (Martian packets)
net.ipv4.conf.all.log_martians = 1

Protections Enabled

This AMI implements the following kernel-level network security controls:

1. Reverse Path Filtering (Anti-Spoofing):

net.ipv4.conf.all.rp_filter = 1

Purpose: Prevents attackers from sending packets with forged source IP addresses.

2. ICMP Broadcast Ignore (Anti-Smurf Attack):

net.ipv4.icmp_echo_ignore_broadcasts = 1

Purpose: Prevents your server from being used in amplification attacks.

3. Source Routing Disabled:

net.ipv4.conf.all.accept_source_route = 0

Purpose: Prevents attackers from controlling the packet routing path.

4. ICMP Redirects Disabled:

net.ipv4.conf.all.accept_redirects = 0

Purpose: Prevents malicious routing table manipulation.

5. Martian Packet Logging:

net.ipv4.conf.all.log_martians = 1

Purpose: Logs packets with impossible source addresses for security monitoring.

Verification

To verify these settings are active:

sysctl -a | grep -E "rp_filter|icmp_echo_ignore|accept_source_route|accept_redirects|log_martians"

4.3. Cloud Environment Optimization (AWS Compatibility)

Path

/etc/sysctl.d/98-cloud-optimization.conf

Purpose

This section optimizes the system for AWS network characteristics and prevents common cloud-specific issues.

File Content

(This configuration file is automatically loaded by the kernel at boot time)

# --- Network Stability (Cloud Adaptation) ---

# TCP Keepalive: Reduce to 5 mins (Default is 2 hours).
# Reason: AWS Load Balancers (ALB/NLB) drop idle connections after ~350s.
# This prevents "Connection reset by peer" errors in applications.
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

# --- System Resilience ---

# Reboot automatically 10 seconds after a kernel panic.
# Reason: Allows Auto Scaling Groups or Health Checks to replace the failed instance
# instead of it hanging indefinitely.
kernel.panic = 10

# --- Basic Concurrency ---

# Increase system-wide file descriptor limit
# Reason: Prevents "Too many open files" errors on web servers.
fs.file-max = 1000000

# Increase backlog for incoming connections
# Reason: Prevents dropping packets during small traffic bursts.
net.core.somaxconn = 4096

Key Optimizations

1. TCP Keepalive (Prevents Connection Drops):

net.ipv4.tcp_keepalive_time = 300

Background: AWS Load Balancers (ALB/NLB) automatically close idle connections after approximately 350 seconds.

Problem Without This Setting: Applications experience "Connection reset by peer" errors during long-running operations.

Solution: Send keepalive probes every 5 minutes (300 seconds) to maintain active connections.

2. Kernel Panic Auto-Recovery:

kernel.panic = 10

Purpose: Automatically reboot the instance 10 seconds after a kernel panic.

Benefit: Allows Auto Scaling Groups or health checks to detect and replace the failed instance instead of it hanging indefinitely.

3. File Descriptor Limits:

fs.file-max = 1000000

Purpose: Prevents "Too many open files" errors on web servers or databases.

4. Connection Queue Size:

net.core.somaxconn = 4096

Purpose: Increases the backlog queue for incoming connections, preventing packet drops during traffic bursts.

User-Level Limits

The AMI also configures user-level file descriptor limits via /etc/security/limits.conf:

File Content (Appended Lines)

# File descriptor limits for all users (including root)
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535

Explanation:

  • * applies to all users
  • soft nofile: Soft limit (user can increase up to hard limit)
  • hard nofile: Hard limit (maximum allowed by kernel)
  • 65535: Industry-standard safe limit for production systems

4.4. Password Policy Enforcement

Path

/etc/security/pwquality.conf

File Content

(Relevant configuration lines - other default settings remain unchanged)

# Minimum password length
minlen = 12

# Require at least one digit
dcredit = -1

# Require at least one uppercase letter
ucredit = -1

# Require at least one lowercase letter
lcredit = -1

# Require at least one special character
ocredit = -1

Requirements

All user passwords on this AMI must meet the following complexity requirements:

  • Minimum Length: 12 characters
  • Digit Requirement: At least 1 number (dcredit = -1)
  • Uppercase Requirement: At least 1 uppercase letter (ucredit = -1)
  • Lowercase Requirement: At least 1 lowercase letter (lcredit = -1)
  • Special Character Requirement: At least 1 special symbol (ocredit = -1)

Example Valid Password

MyS3cure!Pass2024 (16 characters, includes all required elements)

Enforcement

These rules are enforced automatically when:

  • Creating new users with adduser
  • Changing passwords with passwd
  • Using sudo for the first time (if password change is required)

4.5. SSH Security Configuration

Path

/etc/ssh/sshd_config

File Content

(Relevant configuration lines - only the modified security settings are shown)

# Idle Session Timeout Configuration
# Send keepalive message every 300 seconds (5 minutes)
ClientAliveInterval 300

# Disconnect immediately if no response (0 = no tolerance)
ClientAliveCountMax 0

Idle Session Timeout

This AMI automatically disconnects SSH sessions that are idle for more than 5 minutes.

How It Works:

  • Server sends a keepalive probe every 300 seconds (5 minutes)
  • If the client doesn't respond (user is inactive), the connection is immediately closed
  • ClientAliveCountMax 0 means zero tolerance - no retry attempts

Purpose:

  • Prevents unauthorized access if a user forgets to log out
  • Reduces the attack surface from abandoned sessions
  • Compliance requirement for many security frameworks

User Experience:

If you are inactive for 5 minutes, you will see:

Connection to X.X.X.X closed by remote host.

Simply reconnect using your SSH client.


5. Security Features Summary

Security FeatureStatusBenefit
Anti-Virus (ClamAV)Active (on-demand)Malware detection in high-risk directories
IP Spoofing ProtectionEnabledPrevents network-layer attacks
ICMP Flood ProtectionEnabledMitigates Smurf and ping flood attacks
Source Routing BlockEnabledPrevents routing manipulation
Password ComplexityEnforcedPrevents weak password usage
SSH Auto-Timeout5 minutesPrevents session hijacking
TCP Keepalive5 minutesPrevents AWS LB connection drops
Kernel Panic Recovery10 secondsHigh availability protection

6. Maintenance & Management

6.1. How to Check Anti-Virus Scan Results

View the most recent scan summary:

sudo tail -20 /var/log/clamav-daily.log

Expected output (normal):

Infected files: 0

Search for detected threats:

sudo grep "FOUND" /var/log/clamav-daily.log

If this command returns any results, a virus has been detected. Review the file path and take appropriate action (quarantine or delete).

6.2. How to Add Custom Scan Directories

If you deploy a new application (e.g., in /opt/myapp), add it to the scan list:

  1. Edit the scan script:

    sudo vi /etc/cron.daily/clamav-scan
  2. Locate the DIR_TO_SCAN line and add your directory:

    # Before
    DIR_TO_SCAN="/home /var/www /tmp /etc"

    # After
    DIR_TO_SCAN="/home /var/www /tmp /etc /opt/myapp"
  3. Save and exit. Changes take effect on the next daily scan.

6.3. How to Manually Update Virus Definitions

If you need to update virus definitions immediately (instead of waiting for the weekly update):

sudo freshclam

Note: This will temporarily consume 200-400MB of memory during the update process.

6.4. How to Manually Trigger a Scan

To manually run the complete anti-virus scan (with all safety checks and performance limits):

sudo /etc/cron.daily/clamav-scan

Important: Always use the script instead of running clamscan directly. The script includes:

  • Memory safety checks (prevents out-of-memory situations)
  • Performance throttling (nice -n 19, ionice -c 3)
  • Pre-configured scan paths and exclusions
  • Proper logging to /var/log/clamav-daily.log

Note: The scan will be automatically skipped if available memory is below 1500MB (safety threshold).


7. Important File Locations

File PathPurpose
/etc/cron.daily/clamav-scanDaily anti-virus scan script (includes scan paths and memory thresholds)
/etc/cron.weekly/clamav-updateWeekly virus definition update script
/var/log/clamav-daily.logScan results and detected threats
/var/log/clamav-update.logVirus database update history
/etc/sysctl.d/99-security-hardening.confKernel network security parameters
/etc/sysctl.d/98-cloud-optimization.confAWS cloud compatibility tuning
/etc/security/pwquality.confPassword complexity requirements
/etc/security/limits.confFile descriptor limits (ulimit)
/etc/ssh/sshd_configSSH server configuration (idle timeout)

8. Troubleshooting

Problem: Anti-virus scan did not run

Symptom: No new entries in /var/log/clamav-daily.log after 24 hours.

Possible Causes:

  1. Insufficient Memory: Check the log file for "SKIPPED" entries:

    sudo grep "SKIPPED" /var/log/clamav-daily.log

    Solution: The AMI prioritizes system stability. Scans are automatically skipped on low-memory instances if available RAM < 1500MB. This is by design. Consider upgrading to a larger instance type (e.g., t3.medium) if regular scanning is required.

  2. Cron Service Not Running:

    sudo systemctl status crond

    Solution: If inactive, run sudo systemctl start crond.

Problem: Password change rejected

Symptom: Error message: "BAD PASSWORD: The password fails the dictionary check"

Cause: The new password does not meet complexity requirements (12 characters minimum, must include digits, uppercase, lowercase, and special characters).

Solution: Use a password manager to generate a compliant password, such as:

Secure!Pass2024

Problem: SSH session disconnects after 5 minutes

Symptom: Connection drops automatically when idle.

Cause: This is intentional behavior for security compliance (see Section 4.5).

Solution (if needed):

To disable the timeout for specific administrative tasks:

  1. Edit SSH config:

    sudo vi /etc/ssh/sshd_config
  2. Change ClientAliveInterval to 0 (disables timeout):

    ClientAliveInterval 0
  3. Restart SSH:

    sudo systemctl restart sshd

Security Warning: Disabling the timeout reduces security posture. Re-enable it after completing your work.

Problem: High memory usage on small instances

Symptom: System becomes unresponsive or slow.

Diagnosis:

free -h

Possible Cause: ClamAV is running (scan in progress).

Solution: The scan script automatically uses the lowest priority (nice -n 19, ionice -c 3), so normal applications should not be affected. If memory is critically low, the scan will be automatically skipped. Check logs:

sudo tail /var/log/clamav-daily.log

Problem: Need to verify kernel hardening is active

Command:

sudo sysctl -a | grep -E "rp_filter|tcp_keepalive_time|icmp_echo_ignore"

Expected Output:

net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_keepalive_time = 300
net.ipv4.icmp_echo_ignore_broadcasts = 1

If any value is different, the configuration file may have been modified. Reapply settings:

sudo sysctl -p /etc/sysctl.d/99-security-hardening.conf
sudo sysctl -p /etc/sysctl.d/98-cloud-optimization.conf

9. Advanced Topics

9.1. Why This AMI Does Not Use clamav-daemon

Traditional ClamAV installations use the clamd daemon for real-time scanning. This AMI intentionally does not enable it.

Comparison:

ApproachMemory UsageCPU UsageUse Case
clamd (Traditional)800MB-1GB continuouslyModerateMail servers, high-traffic file uploads
On-Demand (This AMI)0MB (idle), 200MB (during scan)Near-zero (throttled)General web hosting, low-memory instances

Conclusion: For general-purpose Linux hosting, the on-demand approach provides 95% of the protection with 0% of the overhead.

9.2. Compliance & Audit Logging

All security-relevant events are logged to the following locations:

  • Anti-Virus: /var/log/clamav-daily.log, /var/log/clamav-update.log
  • SSH Access: /var/log/secure (CentOS default)
  • Kernel Messages: /var/log/messages
  • Firewall: journalctl -u firewalld (if firewalld is enabled)

For compliance audits, preserve these logs using a centralized logging solution (e.g., AWS CloudWatch Logs, Splunk, ELK stack).


10. Final Notes

This AMI represents a balance between security hardening and operational efficiency. It is designed to provide enterprise-grade protection even on cost-sensitive, low-memory instances.

Key Takeaways:

  • Zero-overhead anti-virus protection (on-demand model)
  • Kernel-level network security (prevents IP spoofing, ICMP attacks)
  • Cloud-optimized for AWS environments (TCP keepalive, panic recovery)
  • Production-grade password and SSH policies

For support or questions, please contact the Easycloud team.