netsecurity

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package netsecurity restricts outbound network access for script-facing libraries (requests, wait_for, websocket).

SECURITY MODEL

All enforcement happens in two layers:

  1. CheckURL rejects a request before it starts: unsupported schemes, https-only violations, denied/allowed host rules, and (unless explicitly permitted) IP-literal URLs.
  2. 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

func NewResolver(servers []string) (*net.Resolver, error)

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

func LoadConfig(path string) (*Config, error)

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

func FailClosed(err error) *Guard

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

func NewGuard(cfg *Config) (*Guard, error)

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

func (g *Guard) CheckURL(u *url.URL) error

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

func (g *Guard) DialContext(ctx context.Context, network, addr string) (net.Conn, error)

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

func (g *Guard) HTTPClient() *http.Client

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

func (g *Guard) NewTransport() *http.Transport

NewTransport builds the guarded base transport (pooled, like the shared scriptling HTTP pool, but with policy-controlled dialing and no proxies).

func (*Guard) Resolver

func (g *Guard) Resolver() *net.Resolver

Resolver returns the resolver this guard dials with: the configured DNS servers when set, otherwise the system resolver. Hosts can hand it to other lookups (scriptling.net.resolve) so the whole system resolves through the same servers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL