NIST 800-53 Compliance Monitoring
Build a NIST 800-53 compliance monitoring pipeline using Wazuh on GCP.
Introduction
30 Second Summary
Every organization handling sensitive data faces the same challenge: proving to auditors that their systems are continuously monitored and secured according to federal standards. Doing this manually is slow, error-prone, and impossible to scale.
In this project, you will build an enterprise-grade NIST 800-53 compliance monitoring system using Wazuh that detects unauthorized changes, enforces security baselines, automatically responds to attacks, and produces audit-ready evidence.
What You'll Build
A production-grade compliance monitoring pipeline on GCP that continuously assesses a Linux server endpoint against NIST 800-53 federal security controls using Wazuh's File Integrity Monitoring, Security Configuration Assessment, custom detection rules, and Active Response automation.
By the end of this project, you'll have:
- A monitored GCP VM with a Wazuh agent enrolled in a custom agent group, running real-time file integrity monitoring mapped to NIST SC-28 and CM-3 controls.
- Custom SCA policies and detection rules explicitly tagged to NIST 800-53 control families including AC-7, AC-2, AU-12, and SI-4.
- Automated Active Response actions that lock accounts and drop firewall traffic on brute-force detection, satisfying NIST AC-7 and SC-5.
- Secret Mission: a custom GeoIP Active Response script that performs geolocation-based IP blocking and writes structured entries to a dedicated NIST incident response audit trail.
Are there any prerequisites?
This project assumes you already have a Wazuh Docker single-node stack deployed and running on a GCP Compute Engine VM. You should also be comfortable with basic Linux command-line operations, SSH, and Docker commands.
Provision and Enroll the GCP Agent Endpoint
Your Wazuh manager is running, but it has nothing to monitor yet. A compliance monitoring system needs at least one endpoint sending security data back to the manager for analysis.
In this step, you will provision a fresh GCP Compute Engine VM, install the Wazuh agent on it, and enroll it into your manager. This gives you a real Linux server to monitor against NIST 800-53 controls throughout the rest of the project.
In this step, get ready to:
- Spin up a new GCP VM as your monitored endpoint.
- Install and enroll the Wazuh agent on the new VM.
- Create an agent group and assign your endpoint to it.
Provision the agent VM in GCP
You need a separate VM that acts as the server being monitored. The Wazuh agent runs on this VM and sends security events back to your Wazuh manager over TCP ports 1514 (event data) and 1515 (enrollment).
- Open the GCP Console at console.cloud.google.com and sign in.
- In the left navigation menu, click Compute Engine.
- Click VM instances.
- Click Create Instance at the top of the page.
Configure the VM with these settings:
- In the Name field, enter wazuh-agent-endpoint.
- Set the Region to the same region as your Wazuh manager VM.
- Under Machine configuration, set Series to E2.
- Set Machine type to e2-medium (2 vCPU, 4 GB memory).
- In the Boot disk section, click Change.
- Select Ubuntu as the operating system and Ubuntu 22.04 LTS as the version.
- Click Select to confirm the boot disk.
- Click Create to launch the VM.
Wait for the green checkmark to appear next to your new VM. This confirms it is running.
Why the same region?
Placing both VMs in the same region means they share a low-latency internal network. The agent connects to the manager using its internal IP, so traffic stays inside GCP and never touches the public internet.
Now you need to open the ports that Wazuh uses for agent communication. Without a firewall rule, GCP blocks inter-VM traffic on non-standard ports by default.
- In the left navigation menu, click VPC network.
- Click Firewall.
- Click Create Firewall Rule at the top.
- In the Name field, enter allow-wazuh-agent.
- Set Direction of traffic to Ingress.
- Set Targets to All instances in the network.
- In Source IPv4 ranges, enter the internal IP address of your Wazuh Docker host VM followed by /32 (for example, 10.128.0.2/32).
- Under Protocols and ports, select Specified protocols and ports.
- Check TCP and enter 1514, 1515 in the ports field.
- Click Create.
What are ports 1514 and 1515?
Port 1514 carries ongoing event data from the agent to the manager. Port 1515 is used once during enrollment to exchange keys and register the agent.
Find your Wazuh host internal IP
Go to Compute Engine > VM instances and look at the Internal IP column for your Wazuh Docker host. Copy this IP for the firewall rule and for the agent installation in the next substep.
Don't see Compute Engine or VPC network?
Make sure you have the correct GCP project selected in the project dropdown at the top of the console. If Compute Engine is not listed, you may need to enable the Compute Engine API first.
Help me troubleshoot GCP Console access issues
Install the Wazuh agent and verify enrollment
With the VM running and firewall rules in place, you can now install the Wazuh agent. The agent will auto-enroll with your manager during installation when you set the WAZUH_MANAGER environment variable.
- SSH into your new agent VM by clicking SSH next to it in the VM instances list.
- Import the Wazuh GPG key and add the repository by running these commands:
# Import the Wazuh GPG signing key
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 /usr/share/keyrings/wazuh.gpg
# Add the Wazuh APT repository
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list
# Update the package list
apt-get update
What does this do?
These commands add Wazuh's official package repository to your system. The GPG key ensures packages are verified as authentic before installation.
- Install the Wazuh agent by running this command, replacing the IP with your Wazuh Docker host's internal IP:
# Install the agent and set the manager IP for auto-enrollment
WAZUH_MANAGER="[[WAZUH_HOST_IP="your Wazuh Docker host internal IP"]]" apt-get install wazuh-agent -y
What does WAZUH_MANAGER do?
Setting this environment variable during installation tells the agent exactly where to find its manager. The agent will automatically attempt enrollment on port 1515 as soon as it starts.
- Enable and start the Wazuh agent service by running these commands:
# Reload systemd to pick up the new service
systemctl daemon-reload
# Enable the agent to start on boot
systemctl enable wazuh-agent
# Start the agent now
systemctl start wazuh-agent
- Verify the agent is running by checking its status:
systemctl status wazuh-agent
You should see active (running) in the output.
- Now verify enrollment from the Wazuh dashboard. Open your Wazuh dashboard in a browser (port 443 on your Docker host).
- Log in with the default credentials: username admin and password SecretPassword.
- Navigate to Agents management in the left menu.
- Confirm your new agent appears in the list with an Active status.
Agent not showing as Active?
Check that the firewall rule is applied correctly. The source IP in the rule must match your Wazuh Docker host's internal IP exactly. Also verify the agent VM and manager VM are in the same VPC network.
On the agent VM, check the agent log for connection errors by running cat /var/ossec/logs/ossec.log | tail -20. Look for messages about connection refused or timeouts.
Help me debug why my Wazuh agent is not connecting to the manager
Create the agent group and assign your endpoint
Agent groups let you push configuration policies to multiple endpoints at once. When you add more Linux servers later, assigning them to the same group automatically applies your compliance monitoring settings.
- In the Wazuh dashboard, navigate to Agents management in the left menu.
- Click Groups.
- Click Add new group.
- Enter linux-servers as the group name.
- Click Save to create the group.
Now assign your agent to this new group.
- Go back to Agents management and click on your agent's name to open its details.
- Find the Groups section on the agent details page.
- Click Edit groups and add linux-servers to the agent.
- Confirm the group assignment by clicking Save.
The agent details page should now show linux-servers listed under its groups.
Why create an agent group?
In a real enterprise, you might have dozens or hundreds of Linux servers. Groups let you define compliance policies once and distribute them automatically to every agent in the group.
The centralized configuration file for this group lives at /var/ossec/etc/shared/linux-servers/agent.conf on the Wazuh manager. Any settings you place there will push to all group members automatically.
Don't see the Add new group button?
Make sure you are in the Groups tab within Agents management. If the button does not appear, try refreshing the page. You need admin-level access to create groups.
Help me find where to create agent groups in the Wazuh dashboard
Your monitored endpoint is provisioned, enrolled, and grouped. Next up, you will configure File Integrity Monitoring to detect unauthorized changes to critical system files.
Configure File Integrity Monitoring for NIST SC-28 and CM-3
Your agent is enrolled and reporting to the Wazuh manager. Now you need to tell it exactly what to watch.
File Integrity Monitoring (FIM) continuously tracks changes to critical system files. When someone modifies a configuration file or tampers with authentication data, FIM generates an alert with the exact diff, the user responsible, and the process that made the change. This maps directly to NIST 800-53 controls SC-28 (Protection of Information at Rest) and CM-3 (Configuration Change Control).
In this step, get ready to:
- Configure enhanced FIM monitoring with whodata auditing on the agent VM.
- Trigger a test change and verify the alert appears in the Wazuh dashboard.
- Add a nodiff exclusion for sensitive files to prevent data leakage in alert diffs.
Edit ossec.conf to enable enhanced FIM monitoring
The FIM module lives inside the <syscheck> block of the agent's ossec.conf file. By default, it monitors common directories on a 12-hour schedule. You will replace that default configuration with real-time, whodata-enabled monitoring of the specific paths that matter for NIST compliance.
What is whodata monitoring?
Standard FIM detects that a file changed. Whodata monitoring goes further by using the Linux Audit subsystem to capture exactly who made the change, which process they used, and where they ran it from. This provides the full audit trail that NIST AU-12 and CM-3 require.
Whodata requires the auditd package to be installed and running. Ubuntu 22.04 includes it by default, but the service may not be active.
- SSH into your agent VM.
- Verify that auditd is running by executing this command:
sudo systemctl status auditd
- You should see active (running) in the output.
✔️ auditd is active
You are ready to proceed with whodata configuration.
ⓧ auditd is not running
- Start and enable auditd by running:
sudo systemctl enable auditd
sudo systemctl start auditd
- Confirm it shows active (running) by running sudo systemctl status auditd again.
- Back up your existing ossec.conf before making changes by running:
sudo cp /var/ossec/etc/ossec.conf /var/ossec/etc/ossec.conf.bak
- Open the Wazuh agent configuration file for editing by running:
sudo nano /var/ossec/etc/ossec.conf
- Find the existing <syscheck> block in the file. It starts with <!-- File integrity monitoring --> and contains default directories like /etc,/usr/bin,/usr/sbin.
- Replace the default <directories> entries inside the <syscheck> block with the enhanced FIM configuration below:
<syscheck>
<disabled>no</disabled>
<frequency>43200</frequency>
<scan_on_start>yes</scan_on_start>
<!-- NIST SC-28 and CM-3: Monitor critical system files with whodata -->
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/ssh/sshd_config</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/pam.d</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/passwd</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/shadow</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/var/log/audit</directories>
<!-- Skip system virtual filesystems -->
<skip_nfs>yes</skip_nfs>
<skip_dev>yes</skip_dev>
<skip_proc>yes</skip_proc>
<skip_sys>yes</skip_sys>
</syscheck>
What does this configuration do?
- whodata="yes" uses the Linux Audit subsystem to capture which user and process made each change.
- report_changes="yes" records the actual file diff so you can see exactly what changed.
- check_all="yes" enables all integrity checks: size, permissions, ownership, timestamps, and all hash algorithms.
- The five paths cover SSH configuration, authentication modules, user accounts, password hashes, and audit logs.
- Save the file by pressing Ctrl+O then Enter.
- Exit nano by pressing Ctrl+X.
Seeing a syntax error when you save?
XML is sensitive to typos. Make sure every opening tag like <directories> has a matching closing tag </directories>. Check that all attribute values are enclosed in double quotes.
Help me identify and fix my XML syntax error in ossec.conf
Restart the agent and verify FIM alerts
Configuration changes to ossec.conf only take effect after restarting the Wazuh agent service.
- Restart the Wazuh agent by running:
sudo systemctl restart wazuh-agent
- Confirm the agent restarted successfully by running:
sudo systemctl status wazuh-agent
- You should see active (running) in the output.
Agent failed to start?
Check the Wazuh log for configuration errors by running:
sudo cat /var/ossec/logs/ossec.log | tail -20
Look for lines containing ERROR. The most common cause is malformed XML in ossec.conf. You can restore your backup with sudo cp /var/ossec/etc/ossec.conf.bak /var/ossec/etc/ossec.conf and try again.
Help me diagnose my Wazuh agent startup error
Now you will trigger a real change to confirm FIM is working. You will modify the SSH configuration file, which is one of your monitored paths.
- Modify /etc/ssh/sshd_config to change PasswordAuthentication from yes to no by running:
sudo sed -i 's/^#\?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
What does this command do?
The sed command finds the line containing PasswordAuthentication yes (even if commented out with #) and replaces it with PasswordAuthentication no. This is a common SSH hardening measure and gives FIM something real to detect.
- Wait approximately 5-10 seconds for the FIM module to detect the change.
- Open the Wazuh dashboard in your browser.
- Navigate to the File Integrity Monitoring module from the left menu.
- Click the Events tab.
- Look for an alert with rule ID 550 and the path /etc/ssh/sshd_config.
- Expand the alert to confirm you can see the file diff, the user who made the change, and the process used (sed).
What should I see in the alert?
The expanded alert shows syscheck.diff with the before/after content. The syscheck.audit section shows the user, process name, and process ID. Rule ID 550 means "Integrity checksum changed."
This is the evidence that your FIM configuration is working and producing audit-grade data.
Don't see a rule 550 alert?
- Make sure the agent is still running with sudo systemctl status wazuh-agent.
- Confirm whodata is active by checking for audit rules with sudo auditctl -l | grep wazuh_fim. You should see rules for each monitored path.
- Try waiting up to 30 seconds. Whodata alerts are near real-time but may have a slight delay on first detection.
- Check the dashboard time range is set to the last 15 minutes.
Help me troubleshoot my missing FIM alert
Add a nodiff exclusion for sensitive files
The /etc/shadow file contains password hashes. With report_changes enabled, FIM would include the actual hash content in alert diffs. This is a data leakage risk.
The nodiff option tells FIM to still alert on attribute changes but never include the file content in the diff.
- Open ossec.conf again by running:
sudo nano /var/ossec/etc/ossec.conf
- Inside the <syscheck> block, find the <skip_nfs> line. Add the following lines directly above it:
<!-- Prevent sensitive password hash data from appearing in diffs -->
<nodiff>/etc/shadow</nodiff>
What does nodiff do?
The <nodiff> tag tells FIM to never compute or transmit the content diff for that file. You still receive alerts when the file's attributes change (size, permissions, ownership, checksums). The alert just omits the syscheck.diff field so no sensitive data leaks into your logging pipeline.
- Save the file with Ctrl+O then Enter.
- Exit nano with Ctrl+X.
- Restart the agent to apply the exclusion by running:
sudo systemctl restart wazuh-agent
- Trigger a change to /etc/shadow by changing the root password age attribute. Run this command:
sudo chage -M 90 root
What does this command do?
The chage -M 90 root command sets the maximum password age for root to 90 days. This modifies /etc/shadow attributes without changing the actual password hash, giving FIM something to detect.
- Wait about 10 seconds, then check the Wazuh dashboard File Integrity Monitoring module.
- Click the Events tab.
- Find the alert for /etc/shadow and expand it.
- Confirm that the syscheck.diff field is empty or absent. The alert still shows attribute changes (checksums, timestamps) but no file content.
Still seeing diff content for /etc/shadow?
Make sure the <nodiff>/etc/shadow</nodiff> line is inside the <syscheck> block (between <syscheck> and </syscheck>). Confirm you restarted the agent after adding it. If you placed it outside the syscheck block, the setting is ignored.
Help me check my nodiff configuration placement
✔️ Awesome, I've got everything!
Great. Double check you have saved ossec.conf and the agent is running.
ⓧ I'd like to double check the full code
Here is the complete <syscheck> block as it should look in /var/ossec/etc/ossec.conf at the end of this step:
<syscheck>
<disabled>no</disabled>
<frequency>43200</frequency>
<scan_on_start>yes</scan_on_start>
<!-- NIST SC-28 and CM-3: Monitor critical system files with whodata -->
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/ssh/sshd_config</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/pam.d</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/passwd</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/etc/shadow</directories>
<directories check_all="yes" whodata="yes" report_changes="yes">/var/log/audit</directories>
<!-- Prevent sensitive password hash data from appearing in diffs -->
<nodiff>/etc/shadow</nodiff>
<!-- Skip system virtual filesystems -->
<skip_nfs>yes</skip_nfs>
<skip_dev>yes</skip_dev>
<skip_proc>yes</skip_proc>
<skip_sys>yes</skip_sys>
</syscheck>
Your FIM pipeline is now active and producing the audit evidence that NIST SC-28 and CM-3 require. Next up, you will build a custom Security Configuration Assessment policy that checks whether your SSH hardening actually meets the NIST baseline.
Create a Custom SCA Policy with NIST 800-53 Tags
Your File Integrity Monitoring is now detecting changes to critical files. The next layer of continuous compliance is proactively checking whether your server's configuration meets security baselines in the first place.
Wazuh's Security Configuration Assessment (SCA) module scans endpoints against YAML-based policy files that define expected configurations. In this step, you will write a custom SCA policy that checks SSH hardening settings and maps each check to specific NIST 800-53 controls.
In this step, get ready to:
- Create a custom SCA policy file with NIST 800-53 compliance tags for SSH hardening checks.
- Write three security checks mapped to NIST IA-5, CM-6, and AC-17 controls.
- Enable the policy in the agent configuration and verify scan results in the Wazuh dashboard.
Create the SCA policy directory and file
Custom SCA policies need their own directory separate from the default Wazuh ruleset folder. The default folder at /var/ossec/ruleset/sca is overwritten during updates, so you will store your custom policy in a dedicated location.
- SSH into your agent VM (if not already connected).
- Create the custom SCA directory by running this command:
sudo mkdir -p /var/ossec/etc/custom-sca-files/
- Create and open the custom SCA policy file by running:
sudo nano /var/ossec/etc/custom-sca-files/nist_ssh_hardening.yml
- Paste the following policy header and requirements section into the file:
# NIST 800-53 SSH Hardening Policy
# Maps SSH configuration checks to NIST control families
policy:
id: "nist_ssh_hardening"
file: "nist_ssh_hardening.yml"
name: "NIST 800-53 SSH Hardening"
description: "Checks SSH daemon configuration against NIST 800-53 security controls for access control and authentication."
references:
- https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final
requirements:
title: "Check that SSH configuration file exists"
description: "The SSH daemon configuration file must be present for these checks to run."
condition: all
rules:
- 'f:/etc/ssh/sshd_config'
What does this section do?
The policy section defines metadata like the unique ID, display name, and description that appear in the Wazuh dashboard.
The requirements section defines preconditions. If the SSH config file does not exist, the entire policy scan is skipped. This prevents false failures on systems without SSH installed.
Write the SSH hardening checks with NIST compliance tags
Each SCA check evaluates a specific configuration setting and maps it to a NIST 800-53 control. The compliance field uses the nist_sp_800-53 key (with a hyphen, not an underscore) in policy files.
- Below the requirements section you just pasted, add the following three checks:
checks:
# Check 1: Verify root login is disabled (NIST IA-5 Authenticator Management)
- id: 50001
title: "Ensure SSH root login is disabled"
description: "PermitRootLogin should be set to no to prevent direct root access via SSH."
rationale: "Disabling root login forces administrators to authenticate with individual accounts, supporting accountability and authenticator management."
remediation: "Set PermitRootLogin to no in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["IA-5"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*PermitRootLogin\s+no'
# Check 2: Verify SSH Protocol 2 is enforced (NIST CM-6 Configuration Settings)
- id: 50002
title: "Ensure SSH Protocol 2 is enforced"
description: "SSH Protocol 1 has known cryptographic weaknesses. Only Protocol 2 should be used."
rationale: "Protocol 2 provides stronger encryption and integrity verification, meeting configuration hardening requirements."
remediation: "Add or set Protocol 2 in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["CM-6"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*Protocol\s+2'
# Check 3: Verify password authentication is disabled (NIST AC-17 Remote Access)
- id: 50003
title: "Ensure PasswordAuthentication is disabled"
description: "Password-based SSH authentication should be disabled in favor of key-based authentication."
rationale: "Key-based authentication is significantly more resistant to brute-force attacks, satisfying remote access security requirements."
remediation: "Set PasswordAuthentication to no in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["AC-17"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*PasswordAuthentication\s+no'
What do these checks do?
- Check 50001 scans sshd_config for PermitRootLogin set to no. This maps to NIST IA-5 (Authenticator Management), which requires systems to enforce individual accountability.
- Check 50002 looks for Protocol 2 in the SSH config. This maps to NIST CM-6 (Configuration Settings), which requires secure default configurations across systems.
- Check 50003 verifies PasswordAuthentication is set to no. This maps to NIST AC-17 (Remote Access), which requires strong authentication mechanisms for remote sessions.
Each rule uses the f:/path -> r:regex syntax. This tells Wazuh to open the file at the given path and search for a line matching the regular expression.
- Save the file by pressing Ctrl+O, then Enter, then exit with Ctrl+X.
- Set the required ownership on the policy file by running:
sudo chown wazuh:wazuh /var/ossec/etc/custom-sca-files/nist_ssh_hardening.yml
Why set ownership to wazuh:wazuh?
The Wazuh agent process runs as the wazuh user. If the policy file is owned by root, the agent cannot read it and the SCA scan silently skips the policy.
- Verify the file exists and has correct ownership by running:
ls -la /var/ossec/etc/custom-sca-files/
You should see nist_ssh_hardening.yml listed with wazuh wazuh as the owner and group.
File not showing up?
- Make sure you included the -p flag when creating the directory. Without it, the command fails if parent directories are missing.
- Check that you used sudo for both the mkdir and nano commands. Without elevated permissions, the file cannot be created inside /var/ossec/.
- Help me troubleshoot my custom SCA policy file creation on the Wazuh agent VM.
Enable the custom policy and verify in the dashboard
Custom SCA policies stored outside the default ruleset folder are not scanned automatically. You need to register the policy path in the agent's ossec.conf so the SCA module picks it up during its next scan cycle.
- Open the agent configuration file by running:
sudo nano /var/ossec/etc/ossec.conf
- Find a location near the end of the file (before the closing </ossec_config> tag) and add the following SCA policies block:
<!-- Custom SCA policy for NIST 800-53 SSH hardening -->
<sca>
<policies>
<policy>/var/ossec/etc/custom-sca-files/nist_ssh_hardening.yml</policy>
</policies>
</sca>
What does this configuration do?
The <sca><policies> block tells the Wazuh agent to load additional policy files beyond the defaults in /var/ossec/ruleset/sca. The agent will scan this custom policy alongside any built-in CIS benchmark policies already running.
- Save the file by pressing Ctrl+O, then Enter, then exit with Ctrl+X.
- Restart the Wazuh agent service to load the new policy by running:
sudo systemctl restart wazuh-agent
- Wait about 30 seconds for the SCA scan to run on startup.
- Open your Wazuh dashboard in the browser.
- Navigate to the agent's page by clicking Agents management in the left menu, then selecting your agent.
- Click Configuration Assessment in the agent modules.
You should see NIST 800-53 SSH Hardening listed as a policy alongside any default CIS benchmark policies. Click into it to see the pass/fail results for each of your three checks.
What should I see in the results?
- Check 50003 (PasswordAuthentication disabled) should show Passed, because you set PasswordAuthentication to no during the FIM test in the previous step.
- Check 50001 (root login disabled) may show Passed or Failed depending on your VM's default SSH configuration.
- Check 50002 (Protocol 2) will likely show Failed or Not applicable on modern Ubuntu, because OpenSSH 8.x+ removed the Protocol directive entirely since it only supports Protocol 2 by default.
Policy not showing in the dashboard?
- Verify the file ownership is wazuh:wazuh. Run ls -la /var/ossec/etc/custom-sca-files/ to confirm. If ownership is wrong, re-run the chown command.
- Check that the YAML syntax is valid. A misplaced space or incorrect indentation prevents the policy from loading. Re-open the file and verify indentation uses spaces (not tabs).
- Check the agent log for errors by running: sudo cat /var/ossec/logs/ossec.log | grep -i sca. Look for lines mentioning your policy file.
- Help me troubleshoot why my custom SCA policy is not appearing in the Wazuh dashboard.
✔️ Awesome, I've got everything!
Great. Double check you have saved both files before moving on.
ⓧ I'd like to double check the full code
Here is the complete nist_ssh_hardening.yml file:
# NIST 800-53 SSH Hardening Policy
# Maps SSH configuration checks to NIST control families
policy:
id: "nist_ssh_hardening"
file: "nist_ssh_hardening.yml"
name: "NIST 800-53 SSH Hardening"
description: "Checks SSH daemon configuration against NIST 800-53 security controls for access control and authentication."
references:
- https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final
requirements:
title: "Check that SSH configuration file exists"
description: "The SSH daemon configuration file must be present for these checks to run."
condition: all
rules:
- 'f:/etc/ssh/sshd_config'
checks:
# Check 1: Verify root login is disabled (NIST IA-5 Authenticator Management)
- id: 50001
title: "Ensure SSH root login is disabled"
description: "PermitRootLogin should be set to no to prevent direct root access via SSH."
rationale: "Disabling root login forces administrators to authenticate with individual accounts, supporting accountability and authenticator management."
remediation: "Set PermitRootLogin to no in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["IA-5"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*PermitRootLogin\s+no'
# Check 2: Verify SSH Protocol 2 is enforced (NIST CM-6 Configuration Settings)
- id: 50002
title: "Ensure SSH Protocol 2 is enforced"
description: "SSH Protocol 1 has known cryptographic weaknesses. Only Protocol 2 should be used."
rationale: "Protocol 2 provides stronger encryption and integrity verification, meeting configuration hardening requirements."
remediation: "Add or set Protocol 2 in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["CM-6"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*Protocol\s+2'
# Check 3: Verify password authentication is disabled (NIST AC-17 Remote Access)
- id: 50003
title: "Ensure PasswordAuthentication is disabled"
description: "Password-based SSH authentication should be disabled in favor of key-based authentication."
rationale: "Key-based authentication is significantly more resistant to brute-force attacks, satisfying remote access security requirements."
remediation: "Set PasswordAuthentication to no in /etc/ssh/sshd_config and restart the sshd service."
compliance:
- nist_sp_800-53: ["AC-17"]
condition: all
rules:
- 'f:/etc/ssh/sshd_config -> r:^\s*PasswordAuthentication\s+no'
And here is the SCA block you added to ossec.conf:
<!-- Custom SCA policy for NIST 800-53 SSH hardening -->
<sca>
<policies>
<policy>/var/ossec/etc/custom-sca-files/nist_ssh_hardening.yml</policy>
</policies>
</sca>
Your SCA policy is now continuously assessing SSH hardening against NIST controls. Next up, you will build custom detection rules that fire alerts in real time and map them to additional NIST 800-53 control families.
Build Custom Detection Rules Mapped to NIST Controls
Your Wazuh deployment now monitors file changes and checks security configurations. The next layer of defense is real-time detection of suspicious activity patterns across your infrastructure.
In this step, you will write custom detection rules on the Wazuh manager that correlate multiple events over time. Each rule maps directly to specific NIST 800-53 control families, creating the audit trail that ties security events to compliance requirements.
In this step, get ready to:
- Create a brute-force detection rule mapped to NIST AC-7 and SI-4.
- Create a user account creation detection rule mapped to NIST AC-2 and AU-12.
- Verify rule loading and test detection with a real event.
Access the Wazuh manager container
Custom detection rules live on the Wazuh manager, not the agent. Since your manager runs inside a Docker container, you need to open a shell session inside that container to edit rule files.
- SSH into your Wazuh Docker host VM (the machine running your Docker stack).
- Open a bash session inside the Wazuh manager container by running this command:
docker exec -it single-node-wazuh.manager-1 bash
Your terminal prompt should change to show you are now inside the container.
Container not found?
If you get an error that the container name is not found, run docker ps to list running containers. Look for a container with "wazuh.manager" in its name and use that exact name in the docker exec command.
help me find the correct Wazuh manager container name in my Docker setup
Add the brute-force detection rule
A composite rule correlates multiple events over a time window. Your first custom rule will detect 3 failed SSH login attempts within 120 seconds from the same source, satisfying NIST AC-7 (Unsuccessful Logon Attempts) and SI-4 (Information System Monitoring).
- Inside the manager container, open the custom rules file by running:
vi /var/ossec/etc/rules/local_rules.xml
- You should see a file with an opening <group> tag and a closing </group> tag. If the file has existing content, navigate to just before the final closing </group> tag.
- Replace the entire contents of the file with the following rule definition:
<group name="local,syslog,sshd,">
<!-- Rule 100100: Detect SSH brute-force attempts -->
<!-- Fires when rule 5716 (sshd auth failure) triggers 3 times in 120 seconds -->
<rule id="100100" level="10" frequency="3" timeframe="120">
<if_matched_sid>5716</if_matched_sid>
<same_source_ip />
<description>SSH brute-force: 3 failed login attempts in 120 seconds.</description>
<group>nist_800_53_AC.7,nist_800_53_SI.4,</group>
</rule>
</group>
What does this rule do?
- frequency="3" timeframe="120" tells Wazuh to fire this rule when the parent rule triggers 3 times within a 120-second window.
- if_matched_sid references rule 5716, which is the built-in Wazuh rule for SSH authentication failures (sshd: authentication failed).
- same_source_ip ensures all failures come from the same attacker IP.
- The <group> tag maps this rule to NIST 800-53 controls AC-7 and SI-4, making it visible in compliance dashboards.
- Save the file but do not close it yet. You will add a second rule next.
Add the user account creation detection rule
Unauthorized account creation is a common persistence technique. Your second rule detects when someone runs useradd or adduser on a monitored endpoint, mapping to NIST AC-2 (Account Management) and AU-12 (Audit Record Generation).
- Open /var/ossec/etc/rules/local_rules.xml again (or continue editing if still open). Add the second rule inside the existing <group> block, just before the closing </group> tag:
<!-- Rule 100101: Detect new user account creation -->
<!-- Fires when useradd or adduser activity is detected -->
<rule id="100101" level="8">
<if_sid>5902</if_sid>
<description>New user account created on the system.</description>
<group>nist_800_53_AC.2,nist_800_53_AU.12,</group>
</rule>
What does this rule do?
- if_sid references rule 5902, which is the built-in Wazuh rule that fires when a new user is added to the system.
- Level 8 generates a visible alert without being critical enough to trigger emergency responses.
- The nist_800_53_AC.2 group tag satisfies the Account Management control requirement. nist_800_53_AU.12 satisfies Audit Record Generation.
- Save and close the file.
Why use the group tag for NIST mapping?
The <group> tag inside a rule definition is how Wazuh maps alerts to compliance frameworks. The naming convention nist_800_53_XX.Y (with underscores) is the standard format recognized by the Wazuh NIST 800-53 dashboard module.
When an alert fires, these group tags populate the rule.nist_800_53 field in the alert data. The compliance dashboard reads this field to display which controls are being monitored.
✔️ Awesome, I've got everything!
Great. Double check you have saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete local_rules.xml file with both rules:
<group name="local,syslog,sshd,">
<!-- Rule 100100: Detect SSH brute-force attempts -->
<!-- Fires when rule 5716 (sshd auth failure) triggers 3 times in 120 seconds -->
<rule id="100100" level="10" frequency="3" timeframe="120">
<if_matched_sid>5716</if_matched_sid>
<same_source_ip />
<description>SSH brute-force: 3 failed login attempts in 120 seconds.</description>
<group>nist_800_53_AC.7,nist_800_53_SI.4,</group>
</rule>
<!-- Rule 100101: Detect new user account creation -->
<!-- Fires when useradd or adduser activity is detected -->
<rule id="100101" level="8">
<if_sid>5902</if_sid>
<description>New user account created on the system.</description>
<group>nist_800_53_AC.2,nist_800_53_AU.12,</group>
</rule>
</group>
Restart the manager and verify rule loading
The Wazuh manager must be restarted to load new rules. After restarting, you can confirm the rules loaded successfully by checking the manager log file.
- Still inside the manager container, restart the Wazuh manager service by running:
/var/ossec/bin/wazuh-control restart
- Wait about 10 seconds for the service to fully restart.
- Check the manager log to confirm your rules loaded without errors by running:
grep -i "rule" /var/ossec/logs/ossec.log | tail -20
- Look for lines confirming rules were loaded successfully. You should see entries indicating rules from local_rules.xml were read without errors.
What should I see?
You should see log lines similar to Rules file loaded or Reading local rules with no error messages. If you see Error or Invalid in the output, there is a syntax problem in your XML file.
Seeing an XML syntax error?
Open /var/ossec/etc/rules/local_rules.xml and check for common issues: missing closing tags, mismatched angle brackets, or stray characters outside the <group> wrapper. Every opening tag must have a matching closing tag.
help me fix an XML syntax error in my Wazuh local_rules.xml file
- Now test the user creation rule. Switch to your agent VM SSH session (not the manager container).
- Create a test user on the agent VM by running:
sudo useradd testuser-nist
- Open the Wazuh dashboard in your browser.
- Navigate to the **Threat Hunting** module.
- Search for rule.id:100101 in the search bar.
- You should see an alert for the user creation event. Click into the alert to confirm the rule.nist_800_53 field contains AC.2 and AU.12.
Allow a minute for the alert to appear
The agent sends events to the manager at regular intervals. If you do not see the alert immediately, wait 30-60 seconds and refresh the dashboard.
No alert appearing for rule 100101?
Verify the Wazuh manager restarted cleanly by re-running the log check inside the container. Also confirm the agent is still connected (check agent status in the dashboard under **Agents management**). If the agent shows disconnected, restart the wazuh-agent service on the agent VM.
help me troubleshoot why my custom Wazuh rule 100101 is not firing when I create a user
Your Wazuh manager now has custom detection rules that correlate security events and tag them with NIST control references. Next up, you will configure Active Response to automatically take action when these rules fire.
Configure Active Response for NIST AC-7 and SC-5
Your custom detection rules are loaded and tagging alerts with NIST control families. Detection is only half the story though.
NIST 800-53 expects systems to take automated action when attacks are detected. Active Response is Wazuh's module for executing scripts on monitored endpoints the instant a specific rule fires. In this step, you will configure two automated responses: locking user accounts during brute-force attacks (NIST AC-7) and dropping malicious IP traffic (NIST SC-5).
In this step, get ready to:
- Configure the disable-account active response triggered by your brute-force rule.
- Simulate a brute-force attack and verify the account lockout fires and reverses automatically.
- Add a firewall-drop active response for high-severity alerts to satisfy NIST SC-5.
Add the disable-account active response
Wazuh ships with a default disable-account command that locks a Linux user account using passwd -l. The command definition already exists in the manager's ossec.conf. You only need to add an active-response block that tells Wazuh when to trigger it.
- In your SSH session inside the Wazuh manager container, open the manager's configuration file for editing:
vi /var/ossec/etc/ossec.conf
- Scroll to the end of the <ossec_config> block (before the closing tag) and add the following active-response configuration:
<!-- Active Response: Disable account on brute-force detection (NIST AC-7) -->
<active-response>
<disabled>no</disabled>
<command>disable-account</command>
<location>local</location>
<rules_id>100100</rules_id>
<timeout>300</timeout>
</active-response>
What does this configuration do?
Each element in the active-response block controls a different aspect of the automated action.
- <disabled>no</disabled> enables this active response block.
- <command>disable-account</command> references the built-in command that locks a user account.
- <location>local</location> executes the script on the agent where the event occurred.
- <rules_id>100100</rules_id> triggers only when your custom brute-force rule fires.
- <timeout>300</timeout> makes this a stateful response. After 300 seconds, Wazuh automatically re-enables the account.
- Save the file and restart the Wazuh manager service to load the new active response by running:
/var/ossec/bin/wazuh-control restart
- Confirm the manager started cleanly by checking the logs:
tail -20 /var/ossec/logs/ossec.log
- You should see messages confirming the manager started successfully with no configuration errors.
Seeing a configuration error in the logs?
Verify that your active-response block is inside the <ossec_config> tags and that all XML tags are properly closed. A missing </active-response> closing tag will prevent the manager from starting.
help me fix a Wazuh manager configuration error in ossec.conf after adding an active-response block
Simulate a brute-force attack and verify the lockout
To prove the active response works, you will create two test users on the agent VM and trigger your brute-force rule by failing login attempts rapidly.
- Switch to your agent VM SSH session and create two test user accounts by running these commands:
# Create test users for the brute-force simulation
sudo useradd -m testuser1
sudo useradd -m testuser2
# Set a password for testuser1 so you can log in as them
sudo passwd testuser1
What are these users for?
You will log in as testuser1 and attempt to switch to testuser2 with incorrect passwords. This simulates an attacker trying to guess credentials, which triggers rule 100100 and the active response.
- When prompted, enter a memorable password for testuser1 (this is just for testing).
- Set a password for testuser2 as well by running:
sudo passwd testuser2
- Switch to testuser1 by running:
su testuser1
- Enter the password you set for testuser1 when prompted.
- Now attempt to switch to testuser2 with an incorrect password at least 4 times in rapid succession:
# Type a wrong password each time when prompted
su testuser2
su testuser2
su testuser2
su testuser2
What should I see?
Each attempt will show su: Authentication failure. After 3 or more failures within 120 seconds, rule 100100 fires on the Wazuh manager and triggers the disable-account active response.
- Exit back to your root or sudo-capable session by running:
exit
- Wait about 30 seconds for the active response to execute, then check the status of testuser2's account by running:
sudo passwd --status testuser2
- You should see an L flag in the output, indicating the account is locked.
- Open the Wazuh dashboard in your browser.
- Navigate to the Security events module.
- Verify that an alert for rule ID 100100 appears, followed by an active response event showing the account was disabled.
What about the automatic re-enable?
Because you set a 300-second timeout, the active response is stateful. After 5 minutes, Wazuh automatically reverses the action and unlocks the account. You can verify this by waiting 5 minutes and running sudo passwd --status testuser2 again. The L flag will be gone.
Account not showing as locked?
Make sure you attempted at least 4 failed su commands within a 2-minute window. If too much time passes between attempts, the frequency threshold in rule 100100 will not be met.
Also confirm the Wazuh agent service is running on the VM with sudo systemctl status wazuh-agent. The agent must be connected for the manager to relay the active response command.
help me troubleshoot why the Wazuh disable-account active response did not fire after my brute-force simulation
Add the firewall-drop active response for NIST SC-5
NIST SC-5 (Denial-of-Service Protection) requires systems to actively block malicious traffic. The firewall-drop command uses iptables to block the source IP of any alert that meets a severity threshold.
- Switch back to your Wazuh manager container SSH session and open the configuration file again:
vi /var/ossec/etc/ossec.conf
- Add the following active-response block below the disable-account block you added earlier:
<!-- Active Response: Block source IP on high-severity alerts (NIST SC-5) -->
<active-response>
<disabled>no</disabled>
<command>firewall-drop</command>
<location>local</location>
<level>10</level>
<timeout>600</timeout>
</active-response>
What does this configuration do?
Instead of triggering on a specific rule ID, this response uses the <level>10</level> element. Any alert at severity level 10 or higher will cause the source IP to be blocked via iptables for 600 seconds (10 minutes).
The firewall-drop command is a built-in Wazuh script located at /var/ossec/active-response/bin/firewall-drop on the agent. Like disable-account, its command definition already exists in the default ossec.conf.
- Save the file and restart the Wazuh manager to apply both active responses:
/var/ossec/bin/wazuh-control restart
- Verify the manager restarted successfully by checking the logs:
tail -20 /var/ossec/logs/ossec.log
- You should see a clean startup with no errors. Both active responses are now live.
Manager won't start after adding the second active response?
Check that each active-response block is its own separate section. They cannot be nested inside each other. Each block needs its own opening <active-response> and closing </active-response> tags.
help me debug why the Wazuh manager fails to start after adding a firewall-drop active response block to ossec.conf
✔️ Awesome, I've got everything!
Great. Double check you have saved your file and the manager restarted cleanly.
ⓧ I'd like to double check the full code
Here are the two active-response blocks that should now exist in your Wazuh manager's /var/ossec/etc/ossec.conf:
<!-- Active Response: Disable account on brute-force detection (NIST AC-7) -->
<active-response>
<disabled>no</disabled>
<command>disable-account</command>
<location>local</location>
<rules_id>100100</rules_id>
<timeout>300</timeout>
</active-response>
<!-- Active Response: Block source IP on high-severity alerts (NIST SC-5) -->
<active-response>
<disabled>no</disabled>
<command>firewall-drop</command>
<location>local</location>
<level>10</level>
<timeout>600</timeout>
</active-response>
Why level-based triggering instead of a specific rule ID?
The disable-account response targets a single rule (100100) because it takes a drastic action on a specific user account. The firewall-drop response uses a level threshold instead, catching any high-severity alert (level 10 or above) that includes a source IP. This gives you broad DoS protection without needing to list every rule individually.
Your Wazuh deployment now automatically locks accounts under brute-force attack and blocks IPs generating high-severity alerts. Next up, you will explore the NIST 800-53 compliance dashboard to turn all of this detection and response data into audit-ready reports.
Navigate the NIST 800-53 Compliance Dashboard and Generate Reports
You have built a complete NIST 800-53 monitoring pipeline with FIM, SCA, custom rules, and automated responses. Now it is time to prove that all of this work is actually providing continuous compliance coverage.
Federal auditors do not accept "trust me, it's configured." They need exportable evidence showing which controls were assessed, what events were detected, and when the monitoring was active. The Wazuh dashboard provides a dedicated NIST 800-53 compliance module that maps every alert to its corresponding control. In this step, you will navigate that module, trace alerts back to specific controls, and export the audit-ready CSV report.
In this step, get ready to:
- Review the NIST 800-53 Controls tab to verify coverage across control families.
- Examine individual alert events with full compliance metadata.
- Export an audit-ready compliance report as CSV from the Discover module.
Review the NIST 800-53 Controls tab
The Controls tab groups all alerts by their associated NIST 800-53 control family. This gives you a single view of which controls your system is actively monitoring and which have generated events.
- Open the Wazuh dashboard in your browser.
- Click the menu icon (hamburger menu) in the top left corner.
- Select NIST 800-53 from the modules list. This opens the dedicated NIST 800-53 compliance module.
- Click the Controls tab.
You should see a table listing NIST 800-53 controls alongside the number of related events detected by your configuration.
- Look for controls from the families you configured: AC (Access Control), AU (Audit), CM (Configuration Management), SC (System and Communications), and SI (System and Information Integrity).
- Use the search bar or filter options at the top to filter by a specific control family. Try entering AC to see only Access Control events from your brute-force detection rule.
- Clear the filter, then try CM to see Configuration Management events from your FIM and SCA configurations.
What does the Controls tab show?
The Controls tab groups events by their NIST 800-53 control identifier. Each row shows a control (like AC-7 or SC-28) and the count of alerts that Wazuh mapped to it.
Wazuh performs this mapping automatically for any rule that includes NIST control identifiers in its group tag. Your custom rules (100100 and 100101) and Wazuh's built-in rules both feed into this view.
Not seeing any controls listed?
Make sure the time range in the top right corner covers the period when you ran your tests (FIM changes, brute-force simulation, user creation). Expand it to at least the last 24 hours.
If the NIST 800-53 module is completely empty, confirm your agent is still connected under Agents management and showing active status.
Help me troubleshoot why my NIST 800-53 Controls tab is empty even though I have configured custom rules with NIST group tags.
Examine individual alerts in the Events tab
Auditors need to see the actual evidence behind each control, not just a count. The Events tab lets you drill into individual alerts and see their full NIST 800-53 compliance metadata.
- Click the Events tab in the NIST 800-53 module.
- You should see a list of individual alert events, each tagged with its associated NIST 800-53 controls.
- Click on any alert row to expand its details.
- Look for the rule.nist_800_53 field in the alert details. This field contains the specific control identifiers (like AC.7 or SI.4) that the alert satisfies.
How would an auditor use this?
An auditor starts with a NIST control requirement (for example, AC-7: the system must lock accounts after failed logon attempts). They filter the Events tab to show only AC-7 events. Each event proves the control was actively monitored and enforced.
This traceability chain from control requirement to specific security event is what differentiates continuous compliance monitoring from a one-time audit checklist.
- Add a filter for a specific control. Click Add filter, set the field to rule.nist_800_53, and enter AC.7 as the value.
- Review the filtered results. You should see alerts from your brute-force detection rule (100100) that fired during your earlier testing.
- Clear the filter and try AU.12 to see alerts from your user creation rule (100101).
Generate and export a compliance report
The final deliverable is an exportable CSV that serves as audit evidence. This report captures every NIST-tagged alert from your testing period in a format that auditors can review independently.
- Go to the Wazuh dashboard menu and select Discover under the Explore section.
- Click Add filter in the filter bar.
- Set the field to rule.nist_800_53.
- Set the operator to exists. This captures every alert that has a NIST 800-53 mapping, regardless of which specific control it belongs to.
- Click Save to apply the filter.
Why filter with 'exists' instead of a specific value?
Using the exists operator captures alerts mapped to any NIST 800-53 control in a single query. This gives auditors a complete picture of all compliance-related events across every control family (AC, AU, CM, SC, SI) without requiring separate reports per control.
- Adjust the time range in the top right corner to cover your entire testing period. Click the time picker and select a range that includes all testing you performed in the previous steps.
- Verify that results appear in the table below. You should see alerts from FIM, your custom rules, and Active Response all represented.
- Save the search by clicking Save in the top menu bar. Enter a name like NIST-800-53-Compliance-Evidence and click Save.
- Select Reporting, then choose Generate CSV. This downloads a CSV file containing all NIST-tagged events from your selected time range.
Your CSV download should begin. This file is the audit evidence artifact that proves continuous NIST 800-53 monitoring was active and controls were being assessed.
What makes this report audit-ready?
The CSV contains timestamps, rule IDs, NIST control mappings, source agents, and event descriptions for every compliance-relevant alert. An auditor can open it in a spreadsheet, sort by control family, and verify that each required NIST control has associated monitoring evidence.
This satisfies NIST CA-7 (Continuous Monitoring) and AU-6 (Audit Record Review) by demonstrating that the organization maintains ongoing awareness of security events mapped to federal controls.
CSV download not starting?
Make sure you saved the search before selecting Generate CSV. The reporting feature requires a saved search to generate the export.
If the report is empty, expand your time range. Alerts may have been generated hours ago during earlier steps.
Help me export a CSV compliance report from the Wazuh Discover module.
You now have audit-ready evidence of continuous NIST 800-53 compliance monitoring. This is the end-to-end chain that enterprise compliance teams need: detection rules mapped to controls, automated responses enforcing those controls, and exportable reports proving it all happened. Up next, the Secret Mission challenges you to extend this system with geolocation-based blocking and a dedicated incident response audit trail.
Secret mission
Create a GeoIP-Based Active Response Script with NIST Audit Logging
You've configured Wazuh's built-in Active Response commands to block attackers. But what if you could enrich every block action with geolocation intelligence and create a tamper-proof audit trail that satisfies NIST AU-9? In this mission, you'll build a custom script that does exactly that.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses GCP Compute Engine VMs that incur ongoing charges while running.
Cost warning
Your agent VM (e2-medium) costs approximately $0.03/hr while running. That adds up to roughly $25/month if left on continuously.
If you are not actively using the compliance monitoring setup, stop or delete the agent VM to avoid unnecessary charges.
Resources you used:
- GCP Compute Engine VM (e2-medium, Ubuntu 22.04) running the Wazuh agent.
- Wazuh Docker single-node stack (manager, indexer, dashboard) on your existing GCP VM.
- Custom configuration files: SCA policy, local_rules.xml, FIM settings, Active Response configs.
- Test user accounts (testuser1, testuser2) created on the agent VM.
Keep everything running
No action needed. Choose this if you are still actively practicing compliance monitoring or plan to extend the system with additional agents.
- Both VMs remain running and the Wazuh stack continues monitoring.
- Ongoing cost is approximately $25/month for the agent VM plus your existing Docker host costs.
- All compliance data, rules, and dashboards remain intact and accessible.
Pause - I'll come back to this later
Stop the VMs to eliminate compute charges while preserving your configuration. Disk storage still incurs minimal cost.
- Stop the agent VM from the GCP Console. Navigate to Compute Engine, then VM instances, select your agent VM, and click Stop.
- Stop the Wazuh Docker stack on your manager VM by running:
docker compose down
- When you are ready to resume, start the agent VM from GCP Console and run docker compose up -d on the manager VM.
Delete - I don't want to use this again
Remove all project resources completely. This cannot be undone.
- Delete the agent VM from the GCP Console. Navigate to Compute Engine, then VM instances, select your agent VM, and click Delete.
- If you no longer need the Wazuh deployment, stop and remove the Docker stack and all its data by running:
docker compose down -v
- Remove any GCP firewall rules you created specifically for Wazuh agent communication (TCP 1514 and 1515).
- After deletion, all compliance data, custom rules, SCA policies, and dashboard configurations are permanently lost.
That's a Wrap!
That's a Wrap!
Nice work! You have built an enterprise-grade NIST 800-53 continuous compliance monitoring system using Wazuh that detects threats, enforces security baselines, and produces the audit evidence that compliance officers demand.
You've learned how to:
- Configure real-time File Integrity Monitoring with who-data auditing mapped to NIST SC-28 and CM-3 controls, tracking exactly who changed critical files and how.
- Build custom detection rules and SCA policies mapped to NIST 800-53 control families including AC-7, AC-2, AU-12, SI-4, CM-6, and IA-5.
- Implement Active Response automation for brute-force account lockout and firewall-based DoS mitigation, then generate audit-ready compliance reports proving continuous monitoring coverage.
- Secret Mission: Created a custom GeoIP Active Response script with structured NIST incident response logging and audit protection.
Ready to quiz yourself?