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
- Variables
- func ICMPPermitted() error
- func Normalize(addr netip.Addr) netip.Addr
- func RefreshICMP() (reason string, err error)
- type Destination
- type Match
- type Operator
- type Option
- type Policy
- func (p *Policy) AdmitAddr(ctx context.Context, t Transport, addr netip.AddrPort) (Match, error)
- func (p *Policy) AdmitDestination(ctx context.Context, t Transport, target string) (Destination, error)
- func (p *Policy) Available(t Transport) error
- func (p *Policy) AvailableListener(t Transport) error
- func (p *Policy) RefusesIPv6() bool
- type Resolver
- type Run
- type Spec
- type Transport
Constants ¶
const ( // ICMPDisabled: the operator's network policy switches ICMP off. ICMPDisabled = "disabled" // ICMPNotPermitted: the process may not open raw sockets (CAP_NET_RAW). ICMPNotPermitted = "not_permitted" // ICMPPingSocketOnly: raw sockets are refused, but an unprivileged ping // socket opens. Guests need raw sockets, so ICMP remains unavailable. ICMPPingSocketOnly = "ping_socket_only" // ICMPUnsupported: the raw socket failed for another reason. ICMPUnsupported = "unsupported" )
ICMP probe reasons, reported with an unavailable ICMP capability.
Variables ¶
var ( // 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") // ErrUntagged is traffic the run's tagger cannot attribute: an IPv6 // destination, peer or listener address of a run whose kernel tagger tags // IPv4 only. It is refused rather than sent without the attribution the // run expects. ErrUntagged = errors.New("not tagged on this executor") )
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.
Functions ¶
func ICMPPermitted ¶
func ICMPPermitted() error
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 first call probes; later calls return the latest answer. Repeating the probe on every connect would open a raw socket for every guest call, so the executor refreshes it on the capability cadence with RefreshICMP instead.
func Normalize ¶
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.
func RefreshICMP ¶ added in v0.3.0
RefreshICMP probes again and records the answer for ICMPPermitted. A refusal comes with its reason: ICMPNotPermitted, ICMPPingSocketOnly or ICMPUnsupported. Both are empty when raw sockets are permitted.
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 ¶
Parse compiles a Spec. It reports the first unusable value, naming the configuration key, so an operator can correct the file.
func (Operator) CheckAddr ¶
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 ¶
CheckHost denies a destination by name, which is how an opted-out target is kept unreachable even while its addresses change.
func (Operator) CheckPort ¶
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 ¶
DeniedHosts are the names the operator denied, which admission resolves so that their current addresses are denied too.
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 ¶
WithICMPProbe replaces the raw-socket privilege probe.
func WithResolver ¶
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 (*Policy) AdmitAddr ¶
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 ¶
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 ¶
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.
func (*Policy) RefusesIPv6 ¶ added in v0.3.0
RefusesIPv6 reports whether this run refuses IPv6 destinations and peers. Its listeners are then bound to IPv4 only.
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
// RefuseIPv6 refuses every IPv6 destination and peer with ErrUntagged. It
// is set for a run whose packets the kernel tagger attributes, which tags
// IPv4 only. SCION is not affected: its sockets are never tagged, and its
// traffic is reported untagged instead.
RefuseIPv6 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.