|
| 1 | +--- |
| 2 | +title: "Multi-Cluster Kubernetes Setup with Tailscale and ArgoCD" |
| 3 | +date: 2025-05-18T12:00:00-05:00 |
| 4 | +draft: false |
| 5 | +tags: ["Kubernetes", "Tailscale", "ArgoCD", "Networking", "DevOps"] |
| 6 | +categories: ["Kubernetes"] |
| 7 | +--- |
| 8 | + |
| 9 | +This guide focuses on configuring the Tailscale Kubernetes operator to expose Kubernetes API servers across multiple clusters for ArgoCD multi-cluster management. |
| 10 | + |
| 11 | +## Prerequisites |
| 12 | + |
| 13 | +- Multiple Kubernetes clusters |
| 14 | +- Tailscale account with admin access |
| 15 | +- Tailscale Kubernetes operator [installed in each cluster](https://tailscale.com/kb/1185/kubernetes/) |
| 16 | + |
| 17 | +## Configuring Operator Hostname and API Server Proxy |
| 18 | + |
| 19 | +When installing the Tailscale operator in each cluster, set these critical parameters: |
| 20 | + |
| 21 | +```bash |
| 22 | +helm upgrade --install tailscale-operator tailscale/tailscale-operator \\ |
| 23 | + --namespace=tailscale \\ |
| 24 | + --create-namespace \\ |
| 25 | + --set-string oauth.clientId=<oauth_client_id> \\ |
| 26 | + --set-string oauth.clientSecret=<oauth_client_secret> \\ |
| 27 | + --set operatorConfig.hostname=cluster1-k8s-operator \\ |
| 28 | + --set apiServerProxyConfig.mode=true \\ |
| 29 | + --wait |
| 30 | +``` |
| 31 | + |
| 32 | +Key parameters: |
| 33 | +- `operatorConfig.hostname`: Sets a unique hostname for the operator in your tailnet |
| 34 | +- `apiServerProxyConfig.mode=true`: Enables Kubernetes API server proxying |
| 35 | + |
| 36 | +Configure each cluster with a unique hostname (e.g., `cluster1-k8s-operator`, `cluster2-k8s-operator`). |
| 37 | + |
| 38 | +## Configure Tailscale ACL Grants for Cross-Cluster Access |
| 39 | + |
| 40 | +For egress proxies to communicate with Kubernetes API servers exposed by the Tailscale operators, you need to configure appropriate ACL grants in your Tailscale admin console. |
| 41 | + |
| 42 | +### Why ACL Grants Are Required |
| 43 | + |
| 44 | +Without proper ACL grants: |
| 45 | +1. Access to remote Kubernetes API servers will be blocked by Tailscale\'s access controls |
| 46 | +2. ArgoCD will be unable to manage resources across clusters |
| 47 | +3. Cross-cluster communication will fail with authentication errors |
| 48 | + |
| 49 | +### Configuring ACL Grants |
| 50 | + |
| 51 | +Add the following to your Tailscale ACL configuration: |
| 52 | + |
| 53 | +```json |
| 54 | +"grants": [ |
| 55 | + { |
| 56 | + "src": ["autogroup:admin", "tag:k8s"], |
| 57 | + "dst": ["tag:k8s-operator"], |
| 58 | + "app": { |
| 59 | + "tailscale.com/cap/kubernetes": [ |
| 60 | + { |
| 61 | + "impersonate": { |
| 62 | + "groups": ["system:masters"] |
| 63 | + }, |
| 64 | + "recorder": ["tag:k8s-recorder"], |
| 65 | + "enforceRecorder": false |
| 66 | + } |
| 67 | + ] |
| 68 | + } |
| 69 | + } |
| 70 | +] |
| 71 | +``` |
| 72 | + |
| 73 | +Key components of this configuration: |
| 74 | + |
| 75 | +- `"src": ["autogroup:admin", "tag:k8s"]` - Specifies who can access the Kubernetes API. Here, it allows admin users and any node tagged with `tag:k8s` (your ArgoCD cluster) |
| 76 | +- `"dst": ["tag:k8s-operator"]` - Specifies which Kubernetes operators can be accessed (targets) |
| 77 | +- `"impersonate": {"groups": ["system:masters"]}` - Grants administrative access to the Kubernetes API |
| 78 | +- `"recorder": ["tag:k8s-recorder"]` - Optional audit logging configuration |
| 79 | +- `"enforceRecorder": false` - Makes audit recording optional |
| 80 | + |
| 81 | +This grant enables ArgoCD (tagged with `tag:k8s`) to communicate with the Kubernetes API servers exposed by the Tailscale operators in your remote clusters. |
| 82 | + |
| 83 | +## Set Up DNS Configuration in ArgoCD Cluster |
| 84 | + |
| 85 | +### Why DNS Configuration is Necessary |
| 86 | + |
| 87 | +DNS configuration is a critical component that enables your ArgoCD cluster to resolve Tailnet domain names. Without this configuration: |
| 88 | + |
| 89 | +1. Your cluster cannot resolve `*.ts.net` domains that Tailscale uses |
| 90 | +2. Communication between clusters would fail as hostname resolution would not work |
| 91 | +3. ArgoCD would be unable to connect to remote Kubernetes API servers |
| 92 | + |
| 93 | +The Tailscale DNS nameserver provides resolution for all nodes in your Tailnet, enabling seamless cross-cluster communication through Tailscale\'s private network. |
| 94 | + |
| 95 | +### Implementation |
| 96 | + |
| 97 | +Create a DNSConfig resource in the ArgoCD cluster: |
| 98 | + |
| 99 | +```yaml |
| 100 | +apiVersion: tailscale.com/v1alpha1 |
| 101 | +kind: DNSConfig |
| 102 | +metadata: |
| 103 | + name: ts-dns |
| 104 | +spec: |
| 105 | + nameserver: |
| 106 | + image: |
| 107 | + repo: tailscale/k8s-nameserver |
| 108 | + tag: unstable |
| 109 | +``` |
| 110 | +
|
| 111 | +Find the nameserver IP: |
| 112 | +
|
| 113 | +```bash |
| 114 | +kubectl get dnsconfig ts-dns |
| 115 | +# Note the NAMESERVERIP (e.g., 10.100.124.196) |
| 116 | +``` |
| 117 | + |
| 118 | +Update CoreDNS configuration to forward Tailscale domain lookups to the Tailscale nameserver: |
| 119 | + |
| 120 | +```yaml |
| 121 | +apiVersion: v1 |
| 122 | +kind: ConfigMap |
| 123 | +metadata: |
| 124 | + name: coredns |
| 125 | + namespace: kube-system |
| 126 | +data: |
| 127 | + Corefile: | |
| 128 | + .:53 { |
| 129 | + # ... existing config ... |
| 130 | + } |
| 131 | + ts.net { |
| 132 | + errors |
| 133 | + cache 30 |
| 134 | + forward . 10.100.124.196 |
| 135 | + } |
| 136 | +``` |
| 137 | +
|
| 138 | +This configuration tells CoreDNS to forward all `ts.net` domain resolution requests to the Tailscale nameserver, allowing pods in your cluster to resolve Tailnet hostnames. |
| 139 | + |
| 140 | +## Create Egress Services in ArgoCD Cluster |
| 141 | + |
| 142 | +Apply the following configuration to create egress services in the ArgoCD cluster: |
| 143 | + |
| 144 | +```yaml |
| 145 | +apiVersion: v1 |
| 146 | +kind: Service |
| 147 | +metadata: |
| 148 | + name: cluster1-k8s-operator |
| 149 | + annotations: |
| 150 | + tailscale.com/tailnet-fqdn: cluster1-k8s-operator.<TAILNET>.ts.net |
| 151 | +spec: |
| 152 | + externalName: placeholder |
| 153 | + type: ExternalName |
| 154 | + ports: |
| 155 | + - name: https |
| 156 | + port: 443 |
| 157 | + protocol: TCP |
| 158 | +--- |
| 159 | +apiVersion: v1 |
| 160 | +kind: Service |
| 161 | +metadata: |
| 162 | + name: cluster2-k8s-operator |
| 163 | + annotations: |
| 164 | + tailscale.com/tailnet-fqdn: cluster2-k8s-operator.<TAILNET>.ts.net |
| 165 | +spec: |
| 166 | + externalName: placeholder |
| 167 | + type: ExternalName |
| 168 | + ports: |
| 169 | + - name: https |
| 170 | + port: 443 |
| 171 | + protocol: TCP |
| 172 | +``` |
| 173 | + |
| 174 | +Replace `<TAILNET>` with your Tailscale tailnet name. |
| 175 | + |
| 176 | +## Access Remote Clusters |
| 177 | + |
| 178 | +Generate the kubeconfig for each cluster: |
| 179 | + |
| 180 | +```bash |
| 181 | +tailscale configure kubeconfig cluster1-k8s-operator.<TAILNET>.ts.net |
| 182 | +tailscale configure kubeconfig cluster2-k8s-operator.<TAILNET>.ts.net |
| 183 | +``` |
| 184 | + |
| 185 | +## Add Clusters to ArgoCD |
| 186 | + |
| 187 | +Add the remote clusters to ArgoCD: |
| 188 | + |
| 189 | +```bash |
| 190 | +argocd cluster add cluster1-k8s-operator.<TAILNET>.ts.net --grpc-web |
| 191 | +argocd cluster add cluster2-k8s-operator.<TAILNET>.ts.net --grpc-web |
| 192 | +``` |
| 193 | + |
| 194 | +## References |
| 195 | + |
| 196 | +- [Tailscale Kubernetes Operator Documentation](https://tailscale.com/kb/1185/kubernetes/) |
| 197 | +- [Cross-cluster Connectivity Guide](https://tailscale.com/kb/1442/kubernetes-operator-cross-cluster) |
| 198 | +- [Cluster Egress Configuration](https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress) |
0 commit comments