Migrate Tailscale operator to ArgoCD management (Phase 1 Step 5)

Adds ArgoCD Application to manage Tailscale operator from forge:
- ArgoCD Application sourced from internal Forgejo via SSH
- DNS config for cluster-to-tailnet name resolution
- Egress proxy for accessing forge on indri
- ACL grants for k8s workloads to reach forge (ports 3001, 2200)
- Template for repository secret with 1Password SSH key reference

Key discovery: 1Password op read requires ?ssh-format=openssh parameter
to get keys in OpenSSH format that ArgoCD's SSH client can read.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-01-19 07:12:51 -08:00
commit c47ac189c9
5 changed files with 94 additions and 2 deletions

View file

@ -0,0 +1,22 @@
# ArgoCD Application for Tailscale Kubernetes Operator
# Note: OAuth secret is managed separately (not in git)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: tailscale-operator
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: feature/k8s-phase1-kickoff
path: argocd/manifests/tailscale-operator
destination:
server: https://kubernetes.default.svc
namespace: tailscale
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View file

@ -0,0 +1,27 @@
# ArgoCD repository secret for forge SSH access
#
# IMPORTANT: Use ?ssh-format=openssh to get OpenSSH format (required by ArgoCD)
#
# Create the secret with:
#
# PRIV_KEY=$(op read "op://vg6xf6vvfmoh5hqjjhlhbeoaie/csjncynh6htjvnh2l2da65y32q/private key?ssh-format=openssh")$'\n' && \
# kubectl create secret generic repo-forge -n argocd \
# --from-literal=type=git \
# --from-literal=url='ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git' \
# --from-literal=insecure=true \
# --from-literal=sshPrivateKey="$PRIV_KEY" && \
# kubectl label secret repo-forge -n argocd argocd.argoproj.io/secret-type=repository
#
apiVersion: v1
kind: Secret
metadata:
name: repo-forge
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
type: git
url: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
insecure: "true"
sshPrivateKey: |
# Key from 1Password: op://vg6xf6vvfmoh5hqjjhlhbeoaie/csjncynh6htjvnh2l2da65y32q/private key

View file

@ -0,0 +1,16 @@
# DNSConfig for resolving MagicDNS names from within the cluster
# Deploys a nameserver that resolves ts.net names to egress proxy IPs
#
# Requires CoreDNS/kube-dns configuration to forward ts.net queries.
# See: https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress
---
apiVersion: tailscale.com/v1alpha1
kind: DNSConfig
metadata:
name: ts-dns
namespace: tailscale
spec:
nameserver:
image:
repo: docker.io/tailscale/k8s-nameserver
tag: stable

View file

@ -0,0 +1,20 @@
# Egress proxy to expose Forgejo (forge) to the cluster
# Forge runs on indri:3001, exposed via Tailscale Serve as forge.tail8d86e.ts.net
# We target indri directly since egress can't reach Tailscale Serve hostnames
#
# See: https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress
---
apiVersion: v1
kind: Service
metadata:
name: forge
namespace: tailscale
annotations:
tailscale.com/tailnet-fqdn: indri.tail8d86e.ts.net
tailscale.com/proxy-class: "default"
spec:
type: ExternalName
externalName: placeholder
ports:
- port: 3001
targetPort: 3001

View file

@ -67,6 +67,13 @@
"dst": ["tag:registry"],
"ip": ["tcp:443"],
},
// k8s workloads (e.g., ArgoCD) can access forge on indri for GitOps
// HTTP on 3001, SSH on 2200
{
"src": ["tag:k8s"],
"dst": ["tag:homelab"],
"ip": ["tcp:3001", "tcp:2200"],
},
],
// ============== SSH Access ==============
@ -133,10 +140,10 @@
"src": "tag:homelab",
"accept": ["tag:homelab:22", "tag:nas:445"],
},
// K8s workloads can reach registry
// K8s workloads can reach registry and forge (on indri:3001 HTTP, :2200 SSH)
{
"src": "tag:k8s",
"accept": ["tag:registry:443"],
"accept": ["tag:registry:443", "tag:homelab:3001", "tag:homelab:2200"],
},
],
}