diff --git a/k8s/core/keycloak/hacker-theme-configmap.yaml b/k8s/core/keycloak/hacker-theme-configmap.yaml new file mode 100644 index 0000000..223270d --- /dev/null +++ b/k8s/core/keycloak/hacker-theme-configmap.yaml @@ -0,0 +1,366 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: keycloak-hacker-theme + namespace: keycloak +data: + theme.properties: | + parent=keycloak.v2 + import=common/keycloak + + styles=css/hacker.css + + hacker.css: | + /* ============================================================ + HEXOR — Hacker Terminal Theme for Keycloak + ============================================================ */ + + @import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&display=swap'); + + :root { + --hk-bg: #0a0a0a; + --hk-panel: #0d0d0d; + --hk-border: #00ff4180; + --hk-green: #00ff41; + --hk-green-dim: #00cc33; + --hk-green-glow: rgba(0, 255, 65, 0.15); + --hk-green-glow-strong: rgba(0, 255, 65, 0.35); + --hk-cyan: #00e5ff; + --hk-red: #ff3333; + --hk-text: #b0ffb0; + --hk-text-dim: #5a8a5a; + --hk-input-bg: #050505; + --hk-font: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace; + } + + /* -- Force dark always --------------------------------------------------- */ + html.login-pf, + html.login-pf.pf-v5-theme-dark { + color-scheme: dark; + } + + /* -- Background: scanlines + CRT ---------------------------------------- */ + body#keycloak-bg { + font-family: var(--hk-font) !important; + background: var(--hk-bg) !important; + color: var(--hk-text) !important; + min-height: 100vh; + position: relative; + } + + body#keycloak-bg::before { + content: ''; + position: fixed; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.03) 2px, + rgba(0, 255, 65, 0.03) 4px + ); + pointer-events: none; + z-index: 9999; + } + + /* -- Login container ----------------------------------------------------- */ + .pf-v5-c-login { + background: transparent !important; + } + + .pf-v5-c-login__container { + max-width: 480px !important; + } + + /* -- Header / Brand ------------------------------------------------------ */ + #kc-header-wrapper { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 10px var(--hk-green), 0 0 30px var(--hk-green-glow); + font-size: 1.5rem !important; + letter-spacing: 0.15em; + text-transform: uppercase; + } + + #kc-header-wrapper .kc-logo-text span::before { + content: '> '; + opacity: 0.6; + } + + /* -- Main card ----------------------------------------------------------- */ + .pf-v5-c-login__main { + background: var(--hk-panel) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + box-shadow: + 0 0 15px var(--hk-green-glow), + inset 0 0 30px rgba(0, 0, 0, 0.5) !important; + } + + /* -- Title --------------------------------------------------------------- */ + h1#kc-page-title, + .pf-v5-c-title { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 8px var(--hk-green-glow); + font-weight: 600 !important; + font-size: 1.25rem !important; + letter-spacing: 0.05em; + } + + /* -- Labels -------------------------------------------------------------- */ + .pf-v5-c-form__label-text { + font-family: var(--hk-font) !important; + color: var(--hk-green-dim) !important; + font-size: 0.85rem !important; + text-transform: uppercase; + letter-spacing: 0.08em; + } + + /* -- Input fields -------------------------------------------------------- */ + .pf-v5-c-form-control { + background-color: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-size: 0.9rem !important; + caret-color: var(--hk-green); + transition: border-color 0.2s, box-shadow 0.2s; + } + + .pf-v5-c-form-control:focus-within, + .pf-v5-c-form-control:focus { + border-color: var(--hk-green) !important; + box-shadow: 0 0 8px var(--hk-green-glow), inset 0 0 4px var(--hk-green-glow) !important; + outline: none !important; + } + + .pf-v5-c-form-control input { + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-form-control input::placeholder { + color: var(--hk-text-dim) !important; + } + + /* -- Primary button (Sign In) -------------------------------------------- */ + .pf-v5-c-button.pf-m-primary { + background: transparent !important; + border: 1px solid var(--hk-green) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-weight: 600 !important; + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.9rem !important; + transition: all 0.2s; + position: relative; + overflow: hidden; + } + + .pf-v5-c-button.pf-m-primary:hover { + background: var(--hk-green-glow) !important; + box-shadow: 0 0 15px var(--hk-green-glow-strong), inset 0 0 15px var(--hk-green-glow) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-green); + } + + /* -- Secondary button (Try Another Way, Passkey) ------------------------- */ + .pf-v5-c-button.pf-m-secondary { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + transition: all 0.2s; + } + + .pf-v5-c-button.pf-m-secondary:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 12px rgba(0, 229, 255, 0.3) !important; + color: #fff !important; + } + + /* -- Passkey authenticate button ----------------------------------------- */ + #authenticateWebAuthnButton { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + padding: 0.75rem 1rem; + display: block; + width: 100%; + text-align: center; + text-decoration: none; + transition: all 0.2s; + margin-top: 1rem; + } + + #authenticateWebAuthnButton::before { + content: '\f084 '; + font-family: 'Font Awesome 6 Free', 'FontAwesome'; + font-weight: 900; + } + + #authenticateWebAuthnButton:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 15px rgba(0, 229, 255, 0.3), inset 0 0 10px rgba(0, 229, 255, 0.1) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-cyan); + } + + /* -- Show password button ------------------------------------------------ */ + .pf-v5-c-button.pf-m-control { + background: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-left: none !important; + border-radius: 0 !important; + color: var(--hk-text-dim) !important; + } + + .pf-v5-c-button.pf-m-control:hover { + color: var(--hk-green) !important; + } + + /* -- Social providers (Google, etc) -------------------------------------- */ + .pf-v5-c-login__main-footer { + border-top: 1px solid var(--hk-border) !important; + } + + .kc-social-section .pf-v5-c-button.pf-m-secondary { + border-color: var(--hk-text-dim) !important; + color: var(--hk-text) !important; + } + + .kc-social-section .pf-v5-c-button.pf-m-secondary:hover { + border-color: var(--hk-green) !important; + color: var(--hk-green) !important; + box-shadow: 0 0 10px var(--hk-green-glow) !important; + } + + /* -- Try another way link ------------------------------------------------ */ + #try-another-way { + color: var(--hk-cyan) !important; + text-decoration: none !important; + font-family: var(--hk-font) !important; + } + + #try-another-way:hover { + text-shadow: 0 0 8px rgba(0, 229, 255, 0.5); + } + + /* -- Links --------------------------------------------------------------- */ + a { + color: var(--hk-cyan) !important; + text-decoration: none; + transition: text-shadow 0.2s; + } + + a:hover { + text-shadow: 0 0 6px rgba(0, 229, 255, 0.4); + } + + /* -- Alerts / messages --------------------------------------------------- */ + .pf-v5-c-alert { + background: rgba(0, 255, 65, 0.05) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-alert.pf-m-danger { + border-color: var(--hk-red) !important; + background: rgba(255, 51, 51, 0.05) !important; + } + + .pf-v5-c-alert__title { + color: var(--hk-text) !important; + font-family: var(--hk-font) !important; + } + + /* -- Checkbox (Remember me) ---------------------------------------------- */ + .pf-v5-c-check__label { + color: var(--hk-text-dim) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; + } + + /* -- Helper text (Forgot password, etc.) --------------------------------- */ + .pf-v5-c-helper-text .pf-v5-c-button.pf-m-link { + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; + } + + /* -- Select auth list (Try another way options) -------------------------- */ + .select-auth-container { + background: var(--hk-panel) !important; + font-family: var(--hk-font) !important; + } + + .pf-v5-c-data-list__item { + border-color: var(--hk-border) !important; + background: transparent !important; + } + + .pf-v5-c-data-list__item:hover { + background: var(--hk-green-glow) !important; + } + + .select-auth-box-headline { + color: var(--hk-green) !important; + } + + .select-auth-box-desc { + color: var(--hk-text-dim) !important; + } + + /* -- Footer -------------------------------------------------------------- */ + .pf-v5-c-login__main-footer-band { + background: transparent !important; + border-top: 1px solid var(--hk-border) !important; + } + + /* -- Language selector --------------------------------------------------- */ + select#login-select-toggle { + background: var(--hk-input-bg) !important; + color: var(--hk-green) !important; + border-color: var(--hk-border) !important; + font-family: var(--hk-font) !important; + } + + /* -- Blinking cursor animation for header -------------------------------- */ + @keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } + } + + #kc-header-wrapper::after { + content: '_'; + animation: blink 1s step-end infinite; + color: var(--hk-green); + } + + /* -- Subtle CRT flicker ------------------------------------------------- */ + @keyframes flicker { + 0% { opacity: 0.97; } + 5% { opacity: 0.95; } + 10% { opacity: 0.98; } + 15% { opacity: 0.96; } + 20% { opacity: 0.99; } + 100% { opacity: 0.98; } + } + + .pf-v5-c-login__main { + animation: flicker 4s infinite; + } diff --git a/k8s/core/keycloak/kustomization.yaml b/k8s/core/keycloak/kustomization.yaml index 6f3364d..a5f50ec 100644 --- a/k8s/core/keycloak/kustomization.yaml +++ b/k8s/core/keycloak/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - app.yaml - external-secrets.yaml + - hacker-theme-configmap.yaml helmCharts: - name: keycloakx diff --git a/k8s/core/keycloak/theme/login/css/hacker.css b/k8s/core/keycloak/theme/login/css/hacker.css new file mode 100644 index 0000000..7588758 --- /dev/null +++ b/k8s/core/keycloak/theme/login/css/hacker.css @@ -0,0 +1,353 @@ +/* ============================================================ + HEXOR — Hacker Terminal Theme for Keycloak + ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&display=swap'); + +:root { + --hk-bg: #0a0a0a; + --hk-panel: #0d0d0d; + --hk-border: #00ff4180; + --hk-green: #00ff41; + --hk-green-dim: #00cc33; + --hk-green-glow: rgba(0, 255, 65, 0.15); + --hk-green-glow-strong: rgba(0, 255, 65, 0.35); + --hk-cyan: #00e5ff; + --hk-red: #ff3333; + --hk-text: #b0ffb0; + --hk-text-dim: #5a8a5a; + --hk-input-bg: #050505; + --hk-font: 'Fira Code', 'Cascadia Code', 'JetBrains Mono', monospace; +} + +/* -- Force dark always --------------------------------------------------- */ +html.login-pf, +html.login-pf.pf-v5-theme-dark { + color-scheme: dark; +} + +/* -- Background: scanlines + CRT ---------------------------------------- */ +body#keycloak-bg { + font-family: var(--hk-font) !important; + background: var(--hk-bg) !important; + color: var(--hk-text) !important; + min-height: 100vh; + position: relative; +} + +body#keycloak-bg::before { + content: ''; + position: fixed; + inset: 0; + background: repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.03) 2px, + rgba(0, 255, 65, 0.03) 4px + ); + pointer-events: none; + z-index: 9999; +} + +/* -- Login container ----------------------------------------------------- */ +.pf-v5-c-login { + background: transparent !important; +} + +.pf-v5-c-login__container { + max-width: 480px !important; +} + +/* -- Header / Brand ------------------------------------------------------ */ +#kc-header-wrapper { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 10px var(--hk-green), 0 0 30px var(--hk-green-glow); + font-size: 1.5rem !important; + letter-spacing: 0.15em; + text-transform: uppercase; +} + +#kc-header-wrapper .kc-logo-text span::before { + content: '> '; + opacity: 0.6; +} + +/* -- Main card ----------------------------------------------------------- */ +.pf-v5-c-login__main { + background: var(--hk-panel) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + box-shadow: + 0 0 15px var(--hk-green-glow), + inset 0 0 30px rgba(0, 0, 0, 0.5) !important; +} + +/* -- Title --------------------------------------------------------------- */ +h1#kc-page-title, +.pf-v5-c-title { + font-family: var(--hk-font) !important; + color: var(--hk-green) !important; + text-shadow: 0 0 8px var(--hk-green-glow); + font-weight: 600 !important; + font-size: 1.25rem !important; + letter-spacing: 0.05em; +} + +/* -- Labels -------------------------------------------------------------- */ +.pf-v5-c-form__label-text { + font-family: var(--hk-font) !important; + color: var(--hk-green-dim) !important; + font-size: 0.85rem !important; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +/* -- Input fields -------------------------------------------------------- */ +.pf-v5-c-form-control { + background-color: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-size: 0.9rem !important; + caret-color: var(--hk-green); + transition: border-color 0.2s, box-shadow 0.2s; +} + +.pf-v5-c-form-control:focus-within, +.pf-v5-c-form-control:focus { + border-color: var(--hk-green) !important; + box-shadow: 0 0 8px var(--hk-green-glow), inset 0 0 4px var(--hk-green-glow) !important; + outline: none !important; +} + +.pf-v5-c-form-control input { + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; +} + +.pf-v5-c-form-control input::placeholder { + color: var(--hk-text-dim) !important; +} + +/* -- Primary button (Sign In) -------------------------------------------- */ +.pf-v5-c-button.pf-m-primary { + background: transparent !important; + border: 1px solid var(--hk-green) !important; + border-radius: 0 !important; + color: var(--hk-green) !important; + font-family: var(--hk-font) !important; + font-weight: 600 !important; + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 0.9rem !important; + transition: all 0.2s; + position: relative; + overflow: hidden; +} + +.pf-v5-c-button.pf-m-primary:hover { + background: var(--hk-green-glow) !important; + box-shadow: 0 0 15px var(--hk-green-glow-strong), inset 0 0 15px var(--hk-green-glow) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-green); +} + +/* -- Secondary button (Try Another Way, Passkey) ------------------------- */ +.pf-v5-c-button.pf-m-secondary { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + transition: all 0.2s; +} + +.pf-v5-c-button.pf-m-secondary:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 12px rgba(0, 229, 255, 0.3) !important; + color: #fff !important; +} + +/* -- Passkey authenticate button ----------------------------------------- */ +#authenticateWebAuthnButton { + background: transparent !important; + border: 1px solid var(--hk-cyan) !important; + border-radius: 0 !important; + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem !important; + padding: 0.75rem 1rem; + display: block; + width: 100%; + text-align: center; + text-decoration: none; + transition: all 0.2s; + margin-top: 1rem; +} + +#authenticateWebAuthnButton::before { + content: '\f084 '; + font-family: 'Font Awesome 6 Free', 'FontAwesome'; + font-weight: 900; +} + +#authenticateWebAuthnButton:hover { + background: rgba(0, 229, 255, 0.1) !important; + box-shadow: 0 0 15px rgba(0, 229, 255, 0.3), inset 0 0 10px rgba(0, 229, 255, 0.1) !important; + color: #fff !important; + text-shadow: 0 0 5px var(--hk-cyan); +} + +/* -- Show password button ------------------------------------------------ */ +.pf-v5-c-button.pf-m-control { + background: var(--hk-input-bg) !important; + border: 1px solid var(--hk-border) !important; + border-left: none !important; + border-radius: 0 !important; + color: var(--hk-text-dim) !important; +} + +.pf-v5-c-button.pf-m-control:hover { + color: var(--hk-green) !important; +} + +/* -- Social providers (Google, etc) -------------------------------------- */ +.pf-v5-c-login__main-footer { + border-top: 1px solid var(--hk-border) !important; +} + +.kc-social-section .pf-v5-c-button.pf-m-secondary { + border-color: var(--hk-text-dim) !important; + color: var(--hk-text) !important; +} + +.kc-social-section .pf-v5-c-button.pf-m-secondary:hover { + border-color: var(--hk-green) !important; + color: var(--hk-green) !important; + box-shadow: 0 0 10px var(--hk-green-glow) !important; +} + +/* -- Try another way link ------------------------------------------------ */ +#try-another-way { + color: var(--hk-cyan) !important; + text-decoration: none !important; + font-family: var(--hk-font) !important; +} + +#try-another-way:hover { + text-shadow: 0 0 8px rgba(0, 229, 255, 0.5); +} + +/* -- Links --------------------------------------------------------------- */ +a { + color: var(--hk-cyan) !important; + text-decoration: none; + transition: text-shadow 0.2s; +} + +a:hover { + text-shadow: 0 0 6px rgba(0, 229, 255, 0.4); +} + +/* -- Alerts / messages --------------------------------------------------- */ +.pf-v5-c-alert { + background: rgba(0, 255, 65, 0.05) !important; + border: 1px solid var(--hk-border) !important; + border-radius: 0 !important; + font-family: var(--hk-font) !important; +} + +.pf-v5-c-alert.pf-m-danger { + border-color: var(--hk-red) !important; + background: rgba(255, 51, 51, 0.05) !important; +} + +.pf-v5-c-alert__title { + color: var(--hk-text) !important; + font-family: var(--hk-font) !important; +} + +/* -- Checkbox (Remember me) ---------------------------------------------- */ +.pf-v5-c-check__label { + color: var(--hk-text-dim) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; +} + +/* -- Helper text (Forgot password, etc.) --------------------------------- */ +.pf-v5-c-helper-text .pf-v5-c-button.pf-m-link { + color: var(--hk-cyan) !important; + font-family: var(--hk-font) !important; + font-size: 0.8rem !important; +} + +/* -- Select auth list (Try another way options) -------------------------- */ +.select-auth-container { + background: var(--hk-panel) !important; + font-family: var(--hk-font) !important; +} + +.pf-v5-c-data-list__item { + border-color: var(--hk-border) !important; + background: transparent !important; +} + +.pf-v5-c-data-list__item:hover { + background: var(--hk-green-glow) !important; +} + +.select-auth-box-headline { + color: var(--hk-green) !important; +} + +.select-auth-box-desc { + color: var(--hk-text-dim) !important; +} + +/* -- Footer -------------------------------------------------------------- */ +.pf-v5-c-login__main-footer-band { + background: transparent !important; + border-top: 1px solid var(--hk-border) !important; +} + +/* -- Language selector --------------------------------------------------- */ +select#login-select-toggle { + background: var(--hk-input-bg) !important; + color: var(--hk-green) !important; + border-color: var(--hk-border) !important; + font-family: var(--hk-font) !important; +} + +/* -- Blinking cursor animation for header -------------------------------- */ +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +#kc-header-wrapper::after { + content: '_'; + animation: blink 1s step-end infinite; + color: var(--hk-green); +} + +/* -- Subtle CRT flicker ------------------------------------------------- */ +@keyframes flicker { + 0% { opacity: 0.97; } + 5% { opacity: 0.95; } + 10% { opacity: 0.98; } + 15% { opacity: 0.96; } + 20% { opacity: 0.99; } + 100% { opacity: 0.98; } +} + +.pf-v5-c-login__main { + animation: flicker 4s infinite; +} diff --git a/k8s/core/keycloak/theme/login/theme.properties b/k8s/core/keycloak/theme/login/theme.properties new file mode 100644 index 0000000..4226f22 --- /dev/null +++ b/k8s/core/keycloak/theme/login/theme.properties @@ -0,0 +1,4 @@ +parent=keycloak.v2 +import=common/keycloak + +styles=css/hacker.css diff --git a/k8s/core/keycloak/values.yaml b/k8s/core/keycloak/values.yaml index 77eaf24..26303f6 100644 --- a/k8s/core/keycloak/values.yaml +++ b/k8s/core/keycloak/values.yaml @@ -21,6 +21,19 @@ extraEnv: | - name: JAVA_OPTS_APPEND value: "-Djgroups.dns.query=keycloak-headless.keycloak.svc" +extraVolumes: | + - name: hacker-theme + configMap: + name: keycloak-hacker-theme + +extraVolumeMounts: | + - name: hacker-theme + mountPath: /opt/keycloak/themes/hacker/login/theme.properties + subPath: theme.properties + - name: hacker-theme + mountPath: /opt/keycloak/themes/hacker/login/css/hacker.css + subPath: hacker.css + dbchecker: enabled: true