Wazuh Integration Guide for HashPass
Overview
Wazuh is an open-source unified XDR (Extended Detection and Response) and SIEM (Security Information and Event Management) platform that provides:
- Security Monitoring: Real-time threat detection and incident response
- Log Management: Centralized log collection and analysis
- Compliance: PCI DSS, HIPAA, GDPR compliance monitoring
- Vulnerability Detection: System and application vulnerability scanning
- Cloud Security: AWS, Azure, GCP workload protection
This guide outlines how to integrate Wazuh into the HashPass platform to enhance security monitoring, threat detection, and compliance.
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ HashPass Application │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Frontend │ │ API Layer │ │ Supabase │ │
│ │ (Expo/Web) │ │ (Expo Router)│ │ (Postgres) │ │
│ │ │ │ (Lambda/ │ │ │ │
│ │ │ │ Netlify) │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼─────────────────┘ │
│ │ │
└──────────────────────────────┼────────────────────────────────┘
│
▼
┌──────────────────────┐
│ Wazuh Agent │
│ (Log Forwarder) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Wazuh Manager │
│ (Central Server) │
│ - Log Analysis │
│ - Threat Detection │
│ - Alerting │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ Wazuh Dashboard │
│ (Kibana/OpenSearch) │
└──────────────────────┘
Integration Approaches
1. Application Log Integration (Recommended for Web Apps)
Forward application logs, API requests, and security events to Wazuh.
Use Cases:
- Monitor authentication attempts (success/failure)
- Track API endpoint access patterns
- Detect suspicious user behavior
- Monitor QR code scan events
- Track admin actions
2. Infrastructure Monitoring
Monitor AWS infrastructure (Amplify, Lambda, S3, Supabase).
Use Cases:
- CloudTrail log analysis
- Lambda function execution monitoring
- S3 bucket access monitoring
- Database connection monitoring
3. Endpoint Protection (For Servers)
If you have dedicated servers or VMs, install Wazuh agents.
Use Cases:
- File integrity monitoring
- System log analysis
- Rootkit detection
- Configuration assessment
Implementation Steps
Step 1: Set Up Wazuh Infrastructure
Option A: Cloud Deployment (Recommended)
-
Deploy Wazuh Manager on AWS EC2:
# Use Wazuh CloudFormation template or deploy manually# Minimum requirements: 4GB RAM, 2 CPU cores, 20GB storage -
Or use Wazuh Cloud (SaaS):
- Sign up at https://wazuh.com/cloud
- Get your manager URL and API credentials
Option B: Self-Hosted
-
Install Wazuh Manager:
# Ubuntu/Debiancurl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --dearmor | sudo tee /usr/share/keyrings/wazuh.gpg > /dev/nullecho "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | sudo tee /etc/apt/sources.list.d/wazuh.listsudo apt-get updatesudo apt-get install wazuh-managersudo systemctl daemon-reloadsudo systemctl enable wazuh-managersudo systemctl start wazuh-manager -
Install Wazuh Dashboard (Kibana/OpenSearch):
sudo apt-get install wazuh-dashboardsudo systemctl enable wazuh-dashboardsudo systemctl start wazuh-dashboard
Step 2: Create Wazuh API Integration
Create a library to send security events to Wazuh from your application.
File: lib/wazuh-client.ts
/**
* Wazuh API Client for HashPass
* Sends security events and logs to Wazuh Manager
*/
interface WazuhEvent {
timestamp: string;
level: 'info' | 'warning' | 'error' | 'critical';
rule_id?: number;
location: string;
user?: string;
action: string;
details: Record<string, any>;
ip_address?: string;
user_agent?: string;
}
interface WazuhConfig {
managerUrl: string;
apiKey: string;
agentId?: string;
}
class WazuhClient {
private config: WazuhConfig;
private enabled: boolean;
constructor(config?: Partial<WazuhConfig>) {
this.config = {
managerUrl: process.env.WAZUH_MANAGER_URL || '',
apiKey: process.env.WAZUH_API_KEY || '',
agentId: process.env.WAZUH_AGENT_ID || 'hashpass-web',
...config,
};
this.enabled = !!this.config.managerUrl && !!this.config.apiKey;
}
/**
* Send a security event to Wazuh
*/
async sendEvent(event: WazuhEvent): Promise<void> {
if (!this.enabled) {
console.debug('[Wazuh] Client disabled, skipping event');
return;
}
try {
const wazuhEvent = {
timestamp: event.timestamp || new Date().toISOString(),
level: event.level,
rule: {
id: event.rule_id || this.getDefaultRuleId(event.level),
description: event.action,
},
location: event.location,
user: event.user,
data: {
action: event.action,
...event.details,
},
srcip: event.ip_address,
user_agent: event.user_agent,
};
// Send via Wazuh API (vulnerability endpoint or custom integration)
const response = await fetch(`${this.config.managerUrl}/vulnerability`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent: {
id: this.config.agentId,
name: 'hashpass-web',
},
vulnerability: {
name: event.action,
severity: this.mapLevelToSeverity(event.level),
description: JSON.stringify(event.details),
cve: event.rule_id?.toString(),
},
}),
});
if (!response.ok) {
console.error('[Wazuh] Failed to send event:', await response.text());
}
} catch (error) {
console.error('[Wazuh] Error sending event:', error);
// Don't throw - logging failures shouldn't break the app
}
}
/**
* Log authentication events
*/
async logAuthEvent(
action: 'login' | 'logout' | 'login_failed' | 'token_refresh',
userId?: string,
ipAddress?: string,
userAgent?: string,
details?: Record<string, any>
): Promise<void> {
const level = action.includes('failed') ? 'warning' : 'info';
await this.sendEvent({
level,
location: '/auth',
user: userId,
action: `auth_${action}`,
details: {
auth_action: action,
...details,
},
ip_address: ipAddress,
user_agent: userAgent,
});
}
/**
* Log API access events
*/
async logApiAccess(
endpoint: string,
method: string,
userId?: string,
statusCode: number = 200,
ipAddress?: string,
userAgent?: string
): Promise<void> {
const level = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warning' : 'info';
await this.sendEvent({
level,
location: endpoint,
user: userId,
action: `api_${method.toLowerCase()}`,
details: {
endpoint,
method,
status_code: statusCode,
},
ip_address: ipAddress,
user_agent: userAgent,
});
}
/**
* Log security events (suspicious activity, violations, etc.)
*/
async logSecurityEvent(
action: string,
severity: 'warning' | 'error' | 'critical',
userId?: string,
details?: Record<string, any>,
ipAddress?: string
): Promise<void> {
await this.sendEvent({
level: severity,
location: details?.endpoint || 'unknown',
user: userId,
action: `security_${action}`,
details,
ip_address: ipAddress,
rule_id: this.getSecurityRuleId(action),
});
}
/**
* Log QR scan events
*/
async logQRScan(
qrCodeId: string,
status: 'valid' | 'invalid' | 'expired' | 'revoked',
scannedBy?: string,
ipAddress?: string
): Promise<void> {
const level = status === 'valid' ? 'info' : 'warning';
await this.sendEvent({
level,
location: '/api/qr/scan',
user: scannedBy,
action: 'qr_scan',
details: {
qr_code_id: qrCodeId,
scan_status: status,
},
ip_address: ipAddress,
});
}
/**
* Log admin actions
*/
async logAdminAction(
action: string,
adminId: string,
targetId?: string,
details?: Record<string, any>
): Promise<void> {
await this.sendEvent({
level: 'info',
location: '/admin',
user: adminId,
action: `admin_${action}`,
details: {
target_id: targetId,
...details,
},
});
}
private getDefaultRuleId(level: string): number {
const ruleMap: Record<string, number> = {
info: 1000,
warning: 2000,
error: 3000,
critical: 4000,
};
return ruleMap[level] || 1000;
}
private getSecurityRuleId(action: string): number {
const ruleMap: Record<string, number> = {
unauthorized_access: 5503,
brute_force: 5504,
sql_injection: 5505,
xss_attempt: 5506,
rate_limit_exceeded: 5507,
suspicious_activity: 5508,
};
return ruleMap[action] || 5508;
}
private mapLevelToSeverity(level: string): string {
const severityMap: Record<string, string> = {
info: 'Low',
warning: 'Medium',
error: 'High',
critical: 'Critical',
};
return severityMap[level] || 'Low';
}
}
// Singleton instance
let wazuhClientInstance: WazuhClient | null = null;
export function getWazuhClient(): WazuhClient {
if (!wazuhClientInstance) {
wazuhClientInstance = new WazuhClient();
}
return wazuhClientInstance;
}
export { WazuhClient, type WazuhEvent };
Step 3: Integrate with API Routes
File: lib/api-middleware.ts (Already created above)
import { getWazuhClient } from './wazuh-client';
/**
* Middleware to log API requests to Wazuh
*/
export async function logApiRequest(
request: Request,
userId?: string,
statusCode: number = 200
): Promise<void> {
const wazuh = getWazuhClient();
const url = new URL(request.url);
const ipAddress = request.headers.get('x-forwarded-for') ||
request.headers.get('x-real-ip') ||
'unknown';
const userAgent = request.headers.get('user-agent') || 'unknown';
await wazuh.logApiAccess(
url.pathname,
request.method,
userId,
statusCode,
ipAddress,
userAgent
);
}
/**
* Log authentication events
*/
export async function logAuthEvent(
action: 'login' | 'logout' | 'login_failed' | 'token_refresh',
userId?: string,
request?: Request,
error?: string
): Promise<void> {
const wazuh = getWazuhClient();
const ipAddress = request?.headers.get('x-forwarded-for') || 'unknown';
const userAgent = request?.headers.get('user-agent') || 'unknown';
await wazuh.logAuthEvent(action, userId, ipAddress, userAgent, {
error,
});
}
Step 4: Add to Authentication Flow
Update: app/(shared)/auth.tsx
Add Wazuh logging to authentication events. Since this is client-side code, you'll need to call an API endpoint to log events:
// In your auth component, after successful login
await fetch('/api/wazuh/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'auth_login',
userId: user.id,
level: 'info',
}),
});
// After failed login
await fetch('/api/wazuh/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'auth_login_failed',
level: 'warning',
error: error.message,
}),
});
Or create a helper function in lib/wazuh-client.ts for client-side:
// Client-side logging helper (calls API endpoint)
export async function logEventClient(event: {
action: string;
level: 'info' | 'warning' | 'error' | 'critical';
userId?: string;
details?: Record<string, any>;
}): Promise<void> {
try {
await fetch('/api/wazuh/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event),
});
} catch (error) {
console.error('[Wazuh] Failed to log event:', error);
}
}
Step 5: Add to API Routes (Expo Router)
Example: Update app/api/qr/admin/logs+api.ts
Expo Router API routes are serverless functions that export HTTP method handlers:
import { logApiRequest, logAdminAction } from '@/lib/api-middleware';
import { getWazuhClient } from '@/lib/wazuh-client';
export async function GET(request: Request) {
const userId = await getAuthUserId(request);
// Log API access
await logApiRequest(request, userId || undefined, 200);
// ... existing code ...
// Log admin action
if (isUserAdmin) {
const wazuh = getWazuhClient();
await wazuh.logAdminAction('view_qr_logs', userId, undefined, {
qr_code_id: qrCodeId,
page,
});
}
// ... rest of handler ...
}
Create a Wazuh logging API endpoint: app/api/wazuh/log+api.ts
import { getWazuhClient } from '@/lib/wazuh-client';
export async function POST(request: Request) {
try {
const body = await request.json();
const { action, level, userId, details } = body;
const wazuh = getWazuhClient();
const ipAddress = request.headers.get('x-forwarded-for') ||
request.headers.get('x-real-ip') ||
undefined;
const userAgent = request.headers.get('user-agent') || undefined;
await wazuh.sendEvent({
level: level || 'info',
location: '/api/wazuh/log',
user: userId,
action,
details: details || {},
ip_address: ipAddress,
user_agent: userAgent,
});
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[Wazuh] Log endpoint error:', error);
return new Response(JSON.stringify({ error: 'Failed to log event' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
Step 6: Add to QR Scan Events
Update QR scan handler:
import { getWazuhClient } from '@/lib/wazuh-client';
// After QR scan
const wazuh = getWazuhClient();
await wazuh.logQRScan(
qrCodeId,
scanStatus,
scannedByUserId,
request.headers.get('x-forwarded-for') || undefined
);
Step 7: Environment Variables
Add to your .env file:
# Wazuh Configuration
WAZUH_MANAGER_URL=https://your-wazuh-manager.com
WAZUH_API_KEY=YOUR_WAZUH_API_KEY
WAZUH_AGENT_ID=hashpass-web
WAZUH_ENABLED=true
Step 8: Wazuh Rules Configuration
Create custom Wazuh rules for HashPass-specific events.
File: wazuh/rules/hashpass_rules.xml
<group name="hashpass,">
<!-- Authentication Events -->
<rule id="100100" level="5">
<decoded_as>json</decoded_as>
<field name="action">auth_login</field>
<description>HashPass: User login successful</description>
</rule>
<rule id="100101" level="10">
<decoded_as>json</decoded_as>
<field name="action">auth_login_failed</field>
<description>HashPass: Failed login attempt</description>
</rule>
<rule id="100102" level="12">
<decoded_as>json</decoded_as>
<field name="action">auth_login_failed</field>
<if_matched_sid>100101</if_matched_sid>
<same_source_ip />
<frequency>5</frequency>
<timeframe>300</timeframe>
<description>HashPass: Multiple failed login attempts (possible brute force)</description>
</rule>
<!-- QR Code Events -->
<rule id="100200" level="5">
<decoded_as>json</decoded_as>
<field name="action">qr_scan</field>
<field name="scan_status">valid</field>
<description>HashPass: Valid QR code scan</description>
</rule>
<rule id="100201" level="8">
<decoded_as>json</decoded_as>
<field name="action">qr_scan</field>
<field name="scan_status">invalid</field>
<description>HashPass: Invalid QR code scan attempt</description>
</rule>
<!-- Admin Actions -->
<rule id="100300" level="5">
<decoded_as>json</decoded_as>
<field name="action">admin_</field>
<description>HashPass: Admin action performed</description>
</rule>
<!-- Security Events -->
<rule id="100400" level="12">
<decoded_as>json</decoded_as>
<field name="action">security_unauthorized_access</field>
<description>HashPass: Unauthorized access attempt</description>
</rule>
<rule id="100401" level="12">
<decoded_as>json</decoded_as>
<field name="action">security_rate_limit_exceeded</field>
<description>HashPass: Rate limit exceeded</description>
</rule>
</group>
Step 9: Supabase Integration (Optional)
Forward Supabase audit logs to Wazuh using a database trigger or Edge Function.
Supabase Edge Function: supabase/functions/wazuh-forwarder/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
serve(async (req) => {
const { table, event, record } = await req.json();
// Forward to Wazuh
await fetch(Deno.env.get('WAZUH_MANAGER_URL') + '/vulnerability', {
method: 'POST',
headers: {
'Authorization': `Bearer ${Deno.env.get('WAZUH_API_KEY')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
agent: { id: 'hashpass-supabase', name: 'hashpass-supabase' },
vulnerability: {
name: `supabase_${table}_${event}`,
severity: 'Low',
description: JSON.stringify(record),
},
}),
});
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
});
Step 10: AWS CloudTrail Integration
Forward AWS CloudTrail logs to Wazuh for infrastructure monitoring.
Lambda Function: aws/lambda/wazuh-cloudtrail-forwarder/index.js
const https = require('https');
const url = require('url');
exports.handler = async (event) => {
const wazuhUrl = process.env.WAZUH_MANAGER_URL;
const wazuhKey = process.env.WAZUH_API_KEY;
for (const record of event.Records) {
const cloudTrailEvent = JSON.parse(record.body);
// Forward to Wazuh
await sendToWazuh(cloudTrailEvent, wazuhUrl, wazuhKey);
}
return { statusCode: 200 };
};
async function sendToWazuh(event, wazuhUrl, wazuhKey) {
// Implementation to send CloudTrail events to Wazuh
// Use Wazuh AWS integration or custom API
}
Monitoring and Alerting
Key Metrics to Monitor
-
Authentication Events
- Failed login attempts
- Brute force detection
- Unusual login patterns
-
API Security
- Rate limit violations
- Unauthorized access attempts
- SQL injection attempts
- XSS attempts
-
QR Code Security
- Invalid scan attempts
- Expired/revoked code usage
- Unusual scan patterns
-
Admin Actions
- Privilege escalations
- Data modifications
- Configuration changes
Wazuh Dashboard Views
Create custom dashboards in Wazuh to visualize:
- Real-time security events
- Authentication success/failure rates
- API endpoint access patterns
- Geographic distribution of events
- Top users by activity
- Security event trends
Best Practices
- Performance: Use async logging to avoid blocking requests
- Privacy: Don't log sensitive data (passwords, tokens, PII)
- Rate Limiting: Implement client-side rate limiting for Wazuh API calls
- Error Handling: Ensure Wazuh failures don't break the application
- Testing: Test in development before enabling in production
- Compliance: Ensure logging meets GDPR/privacy requirements
Troubleshooting
Common Issues
-
Events not appearing in Wazuh
- Check Wazuh API connectivity
- Verify API key is valid
- Check Wazuh agent status
- Review Wazuh manager logs
-
Performance impact
- Use async/await for all Wazuh calls
- Implement batching for high-volume events
- Consider using a message queue (Redis/RabbitMQ)
-
Missing events
- Verify environment variables are set
- Check that Wazuh client is enabled
- Review application logs for errors
Next Steps
- Deploy Wazuh Infrastructure: Set up Wazuh Manager and Dashboard
- Configure Environment: Add Wazuh environment variables
- Implement Logging: Add Wazuh logging to critical paths
- Create Rules: Define custom Wazuh rules for HashPass events
- Set Up Alerts: Configure email/Slack notifications for critical events
- Monitor: Review Wazuh dashboard regularly
- Iterate: Refine rules and alerts based on observed patterns
Resources
Support
For issues or questions:
- Wazuh Community: https://wazuh.com/community
- HashPass GitHub Issues: https://github.com/hashpass-tech/hashpass.tech/issues