openmoat_proxy/audit.rs
1//! One record per connection decision.
2//!
3//! As with `moat guard`, the record is part of the decision: a connection that
4//! cannot be recorded is not made. The CLI records into the moat audit log;
5//! this crate stays free of storage.
6
7use openmoat_core::Decision;
8
9/// One connection decision.
10#[derive(Debug, Clone, Copy)]
11pub struct Connection<'a> {
12 /// The session whose credential the request carried (ADR-027). `None` for
13 /// a proxy without sessions, and for a request refused for its credential;
14 /// the recorder then uses its own session.
15 pub session: Option<&'a str>,
16 /// Request method (`CONNECT`, `GET`, …); `None` when the head was unreadable.
17 pub method: Option<&'a str>,
18 /// Destination host and port; `None` when the head was unreadable.
19 pub destination: Option<(&'a str, u16)>,
20 /// The request was read inside a tunnel the proxy terminated (ADR-043):
21 /// `method` is that request's, and the destination was reached over TLS.
22 pub terminated: bool,
23 /// The verdict and the rules behind it.
24 pub decision: &'a Decision,
25 /// Time from accepting the connection to this decision, in microseconds.
26 pub latency_us: u64,
27}
28
29/// Where connection decisions go. An error refuses the connection.
30pub trait Recorder: Send + Sync {
31 /// Record one decision.
32 fn record(&self, connection: &Connection<'_>) -> Result<(), RecordError>;
33
34 /// Told when [`record`](Self::record) failed. The connection is refused
35 /// either way; this is where the failure is reported, somewhere other than
36 /// the audit log that just failed. The default does nothing.
37 fn unrecorded(&self, error: &RecordError) {
38 let _ = error;
39 }
40}
41
42/// A decision could not be recorded.
43#[derive(Debug, thiserror::Error)]
44#[error("audit log unavailable: {0}")]
45pub struct RecordError(pub String);