Documentation
¶
Overview ¶
Package policy implements compilation and evaluation of ServiceNetworkPolicy for the per-node agent (RUNE-064).
The agent embeds an evaluator inside the userspace proxy. When a connection lands on a service VIP listener the evaluator answers "is this peer allowed to talk to this service on this port?" using a pre-compiled rule set built from the destination service's spec.
Default-deny semantics (locked):
- A service with **any** ingress rule defined → traffic that does not match an allow rule is denied (Kubernetes NetworkPolicy parity).
- A service with no policy → all traffic allowed.
Source identity:
- Same-node peers are resolved via the LocalInstances table (containerIP → service+namespace) maintained by RUNE-063.
- Cross-node peers cannot be resolved to a service identity in v1 (no mTLS yet); only CIDR / namespace selectors match.
Egress:
- Evaluated when a local instance dials a service VIP. The destination service+namespace is known from the dial target.
All policy state is derived from the destination Service spec. No dedicated `policy/` orderedlog keyspace is used in v1 — the spec travels with the Service. The `policy/` prefix remains reserved for a future denormalized compiled-policy index.
Index ¶
- Variables
- func Validate(pol *types.ServiceNetworkPolicy) error
- type Compiled
- func (c *Compiled) EgressRuleCount() int
- func (c *Compiled) EvaluateEgress(target EgressTarget, proto string) Result
- func (c *Compiled) EvaluateIngress(peer PeerInfo, port int, proto string) Result
- func (c *Compiled) Explain() ExplainOutput
- func (c *Compiled) HasEgressRules() bool
- func (c *Compiled) HasIngressRules() bool
- func (c *Compiled) IngressRuleCount() int
- type Decision
- type EgressTarget
- type ExplainOutput
- type ExplainRule
- type LocalInstancesTable
- func (t *LocalInstancesTable) Apply(li types.LocalInstances) int
- func (t *LocalInstancesTable) Lookup(ip net.IP) (types.InstanceIdentity, bool)
- func (t *LocalInstancesTable) PeerInfoFor(ip net.IP) PeerInfo
- func (t *LocalInstancesTable) Remove(nodeID string) int
- func (t *LocalInstancesTable) SameNode(ip net.IP) bool
- func (t *LocalInstancesTable) Size() int
- type PeerInfo
- type Reason
- type Result
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidPolicy = errors.New("invalid network policy")
ErrInvalidPolicy wraps validator errors.
Functions ¶
func Validate ¶
func Validate(pol *types.ServiceNetworkPolicy) error
Validate runs the public ServiceNetworkPolicy validator and surfaces any compilation-time issues (currently just CIDR parse errors that Compile silently drops). The validator is the formal gate; this is a softer secondary check used by the CLI dry-run path so operators see CIDR typos.
Types ¶
type Compiled ¶
type Compiled struct {
// ServiceID identifies the destination service this policy
// belongs to. Used in logs and metrics.
ServiceID string
// Namespace is the destination service's namespace.
Namespace string
// PolicyName is a stable label for log/metric attribution. We
// don't have a separate Policy resource yet, so this is just
// the service ID.
PolicyName string
// contains filtered or unexported fields
}
Compiled is the evaluator-friendly form of a ServiceNetworkPolicy. It is small (one per service), immutable, and safe for concurrent reads. Build it once with Compile and replace atomically when the underlying spec changes.
func Compile ¶
Compile builds an evaluator for svc. svc may be nil (returns nil). Spec validation errors are surfaced via Validate; Compile is a pure transform and never returns an error — invalid peers are dropped from the rule set so a misconfigured spec fails closed.
func (*Compiled) EgressRuleCount ¶
EgressRuleCount returns the number of compiled egress rules.
func (*Compiled) EvaluateEgress ¶
func (c *Compiled) EvaluateEgress(target EgressTarget, proto string) Result
EvaluateEgress decides whether the service this Compiled belongs to may dial target on (port, proto).
func (*Compiled) EvaluateIngress ¶
EvaluateIngress decides whether peer is allowed to reach the service this Compiled belongs to on (port, proto). proto is "tcp" or "udp".
A nil Compiled means "no policy" → DecisionNoPolicy / Allow.
func (*Compiled) Explain ¶
func (c *Compiled) Explain() ExplainOutput
Explain returns a human-readable summary of the compiled policy suitable for `rune policy explain <service>`. The output is deterministic given the same input policy.
func (*Compiled) HasEgressRules ¶
HasEgressRules reports whether default-deny egress is in effect.
func (*Compiled) HasIngressRules ¶
HasIngressRules reports whether default-deny ingress is in effect.
func (*Compiled) IngressRuleCount ¶
IngressRuleCount returns the number of compiled ingress rules. Used by metrics (rune_policy_rules gauge).
type Decision ¶
type Decision int
Decision is the outcome of evaluating a connection against a compiled policy.
const ( // DecisionAllow means the connection is permitted. DecisionAllow Decision = iota // DecisionDeny means the connection is rejected by an explicit // or default-deny rule. DecisionDeny // DecisionNoPolicy means the destination service has no policy // at all (open service). Callers treat this as Allow but may // account for it separately in metrics. DecisionNoPolicy )
type EgressTarget ¶
EgressTarget describes the destination of an outbound connection initiated by a local instance.
type ExplainOutput ¶
type ExplainOutput struct {
ServiceID string `json:"serviceId,omitempty"`
Namespace string `json:"namespace,omitempty"`
PolicyName string `json:"policy,omitempty"`
Open bool `json:"open,omitempty"`
DefaultDenyIngress bool `json:"defaultDenyIngress,omitempty"`
DefaultDenyEgress bool `json:"defaultDenyEgress,omitempty"`
Ingress []ExplainRule `json:"ingress,omitempty"`
Egress []ExplainRule `json:"egress,omitempty"`
}
ExplainOutput is the JSON-friendly form rendered by the CLI.
type ExplainRule ¶
ExplainRule is a single human-readable rule entry.
type LocalInstancesTable ¶
type LocalInstancesTable struct {
// contains filtered or unexported fields
}
LocalInstancesTable is a concurrent IP -> InstanceIdentity index.
Records are partitioned by node ID to make Apply / Remove cheap: when a node's local-instances record changes, the table replaces only that node's entries. The query path flattens the partitions behind the lock.
func NewLocalInstancesTable ¶
func NewLocalInstancesTable(localNodeID string) *LocalInstancesTable
NewLocalInstancesTable returns an empty table. localNodeID is the agent's node identity; SameNode(ip) returns true only for IPs owned by that node.
func (*LocalInstancesTable) Apply ¶
func (t *LocalInstancesTable) Apply(li types.LocalInstances) int
Apply replaces the entries for li.NodeID with li.Instances. An empty li.Instances clears the node's entries. Returns the new total entry count for diagnostics.
func (*LocalInstancesTable) Lookup ¶
func (t *LocalInstancesTable) Lookup(ip net.IP) (types.InstanceIdentity, bool)
Lookup returns the identity for ip (and whether it was found). ip is matched as a string — callers should pass ip.String().
func (*LocalInstancesTable) PeerInfoFor ¶
func (t *LocalInstancesTable) PeerInfoFor(ip net.IP) PeerInfo
PeerInfoFor builds a PeerInfo for an inbound source IP, attaching identity + same-node flag from the table.
func (*LocalInstancesTable) Remove ¶
func (t *LocalInstancesTable) Remove(nodeID string) int
Remove drops all entries for nodeID. Idempotent.
func (*LocalInstancesTable) SameNode ¶
func (t *LocalInstancesTable) SameNode(ip net.IP) bool
SameNode reports whether ip belongs to a container hosted on this agent's node.
func (*LocalInstancesTable) Size ¶
func (t *LocalInstancesTable) Size() int
Size returns the total number of entries across all nodes. For metrics.
type PeerInfo ¶
type PeerInfo struct {
// IP is the source IP of the connection. Always present.
IP net.IP
// Identity is the (service, namespace) of the source when the
// peer is a same-node container. Nil for cross-node peers or
// when the agent has no local-instances entry for IP.
Identity *types.InstanceIdentity
// SameNode indicates whether IP belongs to a container on this
// agent's node. Used to gate service-name selectors that cannot
// match cross-node peers in v1.
SameNode bool
}
PeerInfo describes the source of an inbound connection. Same-node peers carry an Identity; cross-node peers only have an IP.
type Reason ¶
type Reason string
Reason is a short, machine-friendly tag summarising why a Decision was reached. It feeds the Prometheus drop counter and the per-drop log line.
const ( ReasonNoPolicy Reason = "no_policy" ReasonAllowedBySvc Reason = "allow_service" ReasonAllowedByNamespace Reason = "allow_namespace" ReasonAllowedBySelector Reason = "allow_selector" ReasonAllowedByCIDR Reason = "allow_cidr" ReasonDeniedNoMatch Reason = "no_matching_rule" ReasonDeniedPort Reason = "port_not_allowed" ReasonDeniedCrossNodeIdent Reason = "cross_node_identity_unresolved" )
type Result ¶
type Result struct {
Decision Decision
Reason Reason
// MatchedRule is the index into the ingress/egress slice that
// produced an Allow decision. -1 when not applicable.
MatchedRule int
}
Result captures both the binary Decision and a Reason for logs + metrics + `rune policy explain` output.