This article explains how to deploy a secure Wazuh All-in-One server on AWS and configure Amazon EKS node groups to automatically install the Wazuh agent. With this setup, we can enable vulnerability detection and continuous monitoring across our Kubernetes workloads.
Before we start with this article, have you heard of Wazuh before ?
What is Wazuh?
Wazuh is a powerful open source platform for threat detection, incident response, and compliance.
Requirements
- AWS Account & CLI (and a profile with enough permissions for: VPC, EC2, ACM PCA, and Client VPN
- ACM Private CA (or an external CA you control
- OpenSSL
What is AWS Client VPN?
AWS Client VPN is a managed VPN service that lets users securely connect to AWS resources using OpenVPN based clients. By using ACM Private CA certificates, we can enforce mutual authentication, ensuring both the server and the client prove their identities before a session is established
Walkthrough
Creating the VPC
Let's start by creating or using an existing VPC. Create subnets for:
- Public subnet – for NAT Gateway
- Private subnet – for Wazuh All-in-One
- VPN subnet – to associate with the Client VPN endpoint
- The Wazuh server must go in the private subnet. No public IP is required
In this case, we are defining one public subnet and two private subnets. The public subnet will host the Internet Gateway and a NAT Gateway, which act as controlled egress points for outbound traffic. The private subnet will serve different purposes: one dedicated to the Wazuh server itself, and another for VPN clients that will provide secure access into the VPC
Note: If your architecture already connects multiple VPCs through an AWS Transit Gateway (TGW), you could simplify this design by deploying Wazuh entirely in private subnets and routing access through the TGW. This way, Wazuh services remain isolated while still being reachable from other connected networks without needing multiple subnet roles or direct internet exposure
We are going to create our VPC, for this in the AWS console we search for VPC > Your VPCs > Create VPC > VPC Only
We follow by creating our subnets in our VPC, by following in the AWS console > VPC > Subnet > Create Subnet > Select the previously created VPC. And we follow the following segmentation, for this example we are going to use a single AZ but for reability we can use two.
Segmentation
VPC CIDR: 10.50.0.0/24
- WAZUH Public Subnet (10.50.0.0/28): Hosts the NAT Gateway to provide controlled outbound internet access.
- WAZUH Server Managed (10.50.0.32/27): Private subnet for the Wazuh All-in-One server.
- VPN Client Subnet (10.50.0.64/27): Private subnet dedicated to Client VPN resources.
- Client VPN Endpoint Range (10.50.8.0/22): Address pool for devices connecting through the VPN, non-overlapping with the VPC.
Now we create. route table for our subnets, for this in the AWS Console > VPC > Route Tables > Create Route Table
Our route table for the Wazuh subnet, should have a NAT Gateway pointing to the public subnet:
The Peering between VPC
Since my EKS lives in another VPC we need to make a VPC peering between both so they can communicate, this step can be ignored if the Wazuh server and EKS lives in the same VPC, but as a good practices both of them are separated.
For this we follow this step in the AWS Console > VPC > Peering Connections. Define the request VPC as the EKS VPC and the acepter as the Wazuh VPC.
Certificates with ACM PCA
Create server and client certificates to use with Client VPN.
Server Certificate:
Generate a CSR and private key with OpenSSL:
openssl req -new -newkey rsa:2048 -nodes \
-keyout server.key \
-out server.csr \
-subj "/CN=wazuh.server" \
-addext "extendedKeyUsage=serverAuth"
Issue the certificate with ACM PCA:
aws acm-pca issue-certificate \
--certificate-authority-arn <pca-arn> \
--csr fileb://server.csr \
--signing-algorithm "SHA256WITHRSA" \
--validity Value=365,Type="DAYS" \
--idempotency-token vpn-cert-01
Fetch the certificate (replace with the ARN from the previous command):
aws acm-pca get-certificate \
--certificate-authority-arn <pca-arn> \
--certificate-arn <certificate-arn> \
--output text > server.crt
Import the certificate into ACM so it can be used by Client VPN:
aws acm import-certificate \
--certificate fileb://server.crt \
--private-key fileb://server.key \
--certificate-chain fileb://ca-chain.pem
Client Certificate
Generate a CSR and private key:
openssl req -new -newkey rsa:2048 -nodes \
-keyout client.key \
-out client.csr \
-subj "/CN=wazuh.client" \
-addext "keyUsage=digitalSignature,keyEncipherment" \
-addext "extendedKeyUsage=clientAuth"
Issue with ACM PCA (using the EndEntityClientAuthCertificate template):
aws acm-pca issue-certificate \
--certificate-authority-arn <pca-arn> \
--csr fileb://client.csr \
--signing-algorithm "SHA256WITHRSA" \
--validity Value=365,Type="DAYS" \
--idempotency-token vpn-cert-02 \
--template-arn arn:aws:acm-pca:::template/EndEntityClientAuthCertificate/V1
Fetch the certificate:
aws acm-pca get-certificate \
--certificate-authority-arn <pca-arn> \
--certificate-arn <certificate-arn> \
--output text > client.crt
Security Groups
Client VPN SG – allow inbound 443 (UDP) and allow traffic from the VPN CIDR.
Wazuh SG – allow 22, 443, 1514, and 1515 only from the VPN CIDR.
SG for Client VPN:
aws ec2 create-security-group \
--group-name wazuh-clientvpn-access \
--description "Enable traffic from Client VPN" \
--vpc-id <vpc-id> \
--region <region>
aws ec2 authorize-security-group-ingress \
--group-id <vpn-sg-id> \
--protocol tcp --port 22 --cidr <vpn-client-cidr> \
--region <region>
aws ec2 authorize-security-group-ingress \
--group-id <vpn-sg-id> \
--protocol tcp --port 443 --cidr <vpn-client-cidr> \
--region <region>
SG for Wazuh Server
aws ec2 create-security-group \
--group-name wazuh-management-access \
--description "Enable access from VPN to Wazuh" \
--vpc-id <vpc-id> \
--region <region>
aws ec2 authorize-security-group-ingress --group-id <wazuh-sg-id> --protocol tcp --port 22 --cidr <vpn-client-cidr> --region <region>
aws ec2 authorize-security-group-ingress --group-id <wazuh-sg-id> --protocol tcp --port 443 --cidr <vpn-client-cidr> --region <region>
aws ec2 authorize-security-group-ingress --group-id <wazuh-sg-id> --protocol tcp --port 1514 --cidr <vpn-client-cidr> --region <region>
aws ec2 authorize-security-group-ingress --group-id <wazuh-sg-id> --protocol tcp --port 1515 --cidr <vpn-client-cidr> --region <region>
Create Client VPN Endpoint
Use the ACM server certificate and your root CA for mutual authentication:
aws ec2 create-client-vpn-endpoint \
--client-cidr-block <vpn-client-cidr> \
--server-certificate-arn <server-cert-arn> \
--authentication-options Type=certificate-authentication,MutualAuthentication={ClientRootCertificateChainArn=<ca-arn>} \
--dns-servers 8.8.8.8 8.8.4.4 \
--transport-protocol udp \
--vpc-id <vpc-id> \
--security-group-ids <vpn-sg-id> \
--region <region>
Associate the endpoint with your VPN subnet, create a route to the Wazuh subnet, and authorize ingress
aws ec2 associate-client-vpn-target-network \
--client-vpn-endpoint-id <cvpn-endpoint-id> \
--subnet-id <subnet-id> \
--region <region>
Route to Wazuh subnet
aws ec2 create-client-vpn-route \
--client-vpn-endpoint-id <cvpn-endpoint-id> \
--destination-cidr-block <wazuh-subnet-cidr> \
--target-vpc-subnet-id <subnet-id> \
--region <region>
Authorize access
aws ec2 authorize-client-vpn-ingress \
--client-vpn-endpoint-id <cvpn-endpoint-id> \
--target-network-cidr <wazuh-subnet-cidr> \
--authorize-all-groups \
--region <region>
Launch Wazuh All-in-One
Deploy the official Wazuh All-in-One AMI from AWS Marketplace. Place it in your private subnet with the Wazuh SG attached. No public IP is needed
Configure the VPN Client
Download the Client VPN config file and add your client certificate, key, and CA:
client
dev tun
proto udp
remote <vpn-endpoint>.prod.clientvpn.<region>.amazonaws.com 443
nobind
<cert>
(client.crt)
</cert>
<key>
(client.key)
</key>
<ca>
(root-ca.pem)
</ca>
Import this into the AWS VPN client or OpenVPN client.
For this Download AWS VPN Client here, after installing go to File > Manage Profiles > Fill the name and import he ovpn.
Test Access
Connect to the VPN.
Open Wazuh UI at https://
Confirm agents can communicate on ports 1514 and 1515.
At this point, your Wazuh All-in-One deployment is securely reachable only via VPN. You’ve kept your server private, used certificate-based authentication, and segmented your AWS environment properly.
Installing Wazuh in our EKS Node
For this we are going to create a new launch template that we can use in our managed EC2 nodes.
Create a bash script like this and execute it after filling the fields.
LT_NAME="wazuh-agents-lt"
AMI_ID="ami-030da889991638ab6" # (AL EKS 2023 or your preferred EKS AMI)
INSTANCE_PROFILE_ARN="<arn:aws:iam::123456789012:instance-profile/MyRole>" # Useful if you want to session manager into the nodes.
SECURITY_GROUP_ID="<sg-xxx>"
SUBNET_ID="<subnet-xxx>"
BOUNDARY="==MYBOUNDARY=="
read -r -d '' MIME <<'EOF'
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==MYBOUNDARY=="
--==MYBOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
set -euxo pipefail
HOSTNAME=$(curl -s http://169.254.169.254/latest/meta-data/local-hostname || hostname)
curl -o /root/wazuh-agent-4.12.0-1.x86_64.rpm https://packages.wazuh.com/4.x/yum/wazuh-agent-4.12.0-1.x86_64.rpm
WAZUH_MANAGER=10.50.0.41 WAZUH_AGENT_NAME="$HOSTNAME" rpm -ihv /root/wazuh-agent-4.12.0-1.x86_64.rpm
systemctl daemon-reload
systemctl enable wazuh-agent
systemctl start wazuh-agent
--==MYBOUNDARY==--
EOF
UD_B64=$(printf "%s" "$MIME" | base64 | tr -d '\n')
aws ec2 create-launch-template \
--launch-template-name "$LT_NAME" \
--version-description "v1-wazuh-agent" \
--launch-template-data "{
\"ImageId\": \"${AMI_ID}\",
\"InstanceType\": \"<your-node-group-instance>\",
\"IamInstanceProfile\": { \"Arn\": \"${INSTANCE_PROFILE_ARN}\" },
\"NetworkInterfaces\": [
{
\"DeviceIndex\": 0,
\"SubnetId\": \"${SUBNET_ID}\",
\"Groups\": [ \"${SECURITY_GROUP_ID}\" ],
\"AssociatePublicIpAddress\": false
}
],
\"MetadataOptions\": { \"HttpTokens\": \"required\", \"HttpEndpoint\": \"enabled\" },
\"TagSpecifications\": [
{
\"ResourceType\": \"instance\",
\"Tags\": [
{ \"Key\": \"Name\", \"Value\": \"wazuh-agent\" },
{ \"Key\": \"Role\", \"Value\": \"wazuh\" }
]
}
],
\"UserData\": \"${UD_B64}\"
}"
After creating the launch template, create the nodegroup, by going to AWS Console > EKS > Compute > Node Groups > Add > Launch Template
Now inside our Wazuh server dashboard we can see our nodes being registered.
Lessons learned and a conclusion
While setting up a Client VPN with private certificates ensures a secure and private connection, it can become costly in the long run. A practical alternative is to leverage AWS Systems Manager Session Manager with port forwarding to reach the Wazuh dashboard. This approach shifts access control to the AWS service layer instead of the networking layer, reducing complexity and cost while still maintaining strong security
Top comments (0)