Motivation
Custom auth.conf rules can currently only be managed inline via Config.Spec.PuppetServer.AuthorizationRules[] (api/v1alpha1/config_types.go:121). This works well for single-team setups but has limits:
- All rules live on one Config object → merge conflicts under GitOps when multiple teams/modules contribute rules.
- No RBAC separation: managing auth.conf rules requires edit rights on the entire
Config.
- No independent reuse/lifecycle per rule set.
The operator already has an established pattern for "discrete policy fragments aggregated into a rendered file": NodeClassifier, ReportProcessor, SigningPolicy. A standalone AuthorizationRule CRD fits right in.
Proposal
A new, additive AuthorizationRule CRD modeled on ReportProcessor — the existing inline field stays as-is.
type AuthorizationRuleSpec struct {
ConfigRef string `json:"configRef"` // back-reference, analogous to ReportProcessor.Spec.ConfigRef
AuthorizationRule `json:",inline"` // existing struct: Name, MatchRequest, Allow, AllowUnauthenticated, Deny, SortOrder
}
Implementation sketch
- Binding: back-reference via
Spec.ConfigRef (consistent with ReportProcessor/SigningPolicy).
- Discovery/watch:
findAuthorizationRules(configRef) analogous to internal/controller/config_reports.go:23; enqueue via enqueueConfigsForAuthorizationRule analogous to config_reports.go:254.
- Rendering: inline rules plus the CRD objects found by
configRef go into one combined list, sorted globally by SortOrder, rendered in renderAuthConf() (config_rendering.go:248) between builtinAuthRules() and the final deny \"*\" rule. Both sources share the existing AuthorizationRule struct (no type duplication).
- Rollout: no new mount/secret needed — auth.conf already lives in the Config ConfigMap, whose hash annotation rolls the Server pods. The extra watch source only triggers a re-render.
Invariants
- Built-in rules and the trailing
deny \"*\" stay untouchable — CRD rules are only inserted between them.
allowUnauthenticated gets no special handling for now and is left fully to RBAC — consistent with today's inline behavior. Protection is enforced via RBAC on the authorizationrules resource.
Open questions
- Surface
SortOrder collisions (same order + path) in the CRD Status, but don't hard-reject them.
- Decide whether
SortOrder == 0 still means "unset → 500" (config_rendering.go:273) or whether a pointer field should distinguish "intentionally 0" from "unset".
Scope
Motivation
Custom
auth.confrules can currently only be managed inline viaConfig.Spec.PuppetServer.AuthorizationRules[](api/v1alpha1/config_types.go:121). This works well for single-team setups but has limits:Config.The operator already has an established pattern for "discrete policy fragments aggregated into a rendered file":
NodeClassifier,ReportProcessor,SigningPolicy. A standaloneAuthorizationRuleCRD fits right in.Proposal
A new, additive
AuthorizationRuleCRD modeled onReportProcessor— the existing inline field stays as-is.Implementation sketch
Spec.ConfigRef(consistent withReportProcessor/SigningPolicy).findAuthorizationRules(configRef)analogous tointernal/controller/config_reports.go:23; enqueue viaenqueueConfigsForAuthorizationRuleanalogous toconfig_reports.go:254.configRefgo into one combined list, sorted globally bySortOrder, rendered inrenderAuthConf()(config_rendering.go:248) betweenbuiltinAuthRules()and the finaldeny \"*\"rule. Both sources share the existingAuthorizationRulestruct (no type duplication).Invariants
deny \"*\"stay untouchable — CRD rules are only inserted between them.allowUnauthenticatedgets no special handling for now and is left fully to RBAC — consistent with today's inline behavior. Protection is enforced via RBAC on theauthorizationrulesresource.Open questions
SortOrdercollisions (same order + path) in the CRDStatus, but don't hard-reject them.SortOrder == 0still means "unset → 500" (config_rendering.go:273) or whether a pointer field should distinguish "intentionally 0" from "unset".Scope
AuthorizationRuleCRD type (+ deepcopy, CRD manifest, RBAC)renderAuthConf()AuthorizationRule→Config