Expand description
openmoat-proxy: the default-deny egress proxy behind moat proxy (ADR-020).
It serves HTTP CONNECT tunnels and absolute-form plain-HTTP requests on a
loopback port:
- Hosts are decided by openmoat-core’s
CompiledPolicyasmoat guarddecides a fetch tool’s URL: only anallowpasses. A host is reached on 443 and 80 unless a rule nameshost:port(port_policy). - Cloud metadata and link-local addresses are refused whatever the policy says, by name and on every address the proxy resolves. Private and CGNAT addresses are refused unless an allow rule names the address.
- A tunnel’s TLS
ClientHellomust name the CONNECT host (SNI). Nothing is decrypted except, withTlsset, a tunnel to the host of a secret that saystls: terminate(ADR-043): its one request is checked against the leak check and the secret’sallowrules, given the value, sent over the proxy’s own verified TLS, and its answer searched for every value. - A request that carries a brokered secret’s placeholder or value to a host
other than the secret’s own is refused (
Broker). - Every decision is recorded through a
Recorder; a failed record is a refused connection. - In session mode (
Sessions, ADR-027, ADR-044) every request must carry its session’s credential: one the proxy holds (Credential) or one aVerifyaccepts. The session’s audit id goes on each row, a session that read secret material reaches only that secret’s host, and no session holds more than half the connections. - A connection that starts with a
LocalService’s prefix goes to that service of the proxy’s own process (ADR-028), not upstream.
Plain std::net and one thread per connection, capped by Limits; the
choice is explained in docs/ARCHITECTURE.md §12.
Structs§
- Broker
- The brokered secrets of one proxy run.
Broker::defaultholds none. - CaFiles
- A new CA, as the files
openmoat proxy ca initwrites. The key is zeroed when dropped and left out ofDebug. - Connection
- One connection decision.
- Credential
- One session’s credential: its audit session id and its token.
- Leak
- A brokered secret found in a request to another host.
- Limits
- Bounds on what one client can make the proxy hold.
- Proxy
- The proxy: a policy, a resolver, an audit sink and limits.
- Proxy
Credential - One
Proxy-Authorizationvalue (ADR-027): zeroed when dropped, never printed, and never part of the head forwarded upstream. - Record
Error - A decision could not be recorded.
- Request
- A parsed request head.
- Sessions
- Session mode: the credentials the proxy accepts and where their taint is read.
- System
Resolver - The operating system’s resolver (
getaddrinfoand its equivalents). - Tls
- One proxy run’s termination: the CA’s signer, the leaves made from it and the configuration upstream connections use.
Enums§
- Auth
Error - Why a request’s credential is refused. The reply does not say whether the session id or the token was wrong.
- Broker
Error - A secret value the broker cannot use.
- Carried
- Which form of a secret a request carried.
- Credential
Error - Why a
Credentialcannot be made. - Forbidden
- The classes of address the proxy refuses.
- Hello
- What the start of a tunnel says about the server it wants.
- Hello
Error - Why the start of a tunnel is not a
ClientHellothe proxy can check. - Refusal
- Why no connection was made.
- Request
Error - Why a request head is refused.
- Roots
- What upstream certificates are verified against.
- Target
- What the client asked the proxy to reach.
- TlsError
- Why a CA cannot be made or used, or a terminated connection set up.
Constants§
- CA_
VALID_ DAYS - How long a CA
new_camakes is valid, in days. - DEFAULT_
PORTS - The ports a host pattern without a port allows: HTTPS and HTTP.
- MAX_
HEADERS - Most headers one request head may carry.
- MAX_
HELLO - Largest
ClientHellohandshake message accepted. Real ones are 0.5–2 KiB, more with post-quantum key shares; 64 KiB leaves room without letting a client make the proxy buffer without bound. - MIN_
TOKEN_ LEN - Shortest token a
Credentialaccepts, in characters. - RULE_
ADDRESS - Rule id for a destination refused whatever the policy says.
- RULE_
AUDIT - Rule id for a connection refused because it could not be recorded.
- RULE_
CAPACITY - Rule id for a connection refused at accept because the proxy, or the connections not yet authenticated, hold all the slots they may.
- RULE_
PORT - Rule id for a host the policy allows, refused on a port no rule names.
- RULE_
REQUEST - Rule id for a request the proxy could not parse or does not serve.
- RULE_
SECRET - Rule id for a request refused because it carries a brokered secret to a host that is not the secret’s own.
- RULE_
SECRET_ ROUTE - Rule id for a request to a secret’s host that the secret’s
allowrules do not name: it would have received the value, so it is not sent. - RULE_
SESSION - Rule id for a request refused for its session: no valid credential, the session’s connection share used up, or its history unreadable.
- RULE_
SNI - Rule id for a tunnel whose TLS SNI is missing or names another host.
- RULE_
TLS - Rule id of a terminated tunnel refused for its TLS: a handshake that failed on either side, or an upstream certificate that did not verify.
- RULE_
UPSTREAM - Rule id for a destination that could not be resolved or reached.
Traits§
- Local
Service - A service that shares the proxy’s port.
- Recorder
- Where connection decisions go. An error refuses the connection.
- Resolve
- Name resolution, injectable so tests can map names without a DNS server.
- Taint
Lookup - What a session was exposed to, read when one of its requests is decided. The CLI reads it from the audit log; tests pass a fixed map.
- Verify
- Checks credentials the proxy does not hold: those the standalone proxy’s sessions were issued after it started (ADR-044). The CLI implements it.
Functions§
- address_
denied - The deny rules of
addresses(compiled fromaddress_policy) that matchip, if any: a deny naming an address holds for every name that resolves to it. Matched in canonical text form, and for a NAT64, IPv4-compatible, IPv4-translated, 6to4 or Teredo address also as the IPv4 addresses it reaches. Allow rules never widen anything here; an address no deny rule matches givesNone. - address_
named - Whether
addresses(compiled fromaddress_policy) allowsip, matched in its canonical text form (::ffff:10.0.0.5as10.0.0.5). - address_
named_ on - Whether
addressesallowsaddr’s address:named, or, for IPv4, an allow rule that names it with this port (10.0.0.5:5432). - address_
policy - The part of
policythat decides a resolved address: its deny rules, and the allow rules’netandfetchpatterns that name an address: a pattern starting with a digit or containing:, not starting with a glob metacharacter, and made only of what an address’s text and a glob use. Anything else is left undecided (review), which neither opens aForbidden::Privateaddress (named) nor refuses one (denied), so a wildcard such as*or*.example, or a name such asdb.internal:5432, never reaches the local network by DNS. Exclusions are all kept, and one without a port excludes the address on every port. - connect
- Resolve
host, refuse if any address is forbidden or matched by a deny rule ofaddresses(compiled fromdecide::address_policy), then connect to the first address that answers. A private address passes only whenaddressesnames it, alone or withport.loopback_oklists loopback addresses that may be reached anyway (tests); nothing exempts link-local or metadata. - constant_
time_ eq - Whether
aandbare equal, in time that depends only on their lengths. - decide_
host - Decide whether
host, normalised asRequest::hostreports it, may be reached. Only anallowlets the connection proceed. The port is not looked at here:host_portdecides both. - decide_
host_ port - Decide whether
host, normalised asRequest::hostreports it, may be reached onport.portsis compiled fromport_policyofpolicy’s policy. Only anallowlets the connection proceed: - decide_
tainted - The refusal for a request to
host:portfrom a session withtaint, if any (ADR-027). The core’s taint rules decide (POLICY.md §4.1): after the session read secret material, network to every host but that secret’s own needs review. The proxy cannot prompt, so that review is refused. Untrusted content alone changes nothing here: its taint protects writes. - forbidden
- Why the proxy never connects to
ip, if it does not. Checked on every resolved address, so a name the policy allows cannot be pointed at a local or metadata service (DNS rebinding). - new_ca
- Make a CA that may sign for
hosts(and names under them) alone, valid from yesterday for a year. - parse_
request - Parse one request head from the start of
buf.Ok(None)means the head is not complete yet; the caller bounds how much it reads. - port_
policy - The part of
policythat decides a host and its port, ashost_portuses it: every deny and repository review rule as written, and the allow rules’host:portpatterns only, so that a pattern without a port (*,example.com) never opens another port. Every exclusion without a port (!evil.example.com) also excludes the host on every port. Anything else is left to the caller (defer). - server_
name - Read the SNI from the TLS records at the start of
buf.