Hardened RHEL 9
CIS Level 2
A production-ready, CIS Level 2 compliant RHEL 9 base AMI for security teams, MSPs, and compliance-driven organizations on AWS. All hardening is applied at pipeline build time — every instance launches in a verified, known-good state with a Lynis score of 87.
What is included
CIS Level 2 Hardening
Full ansible-lockdown RHEL9-CIS role applied at build time. Every control verified before AMI snapshot.
SELinux Enforcing
Targeted policy, enforcing mode. Verified at launch. Persists across reboots via /etc/selinux/config.
Auditd + Immutable Rules
Full CIS audit ruleset with -e 2 immutable flag. All privileged commands logged.
Firewalld Active
Default zone set to drop. SSH explicitly allowed. All other inbound blocked by default.
Fail2ban SSH Jail
5 failed attempts triggers 1-hour IP ban. Watches /var/log/secure automatically.
AWS Native
SSM Agent active. Cloud-init compatible. No baked-in SSH keys. IAM-ready at launch.
AIDE + rkhunter
File integrity monitoring and rootkit detection pre-installed and configured.
Auto Security Updates
dnf-automatic enabled. Security patches applied daily without manual intervention.
Supported regions
| Region | Code | Status |
|---|---|---|
| US East (N. Virginia) | us-east-1 | Available |
| US West (Oregon) | us-west-2 | Available |
| Europe (Ireland) | eu-west-1 | Available |
| Asia Pacific (Singapore) | ap-southeast-1 | Available |
Pricing
| Model | Rate | Notes |
|---|---|---|
| Hourly | $0.10 / hr | Billed per hour of instance running time |
| Monthly | $49.00 / mo | Fixed monthly, unlimited instance launches |
| Free Trial | 7 days | Full access — cancel before day 7 at no charge |
Quick Start
From AWS Marketplace subscription to a verified running instance in under 10 minutes.
You need an active AWS account, an EC2 key pair in your target region, and an IAM user or role with EC2 launch permissions. For SSM access, attach AmazonSSMManagedInstanceCore to your instance role.
Launch steps
Subscribe on AWS Marketplace
Find the PTG Hardened RHEL 9 CIS Level 2 listing on AWS Marketplace. Click Continue to Subscribe. Accept the terms. Click Continue to Configuration.
Configure and launch
Select your region, software version v1.0, and fulfillment option Launch through EC2. Click Continue to Launch.
Choose instance settings
Instance type: t3.medium minimum (2 vCPU, 4 GB RAM). Select your VPC, a public subnet, and your existing key pair.
Do not use t3.micro or t3.small. The security stack requires at minimum 2 vCPU and 4 GB RAM to function correctly.
Security group
Create a security group with inbound SSH (TCP port 22) restricted to your IP address only.
# CLI option — create SG with your current IP only aws ec2 authorize-security-group-ingress \ --group-id sg-xxxxxxxxx \ --protocol tcp --port 22 \ --cidr $(curl -s ifconfig.me)/32
Connect via SSH
Wait 2–3 minutes for both status checks to pass, then connect.
ssh -i /path/to/your-key.pem ec2-user@PUBLIC_IP
Verify security controls
# SELinux enforcing sudo getenforce # Auditd running sudo systemctl status auditd | grep Active # Firewalld running sudo systemctl status firewalld | grep Active # Compliance manifest cat /opt/PTG/compliance/manifest.txt # Full Lynis scan (3–5 min) sudo lynis audit system
Requirements
Everything required before launching a PTG Hardened RHEL 9 instance.
Instance requirements
Supported instance types
| Family | Types | Use case |
|---|---|---|
| General Purpose | t3.medium, t3.large, t3.xlarge, m5.large, m5.xlarge | Most workloads |
| Compute Optimized | c5.large, c5.xlarge, c5.2xlarge | High-CPU workloads |
| Memory Optimized | r5.large, r5.xlarge | Memory-intensive applications |
Network requirements
- Outbound internet access on port 443/80 for dnf-automatic security updates
- Inbound SSH port 22 from your management IP — restrict to your IP, not 0.0.0.0/0 in production
- For SSM Session Manager: VPC endpoint for SSM or NAT Gateway with outbound 443
IAM requirements
The SSM Agent is pre-installed and running. To use Session Manager without SSH, attach AmazonSSMManagedInstanceCore to your EC2 instance role. No additional configuration required on the instance.
CIS Level 2 Hardening
All hardening is applied at build time using the ansible-lockdown RHEL9-CIS role. Controls are baked into the AMI, not applied at runtime, so every instance launches in a known verified state.
SSH Configuration
Applied to /etc/ssh/sshd_config at build time.
| Setting | Value | Purpose |
|---|---|---|
| PermitRootLogin | no | Root cannot authenticate over SSH |
| PasswordAuthentication | no | Key-pair only — no password auth |
| MaxAuthTries | 3 | 3 failed attempts triggers disconnect |
| AllowTcpForwarding | no | Prevent SSH tunneling |
| X11Forwarding | no | No graphical forwarding |
| AllowAgentForwarding | no | No agent forwarding |
| ClientAliveCountMax | 2 | Idle session timeout enforcement |
| MaxSessions | 2 | Limit concurrent sessions per connection |
| LogLevel | VERBOSE | Full SSH activity logging to syslog |
| TCPKeepAlive | no | Use ClientAlive instead of TCP keepalive |
sudo grep -E "^(PermitRootLogin|PasswordAuthentication|MaxAuthTries|X11Forwarding)" /etc/ssh/sshd_config
SELinux
SELinux enforcing mode with targeted policy. Configured in /etc/selinux/config — persists across reboots. Cannot be changed without root access and a reboot.
# Current enforcement mode getenforce # Full status sestatus # Check policy from config file matches running mode sestatus | grep "from config file"
Firewalld
Active with default zone drop. All inbound blocked except SSH (port 22). Add application ports after launch.
# View all active rules sudo firewall-cmd --list-all # Add a port for your application sudo firewall-cmd --permanent --add-port=443/tcp sudo firewall-cmd --reload # Check default zone sudo firewall-cmd --get-default-zone
Auditd
Running with full CIS audit ruleset. Immutable flag -e 2 set — rules cannot change without a reboot. Logs to /var/log/audit/audit.log.
# Service status sudo systemctl status auditd # Confirm immutable flag (enabled 2 = immutable) sudo auditctl -s | grep enabled # View loaded audit rules sudo auditctl -l | head -30 # Search recent audit events sudo ausearch -ts recent | head -40
Kernel Sysctl Hardening
CIS and Lynis recommended values applied via /etc/sysctl.d/99-ptg-lynis.conf.
| Key | Value | Purpose |
|---|---|---|
| kernel.randomize_va_space | 2 | Full ASLR enabled |
| net.ipv4.tcp_syncookies | 1 | SYN flood protection |
| net.ipv4.conf.all.rp_filter | 1 | Reverse path filtering |
| net.ipv4.conf.all.send_redirects | 0 | No ICMP redirects sent |
| net.ipv4.conf.all.accept_redirects | 0 | No redirect acceptance |
| kernel.dmesg_restrict | 1 | Restrict dmesg to root only |
| kernel.kptr_restrict | 2 | Hide kernel symbol addresses |
| fs.protected_fifos | 2 | FIFO / named pipe protection |
| fs.protected_regular | 2 | Regular file protection in sticky dirs |
| net.core.bpf_jit_harden | 2 | BPF JIT compiler hardening |
| kernel.sysrq | 0 | Magic SysRq disabled |
| dev.tty.ldisc_autoload | 0 | Prevent auto-loading line disciplines |
Security Stack
Pre-installed, pre-configured tools active on every instance from first boot.
Fail2ban — Intrusion Prevention
Monitors /var/log/secure for SSH failures. The SSH jail is active and configured out of the box.
| Setting | Value |
|---|---|
| jail | sshd |
| logpath | /var/log/secure |
| maxretry | 5 attempts |
| bantime | 3600 seconds (1 hour) |
| findtime | 600 seconds (10 min window) |
# View SSH jail status and banned IPs sudo fail2ban-client status sshd # Manually unban an IP sudo fail2ban-client set sshd unbanip IP_ADDRESS # View fail2ban log sudo tail -f /var/log/fail2ban.log
AIDE — File Integrity Monitoring
AIDE monitors the filesystem for unauthorized changes. Initialize your baseline database after you finish configuring your application files, then check against it regularly.
# Initialize baseline (run after full app setup) sudo aide --init sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz # Run integrity check against baseline sudo aide --check # Update database after intentional changes sudo aide --update
rkhunter — Rootkit Detection
# Update signatures sudo rkhunter --update # Run full scan sudo rkhunter --check --skip-keypress # View warnings only sudo grep -i warning /var/log/rkhunter.log
dnf-automatic — Unattended Security Updates
Security patches applied automatically on a daily timer. No manual intervention required.
# Verify timer is active sudo systemctl status dnf-automatic-install.timer # View update history sudo dnf history list | head -20
Compiler Access Restriction
GCC and Make are set to 0750 permissions — root access only. Non-root users cannot compile code, reducing post-exploitation attack surface.
Legal warning banners are set on /etc/issue and /etc/issue.net and displayed on every login. Required by many compliance frameworks including HIPAA, PCI-DSS, and FedRAMP.
Compliance & Scoring
Every PTG image is scored with Lynis before release. The score reflects controls actively applied in the AMI — not theoretical compliance.
Lynis Hardening Score
Run sudo lynis audit system at any time. After a reboot (auditd immutable rules fully apply) and after initializing the AIDE baseline, your score may increase further.
Known Lynis suggestions — by design
The following suggestions appear in Lynis output and are intentional architectural decisions, not oversights.
| ID | Description | Reason not fixed |
|---|---|---|
| FILE-6310 | /home, /tmp, /var not on separate partitions | Cannot repartition inside EC2 Image Builder. Attach separate EBS volumes post-launch if required. |
| SSH-7408 | SSH port 22 flagged | Port 22 is required for AWS Marketplace AMI compatibility. CIS requires it be restricted, not moved. |
| LOGG-2154 | No remote syslog target | CloudWatch Agent handles centralized logging. Lynis does not detect CloudWatch as a remote syslog target. |
| NAME-4404 | FQDN missing from /etc/hosts | Instance-specific. Set by cloud-init at launch time — not configurable at AMI build time. |
CIS benchmark sections applied
- Section 1 — Filesystem configuration, kernel module blacklisting, /tmp hardening
- Section 2 — Services — unnecessary services disabled, inetd removed
- Section 3 — Network configuration — full IPv4 and IPv6 stack hardening
- Section 4 — Firewall — firewalld enforced with default deny policy
- Section 5 — SSH server hardening — full key-pair enforcement, all forwarding disabled
- Section 6 — Logging and auditing — auditd with complete CIS ruleset, immutable flag
- Section 7 — User accounts — password policies, locked accounts, PAM, sudoers
Connecting to Your Instance
Two connection methods are supported. SSH password authentication is permanently disabled — key-pair or SSM only.
Method 1 — SSH with key pair
# Connect as ec2-user ssh -i /path/to/your-key.pem ec2-user@PUBLIC_IP # Escalate to root sudo su - # Or run a single command as root sudo COMMAND
Password authentication is permanently disabled. A key pair must be selected at instance launch. A lost key pair cannot be recovered — launch a new instance from the AMI.
Method 2 — AWS Systems Manager Session Manager
SSM Agent is pre-installed and running. Attach AmazonSSMManagedInstanceCore to your instance role to connect without opening any inbound ports.
# Start a session (requires AWS CLI v2 + session-manager-plugin) aws ssm start-session --target INSTANCE_ID # Verify SSM agent is running sudo systemctl status amazon-ssm-agent
Default user reference
| Property | Value |
|---|---|
| Default user | ec2-user |
| sudo access | Yes — passwordless via /etc/sudoers.d/90-cloud-init-users |
| Root SSH login | Disabled (PermitRootLogin no) |
| Password auth | Disabled (PasswordAuthentication no) |
| SSH host keys | Regenerated fresh on every first boot — not baked in |
Post-Launch Validation
Run these commands after every launch to confirm all security controls are active. Under 5 minutes manually — or use the PTG validation playbook for automated reporting.
Full verification script
#!/bin/bash # PTG RHEL 9 — Post-launch verification echo "=== SELinux ===" getenforce echo "=== Auditd ===" sudo systemctl is-active auditd sudo auditctl -s | grep enabled echo "=== Firewalld ===" sudo systemctl is-active firewalld sudo firewall-cmd --state echo "=== Fail2ban ===" sudo systemctl is-active fail2ban sudo fail2ban-client status sshd echo "=== SSH Hardening ===" sudo grep -E "^(PermitRootLogin|PasswordAuthentication|MaxAuthTries)" /etc/ssh/sshd_config echo "=== Compliance Manifest ===" cat /opt/PTG/compliance/manifest.txt echo "=== Lynis Score ===" sudo lynis audit system 2>/dev/null | grep "Hardening index"
Expected outputs
| Check | Expected output |
|---|---|
| getenforce | Enforcing |
| systemctl is-active auditd | active |
| auditctl -s | grep enabled | enabled 2 |
| systemctl is-active firewalld | active |
| firewall-cmd --state | running |
| systemctl is-active fail2ban | active |
| PermitRootLogin | no |
| PasswordAuthentication | no |
| MaxAuthTries | 3 |
| Lynis Hardening index | 87 or higher |
The PTG validation Ansible playbook runs all checks automatically and writes a full report to /opt/PTG/compliance/validation_report.txt. Contact support to request access.
Compliance Manifest
Every instance built from this AMI contains a compliance manifest generated at build time at /opt/PTG/compliance/manifest.txt.
cat /opt/PTG/compliance/manifest.txt
Sample manifest
------------------------------------------------ PTG HARDENED IMAGE MANIFEST Build Date: 2026-04-09 OS: RHEL 9 / AlmaLinux 9 Compliance Level: CIS Level 2 SSH Hardening: Enabled Firewall: firewalld Active SELinux: Enforcing Auditd: Enabled Fail2ban: Active (SSH jail enabled) Process Accounting: psacct + sysstat Active Lynis Sysctl Hardening: Applied (99-ptg-lynis.conf) Firewire: Blacklisted Core Dumps: Disabled File Permissions: Hardened Legal Banners: /etc/issue + /etc/issue.net Compiler Access: Restricted to root PTG Standard: Secure at Every Stage ------------------------------------------------
Changelog
v1.0 — April 2026 Initial Release
- CIS Level 2 hardening via ansible-lockdown RHEL9-CIS role
- SELinux enforcing mode — targeted policy
- auditd with full CIS ruleset and -e 2 immutable flag
- firewalld active — default zone drop, SSH explicitly allowed
- fail2ban with SSH jail — 5 retries, 1-hour ban
- AIDE file integrity monitoring pre-installed and configured
- rkhunter rootkit detection pre-installed
- dnf-automatic enabled for unattended daily security updates
- Lynis sysctl hardening profile — 99-ptg-lynis.conf
- Firewire blacklisted, core dumps disabled via limits.conf
- Compiler access restricted to root — gcc and make chmod 750
- psacct and sysstat process accounting enabled
- Legal banners on /etc/issue and /etc/issue.net
- Compliance manifest at /opt/PTG/compliance/manifest.txt
- No baked-in SSH authorized keys — regenerated on first boot
- Available in us-east-1, us-west-2, eu-west-1, ap-southeast-1
- Lynis hardening score: 87 / 100
Support
PTG provides email support for all AWS Marketplace subscribers. 48-hour response time Monday through Friday, Eastern Time.
Contact
| Type | Contact | Response time |
|---|---|---|
| General support | support@powelltechnologygroup.com | 48 hours Mon–Fri ET |
| Urgent issues | Subject line: PTG RHEL9 AMI — Urgent | Best effort same business day |
What support covers
- Questions about pre-installed security controls and their configuration
- Guidance on CIS compliance controls applied in this image
- Assistance verifying hardening settings after launch
- Explanation of Lynis findings and which are intentional design decisions
- Help running or interpreting the PTG validation playbook output
What support does not cover
- Application-level configuration built on top of the base image
- AWS account, billing, or EC2 service issues — contact AWS Support
- Modifications that intentionally reduce the hardening level
Refunds available within 7 days of initial subscription for any reason. Contact support with your AWS account ID and subscription date. No refunds for hourly usage already consumed.