apiVersion: apps/v1 kind: Deployment metadata: name: fritzbox-exporter namespace: monitoring labels: app: fritzbox-exporter spec: replicas: 1 selector: matchLabels: app: fritzbox-exporter template: metadata: labels: app: fritzbox-exporter spec: initContainers: # This initContainer implements the Egress Reject Workaround (Option B). # The FRITZ!Box TR-064 API redirects login_sid.lua calls to its public WAN IP because # we query it from a different subnet (192.168.11.1). When the WAN IP is dynamic or port # 80/443 are closed, the HTTP client hangs for the default 2-minute Linux TCP timeout, # failing the entire Prometheus scrape. # This container injects iptables rules into the pod's network namespace to instantly # reject (TCP RST) egress to any public WAN IPs on port 80/443. The exporter will immediately # fail the login step (in ms) and continue scraping TR-064 metrics on port 49000 (which is allowed). - name: init-iptables image: alpine:latest securityContext: capabilities: add: - NET_ADMIN command: ["/bin/sh", "-c"] args: - | apk add --no-cache iptables # Allow all local RFC1918 subnets and loopback iptables -A OUTPUT -d 127.0.0.1/32 -j ACCEPT iptables -A OUTPUT -d 10.0.0.0/8 -j ACCEPT iptables -A OUTPUT -d 172.16.0.0/12 -j ACCEPT iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT # Reject port 80 and 443 egress to any public IP (non-RFC1918) iptables -A OUTPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset iptables -A OUTPUT -p tcp --dport 443 -j REJECT --reject-with tcp-reset echo "Egress iptables REJECT rules injected successfully" containers: - name: fritzbox-exporter image: ghcr.io/sberk42/fritzbox_exporter/fritzbox_exporter:latest imagePullPolicy: IfNotPresent env: - name: USERNAME valueFrom: secretKeyRef: name: fritzbox-exporter-secret key: username - name: PASSWORD valueFrom: secretKeyRef: name: fritzbox-exporter-secret key: password - name: GATEWAY_URL value: "http://192.168.10.1:49000" - name: LISTEN_ADDRESS value: "0.0.0.0:9042" ports: - name: http-metrics containerPort: 9042 resources: requests: cpu: 10m memory: 32Mi limits: cpu: 100m memory: 64Mi --- apiVersion: v1 kind: Service metadata: name: fritzbox-exporter namespace: monitoring labels: app: fritzbox-exporter spec: ports: - name: http-metrics port: 9042 targetPort: 9042 protocol: TCP selector: app: fritzbox-exporter --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: fritzbox-exporter namespace: monitoring labels: app: fritzbox-exporter release: prometheus-operator spec: selector: matchLabels: app: fritzbox-exporter namespaceSelector: matchNames: - monitoring endpoints: - port: http-metrics path: /metrics interval: 60s scrapeTimeout: 15s --- apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: fritzbox-exporter-alerts namespace: monitoring labels: release: prometheus-operator spec: groups: - name: fritzbox-exporter.rules rules: - alert: FritzBoxExporterOffline expr: up{job="fritzbox-exporter"} == 0 for: 1h labels: severity: warning annotations: summary: "FritzBox exporter is offline" description: "The FritzBox exporter pod in the cluster cannot reach the router at 192.168.10.1 or the exporter itself is down."