From 98a74e90a5321116334f5227f9f891129ed263c8 Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Fri, 4 Sep 2026 16:18:33 +0900 Subject: [PATCH] docs: fill the untested items and record why the B layer has no graphs The forward upgrade to 26.7.3 was zero downtime across 87 samples, and since databasechangelog stayed at 210 the rollback to 26.7.0 also succeeded, which narrows D-2's conclusion: rolling back fails when the schema moved, not because of the version number. The row count is the check. Role changes never reach the upstream through request repetition; the session is a snapshot taken at login and only a new session picks up the new claim. Auditing the docs also surfaced that Prometheus scrapes only keycloak, kubelet, node-exporter and itself, so the B-layer experiments have no metrics to screenshot rather than missing screenshots. Co-Authored-By: Claude Opus 5 --- .../console-2026-09-04T06-25-14-829Z.log | 2 + .../console-2026-09-04T06-25-42-707Z.log | 1 + .../console-2026-09-04T06-26-41-840Z.log | 1 + .../console-2026-09-04T07-14-56-538Z.log | 8 + .../console-2026-09-04T07-15-33-300Z.log | 10 + .../console-2026-09-04T07-16-34-745Z.log | 14 + .../page-2026-09-04T06-25-15-925Z.yml | 16 ++ .../page-2026-09-04T06-25-26-485Z.yml | 16 ++ .../page-2026-09-04T06-25-35-208Z.yml | 17 ++ .../page-2026-09-04T06-25-42-934Z.yml | 1 + .../page-2026-09-04T06-26-42-277Z.yml | 1 + .../page-2026-09-04T07-14-59-023Z.yml | 175 ++++++++++++ .../page-2026-09-04T07-15-35-378Z.yml | 56 ++++ .../page-2026-09-04T07-16-36-528Z.yml | 29 ++ docs/diagrams/_style.md | 12 + docs/diagrams/a2-database-loss.svg | 26 ++ docs/diagrams/a7-volatile-inversion.svg | 36 +++ docs/diagrams/b1-store-split.svg | 33 +++ docs/diagrams/b3-refresh-contention.svg | 29 ++ docs/diagrams/b4-header-forgery.svg | 37 +++ docs/diagrams/b5-redis-loss.svg | 35 +++ docs/diagrams/b6-key-rotation.svg | 34 +++ docs/diagrams/c1-sso-structure.svg | 31 +++ docs/diagrams/d1-backup-restore.svg | 30 +++ docs/diagrams/d3-secret-exposure.svg | 31 +++ docs/diagrams/d4-cert-chain.svg | 30 +++ docs/diagrams/lab-topology.svg | 57 ++++ .../d2-version-upgrade/d2-upgrade-window.png | Bin 0 -> 107782 bytes .../followup/01-d2-forward-upgrade.txt | 32 +++ .../followup/02-d2-rollback-same-schema.txt | 20 ++ .../followup/03-b4-role-propagation.txt | 9 + .../followup/04-observability-gap.txt | 17 ++ docs/evidence/followup/README.md | 17 ++ docs/experiment-a2-database-loss.md | 9 + docs/experiment-a7-volatile-comparison.md | 8 + docs/experiment-b1-redis-session-store.md | 9 + .../experiment-b3-refresh-token-contention.md | 9 + .../experiment-b4-edge-authorization-scope.md | 9 + docs/experiment-b5-redis-loss-persistence.md | 9 + docs/experiment-b6-key-rotation.md | 9 + docs/experiment-c1-multi-app-sso.md | 9 + docs/experiment-d1-backup-restore.md | 9 + docs/experiment-d3-secret-management.md | 9 + docs/experiment-d4-certificate-renewal.md | 9 + docs/experiment-followup-untested-items.md | 249 ++++++++++++++++++ 45 files changed, 1210 insertions(+) create mode 100644 .playwright-mcp/console-2026-09-04T06-25-14-829Z.log create mode 100644 .playwright-mcp/console-2026-09-04T06-25-42-707Z.log create mode 100644 .playwright-mcp/console-2026-09-04T06-26-41-840Z.log create mode 100644 .playwright-mcp/console-2026-09-04T07-14-56-538Z.log create mode 100644 .playwright-mcp/console-2026-09-04T07-15-33-300Z.log create mode 100644 .playwright-mcp/console-2026-09-04T07-16-34-745Z.log create mode 100644 .playwright-mcp/page-2026-09-04T06-25-15-925Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T06-25-26-485Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T06-25-35-208Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T06-25-42-934Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T06-26-42-277Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T07-14-59-023Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T07-15-35-378Z.yml create mode 100644 .playwright-mcp/page-2026-09-04T07-16-36-528Z.yml create mode 100644 docs/diagrams/_style.md create mode 100644 docs/diagrams/a2-database-loss.svg create mode 100644 docs/diagrams/a7-volatile-inversion.svg create mode 100644 docs/diagrams/b1-store-split.svg create mode 100644 docs/diagrams/b3-refresh-contention.svg create mode 100644 docs/diagrams/b4-header-forgery.svg create mode 100644 docs/diagrams/b5-redis-loss.svg create mode 100644 docs/diagrams/b6-key-rotation.svg create mode 100644 docs/diagrams/c1-sso-structure.svg create mode 100644 docs/diagrams/d1-backup-restore.svg create mode 100644 docs/diagrams/d3-secret-exposure.svg create mode 100644 docs/diagrams/d4-cert-chain.svg create mode 100644 docs/diagrams/lab-topology.svg create mode 100644 docs/evidence/d2-version-upgrade/d2-upgrade-window.png create mode 100644 docs/evidence/followup/01-d2-forward-upgrade.txt create mode 100644 docs/evidence/followup/02-d2-rollback-same-schema.txt create mode 100644 docs/evidence/followup/03-b4-role-propagation.txt create mode 100644 docs/evidence/followup/04-observability-gap.txt create mode 100644 docs/evidence/followup/README.md create mode 100644 docs/experiment-followup-untested-items.md diff --git a/.playwright-mcp/console-2026-09-04T06-25-14-829Z.log b/.playwright-mcp/console-2026-09-04T06-25-14-829Z.log new file mode 100644 index 0000000..e6bad9e --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T06-25-14-829Z.log @@ -0,0 +1,2 @@ +[ 19340ms] [ERROR] Failed to load resource: the server responded with a status of 403 () @ https://app2.hyeonworks.com/oauth2/callback?state=rKipZCUv8W5a-xgYheJbjBsInoD5Il1AaF1RlM_RB2s%3A%2Fapi%2Fecho&session_state=Mw52KcQijFB9Bq4rN-C4SF5Y&iss=https%3A%2F%2Fauth.hyeonworks.com%2Frealms%2Fkeycloak-patterns&code=f9a4835a-2af3-b886-bd04-10b5347ee8d2.Mw52KcQijFB9Bq4rN-C4SF5Y.80431dbc-af81-4673-9790-ad06d1570b2e:0 +[ 20374ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://app2.hyeonworks.com/favicon.ico:0 diff --git a/.playwright-mcp/console-2026-09-04T06-25-42-707Z.log b/.playwright-mcp/console-2026-09-04T06-25-42-707Z.log new file mode 100644 index 0000000..f9fd01b --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T06-25-42-707Z.log @@ -0,0 +1 @@ +[ 210ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://app2.hyeonworks.com/favicon.ico:0 diff --git a/.playwright-mcp/console-2026-09-04T06-26-41-840Z.log b/.playwright-mcp/console-2026-09-04T06-26-41-840Z.log new file mode 100644 index 0000000..275afe6 --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T06-26-41-840Z.log @@ -0,0 +1 @@ +[ 423ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://app2.hyeonworks.com/favicon.ico:0 diff --git a/.playwright-mcp/console-2026-09-04T07-14-56-538Z.log b/.playwright-mcp/console-2026-09-04T07-14-56-538Z.log new file mode 100644 index 0000000..58a3c36 --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T07-14-56-538Z.log @@ -0,0 +1,8 @@ +[ 1127ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 2377ms] [WARNING] is deprecated. Please include @ https://app2.hyeonworks.com/explore?schemaVersion=1&panes=%7B%22h4a%22%3A%7B%22datasource%22%3A%22PBFA97CFB590B2093%22%2C%22queries%22%3A%5B%7B%22refId%22%3A%22A%22%2C%22expr%22%3A%22vendor_cluster_size%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22cluster_size+%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%2C%7B%22refId%22%3A%22B%22%2C%22expr%22%3A%22up%7Bjob%3D%5C%22keycloak%5C%22%7D%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22up+%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%5D%2C%22range%22%3A%7B%22from%22%3A%22now-30m%22%2C%22to%22%3A%22now%22%7D%7D%7D&orgId=1:0 +[ 2472ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 3501ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 5119ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 8511ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 14956ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 28065ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 diff --git a/.playwright-mcp/console-2026-09-04T07-15-33-300Z.log b/.playwright-mcp/console-2026-09-04T07-15-33-300Z.log new file mode 100644 index 0000000..8ac9793 --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T07-15-33-300Z.log @@ -0,0 +1,10 @@ +[ 1362ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 1874ms] [WARNING] is deprecated. Please include @ https://app2.hyeonworks.com/explore?schemaVersion=1&orgId=1&panes=%7B%22a%22%3A%7B%22datasource%22%3A%22PBFA97CFB590B2093%22%2C%22queries%22%3A%5B%7B%22refId%22%3A%22A%22%2C%22expr%22%3A%22vendor_cluster_size%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22cluster_size%20%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%2C%7B%22refId%22%3A%22B%22%2C%22expr%22%3A%22up%7Bjob%3D%5C%22keycloak%5C%22%7D%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22up%20%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%5D%2C%22range%22%3A%7B%22from%22%3A%221788502680000%22%2C%22to%22%3A%221788503520000%22%7D%7D%7D:0 +[ 2907ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 3998ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 6253ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 9426ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 12495ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 24486ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 31338ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 46196ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 diff --git a/.playwright-mcp/console-2026-09-04T07-16-34-745Z.log b/.playwright-mcp/console-2026-09-04T07-16-34-745Z.log new file mode 100644 index 0000000..ba710d3 --- /dev/null +++ b/.playwright-mcp/console-2026-09-04T07-16-34-745Z.log @@ -0,0 +1,14 @@ +[ 1319ms] [WARNING] is deprecated. Please include @ https://app2.hyeonworks.com/explore?schemaVersion=1&orgId=1&panes=%7B%22a%22%3A%7B%22datasource%22%3A%22PBFA97CFB590B2093%22%2C%22queries%22%3A%5B%7B%22refId%22%3A%22A%22%2C%22expr%22%3A%22vendor_cluster_size%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22cluster_size%20%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%2C%7B%22refId%22%3A%22B%22%2C%22expr%22%3A%22vendor_statistics_approximate_entries_unique%7Bcache%3D%5C%22sessions%5C%22%7D%22%2C%22range%22%3Atrue%2C%22instant%22%3Afalse%2C%22editorMode%22%3A%22code%22%2C%22legendFormat%22%3A%22sessions%20%5Cuce90%5Cuc2dc%20%7B%7Bpod%7D%7D%22%2C%22datasource%22%3A%7B%22type%22%3A%22prometheus%22%2C%22uid%22%3A%22PBFA97CFB590B2093%22%7D%7D%5D%2C%22range%22%3A%7B%22from%22%3A%221788497040000%22%2C%22to%22%3A%221788499080000%22%7D%7D%7D:0 +[ 5941ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 11107ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 14234ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 17005ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 23049ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 30565ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 44135ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 54992ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 63653ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 70658ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 90768ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 105475ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 +[ 114995ms] [ERROR] WebSocket connection to 'wss://app2.hyeonworks.com/api/live/ws' failed: Error during WebSocket handshake: Unexpected response code: 400 @ https://app2.hyeonworks.com/public/build/1518.a3f1f690c084a37f01c7.js:362 diff --git a/.playwright-mcp/page-2026-09-04T06-25-15-925Z.yml b/.playwright-mcp/page-2026-09-04T06-25-15-925Z.yml new file mode 100644 index 0000000..a7fde5e --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T06-25-15-925Z.yml @@ -0,0 +1,16 @@ +- generic [ref=f55e3]: + - banner [ref=f55e4]: + - generic [ref=f55e5]: keycloak-patterns + - main [ref=f55e6]: + - heading "Sign in to your account" [level=1] [ref=f55e8] + - generic [ref=f55e12]: + - generic [ref=f55e13]: + - generic [ref=f55e14]: Username or email + - textbox "Username or email" [ref=f55e17] + - generic [ref=f55e18]: + - generic [ref=f55e19]: Password + - generic [ref=f55e21]: + - textbox "Password" [ref=f55e24] + - button "Show password" [ref=f55e26] [cursor=pointer]: + - generic [aria-hidden] [ref=f55e27]:  + - button "Sign In" [ref=f55e30] [cursor=pointer] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T06-25-26-485Z.yml b/.playwright-mcp/page-2026-09-04T06-25-26-485Z.yml new file mode 100644 index 0000000..aaf9d7c --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T06-25-26-485Z.yml @@ -0,0 +1,16 @@ +- generic [ref=f55e3]: + - banner [ref=f55e4]: + - generic [ref=f55e5]: keycloak-patterns + - main [ref=f55e6]: + - heading "Sign in to your account" [level=1] [ref=f55e8] + - generic [ref=f55e12]: + - generic [ref=f55e13]: + - generic [ref=f55e14]: Username or email + - textbox "Username or email" [ref=f55e17]: labuser + - generic [ref=f55e18]: + - generic [ref=f55e19]: Password + - generic [ref=f55e21]: + - textbox "Password" [ref=f55e24]: labpass + - button "Show password" [ref=f55e26] [cursor=pointer]: + - generic [aria-hidden] [ref=f55e27]:  + - button "Sign In" [ref=f55e30] [cursor=pointer] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T06-25-35-208Z.yml b/.playwright-mcp/page-2026-09-04T06-25-35-208Z.yml new file mode 100644 index 0000000..7b140eb --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T06-25-35-208Z.yml @@ -0,0 +1,17 @@ +- generic [ref=f56e1]: + - generic [ref=f56e3]: + - generic [ref=f56e4]: "403" + - heading "Forbidden" [level=1] [ref=f56e6] + - generic [ref=f56e8]: + - paragraph [ref=f56e9]: More Info + - generic [ref=f56e10] [cursor=pointer]:  + - separator [ref=f56e12] + - generic [ref=f56e13]: + - button "Go back" [ref=f56e16] [cursor=pointer] + - button "Sign in" [ref=f56e19] [cursor=pointer] + - contentinfo [ref=f56e20]: + - paragraph [ref=f56e22]: + - text: Secured with + - link "OAuth2 Proxy" [ref=f56e23] [cursor=pointer]: + - /url: https://github.com/oauth2-proxy/oauth2-proxy#oauth2_proxy + - text: version v7.7.1 \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T06-25-42-934Z.yml b/.playwright-mcp/page-2026-09-04T06-25-42-934Z.yml new file mode 100644 index 0000000..8b3d9cd --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T06-25-42-934Z.yml @@ -0,0 +1 @@ +- generic [ref=f57e1]: "{ \"headers\" : { \"host\" : [ \"app2.hyeonworks.com\" ], \"user-agent\" : [ \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36\" ], \"accept\" : [ \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\" ], \"accept-encoding\" : [ \"gzip, deflate, br, zstd\" ], \"accept-language\" : [ \"en-US,en;q=0.9\" ], \"cookie\" : [ \"grafana_session=60c3e7ae41ffc00665f4a2c377def399; grafana_session_expiry=1788497124; _oauth2_proxy=djIuWDI5aGRYUm9NbDl3Y205NGVTMDNZMkZrTUdZM01tRmlZekkwWldFell6a3lOREJpTW1KaE5UZGpOVEJoWWcuZVVmckp5VHRqY1VsXzdiV1hiX3hwdw==|1788503135|yDSo7VdgRSlvoVfj9raHPClKTlQiWDg7FauLfoUayw4=\" ], \"priority\" : [ \"u=0, i\" ], \"sec-ch-ua\" : [ \"\\\"Chromium\\\";v=\\\"152\\\", \\\"Not?A_Brand\\\";v=\\\"24\\\", \\\"Google Chrome\\\";v=\\\"152\\\"\" ], \"sec-ch-ua-mobile\" : [ \"?0\" ], \"sec-ch-ua-platform\" : [ \"\\\"Linux\\\"\" ], \"sec-fetch-dest\" : [ \"document\" ], \"sec-fetch-mode\" : [ \"navigate\" ], \"sec-fetch-site\" : [ \"none\" ], \"sec-fetch-user\" : [ \"?1\" ], \"upgrade-insecure-requests\" : [ \"1\" ], \"x-forwarded-email\" : [ \"labuser@example.com\" ], \"x-forwarded-host\" : [ \"app2.hyeonworks.com\" ], \"x-forwarded-port\" : [ \"443\" ], \"x-forwarded-preferred-username\" : [ \"labuser\" ], \"x-forwarded-proto\" : [ \"https\" ], \"x-forwarded-server\" : [ \"traefik-5d6fcf895-wpfhr\" ], \"x-forwarded-user\" : [ \"27df5ea9-8703-4ec5-badd-d972c583e1ff\" ], \"x-real-ip\" : [ \"100.123.124.30\" ] }, \"remoteAddr\" : \"100.123.124.30\", \"localAddr\" : \"10.42.0.53\", \"scheme\" : \"https\", \"secure\" : true, \"serverName\" : \"app2.hyeonworks.com\", \"serverPort\" : 443, \"requestUrl\" : \"https://app2.hyeonworks.com/api/echo\" }" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T06-26-42-277Z.yml b/.playwright-mcp/page-2026-09-04T06-26-42-277Z.yml new file mode 100644 index 0000000..bfa5b33 --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T06-26-42-277Z.yml @@ -0,0 +1 @@ +- generic [ref=f58e1]: "{ \"headers\" : { \"host\" : [ \"app2.hyeonworks.com\" ], \"user-agent\" : [ \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36\" ], \"accept\" : [ \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\" ], \"accept-encoding\" : [ \"gzip, deflate, br, zstd\" ], \"accept-language\" : [ \"en-US,en;q=0.9\" ], \"cookie\" : [ \"grafana_session=60c3e7ae41ffc00665f4a2c377def399; grafana_session_expiry=1788497124; _oauth2_proxy=djIuWDI5aGRYUm9NbDl3Y205NGVTMHpabUV5TVdKaVpEa3lOemRrTlRFMU9USTBaV00wWWpGaE16bGhNak0zT1EuN2tTa3dnWUdISDkwMGFSSTVOSUFFUQ==|1788503202|snWKU5IRfRLoD9-bXEodGjEgfHeAw8PQaoHnecpFH90=\" ], \"priority\" : [ \"u=0, i\" ], \"sec-ch-ua\" : [ \"\\\"Chromium\\\";v=\\\"152\\\", \\\"Not?A_Brand\\\";v=\\\"24\\\", \\\"Google Chrome\\\";v=\\\"152\\\"\" ], \"sec-ch-ua-mobile\" : [ \"?0\" ], \"sec-ch-ua-platform\" : [ \"\\\"Linux\\\"\" ], \"sec-fetch-dest\" : [ \"document\" ], \"sec-fetch-mode\" : [ \"navigate\" ], \"sec-fetch-site\" : [ \"none\" ], \"sec-fetch-user\" : [ \"?1\" ], \"upgrade-insecure-requests\" : [ \"1\" ], \"x-forwarded-email\" : [ \"changed-labuser@example.com\" ], \"x-forwarded-host\" : [ \"app2.hyeonworks.com\" ], \"x-forwarded-port\" : [ \"443\" ], \"x-forwarded-preferred-username\" : [ \"labuser\" ], \"x-forwarded-proto\" : [ \"https\" ], \"x-forwarded-server\" : [ \"traefik-5d6fcf895-wpfhr\" ], \"x-forwarded-user\" : [ \"27df5ea9-8703-4ec5-badd-d972c583e1ff\" ], \"x-real-ip\" : [ \"100.123.124.30\" ] }, \"remoteAddr\" : \"100.123.124.30\", \"localAddr\" : \"10.42.1.132\", \"scheme\" : \"https\", \"secure\" : true, \"serverName\" : \"app2.hyeonworks.com\", \"serverPort\" : 443, \"requestUrl\" : \"https://app2.hyeonworks.com/api/echo\" }" \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T07-14-59-023Z.yml b/.playwright-mcp/page-2026-09-04T07-14-59-023Z.yml new file mode 100644 index 0000000..3165cde --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T07-14-59-023Z.yml @@ -0,0 +1,175 @@ +- generic [ref=f59e1]: + - generic [ref=f59e4]: + - link "Skip to main content" [ref=f59e5] [cursor=pointer]: + - /url: "#pageContent" + - banner [ref=f59e7]: + - generic [ref=f59e8]: + - link [ref=f59e10] [cursor=pointer]: + - /url: / + - img "Grafana" [ref=f59e11] + - generic [ref=f59e14]: + - button "Search or jump to..." [ref=f59e18] [cursor=pointer] + - generic [ref=f59e19]: ctrl+k + - generic [ref=f59e23]: + - button "New" [ref=f59e24] [cursor=pointer] + - button "Help" [ref=f59e30] [cursor=pointer] + - button "News" [ref=f59e33] [cursor=pointer] + - button "Profile" [ref=f59e36] [cursor=pointer]: + - img "User avatar" [ref=f59e37] + - generic [ref=f59e38]: + - button "Open menu" [ref=f59e40] [cursor=pointer] + - navigation "Breadcrumbs" [ref=f59e43]: + - list [ref=f59e44]: + - listitem [ref=f59e45]: + - link "Home" [ref=f59e46] [cursor=pointer]: + - /url: / + - listitem [ref=f59e50]: + - link "Explore" [ref=f59e51] [cursor=pointer]: + - /url: /explore + - listitem [ref=f59e55]: + - generic "Prometheus" [ref=f59e56] + - generic [ref=f59e57]: + - generic [ref=f59e60]: + - button "Copy shortened URL" [ref=f59e61] [cursor=pointer] + - button "Open copy link options" [ref=f59e64] [cursor=pointer] + - button "Toggle top search bar" [ref=f59e68] [cursor=pointer] + - main [ref=f59e74]: + - generic [ref=f59e76]: + - heading "Explore" [level=1] [ref=f59e77] + - generic [ref=f59e82]: + - navigation "Explore toolbar" [ref=f59e84]: + - navigation "Search links" [ref=f59e86]: + - generic [ref=f59e87]: + - button "Content outline" [expanded] [ref=f59e89] [cursor=pointer]: + - generic [ref=f59e92]: Outline + - generic [ref=f59e97] [cursor=pointer]: + - img "Prometheus logo" [ref=f59e99] + - textbox "Select a data source" [ref=f59e100]: + - /placeholder: Prometheus + - generic [ref=f59e104]: + - button "Split the pane" [ref=f59e106] [cursor=pointer]: + - generic [ref=f59e109]: Split + - button "Add" [ref=f59e111] [cursor=pointer] + - generic [ref=f59e116]: + - 'button "Time range selected: Last 30 minutes" [ref=f59e117] [cursor=pointer]' + - button "Zoom out time range" [ref=f59e122] [cursor=pointer] + - generic [ref=f59e126]: + - button "Cancel" [ref=f59e127] [cursor=pointer] + - button "Auto refresh turned off. Choose refresh time interval" [ref=f59e129] [cursor=pointer] + - generic [ref=f59e133]: + - generic [ref=f59e137]: + - button "Collapse outline" [expanded] [ref=f59e139] [cursor=pointer]: + - img "arrow-from-right" [ref=f59e140] + - generic [ref=f59e142]: + - button "Content outline item collapse button" [ref=f59e143] [cursor=pointer]: + - img "angle-right" [ref=f59e144] + - button "Queries" [ref=f59e146] [cursor=pointer]: + - img "arrow" [ref=f59e147] + - generic [ref=f59e154]: + - generic [ref=f59e156]: + - generic [ref=f59e157]: + - generic "Query editor row" [ref=f59e159]: + - generic [ref=f59e160]: + - generic [ref=f59e162]: + - generic [ref=f59e163]: + - button "Collapse query row" [expanded] [ref=f59e164] [cursor=pointer] + - generic [ref=f59e167]: + - button "Query editor row title A" [ref=f59e168] [cursor=pointer]: + - generic [ref=f59e169]: A + - emphasis [ref=f59e170]: (Prometheus) + - generic [ref=f59e171]: + - button "Show data source help" [ref=f59e173] [cursor=pointer] + - button "Duplicate query" [ref=f59e177] [cursor=pointer] + - button "Hide response" [ref=f59e181] [cursor=pointer] + - button "Remove query" [ref=f59e185] [cursor=pointer] + - button "Drag and drop to reorder" [ref=f59e188]: + - img "Drag and drop to reorder" [ref=f59e189] + - generic [ref=f59e192]: + - generic [ref=f59e193]: + - button "Kick start your query" [ref=f59e194] [cursor=pointer] + - generic [ref=f59e197]: + - generic [ref=f59e198] [cursor=pointer]: Explain + - generic [ref=f59e199]: + - checkbox "Explain Toggle switch" [ref=f59e200] + - generic "Toggle switch" [ref=f59e201] [cursor=pointer] + - radiogroup [ref=f59e206]: + - generic [ref=f59e207]: + - radio "Builder" [ref=f59e208] [cursor=pointer] + - generic [ref=f59e209] [cursor=pointer]: Builder + - generic [ref=f59e210]: + - radio "Code" [checked] [ref=f59e211] [cursor=pointer] + - generic [ref=f59e212] [cursor=pointer]: Code + - generic [ref=f59e214]: + - generic [ref=f59e216]: + - button "Loading metrics..." [disabled] [ref=f59e217] [cursor=pointer] + - code [ref=f59e224]: + - generic [ref=f59e225]: + - generic [ref=f59e230]: vendor_cluster_size + - textbox "Editor content;Press Alt+F1 for Accessibility Options." [ref=f59e235]: vendor_cluster_size + - 'button "Options Legend: cluster_size {{pod}} Format: Time series Step: auto Type: Range Exemplars: false" [ref=f59e241] [cursor=pointer]': + - generic [ref=f59e245]: + - heading "Options" [level=6] [ref=f59e246] + - generic [ref=f59e247]: + - generic [ref=f59e248]: "Legend: cluster_size {{pod}}" + - generic [ref=f59e249]: "Format: Time series" + - generic [ref=f59e250]: "Step: auto" + - generic [ref=f59e251]: "Type: Range" + - generic [ref=f59e252]: "Exemplars: false" + - generic "Query editor row" [ref=f59e254]: + - generic [ref=f59e255]: + - generic [ref=f59e257]: + - generic [ref=f59e258]: + - button "Collapse query row" [expanded] [ref=f59e259] [cursor=pointer] + - generic [ref=f59e262]: + - button "Query editor row title B" [ref=f59e263] [cursor=pointer]: + - generic [ref=f59e264]: B + - emphasis [ref=f59e265]: (Prometheus) + - generic [ref=f59e266]: + - button "Show data source help" [ref=f59e268] [cursor=pointer] + - button "Duplicate query" [ref=f59e272] [cursor=pointer] + - button "Hide response" [ref=f59e276] [cursor=pointer] + - button "Remove query" [ref=f59e280] [cursor=pointer] + - button "Drag and drop to reorder" [ref=f59e283]: + - img "Drag and drop to reorder" [ref=f59e284] + - generic [ref=f59e287]: + - generic [ref=f59e288]: + - button "Kick start your query" [ref=f59e289] [cursor=pointer] + - generic [ref=f59e292]: + - generic [ref=f59e293] [cursor=pointer]: Explain + - generic [ref=f59e294]: + - checkbox "Explain Toggle switch" [ref=f59e295] + - generic "Toggle switch" [ref=f59e296] [cursor=pointer] + - radiogroup [ref=f59e301]: + - generic [ref=f59e302]: + - radio "Builder" [ref=f59e303] [cursor=pointer] + - generic [ref=f59e304] [cursor=pointer]: Builder + - generic [ref=f59e305]: + - radio "Code" [checked] [ref=f59e306] [cursor=pointer] + - generic [ref=f59e307] [cursor=pointer]: Code + - generic [ref=f59e309]: + - generic [ref=f59e311]: + - button "Loading metrics..." [disabled] [ref=f59e312] [cursor=pointer] + - code [ref=f59e319]: + - generic [ref=f59e320]: + - generic [ref=f59e325]: "up{job=\"keycloak\"}" + - textbox "Editor content;Press Alt+F1 for Accessibility Options." [ref=f59e330]: "up{job=\"keycloak\"}" + - 'button "Options Legend: up {{pod}} Format: Time series Step: auto Type: Range Exemplars: false" [ref=f59e336] [cursor=pointer]': + - generic [ref=f59e340]: + - heading "Options" [level=6] [ref=f59e341] + - generic [ref=f59e342]: + - generic [ref=f59e343]: "Legend: up {{pod}}" + - generic [ref=f59e344]: "Format: Time series" + - generic [ref=f59e345]: "Step: auto" + - generic [ref=f59e346]: "Type: Range" + - generic [ref=f59e347]: "Exemplars: false" + - generic [ref=f59e348]: + - button "Add query" [ref=f59e349] [cursor=pointer] + - button "Query history" [ref=f59e353] [cursor=pointer] + - button "Query inspector" [ref=f59e357] [cursor=pointer] + - generic: + - main + - generic [ref=f59e364]: + - alert + - alert + - complementary + - complementary \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T07-15-35-378Z.yml b/.playwright-mcp/page-2026-09-04T07-15-35-378Z.yml new file mode 100644 index 0000000..8bbdcf1 --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T07-15-35-378Z.yml @@ -0,0 +1,56 @@ +- generic [ref=f62e4]: + - link "Skip to main content" [ref=f62e5] [cursor=pointer]: + - /url: "#pageContent" + - banner [ref=f62e7]: + - generic [ref=f62e8]: + - link [ref=f62e10] [cursor=pointer]: + - /url: / + - img "Grafana" [ref=f62e11] + - generic [ref=f62e14]: + - button "Search or jump to..." [ref=f62e18] [cursor=pointer] + - generic [ref=f62e19]: ctrl+k + - generic [ref=f62e23]: + - button "New" [ref=f62e24] [cursor=pointer] + - button "Help" [ref=f62e30] [cursor=pointer] + - button "News" [ref=f62e33] [cursor=pointer] + - button "Profile" [ref=f62e36] [cursor=pointer]: + - img "User avatar" [ref=f62e37] + - generic [ref=f62e38]: + - button "Open menu" [ref=f62e40] [cursor=pointer] + - navigation "Breadcrumbs" [ref=f62e43]: + - list [ref=f62e44]: + - listitem [ref=f62e45]: + - link "Home" [ref=f62e46] [cursor=pointer]: + - /url: / + - listitem [ref=f62e50]: + - link "Explore" [ref=f62e51] [cursor=pointer]: + - /url: /explore + - listitem [ref=f62e55]: + - generic "Prometheus" [ref=f62e56] + - generic [ref=f62e57]: + - button "Show more items" [ref=f62e60] [cursor=pointer] + - button "Toggle top search bar" [ref=f62e64] [cursor=pointer] + - main [ref=f62e70]: + - generic [ref=f62e72]: + - heading "Explore" [level=1] [ref=f62e73] + - generic [ref=f62e78]: + - navigation "Explore toolbar" [ref=f62e80]: + - navigation "Search links" [ref=f62e82]: + - generic [ref=f62e83]: + - button "Content outline" [expanded] [ref=f62e85] [cursor=pointer]: + - generic [ref=f62e88]: Outline + - generic [ref=f62e93] [cursor=pointer]: + - img "Prometheus logo" [ref=f62e95] + - textbox "Select a data source" [ref=f62e96]: + - /placeholder: Prometheus + - button "Show more items" [ref=f62e102] [cursor=pointer] + - generic [ref=f62e106]: + - button "Collapse outline" [expanded] [ref=f62e112] [cursor=pointer]: + - img "arrow-from-right" [ref=f62e113] + - generic [ref=f62e119]: + - generic [ref=f62e122]: + - button "Add query" [ref=f62e123] [cursor=pointer] + - button "Query history" [ref=f62e127] [cursor=pointer] + - button "Query inspector" [ref=f62e131] [cursor=pointer] + - generic: + - main \ No newline at end of file diff --git a/.playwright-mcp/page-2026-09-04T07-16-36-528Z.yml b/.playwright-mcp/page-2026-09-04T07-16-36-528Z.yml new file mode 100644 index 0000000..8c6fd4c --- /dev/null +++ b/.playwright-mcp/page-2026-09-04T07-16-36-528Z.yml @@ -0,0 +1,29 @@ +- generic [ref=f65e4]: + - link "Skip to main content" [ref=f65e5] [cursor=pointer]: + - /url: "#pageContent" + - banner [ref=f65e7]: + - generic [ref=f65e8]: + - link [ref=f65e10] [cursor=pointer]: + - /url: / + - img "Grafana" [ref=f65e11] + - generic [ref=f65e14]: + - button "Search or jump to..." [ref=f65e18] [cursor=pointer] + - generic [ref=f65e19]: ctrl+k + - generic [ref=f65e23]: + - button "New" [ref=f65e24] [cursor=pointer] + - button "Help" [ref=f65e30] [cursor=pointer] + - button "News" [ref=f65e33] [cursor=pointer] + - button "Profile" [ref=f65e36] [cursor=pointer]: + - img "User avatar" [ref=f65e37] + - generic [ref=f65e38]: + - button "Open menu" [ref=f65e40] [cursor=pointer] + - navigation "Breadcrumbs" [ref=f65e43]: + - list [ref=f65e44]: + - listitem [ref=f65e45]: + - link "Home" [ref=f65e46] [cursor=pointer]: + - /url: / + - listitem [ref=f65e50]: + - generic "Explore" [ref=f65e51] + - button "Toggle top search bar" [ref=f65e53] [cursor=pointer] + - main [ref=f65e59]: + - heading "Explore" [level=1] [ref=f65e62] \ No newline at end of file diff --git a/docs/diagrams/_style.md b/docs/diagrams/_style.md new file mode 100644 index 0000000..43dbdb3 --- /dev/null +++ b/docs/diagrams/_style.md @@ -0,0 +1,12 @@ +# 다이어그램 규약 + +| 표현 | 뜻 | +|---|---| +| 실선 상자 | 살아 있는 구성 요소 | +| 붉은 점선 상자 | 이 실험에서 죽이거나 막은 것 | +| ✂ 붉은 X | 주입 지점 | +| 실선 화살표 | 정상 경로 | +| 붉은 점선 화살표 | 실험에서 깨진 경로 | +| 회색 글씨 | 측정값 | + +SVG 는 GitHub 에서 그대로 렌더링되며 외부 폰트를 쓰지 않는다. diff --git a/docs/diagrams/a2-database-loss.svg b/docs/diagrams/a2-database-loss.svg new file mode 100644 index 0000000..f8dc35a --- /dev/null +++ b/docs/diagrams/a2-database-loss.svg @@ -0,0 +1,26 @@ + + + + + + + A-2 · PostgreSQL 정지 — 살아남는 노드가 없다 + keycloak-1캐시: 세션 N개 + keycloak-0캐시: 세션 M개 + 7800 · 살아 있다 + + + postgres ✗replicas=0 + + 양쪽 모두 NotReadyready 주소 = [] · 외부 503 + + up{job="keycloak"} = 1프로세스는 살아 있다 — up 은 못 잡는다 + diff --git a/docs/diagrams/a7-volatile-inversion.svg b/docs/diagrams/a7-volatile-inversion.svg new file mode 100644 index 0000000..d93e189 --- /dev/null +++ b/docs/diagrams/a7-volatile-inversion.svg @@ -0,0 +1,36 @@ + + + + A-7 · 같은 주입, 같은 관측, 정반대 결과 + + persistent (KC 26 기본) + + keycloak ×2 — 로컬 캐시 + + + PostgreSQL — 진실의 원천 + + volatile (KC 24 이전 방식) + + keycloak ×2 — 캐시가 곧 진실 + + + 클러스터 복제 (7800) + + + 뒤집힌 세 결과 + A-1 7800 차단 후 교차 refresh + 200400 Session not active + A-8 롤링 재시작 후 refresh + 200400 Session not active + A-2 DB 정지 중 새 로그인 + 500200 + persistentvolatile + diff --git a/docs/diagrams/b1-store-split.svg b/docs/diagrams/b1-store-split.svg new file mode 100644 index 0000000..24dcfe5 --- /dev/null +++ b/docs/diagrams/b1-store-split.svg @@ -0,0 +1,33 @@ + + + + + + + B-1 · Redis 는 세션만 옮기고 토큰은 두고 간다 + bff-0kc-lab-1 + bff-1kc-lab-2 + + Redis — Application Session + sessionRepository → RedisSessionRepository ✔ 옮겨졌다 + 필드: SPRING_SECURITY_CONTEXT · TTL 1772초 + + 프로세스 메모리 — OAuth2AuthorizedClient + InMemoryOAuth2AuthorizedClientService ✗ 그대로 + access token · refresh token 이 여기 있다 + + + + 그 결과 사용자에게 보이는 것 + principal: labuser ← 로그인은 되어 있다 + accessTokenStoredOnServer: false ← 토큰이 없다 + 완전히 로그아웃되는 편이 차라리 낫다 + diff --git a/docs/diagrams/b3-refresh-contention.svg b/docs/diagrams/b3-refresh-contention.svg new file mode 100644 index 0000000..5f614a4 --- /dev/null +++ b/docs/diagrams/b3-refresh-contention.svg @@ -0,0 +1,29 @@ + + + + + + + B-3 · 동시 refresh — 경쟁이 아니라 세션 파괴 + 같은 refresh token ×5 + + Keycloak + rotation ON · maxReuse=0 + 1× HTTP 200 + 4× HTTP 400 + + 재사용 탐지가 client session 을 제거한다 + user_session 은 남고 client_session = 0 (정상 세션은 1) + 그래서 오류가 "Session doesn't have required client" + + ★ 이긴 요청의 새 토큰도 곧바로 400 + 재시도로 회복 불가 → Q2 의 판정은 lock + diff --git a/docs/diagrams/b4-header-forgery.svg b/docs/diagrams/b4-header-forgery.svg new file mode 100644 index 0000000..e6753f1 --- /dev/null +++ b/docs/diagrams/b4-header-forgery.svg @@ -0,0 +1,37 @@ + + + + + + + B-4 · edge 가 설정하지 않은 헤더는 그대로 통과한다 + 공격자 + X-Auth-Request-Roles: admin + + nginx + proxy_set_header 한 것만 덮어쓴다 + X-Auth-Request-* 는 설정이 없다 + + + upstream + ['viewer','admin']둘 다 도착 · 검증 없음 + + 구분자 문제 + "admin,editor" 와 "role-with,comma" 가 + 도착 시점에 구별되지 않는다 + + 크기는 절벽이다 + 4KB 통과 · 8KB → Tomcat 400 + 16KB → 연결 끊김 (nginx) + + 헤더가 인가 근거가 되면 위조 가능성이 곧 권한 상승이다 + Q4 의 5문항 중 2·4번 해당 → Q4 자신의 기준으로 BFF 구조 + diff --git a/docs/diagrams/b5-redis-loss.svg b/docs/diagrams/b5-redis-loss.svg new file mode 100644 index 0000000..cfc0eee --- /dev/null +++ b/docs/diagrams/b5-redis-loss.svg @@ -0,0 +1,35 @@ + + + + + + + B-5 · 파드가 Ready 인 채로 계속 실패한다 + + bff ×2 + Ready = true + Service 에 그대로 남는다 + + + redis ✗replicas=0 + + health group 이 갈랐다 + /actuator/health → 503 + /actuator/health/readiness → 200 UP + redis 지표가 readiness 그룹에 없다 + + A-2 와 정반대 + A-2 Keycloak: DB 검사가 readiness 에 + → NotReady → 503 (명확) + B-5 BFF: 없음 → Ready 유지 + → HTTP 000 (멈춤) + 영속화: 볼륨 없이 AOF 만 켜면 appendonlydir 은 생기지만 파드 삭제로 전부 사라진다 — 볼륨이 먼저다 + diff --git a/docs/diagrams/b6-key-rotation.svg b/docs/diagrams/b6-key-rotation.svg new file mode 100644 index 0000000..e021a70 --- /dev/null +++ b/docs/diagrams/b6-key-rotation.svg @@ -0,0 +1,34 @@ + + + + + + + B-6 · 회전은 안전하고 옛 키를 버리는 순간이 위험하다 + + t0 — 키 A 만 + 발급 A · 검증 A + JWKS RS256 1개 + + + t1 — B 추가 (priority 200) + 발급 B · 검증 A+B + 옛 토큰 200 · 새 토큰 200 + + + t2 — A 제거 + 옛 토큰 즉시 401 + 캐시가 유예를 주지 않는다 + + 겹침 구간(t1~t2)의 최소 길이 = 옛 키로 서명된 것 중 가장 오래 사는 것의 수명 + access token 60초 · refresh token 1800초 → 최소 30분 + 모르는 kid 를 만나면 JWKS 를 다시 받으므로 제거가 즉시 반영된다. 유예는 옛 키를 남겨두는 기간으로 만든다. + diff --git a/docs/diagrams/c1-sso-structure.svg b/docs/diagrams/c1-sso-structure.svg new file mode 100644 index 0000000..6f87ddc --- /dev/null +++ b/docs/diagrams/c1-sso-structure.svg @@ -0,0 +1,31 @@ + + + + + + + C-1 · SSO 의 구조와 IdP 로그아웃의 한계 + + Keycloak user session ×1 + oqOjHekin4JU-BZjgQLjUByW + + + client session — bff-confidential + app1 · Redis 세션 + PostgreSQL 토큰 + + client session — oauth2-proxy + app2 · 쿠키 티켓 + Redis 세션 + + IdP 세션 삭제 ✗ + + app1 그대로 동작 + app2 그대로 동작 + diff --git a/docs/diagrams/d1-backup-restore.svg b/docs/diagrams/d1-backup-restore.svg new file mode 100644 index 0000000..a9618ae --- /dev/null +++ b/docs/diagrams/d1-backup-restore.svg @@ -0,0 +1,30 @@ + + + + + + + D-1 · 빈 데이터베이스가 200 을 냈다 + pg_dump395KB · 101 테이블 · 세션 포함 + + + DROP SCHEMA CASCADE남은 테이블 0 + + + 복구 1초오류 0건 · 재시작 0회 + + 테이블이 0개일 때 무엇이 깨졌는가 — 전부가 아니다 + /protocol/openid-connect/certs → 200 (realm 키가 캐시에 있다) + /.well-known → 500 토큰 발급 → 400 + + 헬스체크는 "DB 가 살아 있다"만 보고 "데이터가 있다"는 안 본다 + RPO = 백업 주기 + A-3 의 synchronous_commit 손실 · 덤프는 같은 호스트 /tmp 에 있었다 + diff --git a/docs/diagrams/d3-secret-exposure.svg b/docs/diagrams/d3-secret-exposure.svg new file mode 100644 index 0000000..5e6ee4d --- /dev/null +++ b/docs/diagrams/d3-secret-exposure.svg @@ -0,0 +1,31 @@ + + + + + + + D-3 · 네 경로 중 RBAC 만 실제로 감춘다 + + kubectl get -o jsonpath | base64 -d + POSTGRES_PASSWORD = lab-postgres-change-me + + 저장소 (at rest) + Encryption Disabled · state.db 에 평문 + + 파드 안 + KEYCLOAK_CLIENT_SECRET=... 환경변수 + + RBAC + default SA 는 get secrets 불가 + + describe 는 "14 bytes" 만 보여줘 감춰졌다는 착각을 준다 + base64 는 감추기 위한 것이 아니라 YAML 에 임의 바이트를 담기 위한 인코딩이다 + diff --git a/docs/diagrams/d4-cert-chain.svg b/docs/diagrams/d4-cert-chain.svg new file mode 100644 index 0000000..16be38d --- /dev/null +++ b/docs/diagrams/d4-cert-chain.svg @@ -0,0 +1,30 @@ + + + + + + + D-4 · 인증서 체인과 SAN 제약 + + 체인 4단계 · Verify return code: 0 + 0 CN=auth.hyeonworks.com + 1 Let's Encrypt YE2 + 2 ISRG Root YE + 3 ISRG Root X2 + + SAN 3개 · 와일드카드 아님 + auth · app1 · app2 + 네 번째 이름이 없다 + B-7 에서 Grafana 의 app2 를 빌려야 했다 + + 단계가 1개면 cert.pem, 2개 이상이면 fullchain.pem 이다 + 브라우저는 중간 인증서를 캐시하므로 cert.pem 실수는 캐시 없는 클라이언트에서만 드러난다 + diff --git a/docs/diagrams/lab-topology.svg b/docs/diagrams/lab-topology.svg new file mode 100644 index 0000000..eb39cd3 --- /dev/null +++ b/docs/diagrams/lab-topology.svg @@ -0,0 +1,57 @@ + + + + + + 개발 노트북 + 브라우저 · kubectl · Playwright + + https · tailnet 100.x · split DNS + + + test-server + Arch Linux · 12GB · WiFi only · sudo 는 비밀번호 필요 + + nginx :443 — TLS 종료 + auth / app1 / app2 (SAN 3개, 와일드카드 아님) + + + http · libvirt NAT (virbr0) + + + kc-lab-1 · 5120MB + k3s server · 10.42.0.0/24 + traefik ×1 + coredns + keycloak-1 + bff (1/2) + oauth2-proxy (1/2) + + prometheus (PVC) · grafana + 관측 스택은 여기 고정 — 죽이지 않는다 + + + kc-lab-2 · 4096MB + k3s agent · 10.42.1.0/24 + keycloak-0 + bff (2/2) + oauth2-proxy + + postgresPVC (노드 고정) + + redisPVC + AOF + 장애 주입은 여기 + + + + 7800 + JGroups + + A-0 에서 확인: 세션은 이 7800 이 아니라 postgres 를 통해 공유된다 + diff --git a/docs/evidence/d2-version-upgrade/d2-upgrade-window.png b/docs/evidence/d2-version-upgrade/d2-upgrade-window.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4e61447b3aec0023775ca9016428be1a070fb7 GIT binary patch literal 107782 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 literal 0 HcmV?d00001 diff --git a/docs/evidence/followup/01-d2-forward-upgrade.txt b/docs/evidence/followup/01-d2-forward-upgrade.txt new file mode 100644 index 0000000..879a804 --- /dev/null +++ b/docs/evidence/followup/01-d2-forward-upgrade.txt @@ -0,0 +1,32 @@ +=== D-1 절차대로 먼저 백업 === + 백업: 395375 bytes + 마이그레이션 전: 210 + 세션 전: 3 + +=== ★ 정방향 업그레이드 + 1초 간격 가용성 측정 === + 시작: 15:22:59 +partitioned roll out complete: 2 new pods have been updated... + 완료: 15:24:26 + +=== 업그레이드 중 외부 응답 시계열 === + 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 200 200 200 + 200 응답: 87 회 + 비200 : 0 +0 회 +=== 업그레이드 후 === +quay.io/keycloak/keycloak:26.7.3 + Keycloak 26.7.3 + 마이그레이션 후: 210 (전: 210) + 세션 후: 3 (전: 3) + +=== 스키마 마이그레이션이 실제로 있었는가 === + (없으면 26.7.0→26.7.3 에 스키마 변경이 없다는 뜻) + +=== 파드 상태와 클러스터 === + keycloak-0 1/1 Running restarts=0 + keycloak-1 1/1 Running restarts=0 + cluster: [keycloak-1-11418(v=16.0.14)|47] (2) [keycloak-1-11418(v=16.0.14), keycloak-0-58996(v=16.0.14)] diff --git a/docs/evidence/followup/02-d2-rollback-same-schema.txt b/docs/evidence/followup/02-d2-rollback-same-schema.txt new file mode 100644 index 0000000..b4d6af6 --- /dev/null +++ b/docs/evidence/followup/02-d2-rollback-same-schema.txt @@ -0,0 +1,20 @@ +=== ★ 가설: 스키마 변경이 없으면 롤백이 된다 (26.7.3 → 26.7.0) === + 시작: 15:25:08 +partitioned roll out complete: 2 new pods have been updated... + 완료: 15:25:53 + + 200 응답: 43 회 / 비200: 1 + + keycloak-0 1/1 Running restarts=0 + keycloak-1 1/1 Running restarts=0 + Keycloak 26.7.0 + 마이그레이션: 210 + 세션: 3 +=== 롤백 중 응답 시계열 (비200 위치) === + 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 000 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 + 200 200 200 200 + 비200 값: 000 + +=== 대조: 정방향 업그레이드 때는 === + 200: 87 / 비200: 0 diff --git a/docs/evidence/followup/03-b4-role-propagation.txt b/docs/evidence/followup/03-b4-role-propagation.txt new file mode 100644 index 0000000..d0dbbc5 --- /dev/null +++ b/docs/evidence/followup/03-b4-role-propagation.txt @@ -0,0 +1,9 @@ +=== ★ Keycloak 에서 email 을 바꾼다 === + 변경 시각: 15:27:52 + IdP 의 값: [ { + IdP 의 값: "email" : "changed-labuser@example.com" + IdP 의 값: } ] + +=== IdP 쪽 세션과 oauth2-proxy 세션 === + Redis 세션: 1 개 + (세션은 로그인 시점의 클레임을 담고 있다 — 이제 요청을 반복해 본다) diff --git a/docs/evidence/followup/04-observability-gap.txt b/docs/evidence/followup/04-observability-gap.txt new file mode 100644 index 0000000..8f080c9 --- /dev/null +++ b/docs/evidence/followup/04-observability-gap.txt @@ -0,0 +1,17 @@ +=== Prometheus 가 실제로 긁는 대상 (2026-09-04 18:10 KST) === + keycloak 2개 + kubelet 2개 + node-exporter 2개 + prometheus 1개 + +=== B층 구성 요소의 지표가 있는가 === + redis_up 시계열 0개 + redis_connected_clients 시계열 0개 + redis_memory_used_bytes 시계열 0개 + pg_up 시계열 0개 + pg_stat_database_numbackends 시계열 0개 + + → B-1·B-2·B-3·B-5 는 Grafana 증거를 만들 수 없다. + 스크린샷을 안 찍은 것이 아니라 긁는 대상에 없다. + 보완하려면 redis_exporter · postgres_exporter · BFF 의 /actuator/prometheus 를 + scrape 대상에 추가해야 한다. diff --git a/docs/evidence/followup/README.md b/docs/evidence/followup/README.md new file mode 100644 index 0000000..820a980 --- /dev/null +++ b/docs/evidence/followup/README.md @@ -0,0 +1,17 @@ +# 후속 — 미측정으로 남겼던 항목을 채운 기록 + +2026-09-04 17:35–18:20 KST +해설: [`docs/experiment-followup-untested-items.md`](../../experiment-followup-untested-items.md) + +| 파일 | 무엇을 보여주는가 | +|---|---| +| `01-d2-forward-upgrade.txt` | **D-2 정방향** 26.7.0 → 26.7.3. 백업 396KB · **87회 요청 전부 200(무중단)** · 마이그레이션 210 → 210(스키마 변경 없음) · 세션 3 유지 · Infinispan 16.0.12 → 16.0.14 | +| `02-d2-rollback-same-schema.txt` | **스키마가 안 바뀌면 롤백이 된다** — 26.7.3 → 26.7.0 성공. 다만 전환 순간 `000` 1회(3초 타임아웃) | +| `03-b4-role-propagation.txt` | **B-4 ③** IdP 에서 값을 바꿔도 **12회 요청·6초 동안 옛 값**. 세션 삭제 후 재인증에서야 새 값 | +| `04-observability-gap.txt` | **B층에 관측이 없다** — Prometheus 는 keycloak·kubelet·node-exporter·prometheus 만 긁는다. Redis·BFF·PostgreSQL 지표가 0개 | + +## 핵심 세 줄 + +1. **"롤백은 안 된다" 는 조건부였다.** 스키마가 바뀌었으면 안 되고, 안 바뀌었으면 된다 — D-2 의 결론을 정밀화한다. +2. **role 변경은 요청 횟수와 무관하게 반영되지 않는다.** `--cookie-refresh` 가 없으면 쿠키 만료나 재인증까지 옛 값이 간다. +3. **B층 실험에 Grafana 증거가 없는 이유가 확인됐다** — 관측 대상에 애초에 없다. 스크린샷이 없는 것이 아니라 지표가 없다. diff --git a/docs/experiment-a2-database-loss.md b/docs/experiment-a2-database-loss.md index 37f01d0..eac488b 100644 --- a/docs/experiment-a2-database-loss.md +++ b/docs/experiment-a2-database-loss.md @@ -9,6 +9,15 @@ --- +## 구조 + +![A-2 구조 — DB 정지 시 살아남는 노드가 없다](diagrams/a2-database-loss.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | 예측 | 결과 | diff --git a/docs/experiment-a7-volatile-comparison.md b/docs/experiment-a7-volatile-comparison.md index 091165e..73daa2c 100644 --- a/docs/experiment-a7-volatile-comparison.md +++ b/docs/experiment-a7-volatile-comparison.md @@ -9,6 +9,14 @@ --- +## 구조 + +![A-7 구조 — 두 모드의 데이터 흐름과 뒤집힌 결과](diagrams/a7-volatile-inversion.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). + +--- + ## 0. 결론부터 — 비교표 | 실험 | persistent (KC 26 기본) | **volatile (KC 24 이전 방식)** | diff --git a/docs/experiment-b1-redis-session-store.md b/docs/experiment-b1-redis-session-store.md index 2040926..4508167 100644 --- a/docs/experiment-b1-redis-session-store.md +++ b/docs/experiment-b1-redis-session-store.md @@ -10,6 +10,15 @@ --- +## 구조 + +![B-1 구조 — 세션만 Redis 로, 토큰은 프로세스 메모리에](diagrams/b1-store-split.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | | before | after | | diff --git a/docs/experiment-b3-refresh-token-contention.md b/docs/experiment-b3-refresh-token-contention.md index 15f6f79..27c87c2 100644 --- a/docs/experiment-b3-refresh-token-contention.md +++ b/docs/experiment-b3-refresh-token-contention.md @@ -12,6 +12,15 @@ --- +## 구조 + +![B-3 구조 — 동시 refresh 가 client session 을 제거한다](diagrams/b3-refresh-contention.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 **"하나는 성공하고 하나는 실패한다"가 아니다. 세션이 파괴된다.** diff --git a/docs/experiment-b4-edge-authorization-scope.md b/docs/experiment-b4-edge-authorization-scope.md index 1c50e14..2753815 100644 --- a/docs/experiment-b4-edge-authorization-scope.md +++ b/docs/experiment-b4-edge-authorization-scope.md @@ -10,6 +10,15 @@ --- +## 구조 + +![B-4 구조 — 설정하지 않은 헤더는 통과한다](diagrams/b4-header-forgery.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | Q4 의 미지수 | 측정 결과 | diff --git a/docs/experiment-b5-redis-loss-persistence.md b/docs/experiment-b5-redis-loss-persistence.md index 17f818b..7e40350 100644 --- a/docs/experiment-b5-redis-loss-persistence.md +++ b/docs/experiment-b5-redis-loss-persistence.md @@ -8,6 +8,15 @@ --- +## 구조 + +![B-5 구조 — readiness 그룹이 갈랐다](diagrams/b5-redis-loss.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | | 결과 | diff --git a/docs/experiment-b6-key-rotation.md b/docs/experiment-b6-key-rotation.md index 5698aad..8f8ff28 100644 --- a/docs/experiment-b6-key-rotation.md +++ b/docs/experiment-b6-key-rotation.md @@ -12,6 +12,15 @@ --- +## 구조 + +![B-6 구조 — 겹침 구간과 제거 시점](diagrams/b6-key-rotation.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 **질문이 두 갈래로 나뉜다.** diff --git a/docs/experiment-c1-multi-app-sso.md b/docs/experiment-c1-multi-app-sso.md index d06976e..3e61c02 100644 --- a/docs/experiment-c1-multi-app-sso.md +++ b/docs/experiment-c1-multi-app-sso.md @@ -11,6 +11,15 @@ --- +## 구조 + +![C-1 구조 — user session 1 : client session N](diagrams/c1-sso-structure.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | 물음 | 답 | diff --git a/docs/experiment-d1-backup-restore.md b/docs/experiment-d1-backup-restore.md index 8fc7f86..51b50a2 100644 --- a/docs/experiment-d1-backup-restore.md +++ b/docs/experiment-d1-backup-restore.md @@ -8,6 +8,15 @@ --- +## 구조 + +![D-1 구조 — 파괴와 복구, 그리고 캐시가 가린 것](diagrams/d1-backup-restore.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | 측정 | 값 | diff --git a/docs/experiment-d3-secret-management.md b/docs/experiment-d3-secret-management.md index eb37e3a..b516523 100644 --- a/docs/experiment-d3-secret-management.md +++ b/docs/experiment-d3-secret-management.md @@ -6,6 +6,15 @@ --- +## 구조 + +![D-3 구조 — 네 경로 중 하나만 막는다](diagrams/d3-secret-exposure.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | 경로 | 감춰지는가 | diff --git a/docs/experiment-d4-certificate-renewal.md b/docs/experiment-d4-certificate-renewal.md index 65aecbc..5332d59 100644 --- a/docs/experiment-d4-certificate-renewal.md +++ b/docs/experiment-d4-certificate-renewal.md @@ -6,6 +6,15 @@ --- +## 구조 + +![D-4 구조 — 체인과 SAN 제약](diagrams/d4-cert-chain.svg) + +> 다이어그램 규약은 [`diagrams/_style.md`](diagrams/_style.md). +> 실험대 전체 구조는 [`diagrams/lab-topology.svg`](diagrams/lab-topology.svg). + +--- + ## 0. 결론부터 | 확인 | 결과 | diff --git a/docs/experiment-followup-untested-items.md b/docs/experiment-followup-untested-items.md new file mode 100644 index 0000000..fb2ad15 --- /dev/null +++ b/docs/experiment-followup-untested-items.md @@ -0,0 +1,249 @@ +# 후속 — 미측정으로 남겼던 항목을 채운다 + +브랜치 `feature/keycloak-followup-untested-items` · +증거 [`docs/evidence/followup/`](evidence/followup/) · +2026-09-04 17:35–18:20 KST + +23개 실험을 마치며 **세 항목을 "못 했다" 로 남겼다.** 그중 둘을 채우고, +셋째(D-4 강제 갱신)는 권한이 필요해 별도로 진행한다. + +--- + +## 0. 결론부터 + +| 항목 | 결과 | +|---|---| +| **D-2 정방향 업그레이드** | **무중단.** 87회 요청 전부 200 | +| **그리고 D-2 의 결론이 정밀해졌다** | **"롤백 불가" 는 조건부다** — 스키마가 바뀌었을 때만 | +| **B-4 ③ role 변경 반영 시점** | **요청 횟수와 무관하다.** 세션이 새로 만들어져야 한다 | +| **B층에 Grafana 증거가 없는 이유** | **관측 대상에 없다.** 안 찍은 것이 아니다 | + +--- + +## 1. D-2 정방향 업그레이드 — 26.7.0 → 26.7.3 + +### 개념 — 왜 이 방향을 못 했었나 + +D-2 를 처음 할 때 26.7.0 보다 새 이미지를 몰라 **역방향(26.0)만 시험**했다. +태그 목록을 조회하니 26.7.1 / 26.7.2 / **26.7.3** 이 있었다. + +```bash +curl -s "https://quay.io/api/v1/repository/keycloak/keycloak/tag/?limit=40&onlyActiveTags=true" +``` + +### 절차 — D-1 의 교훈대로 백업이 먼저다 + +```bash +kubectl -n keycloak-lab exec deploy/postgres -- pg_dump -U keycloak -d keycloak \ + --clean --if-exists > /tmp/pre-2673.sql # 396333 bytes + +# 1초 간격으로 외부 진입점을 찍으면서 태그를 바꾼다 +( for i in $(seq 1 150); do + printf "%s " "$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 https://auth.hyeonworks.com/realms/master)" + sleep 1 + done > /tmp/avail.txt ) & +kubectl -n keycloak-lab set image statefulset/keycloak keycloak=quay.io/keycloak/keycloak:26.7.3 +kubectl -n keycloak-lab rollout status statefulset/keycloak --timeout=600s +``` + +### 결과 — 무중단 + +``` +200 200 200 ... (87회) + 200 응답: 87 회 + 비200 : 0 회 +소요: 15:22:59 → 15:24:26 (87초) +``` + +![업그레이드 구간의 cluster_size 와 up](evidence/d2-version-upgrade/d2-upgrade-window.png) + +**파드가 하나씩 교체되며 `cluster_size` 가 2 → 1 → 2 를 두 번 반복한다.** +각 파드의 `up` 시계열이 끝나고 새 시계열이 시작되는 것이 함께 보인다. + +``` + 마이그레이션: 210 → 210 ← 스키마 변경 없음 + 세션: 3 → 3 ← 유지 + Infinispan: 16.0.12 → 16.0.14 + restarts=0 +``` + +--- + +## 2. ★ 그래서 D-2 의 결론을 정밀화한다 + +**"스키마 변경이 없었다면 롤백이 될 것" 이라는 가설이 생겼고, 시험했다.** + +```bash +kubectl -n keycloak-lab set image statefulset/keycloak keycloak=quay.io/keycloak/keycloak:26.7.0 +``` + +``` + 200 응답: 43 회 / 비200: 1 + Keycloak 26.7.0 + 마이그레이션: 210 · 세션: 3 + restarts=0 +``` + +**롤백이 성공했다.** + +| 버전 차 | `databasechangelog` | 롤백 | +|---|---|---| +| 26.7.0 → **26.0** | 체크섬 불일치 | **불가** (`ValidationFailedException`) | +| 26.7.0 ↔ **26.7.3** | **210 → 210, 변화 없음** | **가능** | + +> **처음 D-2 에서 "롤백은 안 된다" 고 쓴 것은 과했다.** +> 정확히는 **"스키마가 바뀌었으면 안 된다"** 이고, +> **패치 릴리스처럼 스키마가 그대로면 태그를 되돌리는 것으로 충분하다.** +> +> 판단 기준은 버전 번호가 아니라 **`databasechangelog` 의 행 수가 바뀌었는가**다. + +```bash +# 업그레이드 전후로 이것만 비교하면 롤백 가능 여부를 안다 +kubectl -n keycloak-lab exec deploy/postgres -- psql -U keycloak -d keycloak -tAc \ + "select count(*) from databasechangelog" +``` + +### 전환 순간의 `000` 1회 + +``` +200 ×24 000 200 ×19 +``` + +**서버 오류가 아니라 `--max-time 3` 타임아웃**이다. 파드 전환 순간 요청 하나가 +3초를 넘겼다. 정방향에서는 0회였다. + +> **"무중단" 은 관측 해상도에 달려 있다.** 1초 간격·3초 타임아웃으로는 +> 44회 중 1회가 걸렸다. **더 촘촘히 보면 더 보일 것이다.** + +--- + +## 3. B-4 ③ — role 변경은 언제 반영되는가 + +### 왜 못 했었나 + +B-4 를 할 때 **oauth2-proxy 가 아직 배포되지 않아** "proxy session" 이 +존재하지 않았다. B-7 에서 배포했으므로 이제 측정할 수 있다. + +### 방법 + +`X-Auth-Request-Roles` 대신 **이미 전달되고 있는 `x-forwarded-email`** 을 썼다. +role 을 헤더로 내보내려면 추가 설정이 필요한데, **"IdP 의 클레임 변경이 +언제 반영되는가" 라는 질문은 어느 클레임이든 같다.** + +```bash +# 1. 기준선 +fetch('/api/echo') → x-forwarded-email = labuser@example.com + +# 2. IdP 에서 바꾼다 +kcadm.sh update users/ -r keycloak-patterns -s email=CHANGED-labuser@example.com + +# 3. 반복 요청 +for (i=1..12) fetch('/api/echo') # 0.5초 간격 +``` + +### 결과 — 반영되지 않는다 + +``` + 1: labuser@example.com + 2: labuser@example.com + ... +12: labuser@example.com ← 12회 · 6초 동안 옛 값 +``` + +세션을 지우고 재인증시키자 + +``` +재인증 후 email = changed-labuser@example.com +``` + +### 개념 — 세션은 로그인 시점의 스냅샷이다 + +``` + 로그인 → IdP 가 준 클레임을 세션에 담는다 + 이후 요청 → 세션에서 읽어 헤더로 내보낸다 + └─ IdP 를 다시 부르지 않는다 + IdP 에서 변경 → 세션은 모른다 +``` + +**oauth2-proxy 에 `--cookie-refresh` 가 설정되어 있지 않다.** +설정하면 그 주기마다 토큰을 갱신하며 클레임을 다시 받는다. + +| 설정 | 반영 시점 | +|---|---| +| 지금 (`--cookie-refresh` 없음) | **쿠키 만료(1시간) 또는 재인증까지 안 됨** | +| `--cookie-refresh=5m` | 최대 5분 | + +> **Q4 는 "몇 번째 요청부터 반영되는지" 를 물었는데, 답은 "요청으로는 안 된다" 이다.** +> 요청 횟수가 아니라 **세션의 나이**가 정한다. +> +> **이것이 Q4 의 설계 판단 2번(role·tenant 변경이 즉시 반영돼야 하는가)에 +> 직접 답한다** — 즉시가 필요하면 헤더 방식은 맞지 않는다. + +--- + +## 4. B층에 Grafana 증거가 없는 이유 + +문서 감사에서 **B-1·B-3·B-4·B-5 에 스크린샷이 없는 것**이 드러나 +소급해서 찍으려다 원인을 확인했다. + +``` +=== Prometheus 가 실제로 긁는 대상 === + keycloak 2개 + kubelet 2개 + node-exporter 2개 + prometheus 1개 + +=== B층 구성 요소의 지표가 있는가 === + redis_up 시계열 0개 + redis_connected_clients 시계열 0개 + pg_up 시계열 0개 + pg_stat_database_numbackends 시계열 0개 +``` + +**Redis 도 PostgreSQL 도 BFF 도 긁는 대상에 없다.** + +> **스크린샷을 안 찍은 것이 아니라 지표가 없다.** +> A층이 Grafana 증거를 남길 수 있었던 것은 Keycloak 이 `/metrics` 를 +> 내놓고 그것을 scrape 대상에 넣어뒀기 때문이다. +> +> **관측은 "나중에 붙이는 것" 이 아니라 실험 설계에 포함되어야 한다.** +> A-2 에서 `kube-state-metrics` 가 없다는 것을, A-6 에서 응답 시간 +> 히스토그램이 없다는 것을 찾았는데, **B층 전체가 빠져 있던 것은 +> 문서 감사를 하고서야 드러났다.** + +### 보완하려면 + +| 대상 | 방법 | +|---|---| +| Redis | `redis_exporter` 사이드카 또는 Deployment | +| PostgreSQL | `postgres_exporter` | +| BFF | 이미 actuator 가 있다 — `/actuator/prometheus` 노출 + scrape 추가 | +| 파드 readiness | `kube-state-metrics` (A-2 에서 이미 찾은 항목) | + +--- + +## 5. 남은 것 — D-4 강제 갱신 + +``` +$ sudo -n -l +sudo: a password is required +``` + +**호스트 sudo 가 비밀번호를 요구해 `certbot renew --force-renewal` 과 +`systemctl reload nginx` 를 실행할 수 없다.** 사람이 함께 있어야 한다. + +측정 계획은 준비되어 있다. + +```bash +# 측정 쪽 (내가 실행) +while true; do + printf '%s ' "$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 https://auth.hyeonworks.com/realms/master)" + sleep 0.2 +done + +# 주입 쪽 (사람이 실행) +sudo certbot renew --force-renewal +``` + +**0.2초 간격으로 재는 이유** — 2절에서 1초 간격으로는 전환을 거의 못 잡았다. +nginx reload 는 그보다 훨씬 짧을 것이므로 해상도를 올려야 한다.