From a0ed6523ecb210ea27b6e86da1831c5308571e37 Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Mon, 2 Mar 2026 20:28:51 +0000 Subject: [PATCH] feat: add Tailscale Kubernetes Operator for Grafana/Prometheus access --- ansible/roles/observability/defaults/main.yml | 4 ++ ansible/roles/observability/tasks/main.yml | 57 ++++++++++++++++++- .../tailscale-operator/defaults/main.yml | 8 +++ .../roles/tailscale-operator/tasks/main.yml | 47 +++++++++++++++ .../templates/operator-values.yaml.j2 | 9 +++ ansible/site.yml | 7 +++ 6 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 ansible/roles/tailscale-operator/defaults/main.yml create mode 100644 ansible/roles/tailscale-operator/tasks/main.yml create mode 100644 ansible/roles/tailscale-operator/templates/operator-values.yaml.j2 diff --git a/ansible/roles/observability/defaults/main.yml b/ansible/roles/observability/defaults/main.yml index 5638e4b..8df65f3 100644 --- a/ansible/roles/observability/defaults/main.yml +++ b/ansible/roles/observability/defaults/main.yml @@ -16,3 +16,7 @@ grafana_storage_class: "local-path" loki_storage_class: "local-path" loki_enabled: true + +tailscale_oauth_client_id: "" +tailscale_oauth_client_secret: "" +tailscale_tailnet: "" diff --git a/ansible/roles/observability/tasks/main.yml b/ansible/roles/observability/tasks/main.yml index 753d869..a1ed7e8 100644 --- a/ansible/roles/observability/tasks/main.yml +++ b/ansible/roles/observability/tasks/main.yml @@ -156,7 +156,61 @@ changed_when: true when: loki_enabled -- name: Show observability access details +- name: Configure Grafana for Tailscale access + block: + - name: Patch Grafana service for Tailscale + command: >- + kubectl -n {{ observability_namespace }} patch svc kube-prometheus-stack-grafana + -p '{"metadata":{"annotations":{"tailscale.com/hostname":"grafana"}},"spec":{"type":"LoadBalancer","loadBalancerClass":"tailscale"}}' + register: grafana_patch + changed_when: true + + - name: Patch Prometheus service for Tailscale + command: >- + kubectl -n {{ observability_namespace }} patch svc kube-prometheus-stack-prometheus + -p '{"metadata":{"annotations":{"tailscale.com/hostname":"prometheus"}},"spec":{"type":"LoadBalancer","loadBalancerClass":"tailscale"}}' + register: prometheus_patch + changed_when: true + + - name: Wait for Tailscale to assign LoadBalancer IP (Grafana) + shell: >- + kubectl -n {{ observability_namespace }} get svc kube-prometheus-stack-grafana + -o jsonpath='{.status.loadBalancer.ingress[0].ip}' + register: grafana_lb_ip + until: grafana_lb_ip.stdout | length > 0 + retries: 30 + delay: 10 + changed_when: false + + - name: Wait for Tailscale to assign LoadBalancer IP (Prometheus) + shell: >- + kubectl -n {{ observability_namespace }} get svc kube-prometheus-stack-prometheus + -o jsonpath='{.status.loadBalancer.ingress[0].ip}' + register: prometheus_lb_ip + until: prometheus_lb_ip.stdout | length > 0 + retries: 30 + delay: 10 + changed_when: false + + - name: Show Tailscale access details + debug: + msg: | + Observability stack deployed with Tailscale access! + + Grafana: http://grafana (or http://{{ grafana_lb_ip.stdout }}) + Prometheus: http://prometheus (or http://{{ prometheus_lb_ip.stdout }}) + + Login: admin / {{ grafana_password_effective }} + + Access via: + - MagicDNS: http://grafana or http://prometheus (if enabled) + - Direct IP: http://{{ grafana_lb_ip.stdout }} or http://{{ prometheus_lb_ip.stdout }} + - Tailnet FQDN: http://grafana.{{ tailscale_tailnet | default('tailnet.ts.net') }} + + Note: Ensure Tailscale Kubernetes Operator is installed first + when: tailscale_oauth_client_id is defined and tailscale_oauth_client_id | length > 0 + +- name: Show observability access details (fallback) debug: msg: | Observability stack deployed. @@ -169,3 +223,4 @@ {% else %} Loki: Disabled {% endif %} + when: tailscale_oauth_client_id is not defined or tailscale_oauth_client_id | length == 0 diff --git a/ansible/roles/tailscale-operator/defaults/main.yml b/ansible/roles/tailscale-operator/defaults/main.yml new file mode 100644 index 0000000..ad5a94e --- /dev/null +++ b/ansible/roles/tailscale-operator/defaults/main.yml @@ -0,0 +1,8 @@ +--- +tailscale_operator_namespace: "tailscale-system" +tailscale_operator_version: "1.68.1" + +tailscale_oauth_client_id: "" +tailscale_oauth_client_secret: "" + +tailscale_operator_hostname: "" diff --git a/ansible/roles/tailscale-operator/tasks/main.yml b/ansible/roles/tailscale-operator/tasks/main.yml new file mode 100644 index 0000000..50300fa --- /dev/null +++ b/ansible/roles/tailscale-operator/tasks/main.yml @@ -0,0 +1,47 @@ +--- +- name: Check if Helm is installed + command: helm version --short + register: helm_check + changed_when: false + failed_when: false + +- name: Install Helm + shell: curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + when: helm_check.rc != 0 + changed_when: true + +- name: Create Tailscale operator namespace + command: kubectl create namespace {{ tailscale_operator_namespace }} + register: create_ns + failed_when: create_ns.rc != 0 and "AlreadyExists" not in create_ns.stderr + changed_when: create_ns.rc == 0 + +- name: Add Tailscale Helm repo + command: helm repo add tailscale https://pkgs.tailscale.com/unstable/helmcharts + register: add_repo + failed_when: add_repo.rc != 0 and "already exists" not in add_repo.stderr + changed_when: add_repo.rc == 0 + +- name: Update Helm repos + command: helm repo update + changed_when: false + +- name: Write Tailscale operator values + template: + src: operator-values.yaml.j2 + dest: /tmp/tailscale-operator-values.yaml + mode: "0644" + +- name: Install Tailscale Kubernetes Operator + command: >- + helm upgrade --install tailscale-operator tailscale/operator + --namespace {{ tailscale_operator_namespace }} + --version {{ tailscale_operator_version }} + --values /tmp/tailscale-operator-values.yaml + --wait + --timeout 5m + changed_when: true + +- name: Wait for Tailscale operator to be ready + command: kubectl -n {{ tailscale_operator_namespace }} rollout status deployment/tailscale-operator --timeout=5m + changed_when: false diff --git a/ansible/roles/tailscale-operator/templates/operator-values.yaml.j2 b/ansible/roles/tailscale-operator/templates/operator-values.yaml.j2 new file mode 100644 index 0000000..a43773a --- /dev/null +++ b/ansible/roles/tailscale-operator/templates/operator-values.yaml.j2 @@ -0,0 +1,9 @@ +apiServerProxyConfig: + mode: "true" + +oauth: + clientId: "{{ tailscale_oauth_client_id }}" + clientSecret: "{{ tailscale_oauth_client_secret }}" + +operatorConfig: + hostname: "{{ tailscale_operator_hostname | default('ts-operator') }}" diff --git a/ansible/site.yml b/ansible/site.yml index 1770881..7e77180 100644 --- a/ansible/site.yml +++ b/ansible/site.yml @@ -89,6 +89,13 @@ roles: - csi +- name: Deploy Tailscale Kubernetes Operator + hosts: control_plane[0] + become: true + + roles: + - tailscale-operator + - name: Deploy observability stack hosts: control_plane[0] become: true