Compare commits

...

21 Commits

Author SHA1 Message Date
Gitea Actions Bot 0fc585f17c Auto-update README with current k8s applications
Keycloak Terraform / Terraform (pull_request) Successful in 19s
Generated by CI/CD workflow on 2026-05-18 23:58:07

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2026-05-18 23:58:07 +00:00
ab 0cb0cf9291 Added keycloak theme
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
Keycloak Terraform / Terraform (push) Failing after 14s
Check with kubeconform / lint (push) Successful in 8s
Auto-update README / Generate README and Create MR (push) Successful in 23s
2026-05-19 02:57:24 +03:00
ab 2cca415c2a Added keycloak theme 2026-05-19 02:57:18 +03:00
ab b5a033c043 Added WebAuth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Keycloak Terraform / Terraform (push) Successful in 17s
2026-05-19 02:36:05 +03:00
ab 11357657bb Added WebAuth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Keycloak Terraform / Terraform (push) Successful in 20s
2026-05-19 02:13:37 +03:00
ab 49e7b3b415 Added WebAuth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Keycloak Terraform / Terraform (push) Successful in 15s
2026-05-19 01:57:33 +03:00
ab 555969120e Added WebAuth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Keycloak Terraform / Terraform (push) Successful in 14s
2026-05-19 01:55:50 +03:00
ab 0b95b936ec Added WebAuth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Keycloak Terraform / Terraform (push) Successful in 18s
2026-05-19 01:54:42 +03:00
ab af4242950e Added OIDC auth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 6s
Keycloak Terraform / Terraform (push) Successful in 16s
2026-05-19 00:40:43 +03:00
ab d9d2143a7c Added OIDC auth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 6s
Keycloak Terraform / Terraform (push) Successful in 13s
2026-05-19 00:00:46 +03:00
ab d3d7810585 Added OIDC auth
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Keycloak Terraform / Terraform (push) Successful in 21s
2026-05-18 23:59:00 +03:00
ab 2afd3807d2 Added web-petting
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
Keycloak Terraform / Terraform (push) Successful in 15s
2026-05-18 23:44:25 +03:00
ab 8749818a9c Added keycloak TF
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
Keycloak Terraform / Terraform (push) Successful in 28s
2026-05-18 23:38:48 +03:00
ab cad267b8cd gitea test
Auto-update README / Generate README and Create MR (push) Successful in 22s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 37s
Check with kubeconform / lint (push) Failing after 10m44s
2026-05-18 23:00:20 +03:00
ab c340ad4be5 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
Auto-update README / Generate README and Create MR (push) Failing after 6s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 40s
Check with kubeconform / lint (push) Failing after 11m28s
2026-05-18 22:44:28 +03:00
ab 15cb85b11a gitea test 2026-05-18 22:44:10 +03:00
Ultradesu 0ae912c478 Moved web-petting to master
Check with kubeconform / lint (push) Successful in 6s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2026-05-18 14:58:49 +03:00
Ultradesu 374a825514 Moved web-petting to spb
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 8s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2026-05-18 14:56:21 +03:00
Ultradesu 790ba2c3de Moved argocd to keycloac 2026-05-18 14:54:51 +03:00
ab 4240cd61ad Update k8s/apps/pasarguard/deployment.yaml
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Check with kubeconform / lint (push) Successful in 7s
Auto-update README / Generate README and Create MR (push) Successful in 16s
2026-05-17 10:41:15 +00:00
ab a9f9c29679 Added recreate to web-petting
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 18s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 9s
2026-05-14 16:18:55 +03:00
12 changed files with 885 additions and 114 deletions
+14 -109
View File
@@ -1,10 +1,10 @@
name: 'Terraform'
name: 'Keycloak Terraform'
on:
push:
branches: [ "main" ]
paths:
- 'terraform/authentik/**'
- 'terraform/keycloak/**'
pull_request:
permissions:
@@ -13,7 +13,6 @@ permissions:
jobs:
terraform:
name: 'Terraform'
if: false
runs-on: ubuntu-latest
environment: production
@@ -30,119 +29,25 @@ jobs:
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Setup kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
chmod 600 ~/.kube/config
- name: Terraform Init
env:
TF_VAR_authentik_token: ${{ secrets.AUTHENTIK_TOKEN }}
run: terraform init
working-directory: ./terraform/authentik
working-directory: ./terraform/keycloak
- name: Terraform Format
env:
TF_VAR_authentik_token: ${{ secrets.AUTHENTIK_TOKEN }}
run: terraform fmt -check
continue-on-error: true
working-directory: ./terraform/authentik
working-directory: ./terraform/keycloak
- name: Terraform Apply
env:
TF_VAR_authentik_token: ${{ secrets.AUTHENTIK_TOKEN }}
TF_VAR_keycloak_client_secret: ${{ secrets.KEYCLOAK_CLIENT_SECRET }}
TF_VAR_google_client_id: ${{ secrets.GOOGLE_CLIENT_ID }}
TF_VAR_google_client_secret: ${{ secrets.GOOGLE_CLIENT_SECRET }}
run: terraform apply -input=false -auto-approve -parallelism=100
working-directory: ./terraform/authentik
- name: Generate Wiki Content
if: success()
continue-on-error: true
env:
TF_VAR_authentik_token: ${{ secrets.AUTHENTIK_TOKEN }}
run: |
echo "📋 Starting Wiki generation..."
cd ./terraform/authentik
# Get terraform output
echo "🔍 Generating Terraform output..."
terraform output -json applications_for_wiki > terraform-raw-output.json 2>&1
# Process output to extract clean JSON
echo "📤 Processing Terraform output..."
python3 ../../.gitea/scripts/process-terraform-output.py terraform-raw-output.json terraform-output.json
# Run wiki generation
echo "📊 Running wiki generation script..."
if python3 ../../.gitea/scripts/generate-apps-wiki.py terraform-output.json; then
echo "✅ Wiki content generated successfully"
else
echo "⚠️ Wiki generation failed, retrying with debug..."
python3 ../../.gitea/scripts/generate-apps-wiki.py terraform-output.json --debug || echo "⚠️ Wiki generation failed"
fi
# Check results
if [ -f "Applications.md" ]; then
echo "✅ Wiki file created: $(wc -l < Applications.md) lines"
else
echo "⚠️ Wiki content not generated"
exit 0
fi
working-directory: ./
- name: Upload Wiki to Gitea
if: success()
continue-on-error: true
run: |
cd ./terraform/authentik
# Set variables
GITEA_URL="${{ secrets.GT_URL }}"
GITEA_TOKEN="${{ secrets.GT_WIKI_TOKEN }}"
GITEA_OWNER="${{ secrets.GT_OWNER }}"
GITEA_REPO="${{ secrets.GT_REPO }}"
# Debug variables (without exposing token)
echo "🔍 Checking variables..."
echo "GITEA_URL: ${GITEA_URL:-NOT SET}"
echo "GITEA_OWNER: ${GITEA_OWNER:-NOT SET}"
echo "GITEA_REPO: ${GITEA_REPO:-NOT SET}"
echo "GITEA_TOKEN: $(if [ -n "$GITEA_TOKEN" ]; then echo "SET"; else echo "NOT SET"; fi)"
# Check if file exists
if [ ! -f "Applications.md" ]; then
echo "⚠️ Applications.md not found, skipping wiki update"
exit 0
fi
echo "📤 Uploading to Gitea Wiki..."
# Encode content to base64
CONTENT=$(base64 -w 0 Applications.md)
# Check if wiki page exists
WIKI_PAGE_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Applications" || echo "000")
if [ "$WIKI_PAGE_EXISTS" = "200" ]; then
echo "📝 Updating existing wiki page..."
curl -X PATCH \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Applications\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Update applications list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Applications" || echo "⚠️ Wiki update failed"
else
echo "📄 Creating new wiki page..."
curl -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Applications\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Create applications list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/new" || echo "⚠️ Wiki creation failed"
fi
echo "✅ Wiki update process completed"
working-directory: ./
working-directory: ./terraform/keycloak
+6
View File
@@ -13,10 +13,13 @@ ArgoCD homelab project
| Application | Status |
| :--- | :---: |
| **argocd** | [![argocd](https://ag.hexor.cy/api/badge?name=argocd&revision=true)](https://ag.hexor.cy/applications/argocd/argocd) |
| **auth-proxy** | [![auth-proxy](https://ag.hexor.cy/api/badge?name=auth-proxy&revision=true)](https://ag.hexor.cy/applications/argocd/auth-proxy) |
| **authentik** | [![authentik](https://ag.hexor.cy/api/badge?name=authentik&revision=true)](https://ag.hexor.cy/applications/argocd/authentik) |
| **cert-manager** | [![cert-manager](https://ag.hexor.cy/api/badge?name=cert-manager&revision=true)](https://ag.hexor.cy/applications/argocd/cert-manager) |
| **external-secrets** | [![external-secrets](https://ag.hexor.cy/api/badge?name=external-secrets&revision=true)](https://ag.hexor.cy/applications/argocd/external-secrets) |
| **gpu** | [![gpu](https://ag.hexor.cy/api/badge?name=gpu&revision=true)](https://ag.hexor.cy/applications/argocd/gpu) |
| **kanidm** | [![kanidm](https://ag.hexor.cy/api/badge?name=kanidm&revision=true)](https://ag.hexor.cy/applications/argocd/kanidm) |
| **keycloak** | [![keycloak](https://ag.hexor.cy/api/badge?name=keycloak&revision=true)](https://ag.hexor.cy/applications/argocd/keycloak) |
| **kube-system-custom** | [![kube-system-custom](https://ag.hexor.cy/api/badge?name=kube-system-custom&revision=true)](https://ag.hexor.cy/applications/argocd/kube-system-custom) |
| **kubernetes-dashboard** | [![kubernetes-dashboard](https://ag.hexor.cy/api/badge?name=kubernetes-dashboard&revision=true)](https://ag.hexor.cy/applications/argocd/kubernetes-dashboard) |
| **longhorn** | [![longhorn](https://ag.hexor.cy/api/badge?name=longhorn&revision=true)](https://ag.hexor.cy/applications/argocd/longhorn) |
@@ -62,9 +65,12 @@ ArgoCD homelab project
| **sonarr-stack** | [![sonarr-stack](https://ag.hexor.cy/api/badge?name=sonarr-stack&revision=true)](https://ag.hexor.cy/applications/argocd/sonarr-stack) |
| **stirling-pdf** | [![stirling-pdf](https://ag.hexor.cy/api/badge?name=stirling-pdf&revision=true)](https://ag.hexor.cy/applications/argocd/stirling-pdf) |
| **syncthing** | [![syncthing](https://ag.hexor.cy/api/badge?name=syncthing&revision=true)](https://ag.hexor.cy/applications/argocd/syncthing) |
| **teamspeak** | [![teamspeak](https://ag.hexor.cy/api/badge?name=teamspeak&revision=true)](https://ag.hexor.cy/applications/argocd/teamspeak) |
| **tg-bots** | [![tg-bots](https://ag.hexor.cy/api/badge?name=tg-bots&revision=true)](https://ag.hexor.cy/applications/argocd/tg-bots) |
| **vaultwarden** | [![vaultwarden](https://ag.hexor.cy/api/badge?name=vaultwarden&revision=true)](https://ag.hexor.cy/applications/argocd/vaultwarden) |
| **vpn** | [![vpn](https://ag.hexor.cy/api/badge?name=vpn&revision=true)](https://ag.hexor.cy/applications/argocd/vpn) |
| **web-petting** | [![web-petting](https://ag.hexor.cy/api/badge?name=web-petting&revision=true)](https://ag.hexor.cy/applications/argocd/web-petting) |
| **wedding** | [![wedding](https://ag.hexor.cy/api/badge?name=wedding&revision=true)](https://ag.hexor.cy/applications/argocd/wedding) |
| **xandikos** | [![xandikos](https://ag.hexor.cy/api/badge?name=xandikos&revision=true)](https://ag.hexor.cy/applications/argocd/xandikos) |
</td>
+1 -1
View File
@@ -1,5 +1,5 @@
---
image: &image 'pasarguard/panel:v3.1.0'
image: &image 'pasarguard/panel:v4.0.2'
apiVersion: apps/v1
kind: Deployment
metadata:
+3
View File
@@ -6,6 +6,8 @@ metadata:
app: web-petting
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: web-petting
@@ -16,6 +18,7 @@ spec:
spec:
nodeSelector:
kubernetes.io/os: linux
kubernetes.io/hostname: master.tail2fe2d.ts.net
volumes:
- name: data
persistentVolumeClaim:
@@ -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;
}
+1
View File
@@ -4,6 +4,7 @@ kind: Kustomization
resources:
- app.yaml
- external-secrets.yaml
- hacker-theme-configmap.yaml
helmCharts:
- name: keycloakx
@@ -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;
}
@@ -0,0 +1,4 @@
parent=keycloak.v2
import=common/keycloak
styles=css/hacker.css
+13
View File
@@ -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
+2
View File
@@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/kubernetes" {
constraints = ">= 2.0.0"
hashes = [
"h1:G9QqKNpcztBRqrywtlNylFJSpGzDfRFtO8hcWLdkvRY=",
"h1:iDIPyL9K9FXc8ievR5vRmXsgT1/I9Vq9CFDKm1Zo8bQ=",
"zh:0215c5c60be62028c09a2f22458e89cda3ef5830a632299f1d401eb3538874b0",
"zh:09ebb9f442431e278a310a9423f32caf467cb4b3cad3fe59573ca71fa7b14e20",
"zh:0c4e5912f83bb35846ae0a9ae54fc320706ee61894cd21cc6b4181b1c5a2fa5c",
@@ -26,6 +27,7 @@ provider "registry.terraform.io/keycloak/keycloak" {
constraints = ">= 5.0.0"
hashes = [
"h1:3DuKdVeOxwULh7l6bvJKWZvsgSZo92rtnrdvyp+X2Lc=",
"h1:reFEBcuBZf2SVqDa1YIaNLuIW72uEVsOu5hHhQgfHi0=",
"zh:19be4505b17e4818db121a82917cb6723019cf379cfb82b720eaa2886f15bede",
"zh:2bd1565ed22db6a9fb50d60626e22c277f3b034a71f65e6c0011e42f56cad2bb",
"zh:34a9e2dfb06331dc6146491c4a0721001195c6a769cdc2d4546edb2acf2b39bd",
+115 -3
View File
@@ -8,7 +8,7 @@ resource "keycloak_realm" "hexor" {
display_name = "Hexor"
login_theme = "keycloak"
login_theme = "hacker"
account_theme = "keycloak.v3"
registration_allowed = false
@@ -19,6 +19,119 @@ resource "keycloak_realm" "hexor" {
duplicate_emails_allowed = false
ssl_required = "external"
web_authn_passwordless_policy {
relying_party_entity_name = "Hexor"
relying_party_id = "hexor.cy"
signature_algorithms = ["ES256", "RS256"]
user_verification_requirement = "required"
require_resident_key = "Yes"
attestation_conveyance_preference = "none"
}
}
# =============================================================================
# Passkey (WebAuthn Passwordless) — required action
# =============================================================================
resource "keycloak_required_action" "webauthn_register_passwordless" {
realm_id = keycloak_realm.hexor.id
alias = "webauthn-register-passwordless"
name = "Webauthn Register Passwordless"
enabled = true
default_action = false
}
# =============================================================================
# Browser flow with Passkey support
# =============================================================================
resource "keycloak_authentication_flow" "browser_with_passkey" {
realm_id = keycloak_realm.hexor.id
alias = "browser-with-passkey"
description = "Browser flow with passkey/password alternatives"
provider_id = "basic-flow"
}
# --- Cookie (re-use existing session) ---
resource "keycloak_authentication_execution" "cookie" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias
authenticator = "auth-cookie"
requirement = "ALTERNATIVE"
priority = 10
}
# --- Identity Provider Redirector ---
resource "keycloak_authentication_execution" "idp_redirector" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias
authenticator = "identity-provider-redirector"
requirement = "ALTERNATIVE"
priority = 20
}
# --- Passkey (WebAuthn Passwordless) ---
resource "keycloak_authentication_execution" "passkey" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias
authenticator = "webauthn-authenticator-passwordless"
requirement = "ALTERNATIVE"
priority = 30
}
# --- Username/Password + optional 2FA subflow ---
resource "keycloak_authentication_subflow" "forms" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_flow.browser_with_passkey.alias
alias = "browser-with-passkey-forms"
provider_id = "basic-flow"
requirement = "ALTERNATIVE"
priority = 40
}
resource "keycloak_authentication_execution" "username_password" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_subflow.forms.alias
authenticator = "auth-username-password-form"
requirement = "REQUIRED"
priority = 10
}
resource "keycloak_authentication_subflow" "conditional_2fa" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_subflow.forms.alias
alias = "browser-with-passkey-conditional-2fa"
provider_id = "basic-flow"
requirement = "CONDITIONAL"
priority = 20
}
resource "keycloak_authentication_execution" "condition_user_configured" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias
authenticator = "conditional-user-configured"
requirement = "REQUIRED"
priority = 10
}
resource "keycloak_authentication_execution" "otp_form" {
realm_id = keycloak_realm.hexor.id
parent_flow_alias = keycloak_authentication_subflow.conditional_2fa.alias
authenticator = "auth-otp-form"
requirement = "ALTERNATIVE"
priority = 20
}
# --- Bind the new flow as the browser flow ---
resource "keycloak_authentication_bindings" "browser" {
realm_id = keycloak_realm.hexor.id
browser_flow = keycloak_authentication_flow.browser_with_passkey.alias
}
# =============================================================================
@@ -86,7 +199,6 @@ resource "keycloak_openid_client_default_scopes" "rsauth2_proxy" {
client_id = keycloak_openid_client.rsauth2_proxy.id
default_scopes = [
"openid",
"profile",
"email",
]
@@ -150,7 +262,7 @@ resource "keycloak_openid_client_default_scopes" "oauth2_app" {
client_id = keycloak_openid_client.oauth2_app[each.key].id
default_scopes = concat(
["openid", "profile", "email"],
["profile", "email"],
each.value.extra_default_scopes
)
}
+7 -1
View File
@@ -3,6 +3,7 @@ groups = [
"hexor-guest",
"game-servers-managers",
"argocd-admins",
"web-petting"
]
proxy_applications = {
@@ -23,7 +24,7 @@ oauth2_applications = {
post_logout_redirect_uris = ["https://gt.hexor.cy/*"]
}
ArgoCD = {
redirect_uris = ["https://ag.hexor.cy/auth/callback"]
redirect_uris = ["https://ag.hexor.cy/api/dex/callback"]
web_origins = ["https://ag.hexor.cy"]
post_logout_redirect_uris = ["https://ag.hexor.cy/*"]
extra_optional_scopes = ["offline_access"]
@@ -33,5 +34,10 @@ oauth2_applications = {
web_origins = ["https://gf.hexor.cy"]
post_logout_redirect_uris = ["https://gf.hexor.cy/*"]
}
Web-Petting = {
redirect_uris = ["https://pet.hexor.cy/admin/oidc/callback", "https://xn--l1acako8eb.xn--p1ai/admin/oidc/callback", "https://мурняня.рф/admin/oidc/callback", "http://127.0.0.1:3000/admin/oidc/callback"]
web_origins = ["https://pet.hexor.cy", "https://xn--l1acako8eb.xn--p1ai", "https://мурняня.рф", "http://127.0.0.1:3000"]
post_logout_redirect_uris = ["https://pet.hexor.cy/*", "https://xn--l1acako8eb.xn--p1ai/*", "https://мурняня.рф/*", "http://127.0.0.1:3000/*"]
}
}