Skip to content

Wazuh Setup, Misc. Container Creation Fixes + Other Small Bug Fixes#12

Merged
maxklema merged 20 commits intomieweb:mainfrom
maxklema:main
Aug 13, 2025
Merged

Wazuh Setup, Misc. Container Creation Fixes + Other Small Bug Fixes#12
maxklema merged 20 commits intomieweb:mainfrom
maxklema:main

Conversation

@maxklema
Copy link
Contributor

@maxklema maxklema commented Aug 12, 2025

This PR introduces a Wazuh integration into our existing cluster. The Container Creation Script has been updated to automatically provision new containers as Wazuh Agents communicating with our Wazuh manager.

Wazuh/ folder: Contains all relevant Wazuh files, such as custom decoders, rules, agent configuration scripts, automatic agent prune scripts, and other helper files to make API calls.

Wazuh/firewall-block.sh: A custom firewall-drop script that blocks a source IP using iptables on the pve1 hypervisor.

Wazuh/local_decoders.xml: A custom decoder rule that detects SYN log messages on the hypervisor for ports between 2222-2999 (SSH ports)

Wazuh/local_rules.xml: Rules that respond to the custom decoder and detect a brute force invasion.

Wazuh/manage-alerts.js: A helper JS file using axios to make API requests to our Wazuh API endpoint to create JWT tokens, new agents, and to delete agents.

Wazuh/prune_agents.sh: A script that automatically removes Wazuh agents that are associated with a container that no longer exists. This runs at 3:00 AM EST every night.

Wazuh/register-agent.sh: A script used by Create Container to automatically register new containers on Rocky (CentOS) and Debian systems. Automatic updates are disabled, and the version installed is 4.12.

The Wazuh dashboard is located at https://wazuh-dashboard.opensource.mieweb.org. Contact me for credentials.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces Wazuh security monitoring integration into the existing cluster infrastructure. The integration automatically provisions new containers as Wazuh agents and implements custom security rules for SSH brute force detection.

  • Adds comprehensive Wazuh monitoring setup with custom decoders and rules for SSH security
  • Updates container creation to include automatic Wazuh agent registration
  • Modifies port mapping to include user credentials and OS information

Reviewed Changes

Copilot reviewed 13 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
container creation/register-container.sh Updated to include new parameters for proxmox user and root password in port mapping
container creation/create-container.sh Added random root password generation and Wazuh agent setup integration
ci-cd automation/proxmox-launchpad/action.yml Complete removal of the GitHub Action configuration file
ci-cd automation/proxmox-launchpad/README.md Complete removal of the GitHub Action documentation
Wazuh/runner.js Entry point script for managing Wazuh agent operations
Wazuh/register-agent.sh Script to register new containers as Wazuh agents
Wazuh/prune_agents.sh Automated cleanup script for removing orphaned Wazuh agents
Wazuh/package.json Dependencies for Wazuh management scripts
Wazuh/manage-agents.js Core API client for Wazuh agent management operations
Wazuh/local_rules.xml Custom security rules for SSH brute force detection
Wazuh/local_decoders.xml Custom log decoders for iptables SSH connection attempts
Wazuh/firewall-block.sh Active response script for blocking malicious IPs
README.md Updated documentation to include Wazuh integration details
Files not reviewed (1)
  • Wazuh/package-lock.json: Language not supported

Comment on lines +116 to +121
root_pswd="$9"

jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \
--arg root_pswd "$root_pswd" \
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable root_pswd is defined but referenced as $9 which doesn't exist in the parameter list. The ssh command only passes 8 parameters but tries to access a 9th parameter.

Suggested change
root_pswd="$9"
jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \
--arg root_pswd "$root_pswd" \
jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +121
root_pswd="$9"

jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \
--arg root_pswd "$root_pswd" \
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable root_pswd is used in jq but is undefined since $9 parameter doesn't exist in the ssh command invocation.

Suggested change
root_pswd="$9"
jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \
--arg root_pswd "$root_pswd" \
jq --arg hn "$hostname" \
--arg ip "$container_ip" \
--arg user "$user" \

Copilot uses AI. Check for mistakes.
EXISTING_AGENTS=$(node /var/lib/vz/snippets/Wazuh/runner.js getAgents | sed '1d')

write_log "Retrieved agents from Wazuh manager:"
write_log "$AGENTS"
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable $AGENTS is undefined. It should be $EXISTING_AGENTS based on the variable defined on line 54.

