# Installing Wazuh (v4.9.0) on Debian 13 Without the Installer Script - Part 1
Let's make one thing absolutely clear: the vendor-provided installer script is a fragile black box. Really it's not, it is just not built for Trixie. As soon as you point it at Debian 13 (Trixie), it chokes on the release identifier string and refuses to run. If you want a modern SIEM stack on a clean stable node, you are forced to deploy the architecture manually from scratch. This is an adventure made anywhere that is unpleasant and not nice.
But the raw installation isn't the real hurdle. The real trial is the OpenSearch Security Plugin. Under the hood, this security subsystem is designed with compiled Java overrides that quietly ignore standard administrative configuration updates. We spent hours chasing our own tails, running script-based updates that the system reported as successful, only to realize the configuration was hardcoded inside compiled class structures.
This is Part 1. In this entry, I'll walk through manual baseline sourcing, explain the security index mechanics that caused our configuration updates to fail, catalog the dead ends we ran into, and show the exact loopback configuration bypass we used to break the infinite initialization loop.
---
## I. System-Level Baseline & Sourcing
Because we bypassed the vendor's automation scripts, we mapped the stable production line directly to the operating system's package channels and adjusted the underlying Linux kernel limits to accommodate OpenSearch's virtual memory mapping.
### 1. Sourcing System Prerequisites
```bash
sudo apt-get update && sudo apt-get install -y curl apt-transport-https gnupg2 pinentry-curses
```
### 2. Manual Repository Pinning
```bash
curl -s [https://packages.wazuh.com/key/GPG-KEY-WAZUH](https://packages.wazuh.com/key/GPG-KEY-WAZUH) | gpg --dearmor -o /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] [https://packages.wazuh.com/4.x/apt/](https://packages.wazuh.com/4.x/apt/) stable main" | sudo tee /etc/apt/sources.list.d/wazuh.list
sudo apt-get update
```
### 3. Linux Kernel & PAM Parameter Adjustments
OpenSearch relies heavily on memory-mapped Lucene tables. By default, Debian 13 enforces tight memory map resource caps, which will trigger an immediate JVM crash on bootstrap unless they are manually expanded.
Add the following to your `sysctl` configuration:
```ini
vm.max_map_count=262144
```
Apply the changes:
```bash
sudo sysctl -p
```
Update your system limits (`/etc/security/limits.conf`):
```text
wazuh-indexer soft nofile 65536
wazuh-indexer hard nofile 65536
wazuh-indexer soft nproc 4096
wazuh-indexer hard nproc 4096
```
---
## II. The Security Index Lifecycle Mechanics (Why Disk Files Failed)
Once the core packages are deployed, administrators typically try to update passwords by editing YAML templates. This is a trap. The security plugin does not query configuration files on disk at runtime; it evaluates active configurations against a compiled, database-internal security index.
---
## III. The Circular Dead Ends (Hours Wasted Chasing Tails)
Our deployment was stuck in an infinite initialization loop, throwing `[ResponseError]: Response Error / Unable to retrieve version information from OpenSearch nodes` on the frontend while log trails spammed `Authentication finally failed for kibanaserver from 127.0.0.1` on the backend.
### Dead End 1: The Custom User Hash Mirage
```yaml
# wazuh-dashboard.yml
opensearch.username: "wazuhdashboard"
opensearch.password: "tinkerbellfellhardf0rR0m30!#"
```
**Why it Failed:** Although the main client thread used the custom credentials, the security plugin's internal **Multi-tenant Session Impersonation engine** registers independent, background processes.
### Dead End 2: Overriding the Reserved kibanaserver Account
```yaml
# internal_users.yml
kibanaserver:
hash: "$2y$12$EXAMPLE_NEW_BCRYPT_HASH..."
reserved: true
```
**Why it Failed:** The configuration tool lied to us with a successful terminal status. In reality, the security plugin's compiled Java class files contain a hardcoded list of reserved system accounts.
---
## IV. Master Configurations Reference
To resolve the loop, we structured a clean anonymous loopback bypass. This allows the dashboard's automated background tasks to authenticate successfully without transmitting conflicting basic credential headers.
### 1. Main Security Pipeline Config
**File Path:** `/etc/wazuh-indexer/opensearch-security/config.yml`
```yaml
_meta:
type: "config"
config_version: 2
config:
dynamic:
http:
anonymous_auth_enabled: true
xff:
enabled: false
authc:
clientcert_auth_domain:
description: "Authenticate via SSL client certificates"
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: clientcert
config:
username_attribute: null
challenge: false
authentication_backend:
type: noop
basic_internal_auth_domain:
description: "Authenticate via HTTP Basic"
http_enabled: true
transport_enabled: true
order: 2
http_authenticator:
type: basic
challenge: true
authentication_backend:
type: intern
authz:
roles_from_internal:
description: "Authorize via internal roles mapping"
http_enabled: true
transport_enabled: true
authorization_backend:
type: intern
```
### 2. Live Cluster Authorization Maps
**File Path:** `/etc/wazuh-indexer/opensearch-security/roles_mapping.yml` *(Implied)*
```yaml
_meta:
type: "rolesmapping"
config_version: 2
all_access:
reserved: true
backend_roles:
- "admin"
- "anonymous_backendrole"
users:
- "admin"
kibana_server:
reserved: true
backend_roles:
- "kibana_server"
- "anonymous_backendrole"
```
### 3. Frontend Dashboard Configuration
**File Path:** `/etc/wazuh-dashboard/opensearch_dashboards.yml` *(Implied)*
```yaml
server.host: "0.0.0.0"
server.port: 443
opensearch.hosts: ["[https://127.0.0.1:9200](https://127.0.0.1:9200)"]
opensearch.ssl.verificationMode: "none"
server.ssl.enabled: true
server.ssl.certificate: "/etc/wazuh-dashboard/certs/dashboard.pem"
server.ssl.key: "/etc/wazuh-dashboard/certs/dashboard-key.pem"
opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"]
opensearch.requestHeadersAllowlist: ["securitytenant", "Authorization"]
opensearch_security.multitenancy.enabled: true
opensearch_security.auth.type: "basicauth"
opensearch_security.cookie.secure: true
```
---
## V. Validation & System Maintenance Playbook
Apply the security settings using the admin script:
```bash
sudo env "JAVA_HOME=/usr/share/wazuh-indexer/jdk" /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -cd /etc/wazuh-indexer/opensearch-security/ -cacert /etc/wazuh-indexer/certs/root-ca.pem -cert /etc/wazuh-indexer/certs/admin.pem -key /etc/wazuh-indexer/certs/admin-key.pem -nhnv -icu
```
Verify cluster health via loopback:
```bash
curl -k [https://127.0.0.1:9200/_cluster/health?pretty](https://127.0.0.1:9200/_cluster/health?pretty)
```
---
## VI. Part 1 Conclusion & Current Handshake Obstacles
By applying the loopback bypass, we successfully resolved the internal authentication loop. The logs no longer register the persistent `Authentication finally failed for kibanaserver` warnings on port 9200, and the dashboard's background checker can safely traverse the HTTP pipeline to verify cluster compatibility.
These final resolutions will be detailed in Part 2.