mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-11-15 22:29:09 +01:00
Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7396b3cb7 | ||
|
|
c3597ef0a6 | ||
|
|
baa8c70b55 | ||
|
|
1efa6c2b93 | ||
|
|
15a92294a6 | ||
|
|
144b9dda2b | ||
|
|
920e54b43c | ||
|
|
665147a691 | ||
|
|
1ba1d5f1a0 | ||
|
|
285a64f980 | ||
|
|
6cf41b0f14 | ||
|
|
5678adfa82 | ||
|
|
2e2d367f63 | ||
|
|
2a1394763d | ||
|
|
86bbf25145 | ||
|
|
92d5147994 | ||
|
|
9e88a92ff7 | ||
|
|
7b12ee2028 | ||
|
|
039b956764 | ||
|
|
d187f55e57 | ||
|
|
a36fc6f348 | ||
|
|
66207de2dd | ||
|
|
a8ccbef274 | ||
|
|
d0ce5ddbec | ||
|
|
3fd585de5f | ||
|
|
6c727346f6 | ||
|
|
ed828b426f | ||
|
|
f6186d5a11 | ||
|
|
2ccaea24a8 | ||
|
|
a2abb66fea | ||
|
|
b721c80599 | ||
|
|
09026a5ff0 | ||
|
|
1fcfd3ad61 |
No files matched your search
@@ -18,7 +18,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
@@ -12,7 +12,7 @@ spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://community-charts.github.io/helm-charts
|
||||
targetRevision: 1.8.9
|
||||
targetRevision: 1.9.5
|
||||
chart: actualbudget
|
||||
helm:
|
||||
values: |
|
||||
|
||||
@@ -55,3 +55,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -12,7 +12,7 @@ spec:
|
||||
repoURL: docker.io/envoyproxy
|
||||
chart: gateway-helm
|
||||
# v1.9 supports Kubernetes 1.33 to 1.36
|
||||
targetRevision: v1.9.2
|
||||
targetRevision: 1.9.3
|
||||
helm:
|
||||
values: |
|
||||
config:
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: plugin-barman-cloud
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
chart: plugin-barman-cloud
|
||||
repoURL: https://cloudnative-pg.github.io/charts
|
||||
targetRevision: 0.8.1
|
||||
helm:
|
||||
values: |
|
||||
# CNPG's barman cloud plugin, which backs the PostgreSQL clusters
|
||||
# up to Garage on the NAS (authentik/postgresql-backup.yaml,
|
||||
# immich/postgresql-backup.yaml). It has to run in the operator's
|
||||
# namespace; the chart gets its TLS certificates from cert-manager.
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cnpg-system
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- ServerSideApply=true
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
@@ -108,7 +108,8 @@ spec:
|
||||
---
|
||||
# The CNPG operator checks each instance on its status port and connects to
|
||||
# PostgreSQL; the instance manager inside each pod watches its Cluster
|
||||
# through the API server.
|
||||
# through the API server. The barman cloud sidecar sends the WAL and the
|
||||
# base backups to Garage on the NAS (postgresql-backup.yaml).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
@@ -135,3 +136,34 @@ spec:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.99/32
|
||||
ports:
|
||||
- port: 3900
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -0,0 +1,92 @@
|
||||
# Backups of authentik's database by CNPG itself, through the barman cloud
|
||||
# plugin (apps/templates/plugin-barman-cloud-helm.yaml): a base backup every
|
||||
# night, and every WAL segment as soon as it is full or 5 minutes old
|
||||
# (archive_timeout). Both go to the bucket cnpg-authentik in Garage on the
|
||||
# NAS (nas/garage), so the database can be restored to any moment of the
|
||||
# last 30 days. Velero copies the data directory while PostgreSQL runs,
|
||||
# which is not a consistent copy.
|
||||
# The key is Garage's cnpg-authentik, in Vault as kv/cnpg-authentik-s3; it
|
||||
# can only reach this bucket.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: authentik-postgres-s3
|
||||
spec:
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
refreshInterval: 15m
|
||||
target:
|
||||
name: authentik-postgres-s3
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
engineVersion: v2
|
||||
data:
|
||||
ACCESS_KEY_ID: "{{ .access_key_id }}"
|
||||
ACCESS_SECRET_KEY: "{{ .secret_access_key }}"
|
||||
# Garage rejects requests signed for another region
|
||||
REGION: garage
|
||||
data:
|
||||
- secretKey: access_key_id
|
||||
remoteRef:
|
||||
key: kv/cnpg-authentik-s3
|
||||
property: access_key_id
|
||||
- secretKey: secret_access_key
|
||||
remoteRef:
|
||||
key: kv/cnpg-authentik-s3
|
||||
property: secret_access_key
|
||||
---
|
||||
apiVersion: barmancloud.cnpg.io/v1
|
||||
kind: ObjectStore
|
||||
metadata:
|
||||
name: authentik-postgres
|
||||
annotations:
|
||||
# The CRD comes with the plugin's own app
|
||||
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
|
||||
spec:
|
||||
retentionPolicy: 30d
|
||||
configuration:
|
||||
destinationPath: s3://cnpg-authentik/
|
||||
endpointURL: http://192.168.1.99:3900
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: authentik-postgres-s3
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: authentik-postgres-s3
|
||||
key: ACCESS_SECRET_KEY
|
||||
region:
|
||||
name: authentik-postgres-s3
|
||||
key: REGION
|
||||
wal:
|
||||
compression: gzip
|
||||
data:
|
||||
compression: gzip
|
||||
instanceSidecarConfiguration:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
env:
|
||||
# Newer boto3 adds checksums that S3-compatible stores can reject;
|
||||
# the plugin's docs suggest sending them only when required.
|
||||
- name: AWS_REQUEST_CHECKSUM_CALCULATION
|
||||
value: when_required
|
||||
- name: AWS_RESPONSE_CHECKSUM_VALIDATION
|
||||
value: when_required
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: ScheduledBackup
|
||||
metadata:
|
||||
name: authentik-postgres-daily
|
||||
spec:
|
||||
cluster:
|
||||
name: authentik-postgres
|
||||
# 03:00 UTC, after Longhorn's and Velero's backups; immediate: one right
|
||||
# away, so WAL archiving has a base backup to start from.
|
||||
schedule: "0 0 3 * * *"
|
||||
immediate: true
|
||||
backupOwnerReference: self
|
||||
method: plugin
|
||||
pluginConfiguration:
|
||||
name: barman-cloud.cloudnative-pg.io
|
||||
@@ -6,8 +6,14 @@ spec:
|
||||
instances: 1
|
||||
# Pinned so a new operator default (PostgreSQL 18 since CNPG 1.26.3) can't
|
||||
# start an in-place major upgrade. CNPG filled this in at creation.
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:17.4
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:17.11
|
||||
primaryUpdateStrategy: unsupervised
|
||||
# WAL archiving and base backups to Garage, see postgresql-backup.yaml
|
||||
plugins:
|
||||
- name: barman-cloud.cloudnative-pg.io
|
||||
isWALArchiver: true
|
||||
parameters:
|
||||
barmanObjectName: authentik-postgres
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
|
||||
@@ -54,3 +54,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
+1
-1
@@ -13,7 +13,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: sync
|
||||
image: python:3.12-alpine
|
||||
image: python:3.15-alpine
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
|
||||
@@ -27,7 +27,9 @@ view, and everything is shared with everyone automatically.
|
||||
- **immich has no user groups**; albums are shared per user, as viewer or
|
||||
editor. The owner and editors of an album may add users and change
|
||||
their roles. Search only covers your own photos and those of partners
|
||||
you show in your timeline.
|
||||
you show in your timeline. People (face recognition) are per user too,
|
||||
and a partner's library doesn't bring its people along; since 3.3 a
|
||||
user can share people with the users in their cluster group.
|
||||
|
||||
## Design
|
||||
|
||||
@@ -46,7 +48,10 @@ view, and everything is shared with everyone automatically.
|
||||
every user into every album of the group account (`fotoupload@`), as
|
||||
editor or viewer by quota; the group account's library as a partner of
|
||||
everyone, shown in the timeline; album invitations for albums older than
|
||||
a week hidden; sessions of OAuth users older than 30 days ended. Albums
|
||||
a week hidden; sessions of OAuth users older than 30 days ended;
|
||||
everyone in the group account's cluster group with its people shared
|
||||
(names editable by those who may upload, added 2026-10-28, when nobody
|
||||
but the group account could search by face). Albums
|
||||
that kaderleden create get the group account as editor first (in the
|
||||
database, since the API only lets an album's owner or editors share
|
||||
it), so they go to everyone too; names starting with "Privé"/"Prive"
|
||||
|
||||
+53
-18
@@ -38,7 +38,8 @@ docs/longhorn-second-ssd.md:
|
||||
2026-10-25). The snap is held, so microk8s gets no updates at all until
|
||||
someone refreshes it
|
||||
- [ ] Second SSD, docs/longhorn-second-ssd.md
|
||||
- [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
|
||||
- [x] `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML (PR #39,
|
||||
2026-10-25; the models are cached on a volume)
|
||||
|
||||
## Next, in order
|
||||
|
||||
@@ -82,7 +83,9 @@ friend.
|
||||
certificate; the admin API stays on the LAN. The cluster side (DNS,
|
||||
node001 tunnel, Certificate, HTTPRoute) goes in git. Needed: how much
|
||||
space he wants, free space on the NAS
|
||||
- [ ] Decide which copy of the immich library goes off-site (see 5)
|
||||
- [x] Which copy of the immich library goes off-site: Velero's, with the
|
||||
rest of the bucket (decided 2026-10-26). It is most of the ~190 GB, so
|
||||
ask him for room for it
|
||||
- [ ] A restore test that runs on a schedule. `backup-testing/` predates
|
||||
Envoy Gateway and still rewrites `*.hensen.io` and bypasses sol-auth.
|
||||
Planned on 2026-10-25, not run: a second Velero on the real bucket is
|
||||
@@ -202,9 +205,22 @@ Findings from 2026-10-25:
|
||||
maintenance job pods) was fixed that afternoon by
|
||||
`velero/skip-emptydir-policy.yaml`.
|
||||
|
||||
- [ ] Whether the immich library stays in Velero. It stays for now
|
||||
(2026-10-25); decide again with the off-site copy (2)
|
||||
- [ ] CNPG backups through the barman cloud plugin
|
||||
- [x] Whether the immich library stays in Velero: it stays, since
|
||||
Velero's bucket is what goes off-site (2; decided 2026-10-26)
|
||||
- [x] CNPG backups through the barman cloud plugin (PR #56, merged
|
||||
2026-10-26 22:43 UTC): authentik's and immich's PostgreSQL 17 back up
|
||||
to their own bucket in Garage (`cnpg-authentik`, `cnpg-immich`), a base
|
||||
backup at 03:00 UTC and the WAL every 5 minutes, kept 30 days. Adding
|
||||
the sidecar restarted both instances; authentik was down from 22:44 to
|
||||
22:48, since the old pod waited for its connections to close. The first
|
||||
base backups: 46 MB in 29 s and 776 MB in 3.5 minutes. immich's
|
||||
`immediate` backup ran before the plugin was up and failed; a manual one
|
||||
(`immich-postgres-17-first`) went through
|
||||
- [ ] Restore one of them into a new Cluster (`bootstrap.recovery` from
|
||||
the ObjectStore, to a point in time) to see that it works. After that,
|
||||
decide whether Velero still needs to copy the CNPG data directories
|
||||
- [ ] Take `cnpg-authentik` and `cnpg-immich` along in the off-site copy
|
||||
(2)
|
||||
- [x] Velero's target, MinIO on the NAS, is no longer maintained: no
|
||||
community images since October 2025, maintenance mode since December
|
||||
2025, and the repository is archived. Since 2026-10-26 Velero backs up
|
||||
@@ -298,10 +314,23 @@ Counted 2026-10-25 over the 73 running containers:
|
||||
OIDC logins of Vault and FreshRSS reach authentik, all 24
|
||||
ExternalSecrets refresh, raft intact; Gitea no longer reaches the NAS
|
||||
or Vault, and a pod in `default` no longer reaches Vault or Gitea
|
||||
- [ ] The infrastructure namespaces (cert-manager, cnpg-system,
|
||||
- [ ] Certificates couldn't renew in those 11 namespaces: Envoy may only
|
||||
reach each app's own port, and cert-manager's HTTP-01 solver listens on
|
||||
8089. ynab's renewal hung from 2026-10-26 17:35 UTC; a policy for the
|
||||
solver pods, applied by hand in actual-budget that evening, let it
|
||||
through within 20 seconds (now valid until 2027-01-24). PR #51 adds it
|
||||
to all 11. Without it the next renewals hang too: gitea on 11-15,
|
||||
bolletjestrui on 11-18, the rest from 12-03
|
||||
- [x] The infrastructure namespaces (cert-manager, cnpg-system,
|
||||
external-secrets, envoy-gateway-system, velero, mariadb-system, dns,
|
||||
default) still have none; `default` can reach the LAN and everything
|
||||
without a policy
|
||||
default), merged 2026-10-26: PR #54 for envoy-gateway-system (22:31
|
||||
UTC), PR #53 for the rest (22:42 UTC; one commit per namespace, and
|
||||
`default` denies everything). Checked afterwards: all 16 public hosts
|
||||
answer as before; server-side dry runs reach the CNPG, ESO,
|
||||
cert-manager and MariaDB webhooks (all fail closed); Velero's location
|
||||
Available, every ExternalSecret synced, the dns-sync job ran, no errors
|
||||
from the operators after the barman plugin had started. Velero's policy
|
||||
only lets it reach Garage, not MinIO's port 9000
|
||||
- [x] Requests: PR #33, merged 2026-10-25 11:23 UTC, for everything that
|
||||
had none except the Vault server, from `kubectl top` every 5 minutes
|
||||
from 03:13 to 08:40 UTC (highest memory plus 10%, CPU at p95, 10m
|
||||
@@ -334,10 +363,14 @@ Counted 2026-10-25 over the 73 running containers:
|
||||
`litespeed-cache` only caches pages on a LiteSpeed server, so it does
|
||||
little here, `wordpress-importer` is usually for a one-off import, and
|
||||
`code-snippets` runs PHP from the dashboard
|
||||
- [ ] Rate limiting per visitor in Envoy needs the client's address, which
|
||||
node001 loses: it passes TLS through, so every request arrives from
|
||||
46.224.26.65. That needs the PROXY protocol from node001's nginx to Envoy
|
||||
(ClientTrafficPolicy `enableProxyProtocol`)
|
||||
- [ ] Rate limiting per visitor in Envoy, on hold. Since PR #50
|
||||
(2026-10-26) node001 passes the visitor's address on with the PROXY
|
||||
protocol, and the apps see it: authentik logs it, Apache logs it for
|
||||
WordPress, so Wordfence's lockout after 20 failed logins now hits one
|
||||
visitor instead of everyone. Envoy Gateway's local rate limit can't
|
||||
count per address (no `Distinct`); that takes its global rate limit,
|
||||
with a Redis and a ratelimit service next to it. Not worth two more
|
||||
components on the one node while the apps' own limits work
|
||||
- [x] Probes for gitea (`/api/healthz`), freshrss (TCP, since
|
||||
mod_auth_openidc answers 401 to non-browsers) and serpbear (`/login`)
|
||||
(2026-10-25). Liveness only after 5 failures 30s apart, to ride out the
|
||||
@@ -375,7 +408,10 @@ every PR.
|
||||
fail the run; tested with a planted Secret and an untagged image), plus
|
||||
PR annotations for containers without requests or probes. freshrss's
|
||||
`alpine/git` init container had no tag; now `v2.52.0`
|
||||
- [ ] Automerge patch versions of low-risk apps now that CI runs
|
||||
- [ ] Automerge patch versions of low-risk apps now that CI runs: PR #52
|
||||
(ntfy, gatus, freshrss, serpbear, actual-budget and CI's own tools;
|
||||
three days after the release, only on weekday evenings and at the
|
||||
weekend, when the cluster can be reached)
|
||||
- [x] Renovate listed external-snapshotter twice (#15 and #24): its
|
||||
`argocd` manager and a custom `github-tags` regex both matched. The
|
||||
regex is gone; Renovate should close #15 by itself
|
||||
@@ -432,11 +468,10 @@ step after that, if downtime starts to matter.
|
||||
there and the volumes hold nothing (0 bytes in every backup). Drop both
|
||||
mounts and the two PVCs, or make them ConfigMap files if custom config
|
||||
is wanted
|
||||
- [ ] `kubectl delete namespace secrets`, empty since its app went
|
||||
- [ ] 41 Failed Velero backups: the dailies from 2026-09-26 to 10-21 and
|
||||
the weeklies since 08-02. Velero doesn't restore from a Failed backup;
|
||||
left alone they expire by 2027-01-16.
|
||||
`kubectl -n velero exec deploy/velero -c velero -- /velero backup delete $(kubectl -n velero get backups.velero.io -o json | jq -r '.items[] | select(.status.phase=="Failed") | .metadata.name' | xargs) --confirm`
|
||||
- [x] The namespace `secrets` was already gone on 2026-10-26
|
||||
- [x] The Failed Velero backups (the dailies from 2026-09-26 to 10-21 and
|
||||
the weeklies since 08-02; 40 by then, one had expired), deleted through
|
||||
Velero on 2026-10-26. Left: 13 Completed, 3 PartiallyFailed
|
||||
- [ ] The mariadb-operator Backup `mbgwp-pre-migration-backup`, a dump
|
||||
from 2026-02-22 on its own 10 GiB volume (which has a Longhorn backup
|
||||
from 2026-10-22). The PVC has no owner, so it goes separately:
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
io.kompose.service: freshrss
|
||||
spec:
|
||||
initContainers:
|
||||
- image: alpine/git:v2.52.0
|
||||
- image: alpine/git:v2.54.0
|
||||
name: git
|
||||
command:
|
||||
- git
|
||||
@@ -74,7 +74,7 @@ spec:
|
||||
value: Europe/Amsterdam
|
||||
# 1.30.3 ships PHP 8.4.27, which fixes CVE-2026-109618 and
|
||||
# CVE-2026-109619.
|
||||
image: freshrss/freshrss:1.30.3
|
||||
image: freshrss/freshrss:1.30.4
|
||||
name: freshrss
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -56,3 +56,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -0,0 +1,120 @@
|
||||
# Envoy Gateway: the controller and the proxy it runs (envoyproxy.yaml).
|
||||
# Both reach each other (the proxy gets its config from the controller on
|
||||
# 18000) and DNS, and take connections from each other and the node: the
|
||||
# API server calls the controller's topology webhook (9443) from there and
|
||||
# the kubelet probes come from there too (10.1.7.64 is the node's Calico
|
||||
# VXLAN address, projectcalico.org/IPv4VXLANTunnelAddr). Nothing scrapes
|
||||
# the metrics on 19001.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default
|
||||
namespace: envoy-gateway-system
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector: {}
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
- ipBlock:
|
||||
cidr: 10.1.7.64/32
|
||||
egress:
|
||||
- to:
|
||||
- podSelector: {}
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
# The proxy holds the node's ports 80 and 443 (10080 and 10443 inside) and
|
||||
# takes connections there from anywhere: the internet through node001 and
|
||||
# the router, the LAN, and pods that call a public hostname. It reaches
|
||||
# every app it routes to (pods in any namespace; their own policies decide
|
||||
# which port), Home Assistant on 192.168.1.202:8123 (outside the cluster,
|
||||
# home-assistant/), and the internet on 443 for Longhorn's OIDC login
|
||||
# (authentik's token endpoint, at its public address).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: proxy
|
||||
namespace: envoy-gateway-system
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
app.kubernetes.io/component: proxy
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- ports:
|
||||
- port: 10080
|
||||
- port: 10443
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.202/32
|
||||
ports:
|
||||
- port: 8123
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 100.64.0.0/10
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- port: 443
|
||||
---
|
||||
# Everything but the proxy (the controller, and the chart's certgen job on
|
||||
# upgrades) reaches the API server (the node, port 16443). The controller
|
||||
# also fetches the OIDC discovery document of Longhorn's SecurityPolicy
|
||||
# from authentik's public address.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: control-plane
|
||||
namespace: envoy-gateway-system
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app.kubernetes.io/name
|
||||
operator: NotIn
|
||||
values:
|
||||
- envoy
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 100.64.0.0/10
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
ports:
|
||||
- port: 443
|
||||
@@ -83,3 +83,29 @@ spec:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -55,3 +55,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -73,7 +73,9 @@ spec:
|
||||
---
|
||||
# The CNPG operator checks each instance on its status port and connects to
|
||||
# PostgreSQL; the instance manager inside each pod watches its Cluster
|
||||
# through the API server (the node, port 16443).
|
||||
# through the API server (the node, port 16443). The barman cloud sidecar
|
||||
# sends the WAL and the base backups to Garage on the NAS
|
||||
# (postgresql-backup.yaml).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
@@ -100,3 +102,34 @@ spec:
|
||||
cidr: 192.168.1.204/32
|
||||
ports:
|
||||
- port: 16443
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 192.168.1.99/32
|
||||
ports:
|
||||
- port: 3900
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -0,0 +1,97 @@
|
||||
# Backups of immich's database (albums, faces, the photos' metadata; the
|
||||
# photos themselves are files on immich-claim) by CNPG itself, through the
|
||||
# barman cloud plugin (apps/templates/plugin-barman-cloud-helm.yaml): a base
|
||||
# backup every night, and every WAL segment as soon as it is full or 5
|
||||
# minutes old (archive_timeout). Both go to the bucket cnpg-immich in Garage
|
||||
# on the NAS (nas/garage), so the database can be restored to any moment of
|
||||
# the last 30 days. Velero copies the data directory while PostgreSQL runs,
|
||||
# which is not a consistent copy. Only the PostgreSQL 17 cluster; the 14
|
||||
# one is about to go.
|
||||
# The key is Garage's cnpg-immich, in Vault as kv/cnpg-immich-s3; it can
|
||||
# only reach this bucket.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: immich-postgres-17-s3
|
||||
namespace: immich
|
||||
spec:
|
||||
secretStoreRef:
|
||||
name: vault-backend
|
||||
kind: ClusterSecretStore
|
||||
refreshInterval: 15m
|
||||
target:
|
||||
name: immich-postgres-17-s3
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
engineVersion: v2
|
||||
data:
|
||||
ACCESS_KEY_ID: "{{ .access_key_id }}"
|
||||
ACCESS_SECRET_KEY: "{{ .secret_access_key }}"
|
||||
# Garage rejects requests signed for another region
|
||||
REGION: garage
|
||||
data:
|
||||
- secretKey: access_key_id
|
||||
remoteRef:
|
||||
key: kv/cnpg-immich-s3
|
||||
property: access_key_id
|
||||
- secretKey: secret_access_key
|
||||
remoteRef:
|
||||
key: kv/cnpg-immich-s3
|
||||
property: secret_access_key
|
||||
---
|
||||
apiVersion: barmancloud.cnpg.io/v1
|
||||
kind: ObjectStore
|
||||
metadata:
|
||||
name: immich-postgres-17
|
||||
namespace: immich
|
||||
annotations:
|
||||
# The CRD comes with the plugin's own app
|
||||
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
|
||||
spec:
|
||||
retentionPolicy: 30d
|
||||
configuration:
|
||||
destinationPath: s3://cnpg-immich/
|
||||
endpointURL: http://192.168.1.99:3900
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: immich-postgres-17-s3
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: immich-postgres-17-s3
|
||||
key: ACCESS_SECRET_KEY
|
||||
region:
|
||||
name: immich-postgres-17-s3
|
||||
key: REGION
|
||||
wal:
|
||||
compression: gzip
|
||||
data:
|
||||
compression: gzip
|
||||
instanceSidecarConfiguration:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
env:
|
||||
# Newer boto3 adds checksums that S3-compatible stores can reject;
|
||||
# the plugin's docs suggest sending them only when required.
|
||||
- name: AWS_REQUEST_CHECKSUM_CALCULATION
|
||||
value: when_required
|
||||
- name: AWS_RESPONSE_CHECKSUM_VALIDATION
|
||||
value: when_required
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: ScheduledBackup
|
||||
metadata:
|
||||
name: immich-postgres-17-daily
|
||||
namespace: immich
|
||||
spec:
|
||||
cluster:
|
||||
name: immich-postgres-17
|
||||
# 03:00 UTC, after Longhorn's and Velero's backups; immediate: one right
|
||||
# away, so WAL archiving has a base backup to start from.
|
||||
schedule: "0 0 3 * * *"
|
||||
immediate: true
|
||||
backupOwnerReference: self
|
||||
method: plugin
|
||||
pluginConfiguration:
|
||||
name: barman-cloud.cloudnative-pg.io
|
||||
@@ -18,6 +18,13 @@ spec:
|
||||
shared_preload_libraries:
|
||||
- "vchord.so"
|
||||
|
||||
# WAL archiving and base backups to Garage, see postgresql-backup.yaml
|
||||
plugins:
|
||||
- name: barman-cloud.cloudnative-pg.io
|
||||
isWALArchiver: true
|
||||
parameters:
|
||||
barmanObjectName: immich-postgres-17
|
||||
|
||||
# Filled once from the PostgreSQL 14 cluster (immich-postgres) after its
|
||||
# embedding columns were turned into real[] and pgvecto.rs was dropped,
|
||||
# see docs/upgrade-k8s-1.33.md.
|
||||
|
||||
+66
-2
@@ -8,6 +8,10 @@
|
||||
# - the group account's library is shared as a partner and shown in
|
||||
# everyone's timeline, so search covers it (immich only searches your own
|
||||
# and your timeline partners' photos);
|
||||
# - everyone is put in the group account's cluster group and gets its
|
||||
# people (person sharing, immich 3.3): people are per user, and a partner
|
||||
# library doesn't bring its people along. Those who may upload can name
|
||||
# them, for everyone;
|
||||
# - the invitation notifications for albums older than a week are hidden,
|
||||
# or a new member would get one per album;
|
||||
# - sessions of OAuth users older than SESSION_MAX_AGE_DAYS are ended, so
|
||||
@@ -15,8 +19,11 @@
|
||||
# Online.
|
||||
# Albums and partners go through the API. Joining other people's albums
|
||||
# (the API only lets an album's owner or editors share it), the timeline
|
||||
# flag (which only the receiving user can set), the notifications and the
|
||||
# sessions go straight to the database.
|
||||
# flag and the cluster group (which only the receiving user can set or
|
||||
# accept), sharing the people (the API wants person.update, which the
|
||||
# group's key lacks, and with password login off nobody can log in as the
|
||||
# group to add it), the notifications and the sessions go straight to the
|
||||
# database.
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
@@ -24,6 +31,7 @@ metadata:
|
||||
namespace: immich
|
||||
data:
|
||||
sync.py: |
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
@@ -129,6 +137,62 @@ data:
|
||||
'UPDATE partner SET "inTimeline" = true WHERE "sharedById" = :\'group\' AND NOT "inTimeline"',
|
||||
group=group_id,
|
||||
)
|
||||
# What accepting an invitation to the group account's cluster group does
|
||||
# (ClusterGroupService.acceptRequest): the person groups only the member
|
||||
# has people in move along. Members with people in groups that others
|
||||
# also have people in are left alone; that only happens after sharing.
|
||||
for (email,) in sql(
|
||||
"""
|
||||
BEGIN;
|
||||
CREATE TEMP TABLE moving ON COMMIT DROP AS
|
||||
SELECT u.id FROM "user" u, "user" g
|
||||
WHERE g.id = :'group' AND u.id = ANY (:'members'::uuid[])
|
||||
AND u."clusterGroupId" <> g."clusterGroupId"
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM person p
|
||||
JOIN person o ON o."personGroupId" = p."personGroupId" AND o."ownerId" <> p."ownerId"
|
||||
WHERE p."ownerId" = u.id
|
||||
);
|
||||
UPDATE person_group SET "clusterGroupId" = (SELECT "clusterGroupId" FROM "user" WHERE id = :'group')
|
||||
WHERE id IN (SELECT p."personGroupId" FROM person p JOIN moving m ON m.id = p."ownerId");
|
||||
DELETE FROM cluster_group_request
|
||||
WHERE "userId" IN (SELECT id FROM moving)
|
||||
AND "clusterGroupId" = (SELECT "clusterGroupId" FROM "user" WHERE id = :'group');
|
||||
UPDATE "user" SET "clusterGroupId" = (SELECT "clusterGroupId" FROM "user" WHERE id = :'group')
|
||||
WHERE id IN (SELECT id FROM moving)
|
||||
RETURNING email;
|
||||
COMMIT;
|
||||
""",
|
||||
group=group_id,
|
||||
members="{" + ",".join(members) + "}",
|
||||
):
|
||||
print(f"put {email} in the group's cluster group", flush=True)
|
||||
# What PUT /people/users does for the group account: its people, with
|
||||
# their names, in every member's people list and search filters. Write
|
||||
# access (naming people, for everyone) for those who may upload.
|
||||
shared = sql(
|
||||
"""
|
||||
INSERT INTO person_user ("personGroupId", "sharedById", "sharedWithId", role)
|
||||
SELECT p."personGroupId", p."ownerId", m.id, m.role::person_user_role_enum
|
||||
FROM person p
|
||||
JOIN "user" g ON g.id = p."ownerId"
|
||||
CROSS JOIN unnest(:'ids'::uuid[], :'roles'::text[]) AS m(id, role)
|
||||
JOIN "user" u ON u.id = m.id AND u."clusterGroupId" = g."clusterGroupId"
|
||||
WHERE p."ownerId" = :'group' AND NOT p."isHidden"
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM person_user pu
|
||||
WHERE pu."personGroupId" = p."personGroupId" AND pu."sharedById" = p."ownerId"
|
||||
AND pu."sharedWithId" = m.id AND pu.role = m.role::person_user_role_enum
|
||||
)
|
||||
ON CONFLICT ("personGroupId", "sharedById", "sharedWithId") DO UPDATE SET role = EXCLUDED.role
|
||||
RETURNING "sharedWithId", role
|
||||
""",
|
||||
group=group_id,
|
||||
ids="{" + ",".join(wanted) + "}",
|
||||
roles="{" + ",".join("read" if role == "viewer" else "write" for role in wanted.values()) + "}",
|
||||
)
|
||||
for (uid, role), count in sorted(collections.Counter(map(tuple, shared)).items()):
|
||||
print(f"share {count} people with {members[uid]} ({role})", flush=True)
|
||||
sql(
|
||||
"""
|
||||
UPDATE notification SET "deletedAt" = now()
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
source:
|
||||
chart: mariadb-operator-crds
|
||||
repoURL: https://mariadb-operator.github.io/mariadb-operator
|
||||
targetRevision: 25.10.4
|
||||
targetRevision: 26.10.2
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
source:
|
||||
chart: mariadb-operator
|
||||
repoURL: https://mariadb-operator.github.io/mariadb-operator
|
||||
targetRevision: 25.10.4
|
||||
targetRevision: 26.10.2
|
||||
helm:
|
||||
values: |
|
||||
resources:
|
||||
|
||||
@@ -85,3 +85,29 @@ spec:
|
||||
kubernetes.io/metadata.name: mariadb-system
|
||||
ports:
|
||||
- port: 3306
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -31,5 +31,12 @@ Velero's key is in Vault as `kv/velero-s3` (`access_key_id`,
|
||||
`velero/external-secret.yaml`. Without the cluster, `$g key info velero
|
||||
--show-secret` on the NAS shows it again.
|
||||
|
||||
CNPG's backups (`authentik/postgresql-backup.yaml`,
|
||||
`immich/postgresql-backup.yaml`) have a bucket and a key per cluster, so
|
||||
neither database can touch the other's backups. Made on 2026-10-26 the
|
||||
same way as Velero's, with `cnpg-authentik` and `cnpg-immich` as both
|
||||
bucket and key name; the keys are in Vault as `kv/cnpg-authentik-s3` and
|
||||
`kv/cnpg-immich-s3`.
|
||||
|
||||
Renovate updates the image tag in `compose.yaml`, but nothing deploys it:
|
||||
after merging, `docker compose pull && docker compose up -d` on the NAS.
|
||||
@@ -65,3 +65,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -4,6 +4,7 @@
|
||||
"config:recommended",
|
||||
"customManagers:githubActionsVersions"
|
||||
],
|
||||
"timezone": "Europe/Amsterdam",
|
||||
"argocd": {
|
||||
"description": "Every app folder; Renovate's regexes are RE2, which has no lookahead, so the other folders are skipped through ignorePaths",
|
||||
"managerFilePatterns": [
|
||||
@@ -88,6 +89,27 @@
|
||||
"major"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"description": "Patch updates of apps that hold little state and are easy to roll back, and of CI's tools, merge themselves once CI passes: three days after the release, in the evening or at the weekend, when the cluster can be reached. Not the databases, operators, Vault, authentik, Longhorn, Velero, immich, Gitea or WordPress. Renovate merges itself; the repo has no branch protection for GitHub's auto-merge to wait on",
|
||||
"matchFileNames": [
|
||||
"ntfy/**",
|
||||
"gatus/**",
|
||||
"freshrss/**",
|
||||
"serpbear/**",
|
||||
"actual-budget/**",
|
||||
".github/workflows/**"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"patch"
|
||||
],
|
||||
"minimumReleaseAge": "3 days",
|
||||
"automerge": true,
|
||||
"platformAutomerge": false,
|
||||
"automergeSchedule": [
|
||||
"* 17-21 * * 1-5",
|
||||
"* 10-21 * * 0,6"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -54,3 +54,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
@@ -66,3 +66,29 @@ spec:
|
||||
- 169.254.0.0/16
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
---
|
||||
# cert-manager's HTTP-01 solver, which runs only while a certificate
|
||||
# renews: Let's Encrypt and cert-manager's own self check reach it on 8089
|
||||
# through Envoy. The policies above let Envoy reach the app's port only,
|
||||
# which left the solver unreachable (found 2026-10-26, when ynab's renewal
|
||||
# hung).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: acme-http01-solver
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
acme.cert-manager.io/http01-solver: "true"
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: envoy-gateway-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: envoy
|
||||
ports:
|
||||
- port: 8089
|
||||
Reference in new issue
Block a user