The sidecar on the host
The sidecar is small and its footprint on a host is fixed. This page is the reference for the platforms it runs on, the network it needs, the Wazuh login it reads with, and the one place Wazuh 4.x and 5.x differ. A manager is added from Wazuh Fleet, which installs the sidecar as a plugin and manages its files. See Install the sidecar from Wazuh Fleet.
Supported platforms
The Wazuh Fleet plugin runs on Linux amd64 and arm64 only. A native sidecar, installed on a host before Wazuh Fleet became the only way to add a manager, runs on Linux and macOS, on amd64 and arm64.
What a native sidecar keeps on the host
A native sidecar keeps reporting and keeps updating itself. Every Pharos file
lives under /etc/pharos/, and the binary in the standard place for a local
program.
| Path | What it is |
|---|---|
/usr/local/bin/pharos-sidecar | The static binary |
/etc/pharos/sidecar.env | The configuration, including the Wazuh credentials it reads with. Readable by root only |
/etc/pharos/*.pem | The certificate authorities the sidecar verifies against, when one was discovered or passed |
/etc/systemd/system/pharos-sidecar.service | The service unit that runs the sidecar |
/etc/systemd/system/pharos-sidecar-upgrade.service and .timer | The daily auto-upgrade timer, when it was left on. See Automatic updates |
/var/lib/private/pharos-sidecar | The sidecar's own state, its credential and its read positions. The service runs under a dynamic system user, so this is not readable by ordinary accounts |
No credential is written anywhere but the root-only /etc/pharos/sidecar.env,
and none of it is sent to Pharos. Removing the manager from Pharos revokes its
credential. The files above stay on the host until they are deleted there.
A native manager that loses its credential, because the host was rebuilt or its state was lost, is not re-keyed. It is removed from Pharos and added again from Wazuh Fleet.
Network
The sidecar is outbound only. No port is opened on the host, and no Pharos address ever needs to reach it.
| Connection | Where | Port |
|---|---|---|
| Pharos API | api.pharos.wazuh.com | 443 |
| Pharos download site | dl.pharos.wazuh.com, for the Fleet plugin and a native sidecar's daily upgrade | 443 |
| The Wazuh indexer | the fleet's own indexer | its API port, 9200 by default |
| The Wazuh manager server API | the fleet's own manager, on Wazuh 4.x only | its API port, 55000 by default |
A proxy set through the standard HTTP_PROXY and HTTPS_PROXY environment
variables is used for the connections to Pharos. It is not applied to the
connection to the Wazuh indexer, which is treated as a host on the same network
and reached directly.
Wazuh 4.x and 5.x
The sidecar reads the same things from both, and one difference is worth knowing. On Wazuh 5.x the agent count and the agent list come from the indexer. On Wazuh 4.x there is no such index, so the agent census comes from the manager's server API and needs a read-only credential for it. Without one, a 4.x manager reports its agent count as unknown rather than as zero, and everything else it sends is unaffected. The credential and how to create it are in Counting agents on Wazuh 4.x.
A read-only indexer account
The indexer half of the login typed at Add a manager, or later with
Change Wazuh credentials, can be any account that reads the indexer. This
section is for an environment that would rather the sidecar not hold a
write-capable login such as the 5.x admin:admin default. Create a dedicated
read-only account, map it to a least-privilege role, and type it as the
indexer login. Pharos never creates this account. Creating one would be a write
to the customer's cluster.
The account can carry any name, and this page uses pharos-readonly.
Wazuh 5.x
The shipped wazuh_readonly role already grants everything the sidecar reads,
so on 5.x create the account, give it that role, and use it. No custom role is
needed. The role recipe below is the 4.x case, where no stock role is enough.
Wazuh 4.x
There is no shipped read-only account on 4.x, so an admin creates one, and
no stock backend role is sufficient on its own. The sidecar counts the
state collections with _cat/indices, which needs both
cluster:monitor/state and indices:monitor/settings/get. No stock role
grants both to a read-only user: readall is refused, and so is
readall_and_monitor.
The three commands below run against the customer's own indexer, and the admin credential in them is never given to Pharos.
Create the account, create the role, and map one to the other on the indexer,
from any shell that reaches it. Fill in the admin credential
<admin>:<password> and the indexer address <indexer> in each. Creating
the role without mapping an account to it leaves the account with no
permissions at all.
The -k in these commands skips TLS verification, the way Wazuh's own guides
run one-off setup calls against the cluster's self-signed certificate. To run
them verified instead, replace it with
--cacert /etc/wazuh-indexer/certs/root-ca.pem on a host that has the cluster
CA.
1. Create the account:
curl -sk -u "<admin>:<password>" -H "Content-Type: application/json" \
-X PUT "https://<indexer>:9200/_plugins/_security/api/internalusers/pharos-readonly" \
-d '{
"password": "...",
"description": "read-only account for the Pharos sidecar"
}'
2. Create the role:
curl -sk -u "<admin>:<password>" -H "Content-Type: application/json" \
-X PUT "https://<indexer>:9200/_plugins/_security/api/roles/pharos_sidecar" \
-d '{
"cluster_permissions": ["cluster_composite_ops_ro", "cluster_monitor"],
"index_permissions": [{
"index_patterns": ["wazuh-*", ".wazuh-cti-consumers*"],
"allowed_actions": ["read", "indices_monitor",
"indices:admin/mappings/get",
"indices:admin/resolve/index"]
}]
}'
3. Map the account to the role:
curl -sk -u "<admin>:<password>" -H "Content-Type: application/json" \
-X PUT "https://<indexer>:9200/_plugins/_security/api/rolesmapping/pharos_sidecar" \
-d '{
"users": ["pharos-readonly"]
}'
This role is verified read-only. It grants counting, not writing: with
this credential, creating an index is still refused with
indices:admin/create. The patterns are scoped rather than set to * on
purpose, and they are two rather than one because the CTI consumer document
lives at .wazuh-cti-consumers, outside the wazuh-* prefix. A role granting
only wazuh-* refuses that read with a security_exception, and the
deployment's CTI status then reads as unknown for good rather than as absent.
An account that also carries a broad backend role hides the omission: readall
cannot take the state census, but it does grant the CTI read, so an account
holding both it and a wazuh-*-only role looks correct while a clean account
mapped to that role alone does not. Check on an account carrying nothing but
this role.
Without the role, the sidecar still enrols and still reports, but three panels on the manager's page stay empty for good: the collection census, coverage, and inventory sharing.
Counting agents on Wazuh 4.x
On Wazuh 5.x the agent count comes from the indexer and the indexer login is all it takes. On 4.x there is no such index, so the count comes from the manager's own server API and needs a credential for it, typed as the Wazuh API half of the login. Without one the manager's page reports the agent count as unknown, rather than as zero, and says so.
Creating the account
Neither Wazuh generation ships a read-only server API account, so this is an account an admin creates on the manager. Pharos cannot mint it: creating an account is a write to the customer's cluster, and the sidecar is read-only by construction.
The account is created against the server API as an administrator. Any name
works, and this page uses pharos-readonly. These calls run against the
customer's own manager, and the admin credential in them never reaches Pharos.
The -k here matches the server API's self-signed certificate. On the manager
host, root can run these verified instead with
--cacert /var/ossec/api/configuration/ssl/server.crt.
Authenticate as an administrator first:
TOKEN=$(curl -sk -u "<admin>:<password>" -X POST \
"https://localhost:55000/security/user/authenticate?raw=true")
1. Create the account:
curl -sk -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-X POST "https://localhost:55000/security/users" \
-d '{"username": "pharos-readonly", "password": "..."}'
The API refuses weak passwords: it wants an uppercase letter, a lowercase letter, a digit and a symbol, and says so when it does not get them.
2. Grant it the stock agents_readonly role. Without the grant the
account authenticates and can read nothing. The <id> is the user id from
the creation call's response, or from GET /security/users:
curl -sk -H "Authorization: Bearer $TOKEN" \
-X POST "https://localhost:55000/security/users/<id>/roles?role_ids=4"
agents_readonly is the minimum: the sidecar calls exactly three endpoints,
POST /security/user/authenticate, GET /agents/summary/status for the
count and GET /agents for the per-agent roster, and the stock
agents_readonly role covers all three. The stock readonly role also
works but grants more. Its id is 4 on a stock install. Confirm it with
GET /security/roles rather than trusting the number, since a manager with
custom roles can number them differently.
Verify it by authenticating as the new account and calling the one
endpoint the census uses. The second command returns the agent census. The
third is a write that must fail, printing 403, which is the read-only claim
checked rather than assumed. The write carries a body because the API
validates the request before checking permissions: without one it prints
400 for any caller, an administrator included, and proves nothing. With
it, the refusal comes before the placeholder password is judged, so the
account never has to exist and never gets created:
TOKEN=$(curl -sk -u "pharos-readonly:<password>" -X POST \
"https://localhost:55000/security/user/authenticate?raw=true")
curl -sk -H "Authorization: Bearer $TOKEN" \
"https://localhost:55000/agents/summary/status"
curl -sk -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-X POST "https://localhost:55000/security/users" \
-d '{"username": "read-only-check", "password": "..."}'
Type the account as the Wazuh API half of the login at Add a manager, or with Change Wazuh credentials on a manager's Status tab.