Compare commits

..
Author SHA1 Message Date
Ruben Hensen 7b12ee2028 cnpg: back the databases up to Garage with the barman cloud plugin 2026-10-26 23:38:13 +01:00
Ruben Hensen 66207de2dd Merge pull request #52 from rubenhensen/renovate-automerge
Renovate: automerge patch updates of low-risk apps
2026-10-26 23:28:09 +01:00
Ruben Hensen d0ce5ddbec Merge pull request #51 from rubenhensen/netpol-acme
NetworkPolicies: let Envoy reach cert-manager's HTTP-01 solver
2026-10-26 23:27:48 +01:00
Ruben Hensen 6c727346f6 renovate: patch updates of low-risk apps merge themselves 2026-10-26 23:23:25 +01:00
Ruben Hensen ed828b426f networkpolicy: let Envoy reach cert-manager's HTTP-01 solver 2026-10-26 23:19:28 +01:00
Ruben Hensen 0dc69d34f0 Merge pull request #50 from rubenhensen/proxy-protocol
Pass the visitor's address from node001 to the home cluster
2026-10-26 23:12:14 +01:00
Ruben Hensen 30540ea579 node001: send the visitor's address to the home cluster
The stream server on 443 now starts each connection with a PROXY header.
nginx sends it to every upstream or none, so Stalwart (8443) and the
local nginx for Gatus and ntfy (8444) get theirs through a hop on
8445/8446 that takes it off again; nothing changes for them.

Deploy after the Gateway accepts the header, or the tunneled hosts stop
answering until it does.
2026-10-26 23:11:38 +01:00
Ruben Hensen 53d74768ad gateway: take the visitor's address from a PROXY header
node001 passes TLS through without opening it, so every request from
the tunneled hosts arrives from 46.224.26.65. Wordfence locked that
address out after 20 failed logins on 2026-10-26, and with it everyone
who logs in to scoutingmbg.nl. With a PROXY header from node001, Envoy
puts the visitor's address in X-Forwarded-For instead.

The header is optional: s2 and ntfy2 point straight at home, and
node001's proxy on port 80 sends none either.
2026-10-26 23:11:38 +01:00
Ruben Hensen b6462fb3a9 Merge pull request #49 from rubenhensen/roadmap-garage
docs: Velero on Garage
2026-10-26 23:04:15 +01:00
21 changed files with 608 additions and 7 deletions

No files matched your search

+26
View File
@@ -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
@@ -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
+33 -1
View File
@@ -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
+92
View File
@@ -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
View File
@@ -8,6 +8,12 @@ spec:
# start an in-place major upgrade. CNPG filled this in at creation.
imageName: ghcr.io/cloudnative-pg/postgresql:17.4
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
+26
View File
@@ -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 -2
View File
@@ -38,8 +38,7 @@ 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
- [x] `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML (PR #39, 2026-10-25;
the models are cached on a volume)
- [ ] Optional: `MACHINE_LEARNING_MODEL_REVISION=v2` for immich ML
## Next, in order
+26
View File
@@ -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
+23
View File
@@ -0,0 +1,23 @@
# node001 passes TLS for the tunneled hosts through without opening it, so
# without this every request arrives from 46.224.26.65. Wordfence locks that
# address out after 20 failed logins (2026-10-26), and with it everyone who
# logs in to scoutingmbg.nl. node001 puts a PROXY header with the visitor's
# address in front of each connection; Envoy takes the address from there
# and adds it to X-Forwarded-For.
#
# Optional, because s2 and ntfy2 point straight at home and their clients
# send no header, and neither does node001's proxy on port 80. The price:
# whoever connects to the home address directly can send a header of their
# own and claim any address.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: proxy-protocol
namespace: envoy-gateway-system
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: homelab
proxyProtocol:
optional: true
+26
View File
@@ -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
+26
View File
@@ -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
+34 -1
View File
@@ -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
+97
View File
@@ -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
+7
View File
@@ -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.
+26
View File
@@ -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
+7
View File
@@ -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.
+20 -3
View File
@@ -173,12 +173,18 @@ in
# - mail.rubenhensen.nl (and anything else) → local stalwart on 8443
# - tunneled hosts → home cluster ingress on 443
# - s1 / ntfy1.rubenhensen.nl → local nginx on 8444 (Gatus, ntfy)
#
# Each connection starts with a PROXY header that carries the visitor's
# address, so the home cluster sees who is asking instead of this box
# (gateway/proxy-protocol.yaml in k8scd). nginx sends the header to
# every upstream or none, so the local ones go through a hop on
# 8445/8446 that takes it off again.
streamConfig = ''
map $ssl_preread_server_name $tunnel_upstream {
${sniMapEntries}
s1.rubenhensen.nl 127.0.0.1:8444;
ntfy1.rubenhensen.nl 127.0.0.1:8444;
default 127.0.0.1:8443;
s1.rubenhensen.nl 127.0.0.1:8446;
ntfy1.rubenhensen.nl 127.0.0.1:8446;
default 127.0.0.1:8445;
}
server {
@@ -186,6 +192,17 @@ ${sniMapEntries}
listen [::]:443;
proxy_pass $tunnel_upstream;
ssl_preread on;
proxy_protocol on;
}
server {
listen 127.0.0.1:8445 proxy_protocol;
proxy_pass 127.0.0.1:8443;
}
server {
listen 127.0.0.1:8446 proxy_protocol;
proxy_pass 127.0.0.1:8444;
}
'';
+26
View File
@@ -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
+22
View File
@@ -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"
]
}
]
}
+26
View File
@@ -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
+26
View File
@@ -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