Expand description
One place for the server’s authorization decisions (#524).
ServerPolicy answers every “may this peer do this?” question the
server asks of an application: the covered mutations, LifeSafetyOperation,
DeviceCommunicationControl and both AuditNotification services. Each
method has a documented default, so a policy overrides only what it
decides.
What stays outside it, and why:
MutationPolicy::DenyAllis a static hardening switch onServerConfig. It wins over any policy, which isn’t asked, and its denials are counted apart.- DeviceCommunicationControl’s password, static
DccPolicy, source restriction and disable rate limit are settings, not callbacks. The policy is asked after the password,DccPolicyand source checks, and before the rate limit is charged. on_reinitializecarries out a restart rather than deciding on one, and runs after the server’s own checks.on_time_syncobserves a change it can’t refuse.- Routing control belongs to the router (
bacnet-network’sControlAuthorizer), not to the server.
§Who sent it
Every context carries the claimed source addresses, the ingress
TransportProvenance, and
from those a principal(): the ScPrincipal of the leaf certificate
when the request came over a verified direct BACnet/SC connection from
the peer that originated it, and None otherwise. B/IP, B/IPv6, MS/TP
and Ethernet have no principal. Neither does a request a hub relays: the
hub authenticated its own TLS client, and the origin’s certificate never
reaches this node, so the policy can’t tell one hub member from another.
A routed request (one with an SNET/SADR source) has no principal either,
even over a direct connection: the connection authenticates the router
that forwarded it, not the device behind it. forwarding_peer_principal()
returns that router’s principal, for a policy that trusts a router with
everything it forwards. A source VMAC, UUID or routed SNET/SADR is a
claim, never a principal.
With sc-tls, every context’s certificate() returns the verified leaf
certificate that principal() fingerprints, under the same rule, and
forwarding_peer_certificate() the forwarding peer’s. Its metadata gives
the subject, issuer and Subject Alternative Names, so a rule can follow a
name across certificate renewal instead of listing old and new
fingerprints. Allow on a name found rather than deny on one missing, and
never log a certificate’s contents.
So principal() means the direct SC peer that sent the NPDU without an
SNET. A router that doesn’t follow the standard, or an application
gateway that re-originates requests, shows its own principal for what it
relays: trust a principal as far as everything its holder may relay.
SC mutual TLS authenticates a connection; it doesn’t authorize a service. Without a policy, any peer a CA admits may use every service the server’s defaults allow.
§Calling rules
Every method should be quick and free of side effects. For a received
request the server calls them on Tokio’s blocking pool with no object
database guard held (#2032; each method says when it asks), so a slow
policy delays only its own request, but a call that never returns keeps
the process from exiting. Calls may run concurrently. Returning false, or
panicking, denies, and the request changes nothing.
use bacnet_server::mutation::MutationAuthorizationContext;
use bacnet_server::ServerPolicy;
use bacnet_transport::port::ScPrincipal;
/// Writes only from the operator workstation's certificate.
struct Site {
operator: ScPrincipal,
}
impl ServerPolicy for Site {
fn authorize_mutation(&self, context: &MutationAuthorizationContext) -> bool {
context.principal() == Some(self.operator)
}
}FnPolicy builds one from closures instead, keeping the default of
every decision it isn’t given:
use bacnet_server::policy::FnPolicy;
use bacnet_server::server::BACnetServer;
// Remote writes from verified direct SC peers only; LifeSafetyOperation
// from anyone. Audit notifications keep denying.
let builder = BACnetServer::bip_builder().policy(
FnPolicy::new()
.mutation(|context| context.principal().is_some())
.life_safety_operation(|_| true),
);Structs§
- FnPolicy
- A
ServerPolicymade of closures, one per decision. A decision with no closure keeps the trait’s default; with no mutation closure it doesn’t decide mutations at all (ServerPolicy::decides_mutations). - ScCertificate
Metadata sc-tls - Bounded display metadata. Parsing is informational, never verification.
- ScPeer
Certificate sc-tls - One original DER certificate with optional display interpretation. A parser failure preserves the DER and does not change TLS acceptance.
- ScPrincipal
- Who sent a request, as far as this node can prove it: the leaf certificate of a verified direct BACnet/SC connection.
Enums§
- ScSubject
AltName sc-tls - One Subject Alternative Name of a certificate, as written in it.
Traits§
- Server
Policy - The server’s authorization decisions, each with a documented default.