Documentation
¶
Overview ¶
Package policy is the authored policy domain: services, address groups, and rulesets as operators write them, the admission rules every mutation passes before it is persisted, and the persistence interface the store implements (ADR-0018).
The authored model mirrors the authored layer of the wire contract (ADR-0015) with one addition the contract does not need on the wire: a rule may permit services by reference to a named service definition as well as inline, and the renderer expands the reference. Admission is where v1's inbound-only scope is enforced (ADR-0010): an outbound rule is rejected here with a precise error, and nothing downstream ever sees one.
Index ¶
- Constants
- Variables
- func AssignIDs(rs *Ruleset)
- func CheckLabelKey(key string) error
- func CheckLabelValue(value string) error
- func DirectionName(d innerwallv1.Direction) string
- func FormatEntrySpec(e ServiceEntry) string
- func FormatPortSpec(p PortRange) string
- func FormatVersion(v int64) string
- func MatchWorkloads(s Selector, index map[identity.WorkloadID]map[string]string) []identity.WorkloadID
- func ModeName(m innerwallv1.EnforcementMode) string
- func ParseDirection(s string) (innerwallv1.Direction, error)
- func ParseMode(s string) (innerwallv1.EnforcementMode, error)
- func ParseProtocol(s string) (innerwallv1.Protocol, error)
- func ProtocolName(p innerwallv1.Protocol) string
- func ResolveID(s string, byName map[string]uuid.UUID) (uuid.UUID, bool)
- func RuleEqual(a, b *Rule) bool
- func ValidateAddressGroup(g *AddressGroup) error
- func ValidateLabelSet(labels []LabelPair) error
- func ValidateRule(r *Rule, refs References) error
- func ValidateRuleset(rs *Ruleset, refs References) error
- func ValidateSelector(s Selector) error
- func ValidateService(s *Service) error
- type AddressGroup
- type Authoring
- func (a *Authoring) CreateAddressGroup(ctx context.Context, g *AddressGroup) error
- func (a *Authoring) CreateRule(ctx context.Context, rulesetID uuid.UUID, r *Rule) error
- func (a *Authoring) CreateRuleset(ctx context.Context, rs *Ruleset) error
- func (a *Authoring) CreateService(ctx context.Context, s *Service) error
- func (a *Authoring) DeleteAddressGroup(ctx context.Context, id uuid.UUID, expect string) error
- func (a *Authoring) DeleteRule(ctx context.Context, rulesetID, ruleID uuid.UUID, expect string) error
- func (a *Authoring) DeleteRuleset(ctx context.Context, id uuid.UUID, expect string) error
- func (a *Authoring) DeleteService(ctx context.Context, id uuid.UUID, expect string) error
- func (a *Authoring) References(ctx context.Context) (References, error)
- func (a *Authoring) UpdateAddressGroup(ctx context.Context, g *AddressGroup, expect string) error
- func (a *Authoring) UpdateRule(ctx context.Context, rulesetID uuid.UUID, r *Rule, expect string) error
- func (a *Authoring) UpdateRuleset(ctx context.Context, rs *Ruleset, expect string) error
- func (a *Authoring) UpdateService(ctx context.Context, s *Service, expect string) error
- type EntryDoc
- type Findings
- type LabelPair
- type Names
- type Peer
- type PeerDoc
- type PeerKind
- type PortRange
- type References
- type Renderer
- type Rule
- type RuleDoc
- type Ruleset
- type RulesetDoc
- type Selector
- type Service
- type ServiceDoc
- type ServiceEntry
- type Store
- type ValidationError
- type VersionMismatchError
Constants ¶
const MaxLabelLength = 63
MaxLabelLength bounds a label key and a label value, in bytes.
Variables ¶
var ( // ErrBadLabelKey is a key outside the grammar. ErrBadLabelKey = errors.New("policy: label key must be 1 to 63 letters, digits, '.', '_', '-', or '/', beginning and ending with a letter or digit") // ErrBadLabelValue is a value outside the grammar. ErrBadLabelValue = errors.New("policy: label value must be 1 to 63 letters, digits, '.', '_', or '-', beginning and ending with a letter or digit") // ErrDuplicateLabelKey is a key given twice in one label set. ErrDuplicateLabelKey = errors.New("policy: label key appears more than once") )
var ( ErrServiceUnknown = errors.New("policy: service does not exist") ErrAddressGroupUnknown = errors.New("policy: address group does not exist") ErrRulesetUnknown = errors.New("policy: ruleset does not exist") ErrRuleUnknown = errors.New("policy: rule does not exist in this ruleset") ErrInUse = errors.New("policy: object is referenced by a rule and cannot be deleted") ErrDuplicateName = errors.New("policy: an object of this kind already has that name") ErrDuplicateRuleID = errors.New("policy: a rule with that id already exists") )
Store errors.
var ( ErrDirectionOutbound = errors.New("policy: outbound rules are not admitted in this version; only DIRECTION_INBOUND is compiled (ADR-0010)") ErrDirectionUnspecified = errors.New("policy: rule direction must be stated; DIRECTION_UNSPECIFIED is not a direction") ErrBadDirection = errors.New("policy: direction must be inbound or outbound") ErrEmptySelector = errors.New("policy: selector is empty; an empty selector matches nothing, so state the breadth explicitly") ErrEmptyLabelKey = errors.New("policy: label key must not be empty") ErrEmptyLabelValues = errors.New("policy: label match lists no values; a match with no values can never be satisfied") ErrBadPortRange = errors.New("policy: port range must satisfy start <= end <= 65535") ErrBadPortSpec = errors.New("policy: port must be a number or a start-end range") ErrPortsOnPortless = errors.New("policy: protocol has no ports; list none") ErrBadProtocol = errors.New("policy: protocol must be tcp, udp, or icmp") ErrBadCIDR = errors.New("policy: address is not a valid CIDR") ErrUnknownService = errors.New("policy: rule references a service that does not exist") ErrUnknownAddressGroup = errors.New("policy: rule references an address group that does not exist") ErrBadPeer = errors.New("policy: peer must be exactly one of a workload selector, an address group, or a CIDR") ErrNoPeers = errors.New("policy: rule lists no peers; a rule with no peers permits nothing") ErrNoServices = errors.New("policy: rule lists no services; a rule with no services permits nothing") ErrNoEntries = errors.New("policy: service defines no entries") ErrNoCIDRs = errors.New("policy: address group lists no addresses") ErrEmptyName = errors.New("policy: name must not be empty") ErrBadID = errors.New("policy: id is not a UUID") ErrDuplicateRule = errors.New("policy: a rule id appears more than once in the ruleset") )
Admission errors. Each is wrapped in a *ValidationError that names the offending field, so callers can test the kind with errors.Is and show the operator exactly what to fix.
var ErrNoDocument = errors.New("policy: a ruleset document is required")
ErrNoDocument is returned when a command expects a document and none is given.
var ErrVersionMismatch = errors.New("policy: the object has changed since it was read")
ErrVersionMismatch is returned by a conditional write whose expected version is not the object's current one. The error carrying it is a *VersionMismatchError naming the current version.
Functions ¶
func AssignIDs ¶
func AssignIDs(rs *Ruleset)
AssignIDs gives a ruleset that has no id a fresh one, and does the same for each of its rules, as a create does. A dry run calls it too, so a rule authored without an id renders as the rule saving it would create.
func CheckLabelKey ¶ added in v0.2.0
CheckLabelKey admits a label key: ErrEmptyLabelKey when it is empty, ErrBadLabelKey when it is outside the grammar, nil otherwise.
func CheckLabelValue ¶ added in v0.2.0
CheckLabelValue admits a label value: ErrBadLabelValue when it is empty or outside the grammar, nil otherwise.
func DirectionName ¶
func DirectionName(d innerwallv1.Direction) string
DirectionName is the inverse of ParseDirection.
func FormatEntrySpec ¶
func FormatEntrySpec(e ServiceEntry) string
FormatEntrySpec is the inverse of EntrySpecDoc.
func FormatPortSpec ¶
FormatPortSpec is the inverse of ParsePortSpec.
func FormatVersion ¶
FormatVersion returns the token for the stored version v.
func MatchWorkloads ¶
func MatchWorkloads(s Selector, index map[identity.WorkloadID]map[string]string) []identity.WorkloadID
MatchWorkloads resolves a selector against an index of every workload's current labels and returns the ids it matches, sorted. It is the one scope resolution: the renderer's scope match (Matches) applied to the registry, which the read model's scoped reads, the selector preview, and the bulk mode change all call, so a preview and the change it precedes cannot resolve differently.
func ModeName ¶
func ModeName(m innerwallv1.EnforcementMode) string
ModeName is the inverse of ParseMode.
func ParseDirection ¶
func ParseDirection(s string) (innerwallv1.Direction, error)
ParseDirection parses a direction name.
func ParseMode ¶
func ParseMode(s string) (innerwallv1.EnforcementMode, error)
ParseMode parses an enforcement mode name.
func ParseProtocol ¶
func ParseProtocol(s string) (innerwallv1.Protocol, error)
ParseProtocol parses a protocol name.
func ProtocolName ¶
func ProtocolName(p innerwallv1.Protocol) string
ProtocolName is the inverse of ParseProtocol.
func RuleEqual ¶
RuleEqual reports whether two rules say the same thing: same direction, enablement, description, peers, services, and entries, in order. Timestamps and versions are not compared; they are a consequence of content changing.
func ValidateAddressGroup ¶
func ValidateAddressGroup(g *AddressGroup) error
ValidateAddressGroup admits an address group. CIDRs are checked to parse; the store persists them in canonical masked form.
func ValidateLabelSet ¶ added in v0.2.0
ValidateLabelSet admits a label set (a token's, or a workload's whole set in a label edit): every key and value in the grammar, no key twice. Findings are located at labels[i] in the order given, with the offending text quoted so whitespace in it shows.
func ValidateRule ¶
func ValidateRule(r *Rule, refs References) error
ValidateRule admits one rule on its own, reporting paths relative to the rule.
func ValidateRuleset ¶
func ValidateRuleset(rs *Ruleset, refs References) error
ValidateRuleset admits a ruleset: direction (inbound only), selectors (never empty), peers (well-formed, references resolve), services (references resolve, ranges valid), rule ids (distinct). It checks everything it can about a ruleset in isolation plus the references it is given; uniqueness of the name is the store's constraint. A ruleset converted from a document carries what the conversion could not read, and those faults are reported here in the same pass as everything else, so a name that resolves to nothing never hides a bad port range.
func ValidateSelector ¶
ValidateSelector admits a selector on its own: never empty, every key and value in the label grammar, every key with at least one value.
func ValidateService ¶
ValidateService admits a service definition and normalizes nothing: what is persisted is what was validated. A service converted from a document carries what the conversion could not read, reported here in the same pass as everything else.
Types ¶
type AddressGroup ¶
type AddressGroup struct {
ID uuid.UUID
Name string
CIDRs []string
CreatedAt time.Time
UpdatedAt time.Time
// Version is the object's write count: 1 when created, advanced by
// the store with every write.
Version int64
}
AddressGroup is a named set of CIDRs for peers that are not managed workloads.
type Authoring ¶
type Authoring struct {
Store Store
Renderer Renderer
// Now is the clock; time.Now if nil.
Now func() time.Time
}
Authoring is the mutation surface for the authored model: admission, then persistence, then rendering. The command line and the operator surface go through it and nothing else writes the authored tables; admission runs here, so the two transports cannot diverge on what is admitted.
Updates and deletes take the version the caller last read (expect) and refuse with a *VersionMismatchError when the object has moved; an empty expect writes unconditionally, which is the command line's default.
func (*Authoring) CreateAddressGroup ¶
func (a *Authoring) CreateAddressGroup(ctx context.Context, g *AddressGroup) error
CreateAddressGroup admits and persists a new address group, assigning its id and canonicalizing its CIDRs.
func (*Authoring) CreateRule ¶
CreateRule adds one rule to a ruleset. The ruleset is written as a unit, conditioned on the version it was read at, so a concurrent edit of the same ruleset is refused rather than overwritten. Findings are reported relative to the rule.
func (*Authoring) CreateRuleset ¶
CreateRuleset admits and persists a new ruleset, assigning ids to it and to any rule that has none. Every rule is created now.
func (*Authoring) CreateService ¶
CreateService admits and persists a new service, assigning its id.
func (*Authoring) DeleteAddressGroup ¶
DeleteAddressGroup removes an address group no rule references.
func (*Authoring) DeleteRule ¶
func (a *Authoring) DeleteRule(ctx context.Context, rulesetID, ruleID uuid.UUID, expect string) error
DeleteRule removes one rule from a ruleset. expect is the rule's own version.
func (*Authoring) DeleteRuleset ¶
DeleteRuleset removes a ruleset and its rules.
func (*Authoring) DeleteService ¶
DeleteService removes a service no rule references.
func (*Authoring) References ¶
func (a *Authoring) References(ctx context.Context) (References, error)
References loads the ids admission resolves against.
func (*Authoring) UpdateAddressGroup ¶
UpdateAddressGroup admits and persists a changed address group.
func (*Authoring) UpdateRule ¶
func (a *Authoring) UpdateRule(ctx context.Context, rulesetID uuid.UUID, r *Rule, expect string) error
UpdateRule replaces one rule of a ruleset. expect is the rule's own version; a stale one is refused with the rule's current version.
func (*Authoring) UpdateRuleset ¶
UpdateRuleset admits and persists a changed ruleset. Rules that keep their id keep their provenance across the edit: their CreatedAt is carried from the persisted rule, and their UpdatedAt and Version advance only when the rule itself changed. Rules without an id are new.
type EntryDoc ¶
EntryDoc is the JSON form of a service entry: a protocol name and port specs such as "5432" or "6000-6010". No ports means every port.
func EntrySpecDoc ¶ added in v0.2.0
EntrySpecDoc reads the command-line entry form "tcp:5432,6000-6010" or "icmp" (no ports) into its document form, unparsed: the protocol and each port spec are read by ServiceFromDoc, so a command-line entry's faults reach admission the way a document's do.
func EntryToDoc ¶
func EntryToDoc(e ServiceEntry) EntryDoc
EntryToDoc is the document form of one entry.
type Findings ¶
type Findings struct {
Errors []*ValidationError
// contains filtered or unexported fields
}
Findings is every admission failure of one object, in document order. Admission reports all of them at once so an editor can mark every field that needs attention; errors.Is and errors.As see through to each one.
func AsFindings ¶
AsFindings returns the findings an error carries, or nil when it is not an admission failure.
type Names ¶
type Names struct {
ServiceByName map[string]uuid.UUID
ServiceName map[uuid.UUID]string
AddressGroupByName map[string]uuid.UUID
AddressGroupName map[uuid.UUID]string
}
Names resolves the names a document may use to ids and back.
func NewNames ¶
func NewNames(services []Service, groups []AddressGroup) Names
NewNames indexes services and address groups for document conversion.
type Peer ¶
Peer is the remote end of a rule: managed workloads by selector, a named address group, or a literal CIDR. Exactly one is set.
type PeerDoc ¶
type PeerDoc struct {
Workloads map[string][]string `json:"workloads,omitempty"`
AddressGroup string `json:"address_group,omitempty"`
CIDR string `json:"cidr,omitempty"`
}
PeerDoc is the JSON form of a peer; exactly one field is set.
type PortRange ¶
PortRange is an inclusive port range; a single port has Start == End.
func ParsePortSpec ¶
ParsePortSpec parses "80" or "8000-8010" into an inclusive range. Bounds are checked by admission, not here, so a bad range reports precisely.
type References ¶
References is what admission needs to know about the rest of the authored model: which services and address groups exist.
type Renderer ¶
Renderer is what runs after every admitted mutation: it re-renders every workload's policy, persists the ones whose rendered output changed, and lets the streams push them (ADR-0018). The authoring service holds it so a mutation can never be persisted without triggering it.
type Rule ¶
type Rule struct {
ID uuid.UUID
Direction innerwallv1.Direction
Enabled bool
Description string
Peers []Peer
ServiceIDs []uuid.UUID
Entries []ServiceEntry
CreatedAt time.Time
UpdatedAt time.Time
Version int64
// contains filtered or unexported fields
}
Rule is a single authored rule. The ruleset's scope is the local end, Peers the remote end, Direction orients the two. Services may be named by reference (ServiceIDs) or written inline (Entries); the renderer expands both into the same rendered form. CreatedAt, UpdatedAt, and Version are the rule's own: a rule that keeps its id across an edit of its ruleset keeps its CreatedAt, and its UpdatedAt and Version advance only when the rule itself changed.
func RuleFromDoc ¶
RuleFromDoc converts one rule document; what could not be read is carried to admission at paths relative to the rule.
type RuleDoc ¶
type RuleDoc struct {
ID string `json:"id,omitempty"`
Direction string `json:"direction"`
Enabled *bool `json:"enabled,omitempty"`
Description string `json:"description,omitempty"`
Peers []PeerDoc `json:"peers"`
Services []string `json:"services,omitempty"`
Entries []EntryDoc `json:"entries,omitempty"`
Version string `json:"version,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
RuleDoc is the JSON form of a rule. Services and address groups may be named by name or id; Entries are inline protocol/port specs. Version, CreatedAt, and UpdatedAt are the rule's own, written by the control plane and ignored on the way in.
type Ruleset ¶
type Ruleset struct {
ID uuid.UUID
Name string
Description string
Enabled bool
Scope Selector
Rules []Rule
CreatedAt time.Time
UpdatedAt time.Time
// Version is the ruleset's write count: 1 when created, advanced by
// the store with every write, including a write of one of its rules.
Version int64
// contains filtered or unexported fields
}
Ruleset is a named set of rules applied to the workloads its scope selects.
func DecodeRuleset ¶
DecodeRuleset parses a JSON document into a ruleset, resolving names. It does not admit; admission does, with the store's view of what exists. What the document itself gets wrong (an id that is not a UUID, a name that resolves to nothing, a port that is not a number) travels with the ruleset, and admission reports it at the offending path in the same pass, and the same shape, as everything else it finds. The error is only for a body that is not a ruleset document at all.
func RulesetFromDoc ¶
func RulesetFromDoc(doc *RulesetDoc, names Names) *Ruleset
RulesetFromDoc converts a parsed document. Every element of the document has its place in the result, readable or not, so the paths admission reports name the document's own elements; what could not be read is carried to admission as faults (see DecodeRuleset).
type RulesetDoc ¶
type RulesetDoc struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
Scope map[string][]string `json:"scope"`
Rules []RuleDoc `json:"rules"`
Version string `json:"version,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
RulesetDoc is the JSON form of a ruleset. Version, CreatedAt, and UpdatedAt are written by the control plane and ignored on the way in: a document that echoes them back is accepted, and the version it names is carried by the write's condition, not by the document.
func RulesetToDoc ¶
func RulesetToDoc(rs *Ruleset, names Names) *RulesetDoc
RulesetToDoc converts a ruleset to its document form, using names where they resolve and ids otherwise.
type Selector ¶
Selector matches workloads by label: every key must match (AND) and the label's value must be one of the listed values (OR). An empty selector matches nothing.
type Service ¶
type Service struct {
ID uuid.UUID
Name string
Entries []ServiceEntry
CreatedAt time.Time
UpdatedAt time.Time
// Version is the object's write count: 1 when created, advanced by
// the store with every write.
Version int64
// contains filtered or unexported fields
}
Service is a named, reusable set of entries.
func ServiceFromDoc ¶ added in v0.2.0
func ServiceFromDoc(doc *ServiceDoc) *Service
ServiceFromDoc converts a service document. Like a ruleset's, every entry and port keeps its place, readable or not, and what could not be read travels with the service to admission: ValidateService reports it beside its own findings in one pass, so a port that is not a number never hides an empty name.
type ServiceDoc ¶ added in v0.2.0
ServiceDoc is a service definition as a client writes it.
type ServiceEntry ¶
type ServiceEntry struct {
Protocol innerwallv1.Protocol
Ports []PortRange
}
ServiceEntry is one protocol with the ports it permits. No ports means every port of the protocol, which is the only valid form for a protocol without ports.
type Store ¶
type Store interface {
CreateService(ctx context.Context, s *Service) error
UpdateService(ctx context.Context, s *Service, expect string) error
// DeleteService returns ErrInUse when a rule references the service.
DeleteService(ctx context.Context, id uuid.UUID, expect string) error
GetService(ctx context.Context, id uuid.UUID) (*Service, error)
ListServices(ctx context.Context) ([]Service, error)
CreateAddressGroup(ctx context.Context, g *AddressGroup) error
UpdateAddressGroup(ctx context.Context, g *AddressGroup, expect string) error
// DeleteAddressGroup returns ErrInUse when a rule references the group.
DeleteAddressGroup(ctx context.Context, id uuid.UUID, expect string) error
GetAddressGroup(ctx context.Context, id uuid.UUID) (*AddressGroup, error)
ListAddressGroups(ctx context.Context) ([]AddressGroup, error)
CreateRuleset(ctx context.Context, rs *Ruleset) error
// UpdateRuleset replaces the ruleset and all of its rules, persisting
// each rule's timestamps as given.
UpdateRuleset(ctx context.Context, rs *Ruleset, expect string) error
DeleteRuleset(ctx context.Context, id uuid.UUID, expect string) error
GetRuleset(ctx context.Context, id uuid.UUID) (*Ruleset, error)
ListRulesets(ctx context.Context) ([]Ruleset, error)
}
Store is the persistence the authored model needs. Every method is implemented with hand-written SQL in internal/store (ADR-0006). Writes of a composite object (a service with its entries, a ruleset with its rules) are atomic: the object is either fully persisted or not at all.
Updates and deletes are conditional: expect is the version token the caller last read (FormatVersion of its Version), compared byte-exact against the stored version's form, and the write applies only when the object still holds it, refusing otherwise with a *VersionMismatchError that names the current version. An empty expect writes unconditionally. The check is made inside the write, so two callers holding the same version cannot both succeed. Every update advances the stored version by one and sets the new value on the object it was given.
type ValidationError ¶
type ValidationError struct {
// Path locates the failure, e.g. "rules[1].peers[0].cidr".
Path string
// Err is one of the admission errors above.
Err error
// Detail is the offending value or a specific explanation, if any.
Detail string
}
ValidationError is an admission failure at a named location in the authored object.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Message ¶
func (e *ValidationError) Message() string
Message is the finding for a person: the rule's explanation with the offending value, without the package prefix a log line carries.
func (*ValidationError) Rule ¶
func (e *ValidationError) Rule() string
Rule is the stable name of the admission rule the finding violates.
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
type VersionMismatchError ¶
type VersionMismatchError struct {
Current string
}
VersionMismatchError is a refused conditional write with the version the object holds now, so the caller can re-read and decide.
func (*VersionMismatchError) Error ¶
func (e *VersionMismatchError) Error() string
func (*VersionMismatchError) Unwrap ¶
func (e *VersionMismatchError) Unwrap() error