NG: added Keycloak
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
This commit is contained in:
1 parent
4fe2a2b5f3
commit
e257e3895b
9 files changed
+764
-3
No files matched your search
@@ -48,3 +48,8 @@ apps:
|
||||
namespace: vaultwarden
|
||||
wave: 3
|
||||
skipDryRunOnMissingResource: true
|
||||
- name: keycloak
|
||||
path: k8s-ng/core/keycloak
|
||||
namespace: keycloak
|
||||
wave: 3
|
||||
skipDryRunOnMissingResource: true # ExternalSecret/Certificate CRDs come from earlier waves
|
||||
@@ -1,7 +1,10 @@
|
||||
# Public certificates through DNS-01. The Cloudflare token Secret is created by hand
|
||||
# (it will move to Vaultwarden + External Secrets):
|
||||
# Public certificates through DNS-01. The credential Secrets are created by hand
|
||||
# (they will move to Vaultwarden + External Secrets):
|
||||
# kubectl -n cert-manager create secret generic cloudflare-api-token --from-literal=apiToken=<token>
|
||||
# The token needs Zone:DNS:Edit on the btwiusearch.net zone.
|
||||
# (Zone:DNS:Edit on the btwiusearch.net zone)
|
||||
# kubectl -n cert-manager create secret generic aws-api-key-secret \
|
||||
# --from-literal=accessKey=<id> --from-literal=secretKey=<secret>
|
||||
# (Route53 access to the hexor.cy zone)
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
@@ -21,3 +24,15 @@ spec:
|
||||
apiTokenSecretRef:
|
||||
name: cloudflare-api-token
|
||||
key: apiToken
|
||||
- selector:
|
||||
dnsZones:
|
||||
- hexor.cy
|
||||
dns01:
|
||||
route53:
|
||||
region: eu-north-1
|
||||
accessKeyIDSecretRef:
|
||||
name: aws-api-key-secret
|
||||
key: accessKey
|
||||
secretAccessKeySecretRef:
|
||||
name: aws-api-key-secret
|
||||
key: secretKey
|
||||
@@ -0,0 +1,122 @@
|
||||
# rsauth2-proxy: forward-auth for services that have no OIDC of their own.
|
||||
# Needs, besides this file:
|
||||
# - Secret auth-proxy-creds (AUTH_PROXY_CLIENT_ID, AUTH_PROXY_CLIENT_SECRET,
|
||||
# AUTH_PROXY_COOKIE_SECRET); the client secret is the one of the Keycloak
|
||||
# client "rsauth2-proxy". Created by hand until Vaultwarden is the source.
|
||||
# - ConfigMap auth-proxy-routes, managed by Terraform (terraform/keycloak).
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: auth-proxy
|
||||
labels:
|
||||
app: auth-proxy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: auth-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: auth-proxy
|
||||
spec:
|
||||
containers:
|
||||
- name: auth-proxy
|
||||
image: ultradesu/rsauth2-proxy:latest
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: auth-proxy-creds
|
||||
env:
|
||||
- name: AUTH_PROXY_OIDC_ISSUER
|
||||
value: "https://auth.hexor.cy/auth/realms/hexor"
|
||||
- name: AUTH_PROXY_COOKIE_DOMAIN
|
||||
value: ".hexor.cy"
|
||||
- name: AUTH_PROXY_CALLBACK_URL
|
||||
value: "https://oauth.hexor.cy/callback"
|
||||
- name: AUTH_PROXY_ROUTES_FILE
|
||||
value: "/config/routes.yaml"
|
||||
- name: AUTH_PROXY_LOG_LEVEL
|
||||
value: "debug"
|
||||
volumeMounts:
|
||||
- name: routes
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 64Mi
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 30
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
volumes:
|
||||
- name: routes
|
||||
configMap:
|
||||
name: auth-proxy-routes
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: auth-proxy
|
||||
labels:
|
||||
app: auth-proxy
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: auth-proxy
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: auth-proxy
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`oauth.hexor.cy`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: auth-proxy
|
||||
port: 80
|
||||
tls:
|
||||
secretName: auth-proxy-tls
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: auth-proxy-tls
|
||||
spec:
|
||||
secretName: auth-proxy-tls
|
||||
issuerRef:
|
||||
name: letsencrypt
|
||||
kind: ClusterIssuer
|
||||
dnsNames:
|
||||
- oauth.hexor.cy
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- secrets.yaml
|
||||
- hacker-theme-configmap.yaml
|
||||
- auth-proxy.yaml
|
||||
# ConfigMap auth-proxy-routes is managed by Terraform (terraform/keycloak, kubernetes_config_map_v1).
|
||||
|
||||
helmCharts:
|
||||
- name: keycloakx
|
||||
repo: https://codecentric.github.io/helm-charts
|
||||
version: 7.3.1
|
||||
releaseName: keycloak
|
||||
namespace: keycloak
|
||||
valuesFile: values.yaml
|
||||
@@ -0,0 +1,80 @@
|
||||
# Read-only access to the keycloak-db Secret in the psql namespace
|
||||
# (see postgresql/databases/keycloak.yaml for the other half).
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: db-credentials-reader
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: SecretStore
|
||||
metadata:
|
||||
name: psql-credentials
|
||||
spec:
|
||||
provider:
|
||||
kubernetes:
|
||||
remoteNamespace: psql
|
||||
server:
|
||||
caProvider:
|
||||
type: ConfigMap
|
||||
name: kube-root-ca.crt
|
||||
key: ca.crt
|
||||
auth:
|
||||
serviceAccount:
|
||||
name: db-credentials-reader
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: keycloak-creds
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: SecretStore
|
||||
name: psql-credentials
|
||||
target:
|
||||
name: keycloak-creds
|
||||
template:
|
||||
data:
|
||||
KC_DB_USERNAME: "{{ .username }}"
|
||||
KC_DB_PASSWORD: "{{ .password }}"
|
||||
data:
|
||||
- secretKey: username
|
||||
remoteRef:
|
||||
key: keycloak-db
|
||||
property: username
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: keycloak-db
|
||||
property: password
|
||||
---
|
||||
# Bootstrap admin: only used when the database has no admin user yet (a fresh
|
||||
# install). With the restored database the existing admin accounts are kept.
|
||||
apiVersion: generators.external-secrets.io/v1alpha1
|
||||
kind: Password
|
||||
metadata:
|
||||
name: keycloak-bootstrap
|
||||
spec:
|
||||
length: 40
|
||||
digits: 10
|
||||
symbols: 0
|
||||
noUpper: false
|
||||
allowRepeat: true
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: keycloak-bootstrap
|
||||
spec:
|
||||
refreshPolicy: CreatedOnce
|
||||
target:
|
||||
name: keycloak-bootstrap
|
||||
template:
|
||||
data:
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME: admin
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: "{{ .password }}"
|
||||
dataFrom:
|
||||
- sourceRef:
|
||||
generatorRef:
|
||||
apiVersion: generators.external-secrets.io/v1alpha1
|
||||
kind: Password
|
||||
name: keycloak-bootstrap
|
||||
@@ -0,0 +1,75 @@
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: quay.io/keycloak/keycloak
|
||||
tag: "26.5.6"
|
||||
|
||||
command:
|
||||
- "/opt/keycloak/bin/kc.sh"
|
||||
- "start"
|
||||
- "--http-port=8080"
|
||||
- "--hostname-strict=false"
|
||||
- "--proxy-headers=xforwarded"
|
||||
|
||||
extraEnvFrom: |
|
||||
- secretRef:
|
||||
name: keycloak-creds
|
||||
- secretRef:
|
||||
name: keycloak-bootstrap
|
||||
|
||||
extraEnv: |
|
||||
- name: KC_HOSTNAME
|
||||
value: auth.hexor.cy
|
||||
- name: JAVA_OPTS_APPEND
|
||||
value: "-Djgroups.dns.query=keycloak-keycloakx-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
|
||||
|
||||
database:
|
||||
vendor: postgres
|
||||
hostname: main-rw.psql.svc
|
||||
port: 5432
|
||||
database: keycloak
|
||||
existingSecret: keycloak-creds
|
||||
existingSecretKey: KC_DB_PASSWORD
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
# HTTP is redirected to HTTPS by the cluster-wide Traefik default (core/traefik-config).
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: traefik
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
rules:
|
||||
- host: auth.hexor.cy
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: keycloak-tls
|
||||
hosts:
|
||||
- auth.hexor.cy
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: "3"
|
||||
memory: 2Gi
|
||||
@@ -0,0 +1,81 @@
|
||||
# Database "keycloak": password, role, database and read access for the app.
|
||||
# To add another database copy this file and replace the name.
|
||||
|
||||
# Generated once; the role and the application both read this Secret.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: keycloak-db
|
||||
namespace: psql
|
||||
spec:
|
||||
refreshPolicy: CreatedOnce
|
||||
target:
|
||||
name: keycloak-db
|
||||
template:
|
||||
type: kubernetes.io/basic-auth
|
||||
metadata:
|
||||
labels:
|
||||
cnpg.io/reload: "true"
|
||||
data:
|
||||
username: keycloak
|
||||
password: "{{ .password }}"
|
||||
dataFrom:
|
||||
- sourceRef:
|
||||
generatorRef:
|
||||
apiVersion: generators.external-secrets.io/v1alpha1
|
||||
kind: Password
|
||||
name: db-password
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: DatabaseRole
|
||||
metadata:
|
||||
name: main-keycloak
|
||||
namespace: psql
|
||||
spec:
|
||||
cluster:
|
||||
name: main
|
||||
name: keycloak
|
||||
login: true
|
||||
passwordSecret:
|
||||
name: keycloak-db
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Database
|
||||
metadata:
|
||||
name: main-keycloak
|
||||
namespace: psql
|
||||
spec:
|
||||
cluster:
|
||||
name: main
|
||||
name: keycloak
|
||||
owner: keycloak
|
||||
databaseReclaimPolicy: retain
|
||||
---
|
||||
# Lets the keycloak namespace read only its own credentials Secret.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: keycloak-db-reader
|
||||
namespace: psql
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: [secrets]
|
||||
resourceNames: [keycloak-db]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups: [authorization.k8s.io]
|
||||
resources: [selfsubjectrulesreviews]
|
||||
verbs: [create]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: keycloak-db-reader
|
||||
namespace: psql
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: keycloak-db-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: db-credentials-reader
|
||||
namespace: keycloak
|
||||
@@ -5,3 +5,4 @@ resources:
|
||||
- cluster.yaml
|
||||
- password-generator.yaml
|
||||
- databases/vaultwarden.yaml
|
||||
- databases/keycloak.yaml
|
||||
Reference in new issue
Block a user