sozu-command-lib 2.3.3

configuration library to command a sozu instance
Documentation
# sozu-command-lib, tools to communicate with the Sōzu proxy

The sozu proxy can receive dynamic configuration changes through a unix socket.
This library defines the communication protocol, the message format, the required
structures, serialization and deserialization code.

## Command messages are defined in protobuf

Protobuf is a language-agnostic, binary serialization format
used to efficiently transmit structured data between different systems and languages.

The idea is to define the data _once_ in this format, so that various libraries
of various languages can translate it to their own.

All types are defined in the `command.proto` file.
At build time, `prost-build` generates `command.rs` in Cargo's `OUT_DIR`; the
public `proto::command` module includes it from there. Building a source archive
requires `protoc` and leaves the source tree unchanged.

There are two main types received by, and sent from, Sōzu:

- `Request`
- `Response`

They look like this, in protobuf:

```protobuf
// A message received by Sōzu to change its state or query information
message Request {
  oneof request_type {
    // save Sōzu's parseable state as a file, with a path
    string save_state = 1;
    // load a state file, given its path
    string load_state = 2;
    /*
    many more — see command/src/command.proto
    */
  }
}
```

```protobuf
// Response to a request
message Response {
    // wether the request was a success, a failure, or is processing
    required ResponseStatus status = 1 [default = FAILURE];
    // a success or error message
    required string message = 2;
    // response data, if any
    optional ResponseContent content = 3;
}
```

These are serialized in binary, NOT in plain text formats like JSON.

A response can have 3 possible status:

- `Ok`: the task was done
- `Failure`: there was an unrecoverable error
- `Processing`: the task was started but may not finish right away

As an example, in a soft shutdown, the shutdown message is sent to all
the workers, and they acknowledge the message by sending an answer
with the `Processing` status: in a soft shutdown, a worker stops accepting
new connections but keeps the active ones and exits when they are no longer
active. Once all connections are done, a worker will send an answer
with the same id and the `Ok` status.

## Protected health-check headers

An `HTTP` health check can carry custom request headers whose values are
credentials. `HealthCheckConfig.headers` (field 10) holds metadata only:
`HealthCheckHeader { name, fingerprint }`, with a fingerprint of the form
`sozu-hc-hmac-sha256:v1:<32 lowercase hex epoch>:<64 lowercase hex tag>`. The
clear values travel in `HealthCheckHeaderValue` inside
`SetProtectedHealthCheck`, a request distinct from `SetHealthCheck`, which never
carries a value. The carriers refuse to serialize or print a value, and the
values stay in memory, never in the saved state or a query.

A generic integrator of this library must do the orchestration itself; the
library types do not add it:

1. Send `QueryProtectedHealthCheckSupport` first, and read no secret source and
   send no value until the main process answers with exactly the positive
   `ProtectedHealthCheckReply` capability reply. The `sozu` CLI waits for it for
   a fixed five seconds, unaffected by its generic timeout. Treat a failure, a
   late answer, silence or any other reply as a refusal. The query before file
   or standard-input I/O is a guarantee of the CLI; the hardened main may read
   `value_file` references of its configuration into protected memory before it
   launches the workers, without that query.
2. Validate the headers with `validate_health_check_config` and the values with
   `validate_health_check_header_values`, then send one
   `SetProtectedHealthCheck`. `secret::read_header_value` is the reader the CLI
   uses: it removes one final `LF` and the `CR` right before it, nothing else.
3. The main process applies the change on the workers with the acknowledged
   `Prepare`, `Commit`, `Abort` and `Release` exchange of
   `ProtectedHealthCheckOrder`, and fences other changes to the same cluster in
   the meantime. An error before every `Commit` is acknowledged retains or
   restores the previous configuration; after every `Commit` acknowledgement, a
   refused or timed-out `Release` is a retention or cleanup failure and cannot
   roll back a before-image the workers already destroyed. A replacement or
   respawned worker receives and acknowledges the values before it is Running or
   probes. This applies to this change only; it is not a global transaction.

Peers older than 2.3.2 ignore the unknown field 10 instead of failing, so the
capability query is the only protection against sending a credential to one.
See `doc/health_checks.md#custom-request-headers` for the limits, the cold
restart and the upgrade behaviour.

## Control-plane audit log

Every privileged mutation accepted by the unix command socket is mirrored to a
dedicated audit envelope rendered in the canonical `[session req cluster
backend]\tAUDIT\tCommand(...)` layout. Two optional sinks ship today:
`audit_logs_target` (text, ANSI-stripped, mirrors the `info!` line) and
`audit_logs_json_target` (one stable-schema JSON object per line, ready for SIEM
ingest). Both sinks open `O_APPEND | O_CREAT | chmod 0640` and create new parent
directories with mode `0o750` so PCI-DSS 10.5 remains enforceable across
restarts. Free-form fields (`target`, `actor.user`, `actor.comm`, `socket`,
`extras.reason`) are sanitised before render so attacker-influenced input cannot
forge additional audit lines via embedded `\t` / `\n` / ANSI escapes.

The optional `command_allowed_uids: Vec<u32>` config field rejects any
`SO_PEERCRED` UID outside the list at the top of `Server::handle_client_request`
(defence in depth on top of the socket mode, `command_socket_mode`, `0o600`
by default). Actor identity is captured via `SO_PEERCRED` and enriched on accept with `/proc/<pid>/comm` and
NSS `getpwuid_r(uid)`; `peer_comm` opens `/proc/<pid>/stat` first so a recycled
PID returns `None` rather than the new owner's `comm`, and `peer_user` caches
results in a 16-entry process-local LRU so an SSSD/LDAP wedge cannot stall the
main event loop.

The full schema, retention policy, and supervisor-side lifecycle (accept loop,
fan-out across workers, FD-passing for hot upgrades) live in
`doc/observability.md` and the module-level comments in `bin/src/command/`.