Suggested change
write_log "$AGENTS"
write_log "$EXISTING_AGENTS"

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +39
if [[ "$CTIDS_OUTPUT" =~ "Permission denied" || "$CTIDS_OUTPUT" =~ "Connection refused" || "$CTIDS_OUTPUT" =~ "Host key verification failed" ]]; then
log_message "ERROR: SSH to $node failed: $CTIDS_OUTPUT"
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable $CTIDS_OUTPUT is undefined. It should be $HOSTNAMES based on the SSH command output stored on line 36.

Suggested change
if [[ "$CTIDS_OUTPUT" =~ "Permission denied" || "$CTIDS_OUTPUT" =~ "Connection refused" || "$CTIDS_OUTPUT" =~ "Host key verification failed" ]]; then
log_message "ERROR: SSH to $node failed: $CTIDS_OUTPUT"
if [[ "$HOSTNAMES" =~ "Permission denied" || "$HOSTNAMES" =~ "Connection refused" || "$HOSTNAMES" =~ "Host key verification failed" ]]; then
write_log "ERROR: SSH to $node failed: $HOSTNAMES"

Copilot uses AI. Check for mistakes.
HOSTNAMES=$(ssh "$node" "$HOSTNAMES_CMD")

if [[ "$CTIDS_OUTPUT" =~ "Permission denied" || "$CTIDS_OUTPUT" =~ "Connection refused" || "$CTIDS_OUTPUT" =~ "Host key verification failed" ]]; then
log_message "ERROR: SSH to $node failed: $CTIDS_OUTPUT"
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function log_message is called but it should be write_log based on the function definition on line 9.

Suggested change
log_message "ERROR: SSH to $node failed: $CTIDS_OUTPUT"
write_log "ERROR: SSH to $node failed: $CTIDS_OUTPUT"

Copilot uses AI. Check for mistakes.
if (response.status !== 200) {
console.log('fail');
}
agentKey = response.data.data.key;
Copy link

Copilot AI Aug 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable agentKey is declared without const, let, or var, making it an implicit global variable. This should be declared as a local variable.

Suggested change
agentKey = response.data.data.key;
const agentKey = response.data.data.key;

Copilot uses AI. Check for mistakes.
# Function to add firewall rule
add_rule() {
local ip="$1"
ipset add blacklist "$ip" 2>/dev/null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big thing I wanted to point out that I forgot to mention was the move to ipset for better performance on a larger scope of IP's, but it looks like this was already changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The big thing I wanted to point out that I forgot to mention was the move to ipset for better performance on a larger scope of IP's, but it looks like this was already changed.

Yes, it provides much better performance and cleaner code!

Copy link
Collaborator

@cmyers-mieweb cmyers-mieweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be a good addition to also add the ossec.conf to the PR as a lot of Wazuh's configurations are sourced from it. That way I can review security levels, whitelists, FIM config, etc.

@maxklema
Copy link
Contributor Author

I believe it would be a good addition to also add the ossec.conf to the PR as a lot of Wazuh's configurations are sourced from it. That way I can review security levels, whitelists, FIM config, etc.

Good idea. I will add that.

@maxklema maxklema requested a review from cmyers-mieweb August 13, 2025 16:04
Copy link
Collaborator

@cmyers-mieweb cmyers-mieweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a couple comments to review, unrelated to comments, but you also might find this useful in the case of future rules as they come up in the future for classification: https://documentation.wazuh.com/current/user-manual/ruleset/rules/rules-classification.html

Wazuh/ossec.conf Outdated
<email_notification>no</email_notification>
<smtp_server>smtp.example.wazuh.com</smtp_server>
<email_from>wazuh@example.wazuh.com</email_from>
<email_to>recipient@example.wazuh.com</email_to>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd maybe comment out email config and restart the manager if there is no issues with the restart, if we don't have a email server presently setup, I don't know if the manager actually emails a daily report of our threat logs to the default example email recepient.

Comment on lines +193 to +197
<global>
<white_list>127.0.0.1</white_list>
<white_list>^localhost.localdomain$</white_list>
<white_list>10.15.0.3</white_list>
</global>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be smart to whitelist the DHCP subnet range so we don't accidentally block a container. Outbound SSH is already killed as well from the firewalls perspective. I've seen cases where clients get blocked from a false positive that ends up causing a work stoppage until someone can go in and unblock.

Another alternative is setting up a timeout block specific to the subnet range to maybe block for 10 minutes and then unblock, unlike a permanent for unknown IP's.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I will just whitelist the whole DHCP subnet since the outbound SSH has already been established.

@maxklema maxklema requested a review from cmyers-mieweb August 13, 2025 16:45
Copy link
Collaborator

@cmyers-mieweb cmyers-mieweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@maxklema maxklema merged commit 5f56b8d into mieweb:main Aug 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants