Compare commits

...

2 Commits

Author SHA1 Message Date
35a19a2f7b outline: move to local storage 2026-01-12 23:14:17 -03:00
1a50f51115 planka: enable project owners via oidc 2026-01-12 23:14:17 -03:00
14 changed files with 79 additions and 207 deletions

View File

@ -25,6 +25,5 @@ resources:
- ai-llm/kustomization.yaml
- nextcloud/kustomization.yaml
- nextcloud-mail-sync/kustomization.yaml
- minio/kustomization.yaml
- outline/kustomization.yaml
- planka/kustomization.yaml

View File

@ -1,24 +0,0 @@
# clusters/atlas/flux-system/applications/minio/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: minio
namespace: flux-system
spec:
interval: 10m
path: ./services/minio
prune: true
sourceRef:
kind: GitRepository
name: flux-system
targetNamespace: minio
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: minio
namespace: minio
- apiVersion: v1
kind: Service
name: minio
namespace: minio
wait: false

View File

@ -15,7 +15,6 @@ spec:
dependsOn:
- name: keycloak
- name: mailu
- name: minio
- name: traefik
healthChecks:
- apiVersion: apps/v1

View File

@ -2,7 +2,7 @@
apiVersion: batch/v1
kind: Job
metadata:
name: keycloak-realm-settings-14
name: keycloak-realm-settings-15
namespace: sso
spec:
backoffLimit: 0
@ -251,6 +251,67 @@ spec:
if status not in (201, 204):
raise SystemExit(f"Unexpected group create response for {group_name}: {status}")
# Ensure Planka client exposes groups in userinfo for role mapping.
status, clients = http_json(
"GET",
f"{base_url}/admin/realms/{realm}/clients?clientId=planka",
access_token,
)
planka_client = None
if status == 200 and isinstance(clients, list):
for item in clients:
if isinstance(item, dict) and item.get("clientId") == "planka":
planka_client = item
break
if planka_client:
client_id = planka_client.get("id")
mapper_payload = {
"name": "groups",
"protocol": "openid-connect",
"protocolMapper": "oidc-group-membership-mapper",
"consentRequired": False,
"config": {
"full.path": "false",
"id.token.claim": "true",
"access.token.claim": "true",
"userinfo.token.claim": "true",
"claim.name": "groups",
"jsonType.label": "String",
},
}
status, mappers = http_json(
"GET",
f"{base_url}/admin/realms/{realm}/clients/{client_id}/protocol-mappers/models",
access_token,
)
existing = None
if status == 200 and isinstance(mappers, list):
for item in mappers:
if isinstance(item, dict) and item.get("name") == mapper_payload["name"]:
existing = item
break
if existing and existing.get("id"):
mapper_payload["id"] = existing["id"]
status, _ = http_json(
"PUT",
f"{base_url}/admin/realms/{realm}/clients/{client_id}/protocol-mappers/models/{existing['id']}",
access_token,
mapper_payload,
)
if status not in (200, 204):
raise SystemExit(f"Unexpected protocol mapper update response: {status}")
else:
status, _ = http_json(
"POST",
f"{base_url}/admin/realms/{realm}/clients/{client_id}/protocol-mappers/models",
access_token,
mapper_payload,
)
if status not in (201, 204):
raise SystemExit(f"Unexpected protocol mapper create response: {status}")
# Ensure MFA is on by default for newly-created users.
status, required_actions = http_json(
"GET",

View File

@ -1,50 +0,0 @@
# services/minio/bucket-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: minio-bucket-bootstrap-2
namespace: minio
spec:
backoffLimit: 1
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: mc
image: minio/mc:RELEASE.2025-08-13T08-35-41Z
command: ["/bin/sh", "-c"]
args:
- |
set -euo pipefail
mc alias set local http://minio.minio.svc.cluster.local:9000 "${MINIO_ROOT_USER}" "${MINIO_ROOT_PASSWORD}"
mc mb -p local/outline || true
mc mb -p local/planka || true
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootUser
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootPassword
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi

View File

@ -1,68 +0,0 @@
# services/minio/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
namespace: minio
labels:
app: minio
spec:
replicas: 1
selector:
matchLabels:
app: minio
strategy:
type: Recreate
template:
metadata:
labels:
app: minio
spec:
nodeSelector:
node-role.kubernetes.io/worker: "true"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values: ["rpi4", "rpi5"]
containers:
- name: minio
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
args:
- server
- /data
- --console-address
- ":9001"
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootUser
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: rootPassword
ports:
- name: api
containerPort: 9000
- name: console
containerPort: 9001
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-data

View File

@ -1,10 +0,0 @@
# services/minio/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: minio
resources:
- namespace.yaml
- pvc.yaml
- deployment.yaml
- bucket-job.yaml
- service.yaml

View File

@ -1,5 +0,0 @@
# services/minio/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: minio

View File

@ -1,12 +0,0 @@
# services/minio/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data
namespace: minio
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: asteria
resources:
requests:
storage: 100Gi

View File

@ -1,18 +0,0 @@
# services/minio/service.yaml
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: minio
labels:
app: minio
spec:
selector:
app: minio
ports:
- name: api
port: 9000
targetPort: api
- name: console
port: 9001
targetPort: console

View File

@ -49,13 +49,9 @@ spec:
- name: PGSSLMODE
value: disable
- name: FILE_STORAGE
value: s3
- name: AWS_REGION
value: us-east-1
- name: AWS_S3_FORCE_PATH_STYLE
value: "true"
- name: AWS_S3_ACL
value: private
value: local
- name: FILE_STORAGE_LOCAL_ROOT_DIR
value: /var/lib/outline/data
- name: FORCE_HTTPS
value: "true"
- name: OIDC_ENFORCED
@ -77,13 +73,11 @@ spec:
name: outline-secrets
- secretRef:
name: outline-oidc
- secretRef:
name: outline-s3
- secretRef:
name: outline-smtp
volumeMounts:
- name: app-data
mountPath: /var/lib/outline
- name: user-data
mountPath: /var/lib/outline/data
readinessProbe:
httpGet:
path: /_health
@ -108,6 +102,6 @@ spec:
cpu: "1"
memory: 2Gi
volumes:
- name: app-data
- name: user-data
persistentVolumeClaim:
claimName: outline-app
claimName: outline-user-data

View File

@ -4,7 +4,7 @@ kind: Kustomization
namespace: outline
resources:
- namespace.yaml
- app-pvc.yaml
- user-pvc.yaml
- redis-deployment.yaml
- redis-service.yaml
- deployment.yaml

View File

@ -1,12 +1,12 @@
# services/outline/app-pvc.yaml
# services/outline/user-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: outline-app
name: outline-user-data
namespace: outline
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: astreae
storageClassName: asteria
resources:
requests:
storage: 5Gi

View File

@ -66,6 +66,12 @@ spec:
value: https://tasks.bstein.dev
- name: TRUST_PROXY
value: "true"
- name: OIDC_IGNORE_ROLES
value: "false"
- name: OIDC_PROJECT_OWNER_ROLES
value: "*"
- name: OIDC_ROLES_ATTRIBUTE
value: groups
envFrom:
- secretRef:
name: planka-db