[management] Configure per-target HTTP access actions (#8116)

Let HTTP targets inherit service authentication, bypass it, or block access. Validate and persist the action, expose it through the API, and deliver it to the proxy. Private services reject bypass.

Validation: service/manager, store, and management-to-proxy integration race tests passed; scoped lint passed.
This commit is contained in:
Maycon Santos
2026-10-12 12:56:17 +02:00
committed by GitHub
parent 1161616c02
commit cd22a9d4f1
10 changed files with 1111 additions and 56 deletions
@@ -339,6 +339,9 @@ func (m *Manager) persistNewService(ctx context.Context, accountID string, svc *
}
return m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
if err := validateTargetAccessControl(svc); err != nil {
return err
}
if err := m.validateServiceDomain(ctx, transaction, accountID, svc, svc.ProxyCluster); err != nil {
return err
}
@@ -708,6 +711,12 @@ func (m *Manager) executeServiceUpdate(ctx context.Context, transaction store.St
}
m.preserveExistingAuthSecrets(service, existingService)
if err := preserveTargetAccessActions(service, existingService); err != nil {
return err
}
if err := validateTargetAccessControl(service); err != nil {
return err
}
if err := validateHeaderAuthValues(service.Auth.HeaderAuths); err != nil {
return err
}
@@ -745,6 +754,68 @@ func (m *Manager) validateServiceDomain(ctx context.Context, tx store.Store, acc
return m.clusterDeriver.ValidateServiceDomain(ctx, tx, accountID, svc.Domain, cluster)
}
func validateTargetAccessControl(svc *service.Service) error {
if !svc.HasTargetAccessControl() {
return nil
}
// Legacy requests may acquire an existing action during the update merge.
if err := svc.Validate(); err != nil {
return status.Errorf(status.InvalidArgument, "%s", err)
}
return nil
}
func preserveTargetAccessActions(updated, existing *service.Service) error {
if !existing.HasTargetAccessControl() {
return nil
}
if slices.ContainsFunc(updated.Targets, func(target *service.Target) bool {
return target == nil
}) {
return status.Errorf(status.InvalidArgument, "target must not be nil")
}
if len(updated.Targets) > 0 && !slices.ContainsFunc(updated.Targets, func(target *service.Target) bool {
return !target.AccessActionProvided
}) {
return nil
}
byPath := make(map[string]*service.Target, len(existing.Targets))
for _, target := range existing.Targets {
location := effectiveTargetPath(target)
if _, duplicate := byPath[location]; duplicate {
return status.Errorf(status.InvalidArgument,
"cannot preserve target access control for duplicate location %q", location)
}
byPath[location] = target
}
actionsProvided := false
for _, target := range updated.Targets {
actionsProvided = actionsProvided || target.AccessActionProvided
location := effectiveTargetPath(target)
if previous := byPath[location]; previous != nil && !target.AccessActionProvided {
target.AccessAction = previous.AccessAction
}
delete(byPath, location)
}
if actionsProvided {
return nil
}
for location, target := range byPath {
if target.AccessAction != "" && target.AccessAction != service.TargetAccessActionInherit {
return status.Errorf(status.InvalidArgument,
"include access_action when removing or changing controlled target location %q", location)
}
}
return nil
}
func effectiveTargetPath(target *service.Target) string {
if target.Path == nil || *target.Path == "" {
return "/"
}
return *target.Path
}
// validateL4PortDiffOnClusterDiff checks if custom L4 ports are configured and validates port changes across clusters.
// It ensures no port changes if custom ports are unsupported for a given cluster and protocol mode.
// Returns an error if validation fails, otherwise returns nil.