Initial commit - Runtipi Appstore with Wazuh 4.14.1
Some checks failed
Test / test (push) Has been cancelled

- Added Wazuh 4.14.1 SIEM/XDR application for Runtipi
- Simplified init scripts following official Wazuh Docker patterns
- Complete documentation in French (description.md)
- Health check diagnostic script (wazuh-health-check.sh)
- SSL/TLS certificates auto-generation
- Whoami test application included

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Gui-Gos
2026-01-02 12:26:29 +01:00
commit 46122d5a7f
30 changed files with 2584 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
#!/bin/sh
set -e
echo "CERTS_INIT: Starting certificate initialization..."
# Create all required directories
echo "CERTS_INIT: Creating directories..."
mkdir -p /indexer-data \
/manager-api \
/manager-logs \
/manager-queue \
/dashboard-config \
/indexer-security
# Super-Janitor Sweep: Remove any files/directories that were incorrectly created as directories
echo "CERTS_INIT: Starting Super-Janitor Sweep..."
for path in /certificates/*.pem \
/certificates/*.key \
/dashboard-config/opensearch_dashboards.yml \
/indexer-security/config.yml \
/indexer-security/nodes_dn.yml \
/indexer-security/tenants.yml \
/indexer-security/whitelist.yml \
/indexer-security/roles.yml \
/indexer-security/roles_mapping.yml \
/indexer-security/internal_users.yml \
/indexer-security/action_groups.yml; do
if [ -d "$path" ]; then
echo "CERTS_INIT: Purging fake directory: $path"
rm -rf "$path"
fi
done
# Generate certificates if they don't exist
if [ ! -f /certificates/root-ca.pem ]; then
echo "CERTS_INIT: Generating new certificates..."
/entrypoint.sh
else
echo "CERTS_INIT: Certificates already exist, skipping generation"
fi
# Create symlinks for easier reference
echo "CERTS_INIT: Creating certificate symlinks..."
ln -sf wazuh.indexer.pem /certificates/indexer.pem
ln -sf wazuh.indexer-key.pem /certificates/indexer-key.pem
ln -sf wazuh.manager.pem /certificates/server.pem
ln -sf wazuh.manager-key.pem /certificates/server-key.pem
ln -sf wazuh.dashboard.pem /certificates/dashboard.pem
ln -sf wazuh.dashboard-key.pem /certificates/dashboard-key.pem
# Set correct ownership
# - 1000:1000 for indexer and dashboard (opensearch/kibana user)
# - 999:999 for manager directories (wazuh user in manager container)
echo "CERTS_INIT: Setting ownership and permissions..."
chown -R 1000:1000 /certificates \
/indexer-data \
/dashboard-config \
/indexer-security
chown -R 999:999 /manager-api \
/manager-logs \
/manager-queue
# Set correct permissions
chmod 700 /certificates
chmod 644 /certificates/*.pem 2>/dev/null || true
chmod 600 /certificates/*.key 2>/dev/null || true
echo "CERTS_INIT: Certificates ready"
# Keep container alive (Runtipi requirement)
tail -f /dev/null

View File

@@ -0,0 +1,134 @@
#!/bin/bash
set -e
echo "DASHBOARD_INIT: Starting dashboard initialization..."
CUSTOM_CONFIG="/usr/share/wazuh-dashboard/config/custom/opensearch_dashboards.yml"
# Ensure custom directory exists
echo "DASHBOARD_INIT: Ensuring custom config directory exists..."
mkdir -p /usr/share/wazuh-dashboard/config/custom
# Check if custom config exists, if not create default
if [ ! -s "$CUSTOM_CONFIG" ]; then
echo "DASHBOARD_INIT: Creating default dashboard config..."
cat > "$CUSTOM_CONFIG" << EOF
server.host: 0.0.0.0
server.port: 5601
opensearch.hosts: https://wazuh.indexer:9200
opensearch.ssl.verificationMode: certificate
opensearch.username: ${DASHBOARD_USERNAME:-kibanaserver}
opensearch.password: ${DASHBOARD_PASSWORD:-kibanaserver}
opensearch.requestHeadersWhitelist: ["securitytenant","Authorization"]
opensearch_security.multitenancy.enabled: false
opensearch_security.readonly_mode.roles: ["kibana_read_only"]
server.ssl.enabled: true
server.ssl.certificate: /usr/share/wazuh-dashboard/config/certs/dashboard.pem
server.ssl.key: /usr/share/wazuh-dashboard/config/certs/dashboard-key.pem
opensearch.ssl.certificateAuthorities: ["/usr/share/wazuh-dashboard/config/certs/root-ca.pem"]
uiSettings.overrides.defaultRoute: /app/wazuh
EOF
echo "DASHBOARD_INIT: Default dashboard config created"
else
echo "DASHBOARD_INIT: Custom dashboard config already exists, skipping"
fi
# Create symlink if it doesn't exist
if [ ! -L /usr/share/wazuh-dashboard/config/opensearch_dashboards.yml ]; then
echo "DASHBOARD_INIT: Creating symlink to custom config..."
rm -f /usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
ln -s "$CUSTOM_CONFIG" /usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
else
echo "DASHBOARD_INIT: Symlink already exists"
fi
# Handle keystore initialization idempotently to avoid interactive prompts
KEYSTORE_PATH="/usr/share/wazuh-dashboard/config/opensearch_dashboards.keystore"
if [ ! -f "$KEYSTORE_PATH" ]; then
echo "DASHBOARD_INIT: Creating dashboard keystore..."
# The --allow-root flag is often needed if running as root
/usr/share/wazuh-dashboard/bin/opensearch-dashboards-keystore create 2>/dev/null || true
fi
# Start dashboard in background
echo "DASHBOARD_INIT: Configuration complete, starting dashboard..."
/entrypoint.sh &
DASHBOARD_PID=$!
# Give the process a moment to start and resolve its sub-processes
sleep 5
# Robust PID tracking: looking for the actual Node.js process if entrypoint isn't using exec
if ! kill -0 $DASHBOARD_PID 2>/dev/null; then
echo "DASHBOARD_INIT: Main PID $DASHBOARD_PID died, checking for sub-processes..."
# Look for opensearch-dashboards node process
ACTUAL_PID=$(pgrep -f "opensearch-dashboards" || echo "")
if [ -n "$ACTUAL_PID" ]; then
DASHBOARD_PID=$ACTUAL_PID
echo "DASHBOARD_INIT: Found actual dashboard process at PID $DASHBOARD_PID"
else
echo "DASHBOARD_INIT: ERROR - No dashboard process found!"
exit 1
fi
fi
# Periodic monitoring with migration lock detection
echo "MIGRATION_WATCHDOG: Monitoring dashboard startup (PID: $DASHBOARD_PID)..."
TIMEOUT=600 # Increase timeout to 10 minutes due to Indexer slowness
ELAPSED=0
MIGRATION_LOCK_DETECTED_AT=0
while [ $ELAPSED -lt $TIMEOUT ]; do
# Check if dashboard process is still alive
if ! kill -0 $DASHBOARD_PID 2>/dev/null; then
echo "MIGRATION_WATCHDOG: ERROR - Dashboard process (PID: $DASHBOARD_PID) died after ${ELAPSED}s!"
exit 1
fi
# Check if dashboard API is responding
API_STATUS=$(curl -sk -o /dev/null -w "%{http_code}" https://localhost:5601/api/status 2>/dev/null)
if [[ "$API_STATUS" == "200" || "$API_STATUS" == "302" || "$API_STATUS" == "401" ]]; then
echo "MIGRATION_WATCHDOG: Dashboard is responding (HTTP $API_STATUS) after ${ELAPSED}s"
# We don't break yet, we continue monitoring until it's "Stable" (HTTP 200)
if [ "$API_STATUS" == "200" ]; then
echo "MIGRATION_WATCHDOG: Dashboard is fully UP and Stable."
break
fi
fi
# Check for migration lock (stuck .kibana_1)
HTTP_CODE=$(curl -sk -u "${INDEXER_USERNAME:-admin}:${INDEXER_PASSWORD:-admin}" -o /dev/null -w "%{http_code}" "https://wazuh.indexer:9200/.kibana_1" 2>/dev/null)
if [ "$HTTP_CODE" = "200" ] && [ $MIGRATION_LOCK_DETECTED_AT -eq 0 ]; then
MIGRATION_LOCK_DETECTED_AT=$ELAPSED
echo "MIGRATION_WATCHDOG: Detected .kibana_1 index at ${ELAPSED}s, waiting for natural migration..."
fi
if [ "$HTTP_CODE" = "200" ] && [ $MIGRATION_LOCK_DETECTED_AT -gt 0 ]; then
STUCK_DURATION=$((ELAPSED - MIGRATION_LOCK_DETECTED_AT))
if [ $STUCK_DURATION -ge 240 ]; then # Increase to 4 minutes
echo "MIGRATION_WATCHDOG: Index stuck for ${STUCK_DURATION}s, cleaning up..."
curl -sk -u "${INDEXER_USERNAME:-admin}:${INDEXER_PASSWORD:-admin}" -X DELETE "https://wazuh.indexer:9200/.kibana_1" 2>/dev/null || true
kill $DASHBOARD_PID 2>/dev/null || true
exit 0
fi
fi
sleep 15
ELAPSED=$((ELAPSED + 15))
if [ $((ELAPSED % 60)) -eq 0 ]; then
echo "MIGRATION_WATCHDOG: Still waiting for dashboard (${ELAPSED}s, Status: $API_STATUS)..."
fi
done
echo "MIGRATION_WATCHDOG: Finalizing monitoring. Entering persistent loop."
# Keep watching the dashboard process
while kill -0 $DASHBOARD_PID 2>/dev/null; do
sleep 60
done
echo "DASHBOARD_INIT: Process died. Exiting."
exit 1

View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -e
echo "INDEXER_INIT: Starting security initialization..."
# Check if security files already exist
if [ ! -f /mnt/host-security/internal_users.yml ]; then
echo "INDEXER_INIT: Copying security configs..."
SRC_PATH="/usr/share/wazuh-indexer/config/opensearch-security"
for file in config.yml roles.yml roles_mapping.yml internal_users.yml action_groups.yml tenants.yml nodes_dn.yml whitelist.yml; do
if [ -f "$SRC_PATH/$file" ]; then
cp "$SRC_PATH/$file" /mnt/host-security/
echo "INDEXER_INIT: Copied $file"
else
echo "INDEXER_INIT: $file not found, skipping"
fi
done
echo "INDEXER_INIT: Security files ready"
else
echo "INDEXER_INIT: Security files already exist, skipping copy"
fi
# Set JAVA_HOME
export JAVA_HOME=/usr/share/wazuh-indexer/jdk
# Wait for indexer to be ready
echo "INDEXER_INIT: Waiting for indexer to be available..."
until curl -ks https://wazuh.indexer:9200 -u "${INDEXER_USERNAME:-admin}:${INDEXER_PASSWORD:-admin}"; do
echo "INDEXER_INIT: Indexer not ready, retrying in 5 seconds..."
sleep 5
done
echo "INDEXER_INIT: Indexer is ready, initializing security..."
# Initialize security
/usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh \
-cd /mnt/host-security/ \
-cacert /usr/share/wazuh-indexer/config/certs/root-ca.pem \
-cert /usr/share/wazuh-indexer/config/certs/admin.pem \
-key /usr/share/wazuh-indexer/config/certs/admin-key.pem \
-h wazuh.indexer \
-p 9200 \
-nhnv
echo "INDEXER_INIT: Security initialization completed successfully"
# Create completion marker file
touch /mnt/host-security/.init-complete
# Keep container alive (Runtipi requirement)
# Using tail -f /dev/null keeps the container in a healthy "running" state
echo "INDEXER_INIT: Initialization complete, container will remain alive"
tail -f /dev/null

View File

@@ -0,0 +1,100 @@
#!/bin/bash
set -e
echo "MANAGER_INIT: Starting manager initialization..."
# ============================================================================
# OSSEC.CONF CONFIGURATION
# ============================================================================
# The official Wazuh /init script creates ossec.conf during initialization.
# We use a watchdog to copy it to custom storage for persistence after /init.
OSSEC_CUSTOM="/var/ossec/etc/custom/ossec.conf"
OSSEC_DEFAULT="/var/ossec/etc/ossec.conf"
# Create custom directory if it doesn't exist
mkdir -p /var/ossec/etc/custom
# NOTE: Filebeat SSL configuration is now handled via environment variables:
# - FILEBEAT_SSL_VERIFICATION_MODE=full
# - SSL_CERTIFICATE_AUTHORITIES=/var/ossec/etc/certs/root-ca.pem
# - SSL_CERTIFICATE=/var/ossec/etc/certs/server.pem
# - SSL_KEY=/var/ossec/etc/certs/server-key.pem
# The official cont-init.d/1-config-filebeat script will generate the correct
# configuration automatically. No manual filebeat.yml management needed!
# ============================================================================
# POST-INIT WATCHDOG
# ============================================================================
# The Wazuh /init script creates ossec.conf during initialization.
# This watchdog waits for init completion, then makes ossec.conf persistent.
(
echo "WATCHDOG: Waiting for Wazuh services to be fully started..."
# Wait for wazuh-db to be running (not just starting)
# wazuh-db is one of the last services to start and needs a valid ossec.conf
TIMEOUT=180
ELAPSED=0
while [ $ELAPSED -lt $TIMEOUT ]; do
# Check if wazuh-db process is running
if pgrep -x "wazuh-db" > /dev/null 2>&1; then
echo "WATCHDOG: wazuh-db is running, waiting additional 5s for stability..."
sleep 5
break
fi
sleep 2
ELAPSED=$((ELAPSED + 2))
if [ $((ELAPSED % 20)) -eq 0 ]; then
echo "WATCHDOG: Still waiting for wazuh-db to start (${ELAPSED}s elapsed)..."
fi
done
if [ $ELAPSED -ge $TIMEOUT ]; then
echo "WATCHDOG: WARNING - Timeout waiting for wazuh-db startup!"
echo "WATCHDOG: Will proceed anyway, but persistence may fail"
fi
# Now make ossec.conf persistent
if [ -f "$OSSEC_DEFAULT" ] && [ ! -L "$OSSEC_DEFAULT" ]; then
echo "WATCHDOG: Making ossec.conf persistent..."
# If custom file doesn't exist or is empty, copy current to custom
if [ ! -s "$OSSEC_CUSTOM" ]; then
echo "WATCHDOG: Backing up current ossec.conf to custom storage..."
cp "$OSSEC_DEFAULT" "$OSSEC_CUSTOM"
fi
# Create symlink for persistence
echo "WATCHDOG: Creating symlink /var/ossec/etc/ossec.conf -> custom/ossec.conf"
rm -f "$OSSEC_DEFAULT"
ln -s "$OSSEC_CUSTOM" "$OSSEC_DEFAULT"
# Verify symlink was created
if [ -L "$OSSEC_DEFAULT" ]; then
echo "WATCHDOG: ✓ ossec.conf is now persistent (symlink verified)"
else
echo "WATCHDOG: ✗ ERROR - Failed to create symlink!"
fi
else
echo "WATCHDOG: ossec.conf already persistent (symlink exists)"
fi
echo "WATCHDOG: Initialization complete, entering monitoring mode"
# Keep watchdog alive
while true; do
sleep 3600
done
) &
# ============================================================================
# START WAZUH
# ============================================================================
echo "MANAGER_INIT: Configuration complete, starting Wazuh..."
# Execute the original Wazuh entrypoint
# The cont-init.d/1-config-filebeat script will automatically configure Filebeat
# using the SSL environment variables we defined in docker-compose.json
exec /init