Apache HTTP Server Version 2.5

| Description: | Dynamic Balancer membership where backends announce themselves to the reverse proxy over unicast UDP datagrams |
|---|---|
| Status: | Extension |
| Module Identifier: | proxy_beacon_module |
| Source File: | mod_proxy_beacon.c |
| Compatibility: | Available in Apache 2.5 and later |
This module lets backend servers announce themselves to a
front-end reverse proxy, which then adds each announcing backend as a live
member (worker) of a mod_proxy_balancer balancer. When a
backend stops announcing, the proxy takes it out of rotation. This provides
self-registering, self-healing balancer membership without editing the proxy
configuration or driving the balancer-manager by hand.
Communication uses plain unicast UDP datagrams (not multicast, which is filtered on most networks and does not traverse the public Internet). The data flows from backend to proxy:
ProxyBeaconListen).ProxyBeaconAddress), advertising
its own routable URL
(ProxyBeaconAdvertise).Datagrams are fire-and-forget: a lost announcement is recovered by the next periodic one, and reordering is rejected by a per-backend timestamp check, so no connection, reconnect, or framing layer is needed.
On the proxy, ProxyBeaconBalancer names the balancer
that announced backends are added to. Membership changes are applied using
the same internal mechanism as the balancer-manager web
interface, so a backend added this way behaves exactly like a statically
configured or manually added
BalancerMember, and is visible and
editable in the balancer-manager.
This module requires the service of
mod_watchdog and mod_proxy_balancer. The
background work (listening, publishing, adding and evicting members) runs in
a single mod_watchdog child process, so it is not available
under the prefork MPM behaviour where that singleton cannot
run.
Any host that can reach the proxy's receive port could otherwise announce
an arbitrary backend URL and cause the proxy to send client traffic to it
(and a UDP source address is trivially spoofable). Set
ProxyBeaconSecret to the same value on the proxy and on
every backend so that announcements are authenticated with a keyed
message-authentication code (MAC) and a timestamp. When a secret is
configured the proxy drops any announcement that is not validly signed and
recent. If no secret is configured the channel is unauthenticated
and the proxy logs a warning at startup.
Announcements are authenticated but not encrypted; the payload is operational metadata (backend URLs), not secret data. Transport confidentiality (e.g. DTLS) is not currently provided and would be a separate future layer.
ProxyBeaconAddress
ProxyBeaconAdvertise
ProxyBeaconBalancer
ProxyBeaconInterval
ProxyBeaconListen
ProxyBeaconMaxSkew
ProxyBeaconSecret
ProxyBeaconTimeoutThe following pair of configurations sets up a self-registering balancer.
The backends require no knowledge of each other and the proxy needs no
pre-declared BalancerMember
entries — only an empty balancer with room to grow.
On the reverse proxy:
# Receive backend announcements on the cluster network interface (UDP). ProxyBeaconListen 0.0.0.0:5555 ProxyBeaconSecret "a-long-random-shared-cluster-secret" ProxyBeaconBalancer cluster # A backend is dropped from rotation if it does not announce for 30 seconds. ProxyBeaconTimeout 30 # An initially-empty balancer with spare slots for the dynamic members. <Proxy balancer://cluster> ProxySet growth=16 </Proxy> ProxyPass "/" "balancer://cluster/" ProxyPassReverse "/" "balancer://cluster/"
On each backend server:
# Announce this backend's routable origin to the proxy every 10 seconds (UDP). ProxyBeaconAddress proxy.example.com:5555 ProxyBeaconAdvertise http://10.0.0.5:8080 ProxyBeaconSecret "a-long-random-shared-cluster-secret" ProxyBeaconInterval 10
When a backend starts it begins sending announcements. The proxy
verifies each announcement against the shared secret, adds
http://10.0.0.5:8080 as a member of
balancer://cluster, and enables it. If that backend later stops
announcing for longer than ProxyBeaconTimeout, the proxy
disables the member (taking it out of rotation); a subsequent announcement
re-enables it.
A backend added at runtime occupies one of the balancer's growth slots
for the lifetime of the server process; it is disabled rather than removed
when it stops announcing, matching the behaviour of the
balancer-manager (which can add, but not remove, workers at
runtime). Size growth for the maximum number of backends you
expect to register.
| Description: | Address of the reverse proxy to which a backend sends its announcements |
|---|---|
| Syntax: | ProxyBeaconAddress address:port |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconAddress directive marks a server as an
announcement sender (a backend). It sends UDP datagrams to the
proxy's ProxyBeaconListen address given by
address:port, e.g. proxy.example.com:5555 (a leading
scheme such as tcp:// is accepted and ignored). Because UDP is
connectionless, a backend may be started before the proxy is available:
early datagrams are simply dropped and the next interval retries.
Use ProxyBeaconAdvertise to specify the routable URL
the backend announces. ProxyBeaconAddress and
ProxyBeaconListen are mutually exclusive on the same
server.
| Description: | The routable URL a backend announces to the reverse proxy |
|---|---|
| Syntax: | ProxyBeaconAdvertise url |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconAdvertise directive sets the backend's
own reachable origin (for example http://10.0.0.5:8080) that the
proxy will add as a BalancerMember.
It must be a full scheme://host[:port] URL that the proxy can
reach — not the local listen address — and is validated when the
configuration is parsed.
This directive is used on a backend, alongside
ProxyBeaconAddress. If it is omitted, the backend still
sends a heartbeat but advertises no URL, so the proxy logs the
announcement without adding a member.
| Description: | Name of the balancer that announced backends are added to |
|---|---|
| Syntax: | ProxyBeaconBalancer name |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconBalancer directive names the balancer,
on the reverse proxy, into which announced backends are inserted as members.
Give the bare balancer name (for example cluster for
balancer://cluster); a leading balancer:// is
accepted and stripped.
The named balancer must exist and have spare capacity. Declare it with a
<Proxy> block and a
growth setting (or rely on
BalancerGrowth) so there are free
slots for the dynamically added members. This directive is used together
with ProxyBeaconListen.
| Description: | How often a backend publishes its announcement |
|---|---|
| Syntax: | ProxyBeaconInterval interval |
| Default: | ProxyBeaconInterval 5 |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconInterval directive sets how frequently
a backend (a ProxyBeaconAddress server) publishes its
announcement. It uses the
time-interval directive syntax and
defaults to seconds; the default is 5 seconds.
The interval must be meaningfully smaller than the proxy's
ProxyBeaconTimeout, so that the occasional lost or
delayed announcement does not cause a healthy backend to be evicted.
| Description: | Address on which the reverse proxy receives backend beacons |
|---|---|
| Syntax: | ProxyBeaconListen [address][:port] |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconListen directive marks a server as
the beacon receiver (the reverse proxy). It binds a UDP socket to
the given address, e.g. 0.0.0.0:5555 to receive on all
interfaces. A leading scheme (such as tcp://) is accepted and
ignored.
The address and port are both optional and, when omitted, are inherited
from this server's own address and port (its Listen/ServerName). With no argument at all, the beacon
listener binds the server's own address and port; given just an address it
inherits the port, and so on. Because UDP and TCP are independent port
spaces, binding the beacon socket to the server's port does not
collide with the server's TCP listener — letting the beacon channel
share the service endpoint, which also identifies the proxy to backends by
its real address. (The listener binds in an unprivileged child, so a
privileged port such as 80 or 443 cannot be shared this way; use the
server's port only when it is non-privileged.)
Backends send to this address via
ProxyBeaconAddress. The directive should be used
together with ProxyBeaconBalancer; without it,
announcements are received and logged but no members are added.
ProxyBeaconListen and
ProxyBeaconAddress are mutually exclusive on the same
server.
| Description: | Maximum allowed age of a signed announcement |
|---|---|
| Syntax: | ProxyBeaconMaxSkew interval |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconMaxSkew directive sets the anti-replay
window used when ProxyBeaconSecret is configured: the
proxy rejects any announcement whose signed timestamp differs from the
current time by more than this amount, in either direction. It uses the
time-interval directive syntax and
defaults to seconds.
If unset, the default is 30 seconds. A larger window tolerates greater
clock skew between hosts; a smaller window bounds the freshness check. Note
that the per-backend strictly-increasing-timestamp check (see
ProxyBeaconSecret) blocks replays regardless of this
window. This directive is used on the proxy.
| Description: | Pre-shared secret used to authenticate announcements |
|---|---|
| Syntax: | ProxyBeaconSecret secret |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconSecret directive sets a pre-shared
cluster secret. It must be configured with the same value on the
reverse proxy and on every backend. The backend (sender) signs each
announcement with a keyed message-authentication code (a SipHash MAC) derived
from the secret, together with a timestamp; the proxy (receiver) recomputes the MAC and
checks the timestamp, dropping any announcement that is forged, tampered
with, or replayed. Replayed messages are caught two ways: a freshness window
(ProxyBeaconMaxSkew) rejects old timestamps, and a
per-backend check rejects any announcement whose timestamp does not strictly
advance, so a captured-and-resent message (for example, one replayed to keep
a dead backend from being evicted) is dropped.
If ProxyBeaconSecret is set on the proxy, every
announcement must carry a valid, recent MAC or it is rejected. If the
secrets on the proxy and a backend differ, that backend's announcements are
silently rejected (and logged), which appears as the backend never joining
the balancer.
If no secret is configured the channel is unauthenticated and the proxy emits a warning when it starts listening. Because the secret is stored in the configuration file, restrict that file's permissions as you would for a private key.
The timestamp-based replay protection compares the announcement's time
against the proxy's clock, so the proxy and backends must have reasonably
synchronised clocks (for example via NTP). See
ProxyBeaconMaxSkew.
| Description: | How long the proxy waits, without an announcement, before a backend is taken out of rotation |
|---|---|
| Syntax: | ProxyBeaconTimeout interval |
| Default: | ProxyBeaconTimeout 0 |
| Context: | server config, virtual host |
| Status: | Extension |
| Module: | mod_proxy_beacon |
The ProxyBeaconTimeout directive sets how long the
reverse proxy will wait for an announcement from a backend before disabling
that backend's balancer member (taking it out of rotation). A later
announcement from the same backend re-enables it. It uses the
time-interval directive syntax and
defaults to seconds.
The default, 0, disables eviction entirely: backends are
added when they announce but are never automatically removed. Set this to a
small multiple of the backends' ProxyBeaconInterval to
enable self-healing membership. This directive is used on the proxy.