netpolicy

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package netpolicy holds the one network policy the executor applies to guest traffic: which transports a guest may use, which destinations it may reach, and on which ports. It has two halves that are always checked together. The operator half comes from the executor configuration and denies destinations and transports regardless of what a job asks for; the job half is the destination list the submitter declared for one run.

The package deliberately takes plain strings and addresses rather than configuration or scheduler types, so the same rules can be applied before a connect, before a handshake, on an accepted connection, and on a received datagram, without those call sites sharing anything but this package.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTransportUnavailable is a transport the operator disabled, one the
	// job did not request, or one whose host privileges are missing.
	ErrTransportUnavailable = errors.New("transport unavailable")
	// ErrDenied is an operator denial: a reserved or internal range, an
	// opted-out destination, or a port outside the permitted ranges.
	ErrDenied = errors.New("denied by the operator network policy")
	// ErrNotInPolicy is an address the job did not declare.
	ErrNotInPolicy = errors.New("outside the job's destination policy")
)

Refusals. Every enforcement point wraps one of these, so a caller can tell a policy refusal from a network failure without matching on message text.

View Source
var ICMPPermitted = sync.OnceValue(probeICMP)

ICMPPermitted reports whether this process may open the raw ICMPv4 sockets the connect_icmp4 import needs. It is a real attempt to open one, not a property of the packet counter or of any configuration key: a host that cannot open the socket must not advertise the capability, and a job that asks for it must be refused before it dials.

The answer is determined once. Process privileges do not change while the executor runs, and repeating the probe on every connect would open a raw socket for every guest call.

Functions

func Normalize

func Normalize(addr netip.Addr) netip.Addr

Normalize is the one spelling of an address the policy works with: no IPv4 mapping, no zone. Two guests writing the same destination differently reach the same decision, and the same accounting key.

Types

type Destination

type Destination struct {
	// Addrs are the admitted peers in the order the resolver returned them.
	// A name with several addresses keeps all of them, so a host that tries
	// them in turn reaches the destination the way dialling the name would
	// have, without any of them escaping the policy. The port is zero for
	// portless transports.
	Addrs []netip.AddrPort
	// Key is the address the submitter declared, which is what per-destination
	// bandwidth is accounted against.
	Key string
	// ServerName is the name the guest asked for, for TLS verification. It is
	// empty when the guest gave an address literal.
	ServerName string
	// contains filtered or unexported fields
}

Destination is one admitted outbound target. It carries the addresses that were checked, so the connection is made to one of exactly those rather than to a name that may resolve differently a moment later.

func (Destination) CheckSocket

func (d Destination) CheckSocket(network, address string) error

CheckSocket is the last gate before a socket is connected. The dialer calls it with the address the operating system is about to use, after any name resolution of its own, so a destination that changed underneath admission never reaches connect. Only an address this destination was admitted for passes, whichever of them the host is trying.

func (Destination) DialAddresses

func (d Destination) DialAddresses() []string

DialAddresses are the literals the host dials, in the order it should try them: the checked addresses, never the name the guest wrote.

type Match

type Match struct {
	Key string
}

Match is an admitted address that the host did not resolve itself: an accepted peer, a datagram sender, or a SCION destination. Key is the declared address its traffic is accounted against.

type Operator

type Operator struct {
	// contains filtered or unexported fields
}

Operator is a compiled operator policy. It is immutable once parsed and safe for concurrent use.

func Parse

func Parse(spec Spec) (Operator, error)

Parse compiles a Spec. It reports the first unusable value, naming the configuration key, so an operator can correct the file.

func (Operator) CheckAddr

func (o Operator) CheckAddr(addr netip.Addr) error

CheckAddr applies the operator's destination rules to one address. It is the rule that holds whatever a job declared: a destination the operator denies is never reached, on any transport.

func (Operator) CheckHost

func (o Operator) CheckHost(name string) error

CheckHost denies a destination by name, which is how an opted-out target is kept unreachable even while its addresses change.

func (Operator) CheckPort

func (o Operator) CheckPort(port int) error

CheckPort applies the permitted port ranges. Transports without a port, such as ICMP, pass a port of zero and are not checked.

func (Operator) DeniedHosts

func (o Operator) DeniedHosts() []string

