Compare commits

..
Author SHA1 Message Date
Ruben Hensen a8ccbef274 envoy-gateway-system: NetworkPolicy 2026-10-26 23:28:05 +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
4 changed files with 164 additions and 5 deletions

No files matched your search

+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
+120
View File
@@ -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
+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
+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;
}
'';