CentOS Stream 9 (ARM64) - Security Hardened AMI Administrator Guide
1. Quick Start Information
Install Information:
- OS: CentOS Stream 9 (ARM64)
- 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-useruser. - Use
sudoto 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: t4g.small (2GB RAM)
- Works seamlessly on all larger instance sizes (t4g.medium, t4g.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 ARM-based projects using AWS Graviton 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
- Get your instance's public IP address from the cloud console.
- Use your SSH client to connect:
ssh ec2-user@[Your_Public_IP] - If prompted about host authenticity, type
yesto 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:
clamddaemon 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
- Memory Check: Verifies at least 1000MB is available before starting
- Update Execution: Downloads latest virus definitions from ClamAV official servers
- Error Handling: Logs both success and failure states
- 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 t4g.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
- Memory Safety: Only runs if available memory exceeds 1500MB
- 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)
- 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
- System Directory Exclusion: Skips
/sys,/proc,/dev(virtual filesystems) - 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 userssoft 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
sudofor 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 0means 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 Feature | Status | Benefit |
|---|---|---|
| Anti-Virus (ClamAV) | Active (on-demand) | Malware detection in high-risk directories |
| IP Spoofing Protection | Enabled | Prevents network-layer attacks |
| ICMP Flood Protection | Enabled | Mitigates Smurf and ping flood attacks |
| Source Routing Block | Enabled | Prevents routing manipulation |
| Password Complexity | Enforced | Prevents weak password usage |
| SSH Auto-Timeout | 5 minutes | Prevents session hijacking |
| TCP Keepalive | 5 minutes | Prevents AWS LB connection drops |
| Kernel Panic Recovery | 10 seconds | High 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:
-
Edit the scan script:
sudo vi /etc/cron.daily/clamav-scan -
Locate the
DIR_TO_SCANline and add your directory:# Before
DIR_TO_SCAN="/home /var/www /tmp /etc"
# After
DIR_TO_SCAN="/home /var/www /tmp /etc /opt/myapp" -
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 Path | Purpose |
|---|---|
/etc/cron.daily/clamav-scan | Daily anti-virus scan script (includes scan paths and memory thresholds) |
/etc/cron.weekly/clamav-update | Weekly virus definition update script |
/var/log/clamav-daily.log | Scan results and detected threats |
/var/log/clamav-update.log | Virus database update history |
/etc/sysctl.d/99-security-hardening.conf | Kernel network security parameters |
/etc/sysctl.d/98-cloud-optimization.conf | AWS cloud compatibility tuning |
/etc/security/pwquality.conf | Password complexity requirements |
/etc/security/limits.conf | File descriptor limits (ulimit) |
/etc/ssh/sshd_config | SSH 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:
-
Insufficient Memory: Check the log file for "SKIPPED" entries:
sudo grep "SKIPPED" /var/log/clamav-daily.logSolution: 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., t4g.medium) if regular scanning is required.
-
Cron Service Not Running:
sudo systemctl status crondSolution: 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:
-
Edit SSH config:
sudo vi /etc/ssh/sshd_config -
Change
ClientAliveIntervalto0(disables timeout):ClientAliveInterval 0 -
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:
| Approach | Memory Usage | CPU Usage | Use Case |
|---|---|---|---|
| clamd (Traditional) | 800MB-1GB continuously | Moderate | Mail 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.