Compare commits

..
Author SHA1 Message Date
rock c7d8370a5a chore(kyverno): drop the two Application manifests from 00-substrate
PR #100 removed k8s/bootstrap/kyverno but not this file: the git add for it
failed and the error was redirected away, so the commit landed with only the
deletions. main therefore still declared both Applications and ArgoCD kept
them alive as Unknown / OutOfSync-Missing.

Removes the kyverno and kyverno-policies Application documents. The bootstrap
directory is already gone from main.
2026-11-04 05:33:19 +08:00
rock 13ec9afd32 Merge pull request 'chore(kyverno): remove the never-deployed policy engine' (#100) from chore/remove-kyverno-future-work into main 2026-11-03 21:30:07 +00:00
rock d8bbf122f9 chore(kyverno): remove the never-deployed policy engine
Both ArgoCD Applications reported broken: 'kyverno' Unknown and
'kyverno-policies' OutOfSync/Missing. Neither the namespace nor a single
kyverno pod ever existed, so no policy was enforced and nothing is lost.

Kyverno is future work. Removing the two Applications and the bootstrap
directory keeps the root app from reporting two permanent failures.
2026-11-04 05:29:48 +08:00
rock c8e8b9532d Merge pull request 'fix(gotify): run gotify-db with 2 instances, not 3' (#99) from fix/gotify-db-two-instances into main 2026-11-03 21:24:18 +00:00
rock 1bfe08eeb8 fix(gotify): run gotify-db with 2 instances, not 3
The header of this file always said 2 instances while the spec said 3. The
third never ran: gotify-db-3's volume held a single replica that could not
start, because talos-cp-2 disk2 is at 2.9% free. That left the cluster
permanently in 'Creating a new replica' with one degraded Longhorn volume.

Two replicas on two different nodes is the redundancy that actually exists.
2026-11-04 05:24:09 +08:00
rock 58bf245920 feat(poimen): publish the memory API at api-poimen.riotpiao.com
poimen-memory is a ClusterIP service on :8080, so a local pi session had no
route to the API. This adds an edge Ingress on the same nginx class and the
same Cloudflare-terminated shape as the frontend ingress.

Auth stays ZITADEL JWT (MEM_AUTH_MODE=jwt): a request without a valid bearer
token gets 401, so exposing the route does not expose the data.

Requires one Cloudflare proxied A record: api-poimen.riotpiao.com -> 192.168.1.160
2026-11-04 04:52:11 +08:00
rock d62ac3d861 Merge pull request 'fix(coredns): declare the poimen rewrite in the source of truth' (#98) from chore/retire-comfyui-fully into main 2026-11-03 19:52:20 +00:00
rock b45ae269fd Merge pull request 'chore(comfyui): retire the app for good' (#97) from chore/retire-comfyui-fully into main 2026-11-03 19:44:18 +00:00
rock 814f96040f Merge pull request 'fix(forgejo): keep 60Gi storage, revert CPU limit to 1000m' (#96) from fix/forgejo-storage-60gi-cpu-revert into main 2026-11-02 05:43:06 +00:00
7 changed files with 45 additions and 304 deletions
+5 -1
View File
@@ -8,7 +8,11 @@ metadata:
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
instances: 3
# 2, not 3. The file's own header always said 2, and the third instance never
# existed as a running pod: its volume only ever held a replica that could not
# start, because talos-cp-2 disk2 has no free space. A third replica on a disk
# that cannot host it is not redundancy, it is a permanent degraded volume.
instances: 2
imageName: ghcr.io/cloudnative-pg/postgresql:16.2
bootstrap:
initdb:
+38
View File
@@ -0,0 +1,38 @@
---
# Public entry point for the poimen-memory HTTP API.
#
# The API was reachable only inside the cluster (ClusterIP :8080), so a local pi
# session had no way to use it. This Ingress exposes the same handler set the
# in-cluster clients use: /mcp, /memory/*, /graphql, /health, /ready.
#
# Cloudflare terminates TLS in front of the nginx ingress, which is why this rule
# carries no `tls:` section — the same shape as ingress.yaml next to it.
#
# Auth: poimen-memory validates ZITADEL JWTs (MEM_AUTH_MODE=jwt). A caller
# without a valid bearer token gets 401, so exposing the route does not expose
# the data.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: poimen-api
namespace: poimen
annotations:
# MCP calls and ingest can be slow; the provider may stream. Match the
# frontend ingress so a long tool call is not cut at 60s.
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
rules:
- host: api-poimen.riotpiao.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: poimen-memory
port:
number: 8080
@@ -6,3 +6,5 @@ kind: Kustomization
# riotpiao-poimen/poimen-frontend.git k8s/ — each repo owns its own k8s.
resources:
- ingress.yaml
# The memory API. Without it a local pi session has no route to :8080.
- api-ingress.yaml
-51
View File
@@ -159,54 +159,3 @@ spec:
automated:
prune: true
selfHeal: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kyverno
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
project: homelab
source:
repoURL: https://kyverno.github.io/kyverno/
chart: kyverno
targetRevision: "1.14.0"
helm:
valueFiles:
- $values/k8s/bootstrap/kyverno/kyverno-values.yaml
sources:
- repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
targetRevision: main
ref: values
destination:
server: https://kubernetes.default.svc
namespace: kyverno
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kyverno-policies
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
project: homelab
source:
repoURL: https://forgejo.riotpiao.com/riotpiao-poimen/homelab.git
targetRevision: main
path: k8s/bootstrap/kyverno
destination:
server: https://kubernetes.default.svc
namespace: kyverno
syncPolicy:
automated:
prune: true
selfHeal: true
-6
View File
@@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kyverno
resources:
- policies.yaml
-42
View File
@@ -1,42 +0,0 @@
# Kyverno: Policy engine for Kubernetes image scanning, Pod security, and admission control
# Scan all images, enforce baseline Pod Security Standard, prevent privilege escalation
replicaCount: 1
image:
registry: ghcr.io
repository: kyverno/kyverno
tag: "v1.14.0"
config:
# Webhook timeout for policy evaluation. Increase if scanning takes longer.
webhookTimeoutSeconds: 30
# Failure policy: fail-open (audit/log) vs fail-closed (reject on error)
failurePolicy: fail
# Resource limits for webhook
webhookAnnotations:
rules: "allow"
# Pod security via Kyverno instead of Pod Security Policies (deprecated)
# Enforces baseline restrictions cluster-wide, with exceptions for privileged namespaces
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
rbac:
create: true
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
# Webhook configuration
webhook:
timeoutSeconds: 30
# Failure policy: "Fail" (reject on error) or "Ignore" (audit-only)
# Set to "Ignore" for initial testing, then change to "Fail"
failurePolicy: ignore
-204
View File
@@ -1,204 +0,0 @@
# Kyverno ClusterPolicies: Image scanning, Pod security, and admission control
---
# Policy 1: Require non-root containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root
namespace: kyverno
spec:
validationFailureAction: audit # audit first, then change to enforce
rules:
- name: check-runAsNonRoot
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
pod-security.kubernetes.io/enforce: "!privileged"
validate:
message: "Container must not run as root"
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
---
# Policy 2: Drop all Linux capabilities, add only required ones
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-dropped-caps
namespace: kyverno
spec:
validationFailureAction: audit
rules:
- name: drop-all-capabilities
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
pod-security.kubernetes.io/enforce: "!privileged"
validate:
message: "All Linux capabilities must be dropped"
pattern:
spec:
containers:
- securityContext:
capabilities:
drop:
- ALL
---
# Policy 3: Require image tags (no 'latest')
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
namespace: kyverno
spec:
validationFailureAction: audit # Change to enforce after testing
rules:
- name: disallow-latest
match:
any:
- resources:
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
- Job
validate:
message: "Image tag 'latest' is not allowed. Use explicit version tags."
pattern:
spec:
=(template):
spec:
containers:
- image: "!*:latest"
=(initContainers):
- image: "!*:latest"
---
# Policy 4: Restrict images to trusted registries
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-registries
namespace: kyverno
spec:
validationFailureAction: audit
rules:
- name: trusted-registries
match:
any:
- resources:
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
- Job
selector:
matchLabels:
pod-security.kubernetes.io/enforce: "!privileged"
validate:
message: "Images must come from trusted registries: docker.io, ghcr.io, quay.io, k8s.gcr.io, registry.k8s.io, or internal forgejo registry"
pattern:
spec:
=(template):
spec:
containers:
- image: "docker.io/* | ghcr.io/* | quay.io/* | k8s.gcr.io/* | registry.k8s.io/* | forgejo.riotpiao.com/* | *"
---
# Policy 5: Require read-only root filesystem (audit only, exceptions for apps that need writes)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-readonly-filesystem
namespace: kyverno
spec:
validationFailureAction: audit
rules:
- name: check-readOnlyRootFilesystem
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
pod-security.kubernetes.io/enforce: "!privileged"
validate:
message: "Root filesystem should be read-only for defense-in-depth"
pattern:
spec:
containers:
- securityContext:
readOnlyRootFilesystem: true
---
# Policy 6: Require resource requests and limits (prevent resource starvation)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
namespace: kyverno
spec:
validationFailureAction: audit
rules:
- name: check-resources
match:
any:
- resources:
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
excludeResources:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: "kyverno|kube-system|kube-node-lease"
validate:
message: "CPU and memory requests and limits are required"
pattern:
spec:
=(template):
spec:
containers:
- resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"
---
# Policy 7: Require securityContext on all containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-security-context
namespace: kyverno
spec:
validationFailureAction: audit
rules:
- name: check-securityContext
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
pod-security.kubernetes.io/enforce: "!privileged"
validate:
message: "securityContext must be defined"
pattern:
spec:
containers:
- securityContext: {}