Documentation
¶
Overview ¶
Package netsecurity restricts outbound network access for script-facing libraries (requests, wait_for, websocket).
SECURITY MODEL ¶
All enforcement happens in two layers:
- CheckURL rejects a request before it starts: unsupported schemes, https-only violations, denied/allowed host rules, and (unless explicitly permitted) IP-literal URLs.
- DialContext resolves the hostname via the configured resolver, validates EVERY resolved IP against the policy, and dials the validated IP directly. Dialing the validated IP (rather than the hostname) is what defeats DNS rebinding: Go never re-resolves after the check, so a resolver that flips its answer between check and connect cannot reach a blocked address. Validating every answer defeats multi-answer tricks where one record is public and another is private.
Redirects are covered because every hop produces a new RoundTrip, which re-runs CheckURL, and every connection goes through DialContext.
A nil Config (or nil *Guard) means no restrictions: existing embedders keep today's behavior. A non-nil Config enables the policy with safe defaults — loopback, link-local (including cloud metadata endpoints), private, unspecified and multicast addresses are all denied unless explicitly allowed.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewResolver ¶
NewResolver returns a *net.Resolver that queries the given DNS servers (plain DNS, port 53; entries like "1.1.1.1" or "8.8.8.8:53"). An empty list returns the system resolver. Guards use it internally; hosts can use it directly to resolve through the same servers everywhere.
Types ¶
type Config ¶
type Config struct {
// RequireHTTPS rejects plain http:// and ws:// URLs.
RequireHTTPS bool
// AllowIPLiterals permits URLs that name an IP directly (http://1.2.3.4/).
// Off by default: literals are the classic SSRF vector. An IP literal
// inside AllowedCIDRs is always permitted regardless of this flag.
AllowIPLiterals bool
// AllowLoopback permits 127.0.0.0/8 and ::1 (off by default).
AllowLoopback bool
// AllowPrivateIPs permits RFC1918 and IPv6 unique-local ranges (off by
// default). This is the explicit "this script may reach the LAN" switch.
AllowPrivateIPs bool
// AllowHosts, when non-empty, is an allowlist: only listed hosts may be
// contacted. Listed hosts are trusted — their resolved IPs bypass the
// loopback/private/link-local categories (that is the way to grant a
// script access to an internal service by name). Entries are exact
// hostnames (case-insensitive) or domain suffixes written with a leading
// dot (".corp.example.com" matches any subdomain).
AllowHosts []string
// DenyHosts always wins, regardless of any other setting. Same entry
// syntax as AllowHosts.
DenyHosts []string
// AllowedCIDRs permits explicit address ranges (e.g. a corporate block),
// overriding the built-in loopback/private/link-local categories.
AllowedCIDRs []string
// DeniedCIDRs blocks explicit ranges and wins over AllowedCIDRs and every
// other allow.
DeniedCIDRs []string
// DNSServers resolves hostnames through these servers instead of the
// host's resolver (e.g. "1.1.1.1", "8.8.8.8:53"). Plain DNS (53/udp and
// 53/tcp) only. Resolution and validation use the same resolver, so
// policy decisions and connections see the same answers.
DNSServers []string
// AllowAll disables every address and host check, leaving only the
// shared DNS resolver. Hosts use it to configure nameservers without
// imposing a policy; it cannot be set from a policy file.
AllowAll bool
}
Config holds the outbound network policy for a library registration. The zero value plus a non-nil pointer enables safe-default blocking.
func LoadConfig ¶
LoadConfig reads a TOML policy file. Every key is optional, for example:
https_only = true allow_hosts = ["api.example.com", ".internal.corp"] allow_cidrs = ["10.1.0.0/16"] dns_servers = ["1.1.1.1", "8.8.8.8:53"]
The file must parse and compile: an invalid policy is an error, never an open policy.
type Guard ¶
type Guard struct {
// contains filtered or unexported fields
}
Guard is an immutable, compiled network policy. It is safe for concurrent use by multiple libraries and interpreters.
func FailClosed ¶
FailClosed returns a Guard that rejects every URL and dial with an error derived from err. Use it when a Config fails to compile.
func NewGuard ¶
NewGuard compiles a Config into a Guard. Invalid CIDRs or malformed DNS server entries are reported as errors; callers should treat a config error as a registration failure rather than falling back to an open policy.
func (*Guard) CheckURL ¶
CheckURL validates a request URL before it is sent: scheme, host list rules, and IP-literal rules. Hostname resolution is NOT checked here — that happens at dial time. It returns a descriptive error suitable for surfacing to the script.
func (*Guard) DialContext ¶
DialContext implements the resolve-validate-dial pattern described in the package comment. It is suitable as an http.Transport.DialContext and as a gorilla websocket NetDialContext.
func (*Guard) HTTPClient ¶
HTTPClient returns a guarded client for script HTTP requests. The transport disables proxy environment variables: an HTTP(S)_PROXY would tunnel the connection to the proxy host, bypassing the address policy for the target.
func (*Guard) NewTransport ¶
NewTransport builds the guarded base transport (pooled, like the shared scriptling HTTP pool, but with policy-controlled dialing and no proxies).