http_gun/destination
Decides which hosts, ports and network addresses a client may reach.
A Policy admits public addresses by default. The three setups that tests
and local services need are each one line:
// Public addresses and loopback.
config.default() |> config.allow_loopback
// Loopback only.
config.default() |> config.with_destination(destination.loopback_only())
// Loopback, pinned to one local server.
config.default()
|> config.with_destination(
destination.loopback_only() |> destination.only_hosts(["127.0.0.1:8080"]),
)
allow_private admits private networks. Reserved addresses, including
cloud metadata addresses, are always refused. Every address that a host
name resolves to is checked before a connection opens, and the connection
uses that checked address.
with_plaintext decides whether http:// is admitted. The default allows
it; PlaintextToLoopbackOnly admits it only when every resolved address
is loopback, so http://localhost passes and http:// to anywhere else
fails, while https:// is unaffected:
destination.default()
|> destination.allow_loopback
|> destination.with_plaintext(destination.PlaintextToLoopbackOnly)
http_gun.with_destination narrows the policy for one client view: a
request must satisfy both the client’s policy and every policy its view
adds, so a view can never widen what the client admits. check applies a
policy to a host and port without resolving names, for an application
that validates URLs itself.
Types
An IP address. parse_address reads one from text.
pub type Address {
Ipv4(Int, Int, Int, Int)
Ipv6(Int, Int, Int, Int, Int, Int, Int, Int)
}
Constructors
-
Ipv4(Int, Int, Int, Int) -
Ipv6(Int, Int, Int, Int, Int, Int, Int, Int)
The class of an address, as classify reports it.
pub type Class {
Public
Loopback
Private
Reserved
}
Constructors
-
Public -
Loopback -
Private -
Reserved
Whether a policy admits plaintext http://. https:// is never affected.
pub type Plaintext {
AllowPlaintext
PlaintextToLoopbackOnly
RequireTls
}
Constructors
-
AllowPlaintextAdmit
http://to every address the policy admits. The default. -
PlaintextToLoopbackOnlyAdmit
http://only when every resolved address is loopback. -
RequireTlsRefuse every
http://request.
Which destinations a client admits. Build one with default or
loopback_only and the functions below.
pub opaque type Policy
Values
pub fn allow_private(policy: Policy) -> Policy
Also admit private network addresses: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 and fc00::/7.
pub fn check(
policy: Policy,
host: String,
port: Int,
) -> Result(Nil, Rejection)
Check a host and port against the policy without resolving names. A host that is an IP literal (with or without brackets) is also classified; a host name passes when the host list admits it, and its resolved addresses are checked again when a client connects.
pub fn check_address(
policy: Policy,
address: Address,
) -> Result(Nil, Rejection)
Check one resolved address against the policy’s address classes.
pub fn check_plaintext(
policy: Policy,
address: Address,
) -> Result(Nil, Rejection)
Check a plaintext http:// connection to one resolved address against
the policy’s plaintext rule. The address classes are checked separately
by check_address.
pub fn classify(address: Address) -> Class
Classify an address. Invalid components fail closed as Reserved.
IPv4-mapped and well-known NAT64 IPv6 addresses inherit the embedded IPv4
classification.
pub fn loopback_only() -> Policy
Loopback addresses only (127.0.0.0/8 and ::1): public, private and reserved addresses are refused.
pub fn narrows(policy: Policy, within client: Policy) -> Bool
Whether policy, added by a client view, narrows client, the client’s
policy: it has an only_hosts list, or it refuses an address class that
client admits. The plaintext rule never counts. A client configured with
config.require_view_destination admits a view only when one of its
policies narrows the client’s policy this way.
let client = destination.default() |> destination.allow_loopback
destination.narrows(destination.loopback_only(), within: client) // True
destination.default()
|> destination.allow_loopback
|> destination.with_plaintext(destination.RequireTls)
|> destination.narrows(within: client) // False
pub fn only_hosts(policy: Policy, hosts: List(String)) -> Policy
Admit only these hosts, compared without case. An entry is "host", which
admits every port, or "host:port", which admits one port; write an IPv6
address with a port as "[::1]:8080". Address classes still apply, so
loopback_only() |> only_hosts(["127.0.0.1:8080"]) admits exactly one
local server. Calling it again replaces the list.
pub fn parse_address(text: String) -> Result(Address, Nil)
Parse an IPv4 or IPv6 literal, as "127.0.0.1" or "::1".
pub fn validate(policy: Policy) -> Result(Policy, String)
Return the first malformed only_hosts entry: empty, containing
whitespace, /, ?, # or @, or with a port outside 1..65535. A
malformed entry never matches a request; config.validate and
http_gun.start report it.
pub fn with_plaintext(
policy: Policy,
plaintext: Plaintext,
) -> Policy
Choose whether http:// is admitted. The rule is decided against each
resolved address, like the address classes, including the addresses of
a pooled connection a request would reuse; a refused request fails with
PlaintextRefused and NotSent. A view’s policy can tighten the rule
and never loosen it, since every policy must admit the request.