DeniedHosts are the names the operator denied, which admission resolves so that their current addresses are denied too.

func (Operator) Enabled

func (o Operator) Enabled(t Transport) bool

Enabled reports whether the operator left transport t switched on.

type Option

type Option func(*Policy)

Option adjusts a Policy at construction. Options exist for the seams tests need: name resolution and the host privilege probe.

func WithICMPProbe

func WithICMPProbe(probe func() error) Option

WithICMPProbe replaces the raw-socket privilege probe.

func WithResolver

func WithResolver(resolver Resolver) Option

WithResolver replaces the name resolver.

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

Policy is the operator policy and one job's policy together. It is the only object the host functions consult, so a destination cannot be admitted by one half alone.

func New

func New(op Operator, run Run, options ...Option) *Policy

New combines an operator policy with one run's declared policy.

func (*Policy) AdmitAddr

func (p *Policy) AdmitAddr(ctx context.Context, t Transport, addr netip.AddrPort) (Match, error)

AdmitAddr admits an address the host already holds: a peer that connected, the sender of a datagram, or a SCION destination this executor parsed. The source port of an inbound peer is whatever it happened to pick, so the permitted destination ports are not applied to it.

func (*Policy) AdmitDestination

func (p *Policy) AdmitDestination(ctx context.Context, t Transport, target string) (Destination, error)

AdmitDestination admits one outbound target written as the guest wrote it. It resolves the target and the job's declared destinations with the same resolver, applies the operator's rules to the result, and returns the address the caller must connect to. Nothing is sent to the target here: a refusal happens before any socket is connected.

func (*Policy) Available

func (p *Policy) Available(t Transport) error

Available reports whether transport t may be used at all: the operator's switch, the capability the submission requested, and, for ICMP, whether this process actually holds the privilege the transport needs.

func (*Policy) AvailableListener

func (p *Policy) AvailableListener(t Transport) error

AvailableListener reports whether the job may run the listener for t. Inbound traffic needs both the operator's inbound switch and the listener the submission asked for.

type Resolver

type Resolver interface {
	LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error)
}

Resolver is the name resolution admission uses. The default is the host resolver; tests install their own so that alias and DNS-change behaviour can be observed without a name server.

type Run

type Run struct {
	Addresses   []string
	RequireICMP bool
	ListenTCP   bool
	ListenUDP   bool
	ListenSCION bool
}

Run is the job's half of the policy: the destinations the submitter declared and the capabilities the submission requested.

type Spec

type Spec struct {
	// Per-transport capability switches.
	TCP, TLS, UDP, ICMP, SCION, Inbound bool
	// LocalTargets re-allows loopback destinations, which the reserved-range
	// rule denies. It is the local profile's switch and nothing else: link
	// local, private and unique-local ranges stay denied.
	LocalTargets bool
	// DeniedDestinations are additional operator denials as a comma-separated
	// list of CIDR blocks, IP addresses and DNS names.
	DeniedDestinations string
	// PermittedPorts are the destination ports a guest may reach, written as
	// the same comma-separated ranges as the listener pool. Empty permits
	// every port.
	PermittedPorts string
}

Spec is the operator policy as the configuration file writes it. It is plain data: the configuration package resolves omitted keys to their documented defaults and hands the result here.

The zero Spec disables every transport, which is what an unconfigured executor should do rather than fall open.

func Defaults

func Defaults() Spec

Defaults is the policy an executor applies where its configuration says nothing: the transports this build supports are on, SCION is off because it cannot be held to the same contract, and every reserved or internal range is denied on every port.

Loopback is denied with them. An executor that measures against services on its own host — the local, wallet-free profile — has to say so with local_targets, so that a deployment which never writes the section cannot be asked by a submitter to reach the services behind its own loopback interface.

type Transport

type Transport uint8

Transport identifies one guest transport. Every enforcement point names the transport it is about, so a capability switch and an error message can be specific about which path was refused.

const (
	TCP Transport = iota
	TLS
	UDP
	ICMP
	SCION
	// Inbound covers accepted TCP connections and datagrams received on the
	// job's UDP listener: traffic a peer starts, not the guest.
	Inbound
)

func (Transport) String

func (t Transport) String() string

Jump to

Keyboard shortcuts

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