Compare commits
No commits in common. "b333b5e6d9996fd934b10353b1c6690bdf8ece69" and "9b799c5ea2acb33048c38c61ebf2e21e365af281" have entirely different histories.
b333b5e6d9
...
9b799c5ea2
@ -16,7 +16,7 @@ subdirectories are consumed by Argo CD Applications in `argocd/apps`.
|
||||
| `homeassistant/` | Home Assistant external service and ingress |
|
||||
| `media/` | Jellyfin, qBittorrent, JDownloader ([sending links](media/jdownloader.md)) |
|
||||
| `monitoring/` | Monitoring PVs, Grafana datasource, Grafana/Auth OIDC secrets |
|
||||
| `multus/` | Multus CNI, VLAN 50 NetworkAttachmentDefinition, canary, browser-vpn-proxy SOCKS5 proxy ([details](multus/README.md)) — manual-sync Application, see `argocd/apps/multus.yaml` |
|
||||
| `multus/` | Multus CNI, VLAN 50 NetworkAttachmentDefinition, canary ([details](multus/README.md)) — manual-sync Application, see `argocd/apps/multus.yaml` |
|
||||
| `network/` | Pi-hole secrets, DDNS, Traefik dashboard, external host ingresses |
|
||||
| `portfolio/` | Portfolio deployment, ingress, registry pull secret |
|
||||
|
||||
|
||||
@ -13,9 +13,9 @@ comment. Nothing here applies to the cluster just because it's merged to
|
||||
| `01-rbac.yaml` | 0 | Multus ServiceAccount/ClusterRole/ClusterRoleBinding |
|
||||
| `02-daemonset.yaml` | 0 | Multus thick-plugin DaemonSet — **nodeSelector restricted to `nik-debian` only**, k3s-specific CNI paths |
|
||||
| `10-nad-vlan50.yaml` | 1 | VLAN 50 `NetworkAttachmentDefinition`, macvlan bridge on `enp1s0.50`, static IPAM |
|
||||
| `vlan50-egress-guard-script.yaml` | (no wave annotation — apply alongside 10) | Shared init-container script consumed by the canary, qBittorrent, JDownloader, and `browser-vpn-proxy` |
|
||||
| `vlan50-egress-guard-script.yaml` | (no wave annotation — apply alongside 10) | Shared init-container script consumed by the canary and, later, qBittorrent/JDownloader |
|
||||
| `20-canary.yaml` | 2 | Temporary canary pod — **do not sync without separate explicit approval**, see its own header |
|
||||
| `30-browser-vpn-proxy.yaml` | 3 | `.12` — SOCKS5 proxy for browser traffic via PIA, replaces `manifests/media/gluetun-proxy.yaml`. No auth, no app-level IP allowlist (see its own header for why and what actually restricts access instead) — **do not sync without separate explicit approval** |
|
||||
| `reserved/browser-vpn-proxy.yaml` | n/a | The `.12` third workload, prepared not deployed — see its own header for why it lives in a subdirectory `argocd/apps/multus.yaml` never scans at all |
|
||||
|
||||
## Before applying anything here
|
||||
|
||||
@ -31,9 +31,6 @@ comment. Nothing here applies to the cluster just because it's merged to
|
||||
01 → 02, confirm the Multus pod is actually Running on `nik-debian`
|
||||
and `/var/lib/rancher/k3s/agent/etc/cni/net.d` now has a generated
|
||||
`00-multus.conf`, *then* 10, *then* — only with separate approval — 20.
|
||||
`30-browser-vpn-proxy.yaml` follows the same "separate approval" gate
|
||||
as 20 — no Secret precondition, it needs no credentials at all (see
|
||||
its own header for why).
|
||||
|
||||
## Canary lifecycle — re-running it after a Multus/CNI change
|
||||
|
||||
@ -116,8 +113,8 @@ edge cases).
|
||||
## Why `ipam.type: static` and no shared allocator (Whereabouts etc.)
|
||||
|
||||
Only ever a handful of fixed IPs on this network — qBittorrent (`.10`),
|
||||
JDownloader (`.11`), `browser-vpn-proxy` (`.12`), and a single canary
|
||||
(`.100`). A dynamic IPAM controller (CRD, webhook, its own failure
|
||||
JDownloader (`.11`), one reserved-not-deployed (`.12`), and a single
|
||||
canary (`.100`). A dynamic IPAM controller (CRD, webhook, its own failure
|
||||
modes) is unjustified complexity for that; each pod's own
|
||||
`k8s.v1.cni.cncf.io/networks` annotation just states its IP directly.
|
||||
|
||||
@ -167,7 +164,6 @@ kubectl wait --for=condition=Ready pod/vlan50-rollback-probe --timeout=60s
|
||||
kubectl delete pod vlan50-rollback-probe
|
||||
|
||||
# 3. Only now, the Kubernetes-side resources, in this order:
|
||||
kubectl delete -f manifests/multus/30-browser-vpn-proxy.yaml # if it was ever applied
|
||||
kubectl delete -f manifests/multus/20-canary.yaml # if it was ever applied
|
||||
kubectl delete -f manifests/multus/10-nad-vlan50.yaml # only after no pod still references it
|
||||
kubectl delete -f manifests/multus/vlan50-egress-guard-script.yaml
|
||||
|
||||
@ -1,25 +1,34 @@
|
||||
# sync-wave 3 — after RBAC/DaemonSet (0), the NAD (1), and the canary
|
||||
# (2). Moved here from manifests/multus/reserved/ (2026-08-24) now that
|
||||
# qBittorrent and JDownloader have both independently passed their
|
||||
# canary, validation, and soak gates — the reservation this file's
|
||||
# earlier header described is satisfied. Still governed only by the
|
||||
# `multus` Argo CD Application's own manual-sync gate (no automated
|
||||
# sync there at all) — the same gate already used for the DaemonSet, NAD,
|
||||
# and canary, not a new mechanism.
|
||||
# NOT applied by any Argo CD Application. Deliberately kept in
|
||||
# manifests/multus/reserved/ rather than manifests/multus/ directly:
|
||||
# argocd/apps/multus.yaml's source has no directory.recurse: true, so it
|
||||
# only ever scans files directly in manifests/multus/, never this
|
||||
# subdirectory — even selective `argocd app sync multus --resource ...`
|
||||
# has no way to target a resource Argo CD doesn't know exists. This is
|
||||
# stronger isolation than the canary's own manual-sync-only Application
|
||||
# gate, on purpose: plan.md Phase 8/12 requires this workload stay out of
|
||||
# reconciliation entirely until qBittorrent AND JDownloader have
|
||||
# independently passed their canary, validation, and soak gates — not
|
||||
# just "not yet approved to sync", but "not a candidate for sync at all".
|
||||
#
|
||||
# Before applying:
|
||||
# 1. Verify the NodePort below (30889) doesn't collide with anything
|
||||
# live — confirmed free 2026-08-24 (`kubectl get svc -A`, checked
|
||||
# against every NodePort in use). manifests/media/gluetun-proxy.yaml
|
||||
# (the existing browser proxy this replaces) uses 30888, not this
|
||||
# one — no collision, but that file stays live and unmodified until
|
||||
# this replacement is confirmed working end to end, then gets
|
||||
# decommissioned as its own separate, explicitly-approved step.
|
||||
# When ready to actually build this (not yet — this file is prepared,
|
||||
# not deployed):
|
||||
# 1. Move it into manifests/multus/ (a sync-wave-numbered filename,
|
||||
# e.g. 30-browser-vpn-proxy.yaml) once qBittorrent/JDownloader are
|
||||
# both soaked.
|
||||
# 2. Create the browser-vpn-proxy-credentials Secret first (see the
|
||||
# companion secret script this needs — not written yet, follow the
|
||||
# exact pattern of manifests/media/pia-secret.sh: add
|
||||
# BROWSER_PROXY_USER/BROWSER_PROXY_PASSWORD to .env, a script that
|
||||
# kubeseals a Secret with those as PROXY_USER/PROXY_PASSWORD).
|
||||
# 3. Verify the NodePort below (30889) doesn't collide with anything
|
||||
# live — manifests/network/gluetun-proxy.yaml already uses 30888
|
||||
# for the existing browser SOCKS5 proxy this is meant to eventually
|
||||
# replace; confirm 30889 is actually free before applying.
|
||||
#
|
||||
# Design:
|
||||
# - serjs/go-socks5-proxy (github.com/serjs/socks5-server) — a small,
|
||||
# single-purpose Go SOCKS5 server, not a VPN client itself. It
|
||||
# doesn't need to be, here: unlike the legacy manifests/media/
|
||||
# doesn't need to be, here: unlike the legacy manifests/network/
|
||||
# gluetun-proxy.yaml it's replacing (which runs its own OpenVPN
|
||||
# client), PIA egress for this workload comes from the same
|
||||
# netns-level routing as qBittorrent/JDownloader — the init
|
||||
@ -28,37 +37,10 @@
|
||||
# proxy. Pinned to v0.0.4's digest, confirmed live against Docker
|
||||
# Hub's registry API 2026-08-24:
|
||||
# sha256:6828ddb2a6a93dec85209b69cd3842bd80094f7dd52f35ee1dd0f9e9165e2188
|
||||
# - No auth (REQUIRE_AUTH=false), no ALLOWED_IPS — a deliberate choice,
|
||||
# not an oversight. plan.md originally called for auth AND
|
||||
# LAN-restriction together as a floor; discussed and explicitly
|
||||
# relaxed (2026-08-24) after confirming the home network's own
|
||||
# firewall already restricts this Service's NodePort to Trusted
|
||||
# (VLAN 10) and Lab (VLAN 40) only — Trusted/Lab → Lab is ALLOW,
|
||||
# IoT/Guest/Management → Lab is DENY except the Technitium DNS
|
||||
# exception (see ~/repo/home-network/Home Network Implementation
|
||||
# Plan.md section 10). Residual risk accepted knowingly: Lab has its
|
||||
# own Wi-Fi SSID with open DHCP, so "reachable" means "anyone with
|
||||
# that Wi-Fi password or a device already on Trusted/Lab," not just
|
||||
# deliberately provisioned devices — there is no proxy-level control
|
||||
# backing this up.
|
||||
# - ALLOWED_IPS is not a fallback for the above — it's removed because
|
||||
# it cannot do what it looks like it does. Read this image's actual
|
||||
# source (server.go, and the go-socks5 fork it replaces via go.mod's
|
||||
# replace directive) to confirm, not assumed: ALLOWED_IPS values are
|
||||
# parsed with net.ParseIP (not net.ParseCIDR) and matched with exact
|
||||
# net.IP.Equal(), never CIDR containment. The original draft's value
|
||||
# here, "10.10.40.0/24", would have failed to parse as a plain IP,
|
||||
# producing a whitelist containing one nil entry — since Equal()
|
||||
# against nil never matches any real client address, every single
|
||||
# connection would have been rejected, including legitimate ones.
|
||||
# This library has no way to express a subnet allowlist at all, only
|
||||
# a list of exact individual IPs, which doesn't fit a dynamic-DHCP
|
||||
# LAN. Access restriction is the OpenWrt firewall alone; there is no
|
||||
# compensating netns-level (nft) inbound filter here either — one
|
||||
# could be added later, but would first need confirming whether this
|
||||
# NodePort Service actually preserves real client source IPs
|
||||
# end-to-end (default `externalTrafficPolicy: Cluster` typically
|
||||
# does not) rather than assuming it does.
|
||||
# - REQUIRE_AUTH=true + PROXY_USER/PROXY_PASSWORD (from the Secret
|
||||
# above) AND ALLOWED_IPS restricted to the home LAN range — both, not
|
||||
# either/or, matching plan.md's "authentication or LAN-restricted
|
||||
# exposure" as a floor, not a ceiling.
|
||||
# - Service is NodePort, not LoadBalancer/Ingress — reachable from the
|
||||
# LAN at <any node IP>:30889, never exposed publicly (no port-forward
|
||||
# on Flint/the router for it, same as the existing gluetun-proxy).
|
||||
@ -84,8 +66,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: browser-vpn-proxy
|
||||
namespace: downloads
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "3"
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
@ -123,16 +103,10 @@ spec:
|
||||
value: "10.43.0.0/16"
|
||||
- name: NODE_IP
|
||||
value: "10.10.40.20"
|
||||
- name: EXPECTED_VLAN50_IP
|
||||
value: "10.10.50.12"
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
# NET_RAW alongside NET_ADMIN: the guard script's
|
||||
# arping-based duplicate-address and gateway-reachability
|
||||
# checks need it — see
|
||||
# vlan50-egress-guard-script.yaml's header comment.
|
||||
add: ["NET_ADMIN", "NET_RAW"]
|
||||
add: ["NET_ADMIN"]
|
||||
volumeMounts:
|
||||
- name: guard-script
|
||||
mountPath: /scripts
|
||||
@ -145,15 +119,20 @@ spec:
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
env:
|
||||
# REQUIRE_AUTH defaults to true in this image (confirmed
|
||||
# against its real source, params struct in server.go) — it
|
||||
# must be explicitly set to "false" to actually go
|
||||
# passwordless, omitting it entirely would NOT have that
|
||||
# effect. Deliberately no auth, no ALLOWED_IPS — see the
|
||||
# header comment for the reasoning and what actually
|
||||
# restricts access instead.
|
||||
- name: REQUIRE_AUTH
|
||||
value: "false"
|
||||
value: "true"
|
||||
- name: PROXY_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: browser-vpn-proxy-credentials
|
||||
key: PROXY_USER
|
||||
- name: PROXY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: browser-vpn-proxy-credentials
|
||||
key: PROXY_PASSWORD
|
||||
- name: ALLOWED_IPS
|
||||
value: "10.10.40.0/24" # LAN only — narrow this further if a specific client range is confirmed
|
||||
resources:
|
||||
requests:
|
||||
cpu: 20m
|
||||
Loading…
x
Reference in New Issue
Block a user