Files
CosmicClash/deploy/k8s/base/network-policies.yaml
T
Josh Creek d40344a2c0 fix(deploy): ship runnable control-plane and matcher workloads
The Kubernetes base deployed a control-plane image the Dockerfile never
built -- cmd/control-plane was absent from the Go build stage and no
target existed -- while the Dockerfile built a matcher image no manifest
ever deployed. Applying the checked-in base therefore could not produce
the advertised topology: one required workload had no repository-defined
artifact, and nothing consumed queued tickets. Tickets could be created
but never became proposals.

Add the production control-plane build and image target, explicitly not
the testkit-api target, which injects a fake login accepting any ticket.
Add casual and ranked matcher Deployments as separate workloads: they
have different match sizes, and separating them keeps a ranked backlog
from delaying casual formation. One replica each -- CreateProposal's
SKIP LOCKED fences make more replicas safe, but they would halve the
candidate pool each worker sees per poll and worsen formation for no
throughput gain at this scale. Their PDB uses maxUnavailable, since
minAvailable against a single replica blocks node drains outright.

Also fix both blocked traffic directions. No ingress policy admitted
UDP/7777 to game-server pods, so an allocated server was unreachable
from the internet under the namespace-wide default deny. And
control-plane ingress admitted only edge-gateway pods, so roster fetch,
registration, connection receipts, shutdown and result submission from
game servers were dropped even inside the cluster, despite their egress
being permitted. The default deny stays.

Manifest tests now assert every required role is deployed, both
playlists are scheduled, every referenced image maps to a real
Dockerfile target, and both traffic directions are permitted. Each was
verified to fail against the defect it covers. The control-plane image
was built and run to confirm the target works.
2026-09-05 10:52:52 +01:00

262 lines
6.8 KiB
YAML

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress-egress
namespace: cosmic-clash
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: control-plane-allowed-flows
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: control-plane
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app.kubernetes.io/name: edge-gateway
ports:
- protocol: TCP
port: 8080
# Allocated game servers are control-plane clients too: roster fetch,
# registration, connection receipts, shutdown acknowledgement and result
# submission all target this port. Their egress was already permitted, but
# without a matching ingress rule every one of those calls was dropped, so
# no allocated match could complete even inside the cluster.
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: game-server
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: redis
ports:
- protocol: TCP
port: 6379
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: game-server-allowed-egress
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: game-server
policyTypes: [Egress]
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: control-plane
ports:
- protocol: TCP
port: 8080
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allocator-allowed-flows
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: allocator
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- protocol: TCP
port: 9091
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
ports:
- protocol: TCP
port: 5432
# The kubernetes.default Service endpoint is implementation-specific and
# may be a control-plane/node IP that cannot be selected by pod labels.
# Keep API egress portable while limiting it to TLS only.
- ports:
- protocol: TCP
port: 443
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: maintenance-allowed-egress
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: maintenance
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
ports:
- protocol: TCP
port: 5432
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
---
# Public players connect straight to the allocated GameServer's UDP port; the
# control plane only ever hands out its address. The namespace-wide default
# deny blocked that ingress entirely, so an allocated server was unreachable
# from the internet and no matchmade game could be joined.
#
# The source cannot be narrowed by selector: these peers are player machines
# outside the cluster. It is narrowed instead to exactly one protocol and port
# on exactly the game-server pods, and the game server admits a peer only with
# a valid signed join authorisation for its own match.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: game-server-allowed-ingress
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: game-server
policyTypes:
- Ingress
ingress:
- ports:
- protocol: UDP
port: 7777
---
# The matcher reads queued candidates and writes proposals. It exposes nothing
# and talks to nobody but its two datastores.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: matcher-allowed-egress
namespace: cosmic-clash
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: matcher
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app.kubernetes.io/name: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53