The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
47 lines
2.1 KiB
YAML
47 lines
2.1 KiB
YAML
# Experiment A-1 — cut the JGroups transport (TCP 7800) while leaving discovery alone.
|
|
#
|
|
# The point is to separate two things that are easy to conflate:
|
|
#
|
|
# discovery how the nodes FIND each other -> PostgreSQL JGROUPS_PING table
|
|
# transport how they actually TALK -> TCP 7800
|
|
#
|
|
# Blocking only the transport produces a state that cannot happen on a single
|
|
# node: both members stay registered in the database, so each believes the other
|
|
# exists, yet no message gets through.
|
|
#
|
|
# kubectl apply -f deploy/lab/k8s/a1-block-jgroups-transport.yaml
|
|
# kubectl -n keycloak-lab delete networkpolicy a1-block-jgroups-transport
|
|
#
|
|
# NetworkPolicy is an ALLOWLIST, not a firewall with deny rules. There is no way
|
|
# to write "deny 7800". The moment a pod is selected by a policy carrying
|
|
# policyTypes: [Ingress], every inbound port is denied unless a rule permits it.
|
|
# So 7800 is blocked by *omission*: 8080 and 9000 are listed, 7800 is not.
|
|
#
|
|
# That makes the two allow rules load-bearing — get them wrong and the experiment
|
|
# measures a dead Keycloak instead of a partitioned cluster:
|
|
#
|
|
# 8080 the HTTP endpoint. Traefik, the other pod's REST calls, and the probe
|
|
# traffic all arrive here.
|
|
# 9000 the management port: /health/started, /health/ready, /health/live and
|
|
# /metrics. Losing it means the kubelet fails the readiness probe and
|
|
# kills the pod — the cluster would break for the wrong reason.
|
|
#
|
|
# Both rules deliberately omit `from:`, which allows those ports from any source.
|
|
# Narrowing the source is not the subject here; the 2-hop experiment already
|
|
# established how to do that by label when it matters.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: a1-block-jgroups-transport
|
|
namespace: keycloak-lab
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: keycloak
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- ports:
|
|
- { port: 8080, protocol: TCP } # HTTP — must stay open
|
|
- { port: 9000, protocol: TCP } # health + metrics — must stay open
|
|
# 7800 is absent on purpose. That is the whole experiment.
|