Skip to content

Narrow probe and apiserver exceptions and isolate sentinel-to-game traffic per server #713

Open

Description

@Baylem

The default policies admit more internal traffic than the per-server advertised-port rules suggest. A compromised wake sentinel can connect to every game pod on every port. Broad probe ingress also admits other RFC1918/link-local sources if their egress permits it, while the apiserver exception permits game pods to reach arbitrary private HTTPS services. This request tightens these explicit allowances and corrects comments that imply another policy restricts them.

Inspected 04e41075cafc4c1a99bcb3723b0a8745ee810957 statically; no runtime exploit or CNI connectivity test was run. #514 deliberately preserved and documented the broad apiserver fallback. This is a follow-up hardening change, not a claim that its documentation fix was absent.

Evidence:

Prerequisites and limits: an ordinary compromised game pod remains subject to its own egress rules, so this is not a claim that every game can reach every peer RCON port. Agent and capture mTLS still protect their control endpoints. A compromised sentinel, however, has both matching ingress and egress allowances to other games, including management ports.

Recommended implementation:

  • Provide a strict installation profile requiring actual apiserver endpoint CIDRs and kubelet source CIDRs/probe ports. Validate supplied values and document CNI/NAT-specific address selection. Preserve compatibility through an explicit migration path rather than silently assuming universal endpoint addresses.
  • Replace the two namespace-wide sentinel allowances with controller-owned per-GameServer policies. Existing game and sentinel pods already carry app.kubernetes.io/instance: <server> (sentinel labels). Generate both policy directions using the owning instance and the template's advertised container ports. Example ingress fragment for server example:
podSelector:
  matchLabels:
    app.kubernetes.io/name: gameplane-game
    app.kubernetes.io/instance: example
policyTypes: [Ingress]
ingress:
  - from:
      - podSelector:
          matchLabels:
            app.kubernetes.io/name: gameplane-waker
            app.kubernetes.io/instance: example
    ports:
      - {protocol: TCP, port: 25565}
  • Generate the matching egress half selecting that sentinel and destination game instance. Treat zero advertised ports as no allowance, not an empty unrestricted ports list. Remove old broad policies during upgrade and narrow probe ingress as well.

Acceptance criteria:

  • CI verifies complete destination/source/port tuples, not independent presence of CIDRs and ports.
  • CNI-backed checks allow each sentinel's intended proxy traffic and deny other servers and unadvertised management ports.
  • Heartbeats, game startup, probes, and approved API/operator agent access remain functional.
  • An upgrade removes obsolete broad grants; strict settings and comments describe the effective union of all policies accurately.

Activity

  1. ValgulNecron commented on Oct 20, 2026

    @ValgulNecron
    Collaborator

    Thanks, confirmed on master: the probe and apiserver rules fall back to every RFC1918 and link-local range (charts/gameplane/templates/networkpolicies.yaml:47-73, :184-201), probe ingress has no port limit when probePorts is empty, and the two namespace-wide sentinel policies (:343-385) let any sentinel reach every game pod on every port. You're right that the comments saying the per-server policy narrows this are wrong, since NetworkPolicies are additive.

    Go ahead with a PR, split like this:

    • Sentinel isolation: the module chooses the default. Add per-server sentinel policies generated by the operator (each sentinel to its own game pod, advertised ports only, both directions), and let each module's template declare whether isolation is on by default. A template that doesn't declare it keeps today's behavior, so no existing module or install breaks; modules opt in one by one after they're checked (that part happens in GameplanePanel/module). Because NetworkPolicies add up, the two namespace-wide sentinel policies must then only select game pods whose template has not opted in (for example by a pod label the operator sets), otherwise the per-server policy changes nothing. Zero advertised ports means no allowance, as the game-ingress policy already does (operator/internal/controller/gameserver_controller.go:1078-1079).
    • Probe and apiserver narrowing: opt-in. Add a strict profile that requires kubeletCIDRs, probePorts and apiServerCIDRs and fails validation when they're missing. Keep the current defaults so existing installs don't break, and document how to switch.

    Please fix the misleading comments in networkpolicies.yaml in the same PR.

  2. Baylem commented on Oct 25, 2026

    @Baylem
    MemberAuthor

    Implemented in #763: module opt-in sentinel policies in both directions, exclusion from legacy grants, and an optional strict profile with required endpoint/probe settings. The follow-up also preserves traffic during opt-out and reports zero-port opt-ins before workload creation. Module defaults remain unchanged; module-by-module adoption stays in GameplanePanel/module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions