Chapters & navigation
BUILD AND INTEGRATE
BunkerWeb integration
Connect web detections to host enforcement through authenticated APIs, explicit ownership and migration fences.
On this page
This page defines the SysWarden side of the BunkerWeb integration and migration fence. It does not claim that any plugin release has completed its external end-to-end matrix. Use only a partner version whose release notes explicitly name compatibility with this frozen contract.
This page preserves the historical partner contract and its explicitly stated later additions. The version-specific v4.10.0 candidate introduces a separate HA v2 runtime with mutual TLS and additional writer and recovery requirements. This page does not qualify that successor or define a mixed-version cluster migration. For candidate laboratory work, review the HA v2 operator prerequisites and native qualification plan.
Integration boundary#
The BunkerWeb scheduler can use SysWarden's authenticated HA API to:
- push temporary Layer 7 bans into Linux nftables;
- withdraw only provenance records owned by the same claimed source and observed peer scope;
- read bounded blocklist, whitelist, status and telemetry data;
- migrate its own durable historical static submissions to provenance-aware ownership without inferring ownership from effective peer state.
The plugin uses only the HTTPS HA API. It must not edit SysWarden files, invoke the local CLI or access a local process socket. Source-owned temporary records are sent directly to every declared peer; SysWarden nodes do not relay them transitively.
The local TUI is unrelated to this integration. It opens no listening socket. The current product contains no browser terminal or remote PTY service and owns no TCP 62027 listener or firewall permission.
Prerequisites#
- A published SysWarden release explicitly compatible with the frozen v4.03.2 integration contract on every participating node. Before the next compatible Patch is public, only its exact merged candidate may be used in release qualification; it is not a production integration release.
- A compatible BunkerWeb plugin candidate.
- Console or out-of-band recovery access.
- TCP 62026 reachability on a dedicated management network.
- A high-entropy bearer token delivered through a protected secret channel.
- Exact member endpoints and an operator-approved inbound peer scope.
- Authenticated distribution of every SysWarden public HA certificate or CA.
- One operator-provided inventory containing every cluster member and every external legacy writer that can still submit historical static state.
- A durable BunkerWeb record of the historical static addresses it submitted to each exact peer.
An omitted peer or writer invalidates the migration campaign. The integrator must never manufacture membership from what one peer happens to report.
Enable the SysWarden API#
Configure the HA listener and partner extensions:
[integrations.ha]
enabled = true
peer_ips = ["172.30.0.0/29"]
peer_port = 62026
token = "replace-with-a-random-shared-secret"
[integrations.bunkerweb]
enabled = trueAn exact IP can authorize inbound requests and serve as an outbound SysWarden peer. A CIDR authorizes inbound clients only and is never converted into an outbound destination. The bearer token remains mandatory.
Validate and apply the reviewed configuration:
sudo syswarden config validate
sudo syswarden reloadConfiguration validation rejects HA when the token is empty, contains whitespace or control characters, or the peer list is empty or invalid. A rejected candidate never reaches reload mutation or service restart, so the previously validated configuration and its running listener remain active. Repair and validate the candidate before retrying the reload.
TLS and authentication#
Every /ha/sync, /ha/status and /ha/telemetry request requires TLS 1.3 and
Authorization: Bearer. HA cannot be activated from a configuration with an
empty or otherwise invalid bearer token.
The persistent server identity is stored at:
/var/lib/syswarden/ha/server.crt/var/lib/syswarden/ha/server.key
Never copy server.key to a client. Transfer only the public certificate or CA
through a trusted channel and verify its SHA-256 fingerprint:
openssl x509 -in /var/lib/syswarden/ha/server.crt -noout -fingerprint -sha256Do not enable an insecure TLS bypass in production. A certificate identity change invalidates the current manifest and resets every partner observation window.
Dialect selection#
For one exact peer and one serialized cycle, issue one authenticated
GET /ha/status. Use the enriched dialect only when both sync_ttl and
sync_provenance are present. If either capability is missing, partial,
malformed or unavailable, stop the enriched handoff for that peer. Never relax
TLS or bearer authentication and never mix historical and enriched bodies in
one request.
The peer advertises sync_ttl and sync_provenance only while
integrations.bunkerweb.enabled = true is configured and the validated
configuration is active. Do not infer those capabilities from the candidate
version or from a previous status response.
Historical static submission uses the following template:
{"ips":["REPLACE_WITH_CONTROLLED_PUBLIC_HOST"]}An enriched temporary submission can use this template:
{
"bans": [
{
"ip": "REPLACE_WITH_CONTROLLED_PUBLIC_HOST",
"ttl": 3600,
"reason": "Layer 7 detection",
"source": "bunkerweb"
}
]
}Replace each placeholder before creating the protected request body. Do not
submit the literal placeholder and do not substitute an administrative, local,
special-use, whitelisted or HA-peer address. Both historical and enriched POST
bodies accept canonical public host addresses only. CIDRs are rejected on POST.
Only an explicit historical DELETE {"ips": [...]} recovery request may name a
canonical CIDR that is already present in the historical ownership store.
The enriched contract accepts no more than 500 records per request. TTL is an
integer from 1 to 2,592,000 seconds. Host addresses must be canonical, source
is bounded approved ASCII and reason is bounded printable UTF-8.
Unknown fields, duplicate keys and mixed forms fail closed.
Separate ownership stores#
Historical static entries and provenance ledger entries are distinct stores:
POST {"ips": [...]}creates historical static state;DELETE {"ips": [...]}explicitly removes historical static entries;POST {"bans": [...]}creates provenance-aware state;DELETE {"bans": [...]}removes provenance ledger entries only;- deleting a provenance record never implicitly deletes a historical static entry;
- deleting one peer never cascades into another peer.
The enriched DELETE contract introduced by the historical v4.04.0 change set was first published in the historical v4.04.2 release and is carried forward in the version-specific v4.04.3 release. It returns an exact removal count after successful processing:
{"status":"ok","deleted":1}deleted counts provenance claims actually removed for the exact canonical IP,
source and authenticated peer scope. It does not count distinct IP addresses
and does not assert that an address was removed from nftables. Another source,
peer or historical static claim can keep the effective ban active. A valid
source or scope mismatch is an idempotent no-op and returns
{"status":"ok","deleted":0}. Only a successful response carries this
completed-removal meaning; an error requires reconciliation before retry.
The BunkerWeb cleanup set comes only from its durable, peer-specific registry of
addresses it previously submitted historically. It must never infer ownership
from GET /ha/sync, provenance pagination, another peer or an effective union.
This prevents deletion of operator, WAAP or second-producer ownership.
A claim remains retained until one complete observation finds the address absent from every declared peer and absent from BunkerWeb. An unavailable peer does not count as absence. Membership changes reset every observation window.
Operator activation manifest#
One trusted operator control host creates one strict, canonical manifest from a
complete inventory. The manifest contains exact member endpoints, live TLS leaf
fingerprints, external legacy-writer identifiers, one random epoch,
membership_sha256 and legacy_writer_inventory_sha256.
Create the operator inventory with this schema. The documentation address is an example only and must be replaced with the canonical IP literal of the receiving HA endpoint:
{
"schema_version": 1,
"membership_scope": "one_receiving_api_endpoint_per_syswarden_node",
"legacy_writer_ids": [
"bunkerweb-primary"
],
"members": [
{
"address": "192.0.2.10",
"port": 62026
}
]
}The inventory path must be absolute and canonical. The input must be a regular,
root-owned mode 0600 file no larger than 1 MiB, reached through a protected
directory chain. JSON must be UTF-8 without a byte-order mark, duplicate or
unknown keys, or trailing data. members must contain at least one unique
address and port endpoint. Each address is a canonical IP literal without a
CIDR, zone, brackets or IPv4-mapped IPv6 form, and each port is from 1 through
65535. legacy_writer_ids is mandatory and may be []; every non-empty value
must match [a-z0-9][a-z0-9._-]{0,63} and be unique. Manifest creation sorts
members and writer IDs canonically. --assert-complete is the operator's
explicit attestation that both inventories are complete.
sudo syswarden ha-fence manifest create \
--inventory /root/syswarden-ha-inventory.json \
--output /root/syswarden-ha-manifest.json \
--assert-complete
sudo syswarden ha-fence manifest verify \
--manifest /root/syswarden-ha-manifest.json
sudo syswarden ha-fence engage \
--manifest /root/syswarden-ha-manifest.jsonThe operator distributes that exact protected manifest to every member and to the integrator. The integrator does not submit cluster membership or writer state through another API. A member, endpoint, certificate or writer change requires a new manifest.
Dynamic fence proof#
The capability native_sync_fence_v1 announces schema support only. It is not
proof that the node is fenced or drained.
For every manifest member, send a unique 32-byte base64url challenge in
X-SysWarden-HA-Challenge and obtain one fresh authenticated
GET /ha/status. Accept proof only when all conditions hold in the same
response:
native_sync_fence.stateisactive_drained;- the returned challenge equals the request challenge;
- the live TLS leaf fingerprint equals the manifest member pin;
epochequals the manifest epoch;membership_sha256equals the manifest membership digest;legacy_writer_inventory_sha256equals the manifest writer digest;- the
X-SysWarden-HA-Fence-Conditionresponse header equals the JSONconditionvalue; - server instance, generation and condition remain stable for the observation.
The integrator treats the epoch and both digest values as opaque, case-sensitive strings. It compares them for exact equality with the operator-provided manifest and does not recalculate either digest or reimplement SysWarden canonicalization.
An unreachable member, partial view, invalid challenge echo, changed certificate, changed server instance, changed generation, changed membership, blind interval or reappearing address resets the campaign observation.
Condition-bound cleanup#
Every DELETE {"ips": [...]} migration request carries the exact observed
condition:
X-SysWarden-HA-Fence-Condition: <condition>While the fence is active and drained:
The outcomes are fail-closed: a missing condition receives HTTP 428, a malformed condition receives HTTP 400 and a stale condition receives HTTP 412. Each rejection performs no mutation.
| Request condition | Result | Mutation |
|---|---|---|
| Missing | HTTP 428 | None |
| Malformed | HTTP 400 | None |
| Stale or changed | HTTP 412 | None |
| Exact current condition | Normal authenticated request processing | Peer-scoped only |
HTTP 412 means the fence moved. Stop the cleanup, obtain a new all-member proof and require an operator decision. Do not retry blindly.
A one-hour continuous absence window is additional evidence only. It is never proof of drain and never permits claim release from an incomplete view. The window must cover every declared peer, and a reappearance resets the timer and must be logged.
Fence release#
Release requires the unchanged activation manifest and durable terminal closure evidence for every external legacy writer:
{
"schema_version": 1,
"epoch": "COPY_EXACT_EPOCH_FROM_MANIFEST",
"membership_sha256": "COPY_EXACT_MEMBERSHIP_SHA256_FROM_MANIFEST",
"legacy_writer_inventory_sha256": "COPY_EXACT_WRITER_SHA256_FROM_MANIFEST",
"legacy_retry_queue_drained": true,
"writers": [
{
"id": "bunkerweb-primary",
"disposition": "migrated_enriched_only",
"closure_generation": "REPLACE_WITH_DURABLE_GENERATION",
"closed_at": "REPLACE_WITH_CANONICAL_UTC_RFC3339",
"evidence_sha256": "REPLACE_WITH_LOWERCASE_EVIDENCE_SHA256"
}
]
}Replace every placeholder with evidence bound to the unchanged manifest. The
closure uses the same protected-file rules as the inventory and must match the
canonical indented JSON layout shown, including one final newline. writers
must have exactly the manifest writer IDs in canonical order. An empty manifest
writer inventory therefore requires "writers": []. The retry queue flag must
be true. Accepted terminal dispositions are migrated_enriched_only,
disabled, credential_revoked and network_quarantined.
closure_generation is 1 through 256 non-space printable ASCII characters,
closed_at is canonical whole-second UTC RFC3339, and evidence_sha256 is one
lowercase SHA-256 digest of the durable closure evidence.
sudo syswarden ha-fence release \
--manifest /root/syswarden-ha-manifest.json \
--writer-closure /root/syswarden-ha-writer-closure.jsonNo queued historical write may become eligible after release. Use
ha-fence recover with the same manifest after an interrupted engagement.
Never reuse a retired epoch or reconstruct a manifest from peer output.
Partner settings#
The compatible plugin documentation is authoritative for final names. The current design uses these settings:
| Setting | Purpose |
|---|---|
SYSWARDEN_PEERS | Complete SysWarden peer list |
SYSWARDEN_API_TOKEN | Bearer token delivered as a secret |
SYSWARDEN_CA_BUNDLE | Trusted CA or certificate bundle |
SYSWARDEN_SSL_FINGERPRINT | Explicit SHA-256 leaf pin |
SYSWARDEN_ENFORCEMENT | Partner audit or enforcing mode |
SYSWARDEN_BAN_CHUNK_SIZE | Request batch size, never above 500 |
SYSWARDEN_BAN_MAX_ITEMS | Per-pass safety limit |
SYSWARDEN_BAN_SCOPE_FILTER | BunkerWeb services whose bans are propagated |
SYSWARDEN_BAN_MIN_TTL | Minimum TTL eligible for propagation |
USE_SYSWARDEN_BLOCKLIST | Enable downloaded blocklist use |
USE_SYSWARDEN_WHITELIST | Enable downloaded whitelist use |
Start in audit mode, inspect scheduler requests and live status responses, then enable only approved directions.
Verification and release gate#
An isolated end-to-end campaign must verify:
- unauthenticated and untrusted TLS requests fail;
- dialect selection uses exactly one status request per peer cycle;
- temporary provenance expiry and owner-scoped withdrawal converge;
- operator and second-source entries survive partner cycles;
- historical cleanup comes only from the durable per-peer partner registry;
- a peer outage freezes progress and membership change resets observations;
- challenge, condition, epoch and opaque digest comparisons fail closed;
- HTTP 428, 400 and 412 perform no mutation;
- a resurrection is detected and deleted again under operator control;
- fence release requires closure for every declared writer.
The historical v4.03.2 freeze required written partner confirmation against the exact frozen contract. Any compatible plugin publication remains blocked if there is unexplained partner-attributable static residue, lost ownership, partial membership or missing writer closure.
Return to the deployment tutorial for package and operator procedures.