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>
50 lines
2.5 KiB
Plaintext
50 lines
2.5 KiB
Plaintext
# 값 타입에 목적지를 직접 넣어 무엇이 통과하는지 본다
|
|
String[] endpoints = {
|
|
"https://169.254.169.254/latest/meta-data/",
|
|
"https://metadata.google.internal/computeMetadata/v1/",
|
|
"https://10.0.0.5:8080/internal",
|
|
"https://192.168.1.1/admin",
|
|
"https://[fd00::1]/ula",
|
|
"https://user:pw@evil.example.com/",
|
|
"http://169.254.169.254/latest/meta-data/",
|
|
"http://evil.example.com/x",
|
|
};
|
|
|
|
통과 https://169.254.169.254/latest/meta-data/
|
|
통과 https://metadata.google.internal/computeMetadata/v1/
|
|
통과 https://10.0.0.5:8080/internal
|
|
통과 https://192.168.1.1/admin
|
|
통과 https://[fd00::1]/ula
|
|
통과 https://user:pw@evil.example.com/
|
|
거절 http://169.254.169.254/latest/meta-data/ -> endpoint must be https outside the loopback interface
|
|
거절 http://evil.example.com/x -> endpoint must be https outside the loopback interface
|
|
|
|
# 이웃 호출처는 같은 결함을 이미 고쳤고, 고치면서 무엇이 남았는지 적었다
|
|
// requireExternallyRoutable, not requireSecureOrLoopback. The scheme check accepted any HTTPS
|
|
// URL, so `https://169.254.169.254/` — the cloud metadata service — and every RFC 1918 address
|
|
// passed. The stronger guard was written for exactly this call site and then called from
|
|
// nowhere: it existed, its own tests were green, and the two sites it was written for kept the
|
|
// weaker check.
|
|
|
|
# 그 엔드포인트가 가는 곳
|
|
113: return new NotificationHttpRequest(
|
|
114- "POST",
|
|
115- subscription.endpoint(),
|
|
116- JdkNotificationHttpGateway.headers(headers),
|
|
|
|
# 그 값을 만드는 main 코드 — 저장이 아니라 저장된 것을 되읽는 경로다
|
|
AesGcmContactPointProtector.java:217: return new WebPushSubscriptionValue(
|
|
64: public ProtectedContactPoint protect(ContactPointValue value) {
|
|
86: public ContactPointValue reveal(ProtectedContactPoint protectedValue, AccessContext context) {
|
|
210: private static WebPushSubscriptionValue parseWebPush(String stored) {
|
|
# 접수 유스케이스는 그 채널을 주소 문자열로 받지 않는다
|
|
case EMAIL -> EmailAddress.parse(address);
|
|
case SMS -> new PhoneNumber(address);
|
|
case IN_APP -> new InAppRecipientRef(address);
|
|
case PUSH, WEB_PUSH, WEBHOOK ->
|
|
throw new IllegalArgumentException(
|
|
"channel "
|
|
+ channel
|
|
# 알림 플랫폼 마스터 스위치의 출하 기본값
|
|
486: enabled: ${APP_NOTIFICATION_PLATFORM_ENABLED:false}
|