Router-Hosts Integration Design
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Deploy router-hosts-operator v0.7.0 to manage K8s service DNS entries via CRDs, and use terraform-provider-routerhosts for infrastructure hosts, replacing the manually-maintained /extdata/hosts/main file on Firewalla.
Architecture: Three-component system where Ansible manages the router-hosts gRPC server on Firewalla, ArgoCD deploys the Kubernetes operator for service DNS entries, and Terraform manages infrastructure host entries. All components communicate via mTLS-secured gRPC.
Tech Stack: router-hosts v0.7.0, router-hosts-operator Helm chart, terraform-provider-routerhosts, Vault PKI, ArgoCD, HCP Terraform
1. Architecture Overview
Section titled “1. Architecture Overview”┌─────────────────────────────────────────────────────────────────────────┐│ Firewalla Router ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ router-hosts server v0.7.0 │ ││ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │ ││ │ │ gRPC API │────│ SQLite3 │────│ hosts file │ │ ││ │ │ (mTLS) │ │ Database │ │ /hosts/main │ │ ││ │ └─────────────┘ └──────────────┘ └──────────────────┘ │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ▲ ││ │ dnsmasq hostsdir ││ ▼ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ dnsmasq │ ││ │ hostsdir=/extdata/router-hosts/hosts │ ││ └─────────────────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────────────────┘ ▲ ▲ │ gRPC (mTLS) │ gRPC (mTLS) │ │┌───────────────────┴───────────┐ ┌──────────┴──────────────────────────┐│ Kubernetes Cluster │ │ HCP Terraform ││ ┌─────────────────────────┐ │ │ ┌──────────────────────────────┐ ││ │ router-hosts-operator │ │ │ │ terraform-provider- │ ││ │ ┌───────────────────┐ │ │ │ │ routerhosts │ ││ │ │ HostEntry CRDs │ │ │ │ │ ┌────────────────────────┐ │ ││ │ │ (K8s services) │ │ │ │ │ │ routerhosts_entry │ │ ││ │ └───────────────────┘ │ │ │ │ │ (infra hosts) │ │ ││ └─────────────────────────┘ │ │ │ └────────────────────────┘ │ │└───────────────────────────────┘ │ └──────────────────────────────┘ │ └─────────────────────────────────────┘Responsibility Split:
| Component | Manages | Examples |
|---|---|---|
| K8s Operator | Services on MetalLB IPs | vault, grafana, argocd, auth, longhorn, windmill, mealie |
| Terraform | Infrastructure hosts | TPI nodes (tpi-alpha-, tpi-beta-), NAS (nas-*), API VIP |
2. Router-hosts Server Upgrade
Section titled “2. Router-hosts Server Upgrade”Upgrade the Ansible-deployed router-hosts server from v0.6.0 to v0.7.0.
Changes to ansible/roles/router-hosts/defaults/main.yml
Section titled “Changes to ansible/roles/router-hosts/defaults/main.yml”# Version upgraderouter_hosts_version: "v0.7.0" # was v0.6.0Changes to ansible/roles/router-hosts/templates/server.toml.j2
Section titled “Changes to ansible/roles/router-hosts/templates/server.toml.j2”[server]bind_address = "{{ router_hosts_bind_address }}:{{ router_hosts_grpc_port }}"hosts_file_path = "/hosts/main" # was /data/hosts
[database]path = "/data/router-hosts.sqlite3" # was /data/router-hosts.db
[tls]cert_file = "/certs/tls.crt"key_file = "/certs/tls.key"ca_file = "/certs/ca.crt"Changes to Docker Compose Volume Mounts
Section titled “Changes to Docker Compose Volume Mounts”Add new volume mount for hosts directory:
volumes: - {{ router_hosts_data_dir }}:/data - {{ router_hosts_hosts_dir }}:/hosts # NEW - {{ router_hosts_certs_dir }}:/certs:roNew Variable in defaults/main.yml
Section titled “New Variable in defaults/main.yml”router_hosts_hosts_dir: "{{ router_hosts_base_dir }}/hosts"3. Kubernetes Operator Deployment
Section titled “3. Kubernetes Operator Deployment”Deploy via ArgoCD using the Helm chart from ghcr.io/fzymgc-house/router-hosts-operator.
Directory Structure
Section titled “Directory Structure”argocd/└── apps/ └── router-hosts-operator/ ├── kustomization.yaml ├── namespace.yaml ├── application.yaml ├── external-secret.yaml └── values.yamlargocd/apps/router-hosts-operator/namespace.yaml
Section titled “argocd/apps/router-hosts-operator/namespace.yaml”apiVersion: v1kind: Namespacemetadata: name: router-hosts-operatorargocd/apps/router-hosts-operator/external-secret.yaml
Section titled “argocd/apps/router-hosts-operator/external-secret.yaml”apiVersion: external-secrets.io/v1beta1kind: ExternalSecretmetadata: name: router-hosts-mtls namespace: router-hosts-operatorspec: refreshInterval: 1h secretStoreRef: name: vault-backend kind: ClusterSecretStore target: name: router-hosts-mtls template: type: kubernetes.io/tls data: tls.crt: "{{ .cert }}" tls.key: "{{ .private_key }}" ca.crt: "{{ .issuing_ca }}" dataFrom: - extract: key: pki/issue/router-hosts-client conversionStrategy: Default rewrite: - regexp: source: "certificate" target: "cert"argocd/apps/router-hosts-operator/values.yaml
Section titled “argocd/apps/router-hosts-operator/values.yaml”operator: routerHostsServer: address: "192.168.20.1:50051" tls: enabled: true secretName: router-hosts-mtlsargocd/apps/router-hosts-operator/application.yaml
Section titled “argocd/apps/router-hosts-operator/application.yaml”apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: router-hosts-operator namespace: argocdspec: project: default source: repoURL: ghcr.io/fzymgc-house/router-hosts-operator targetRevision: 0.7.0 chart: router-hosts-operator helm: valueFiles: - values.yaml destination: server: https://kubernetes.default.svc namespace: router-hosts-operator syncPolicy: automated: prune: true selfHeal: trueExample HostEntry CRD
Section titled “Example HostEntry CRD”apiVersion: routerhosts.fzymgc.house/v1alpha1kind: HostEntrymetadata: name: vault namespace: vaultspec: hostname: vault ip: 192.168.20.1454. Terraform Provider & Workspace
Section titled “4. Terraform Provider & Workspace”New HCP Terraform workspace for infrastructure host management.
Directory Structure
Section titled “Directory Structure”tf/└── router-hosts/ ├── versions.tf ├── terraform.tf ├── variables.tf ├── outputs.tf ├── main.tf └── hosts.tftf/router-hosts/versions.tf
Section titled “tf/router-hosts/versions.tf”terraform { required_version = ">= 1.5.0"
required_providers { routerhosts = { source = "fzymgc-house/routerhosts" version = "~> 0.7.0" } vault = { source = "hashicorp/vault" version = "~> 4.0" } }}tf/router-hosts/terraform.tf
Section titled “tf/router-hosts/terraform.tf”terraform { cloud { organization = "fzymgc-house"
workspaces { name = "main-cluster-router-hosts" } }}tf/router-hosts/variables.tf
Section titled “tf/router-hosts/variables.tf”variable "router_hosts_address" { description = "Address of the router-hosts gRPC server" type = string default = "192.168.20.1:50051"}tf/router-hosts/main.tf
Section titled “tf/router-hosts/main.tf”# Get mTLS certificates from Vault PKIdata "vault_pki_secret_backend_cert" "router_hosts" { backend = "pki" name = "router-hosts-client" common_name = "terraform-router-hosts" ttl = "24h"}
provider "routerhosts" { address = var.router_hosts_address
tls { cert = data.vault_pki_secret_backend_cert.router_hosts.certificate key = data.vault_pki_secret_backend_cert.router_hosts.private_key ca_cert = data.vault_pki_secret_backend_cert.router_hosts.ca_chain }}tf/router-hosts/hosts.tf
Section titled “tf/router-hosts/hosts.tf”# TuringPi Alpha Boardresource "routerhosts_entry" "tpi_alpha_1" { hostname = "tpi-alpha-1" ip = "192.168.20.141"}
resource "routerhosts_entry" "tpi_alpha_2" { hostname = "tpi-alpha-2" ip = "192.168.20.142"}
resource "routerhosts_entry" "tpi_alpha_3" { hostname = "tpi-alpha-3" ip = "192.168.20.143"}
resource "routerhosts_entry" "tpi_alpha_4" { hostname = "tpi-alpha-4" ip = "192.168.20.144"}
# TuringPi Beta Boardresource "routerhosts_entry" "tpi_beta_1" { hostname = "tpi-beta-1" ip = "192.168.20.151"}
resource "routerhosts_entry" "tpi_beta_2" { hostname = "tpi-beta-2" ip = "192.168.20.152"}
resource "routerhosts_entry" "tpi_beta_3" { hostname = "tpi-beta-3" ip = "192.168.20.153"}
resource "routerhosts_entry" "tpi_beta_4" { hostname = "tpi-beta-4" ip = "192.168.20.154"}
# Kubernetes API VIPresource "routerhosts_entry" "k8s_api" { hostname = "k8s-api" ip = "192.168.20.140"}
# NAS Systemsresource "routerhosts_entry" "nas_alpha" { hostname = "nas-alpha" ip = "192.168.20.200"}
resource "routerhosts_entry" "nas_alpha_iscsi" { hostname = "nas-alpha-iscsi" ip = "192.168.20.201"}
resource "routerhosts_entry" "nas_alpha_replication" { hostname = "nas-alpha-replication" ip = "192.168.20.202"}
resource "routerhosts_entry" "nas_beta" { hostname = "nas-beta" ip = "192.168.20.210"}5. CLI Setup & Verification
Section titled “5. CLI Setup & Verification”Installation (Homebrew)
Section titled “Installation (Homebrew)”brew install fzymgc-house/tap/router-hostsCertificate Setup
Section titled “Certificate Setup”Bash/Zsh:
# Create config directory and request certificates from Vaultmkdir -p ~/.config/router-hosts && \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-$(whoami)" ttl=720h | \jq -r '.data.certificate' > ~/.config/router-hosts/tls.crt && \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-$(whoami)" ttl=720h | \jq -r '.data.private_key' > ~/.config/router-hosts/tls.key && \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-$(whoami)" ttl=720h | \jq -r '.data.issuing_ca' > ~/.config/router-hosts/ca.crt && \cat > ~/.config/router-hosts/config.toml << 'EOF'[server]address = "192.168.20.1:50051"
[tls]cert_file = "~/.config/router-hosts/tls.crt"key_file = "~/.config/router-hosts/tls.key"ca_file = "~/.config/router-hosts/ca.crt"EOFFish:
# Create config directory and request certificates from Vaultmkdir -p ~/.config/router-hosts; and \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-"(whoami) ttl=720h | \jq -r '.data.certificate' > ~/.config/router-hosts/tls.crt; and \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-"(whoami) ttl=720h | \jq -r '.data.private_key' > ~/.config/router-hosts/tls.key; and \vault write -format=json pki/issue/router-hosts-client \ common_name="cli-"(whoami) ttl=720h | \jq -r '.data.issuing_ca' > ~/.config/router-hosts/ca.crt; and \cat > ~/.config/router-hosts/config.toml << 'EOF'[server]address = "192.168.20.1:50051"
[tls]cert_file = "~/.config/router-hosts/tls.crt"key_file = "~/.config/router-hosts/tls.key"ca_file = "~/.config/router-hosts/ca.crt"EOFVerification Commands
Section titled “Verification Commands”# List all hostsrouter-hosts list
# Get specific hostrouter-hosts get vault
# Add test entryrouter-hosts add test-entry 192.168.20.99
# Remove test entryrouter-hosts delete test-entry6. Migration Strategy
Section titled “6. Migration Strategy”Phase 1: Infrastructure Preparation
Section titled “Phase 1: Infrastructure Preparation”- Upgrade router-hosts server to v0.7.0 via Ansible
- Configure directory separation (hosts vs database)
- Verify server connectivity with CLI
Phase 2: Terraform Infrastructure Hosts
Section titled “Phase 2: Terraform Infrastructure Hosts”- Create
tf/router-hostsworkspace - Import existing infrastructure hosts as Terraform resources
- Verify entries via CLI
Phase 3: Kubernetes Operator Deployment
Section titled “Phase 3: Kubernetes Operator Deployment”- Deploy operator via ArgoCD
- Create HostEntry CRDs for each K8s service
- Verify entries appear in router-hosts database
Phase 4: Cutover
Section titled “Phase 4: Cutover”- Update Firewalla dnsmasq configuration to point to new hosts directory
- Remove manual
/extdata/hosts/mainfile - Verify DNS resolution for all hosts
7. Firewalla dnsmasq Integration
Section titled “7. Firewalla dnsmasq Integration”Current State
Section titled “Current State”- dnsmasq config:
~/.firewalla/config/dnsmasq_local/local-hosts.conf - Points to:
/extdata/hosts(manual file) - router-hosts server writes to:
/extdata/router-hosts/data/hosts
Target State
Section titled “Target State”- dnsmasq config updated to point to router-hosts managed directory
- Separate hosts output from database storage
Directory Structure After Migration
Section titled “Directory Structure After Migration”/extdata/router-hosts/├── data/│ └── router-hosts.sqlite3 # SQLite3 database├── hosts/│ └── main # Generated hosts file (dnsmasq reads this)└── certs/ ├── tls.crt ├── tls.key └── ca.crtAnsible Template: templates/local-hosts.conf.j2
Section titled “Ansible Template: templates/local-hosts.conf.j2”hostsdir={{ router_hosts_hosts_dir }}expand-hostsdomain=fzymgc.houseAnsible Task Addition
Section titled “Ansible Task Addition”- name: Configure dnsmasq to use router-hosts managed directory ansible.builtin.template: src: local-hosts.conf.j2 dest: ~/.firewalla/config/dnsmasq_local/local-hosts.conf mode: '0644' notify: Restart dnsmasqMigration Notes
Section titled “Migration Notes”- No database migration required (current DB is test data only)
- v0.7.0 uses SQLite3 as default database backend
- Directory separation ensures dnsmasq only reads the hosts file, not the database
Implementation Tasks
Section titled “Implementation Tasks”Task 1: Upgrade Ansible Role
Section titled “Task 1: Upgrade Ansible Role”Files:
- Modify:
ansible/roles/router-hosts/defaults/main.yml - Modify:
ansible/roles/router-hosts/templates/server.toml.j2 - Modify:
ansible/roles/router-hosts/templates/docker-compose.yml.j2 - Create:
ansible/roles/router-hosts/templates/local-hosts.conf.j2 - Modify:
ansible/roles/router-hosts/tasks/main.yml
Task 2: Create Terraform Workspace
Section titled “Task 2: Create Terraform Workspace”Files:
- Create:
tf/router-hosts/versions.tf - Create:
tf/router-hosts/terraform.tf - Create:
tf/router-hosts/variables.tf - Create:
tf/router-hosts/outputs.tf - Create:
tf/router-hosts/main.tf - Create:
tf/router-hosts/hosts.tf
Task 3: Deploy Kubernetes Operator
Section titled “Task 3: Deploy Kubernetes Operator”Files:
- Create:
argocd/apps/router-hosts-operator/kustomization.yaml - Create:
argocd/apps/router-hosts-operator/namespace.yaml - Create:
argocd/apps/router-hosts-operator/application.yaml - Create:
argocd/apps/router-hosts-operator/external-secret.yaml - Create:
argocd/apps/router-hosts-operator/values.yaml
Task 4: Create HostEntry CRDs for K8s Services
Section titled “Task 4: Create HostEntry CRDs for K8s Services”Files:
- Create:
argocd/apps/*/hostentry.yamlfor each service namespace
Task 5: Document CLI Setup
Section titled “Task 5: Document CLI Setup”Files:
- Create:
docs/operations/router-hosts.md
Task 6: Execute Migration
Section titled “Task 6: Execute Migration”- Run Ansible playbook to upgrade server
- Apply Terraform to create infrastructure hosts
- Sync ArgoCD to deploy operator
- Apply HostEntry CRDs
- Update dnsmasq configuration
- Verify DNS resolution