From 0c38c5e4faf6aedc14345a2308feb2bb6f5675dc Mon Sep 17 00:00:00 2001 From: Jean-Francois Roy Date: Tue, 19 Mar 2024 23:23:59 -0700 Subject: [PATCH 1/4] feat: Support remotely-managed cloudflare tunnels --- bootstrap/scripts/validation.py | 26 ++++++++++++++----- .../cloudflared/app/helmrelease.yaml.j2 | 7 +++++ .../cloudflared/app/secret.sops.yaml.j2 | 1 + config.sample.yaml | 23 +++++++++------- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/bootstrap/scripts/validation.py b/bootstrap/scripts/validation.py index ef3e0bb6c17..98cf22b26fe 100644 --- a/bootstrap/scripts/validation.py +++ b/bootstrap/scripts/validation.py @@ -1,34 +1,38 @@ from functools import wraps from shutil import which -from typing import Callable +from typing import Callable, cast from zoneinfo import available_timezones import netaddr import sys DISTRIBUTIONS = ["k3s", "talos"] -GLOBAL_CLI_TOOLS = ["age", "cloudflared", "flux", "helmfile", "sops", "jq", "kubeconform", "kustomize"] +GLOBAL_CLI_TOOLS = ["age", "flux", "helmfile", "sops", "jq", "kubeconform", "kustomize"] TALOS_CLI_TOOLS = ["talosctl", "talhelper"] +CLOUDFLARE_TOOLS = ["cloudflared"] + def required(*keys: str): def wrapper_outter(func: Callable): @wraps(func) - def wrapper(data: dict, *args, **kwargs) -> None: + def wrapper(data: dict, *_, **kwargs) -> None: for key in keys: if data.get(key) is None: raise ValueError(f"Missing required key {key}") return func(*[data[key] for key in keys], **kwargs) + return wrapper + return wrapper_outter def validate_python_version() -> None: required_version = (3, 11, 0) if sys.version_info < required_version: - raise ValueError(f"Python version is below 3.11. Please upgrade.") + raise ValueError(f"Python {sys.version_info} is below 3.11. Please upgrade.") -@required("bootstrap_distribution") -def validate_cli_tools(distribution: dict, **_) -> None: +@required("bootstrap_distribution", "bootstrap_cloudflare") +def validate_cli_tools(distribution: str, cloudflare: dict, **_) -> None: if distribution not in DISTRIBUTIONS: raise ValueError(f"Invalid distribution {distribution}") for tool in GLOBAL_CLI_TOOLS: @@ -37,10 +41,18 @@ def validate_cli_tools(distribution: dict, **_) -> None: for tool in TALOS_CLI_TOOLS if distribution in ["talos"] else []: if not which(tool): raise ValueError(f"Missing required CLI tool {tool}") + for tool in ( + CLOUDFLARE_TOOLS + if cloudflare.get("enabled", False) + and cast(dict, cloudflare.get("tunnel", {})).get("token", "") == "" + else [] + ): + if not which(tool): + raise ValueError(f"Missing required CLI tool {tool}") @required("bootstrap_distribution") -def validate_distribution(distribution: dict, **_) -> None: +def validate_distribution(distribution: str, **_) -> None: if distribution not in DISTRIBUTIONS: raise ValueError(f"Invalid distribution {distribution}") diff --git a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 index 785fc81855a..c3ca4d4b7d7 100644 --- a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 +++ b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 @@ -44,6 +44,13 @@ spec: secretKeyRef: name: cloudflared-secret key: TUNNEL_ID + #% if bootstrap_cloudflare.tunnel.token %# + TUNNEL_TOKEN: + valueFrom: + secretKeyRef: + name: cloudflared-secret + key: TUNNEL_TOKEN + #% endif %# args: - tunnel - --config diff --git a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 index 67d169ed7c3..cf97b857b36 100644 --- a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 +++ b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 @@ -5,6 +5,7 @@ metadata: name: cloudflared-secret stringData: TUNNEL_ID: "#{ bootstrap_cloudflare.tunnel.id }#" + TUNNEL_TOKEN: "#{ bootstrap_cloudflare.tunnel.token }#" credentials.json: | { "AccountTag": "#{ bootstrap_cloudflare.tunnel.account_id }#", diff --git a/config.sample.yaml b/config.sample.yaml index ef071daed60..3e387d890f6 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -184,18 +184,23 @@ bootstrap_cloudflare: # in your nodes host network that is NOT being used. This is announced over L2. gateway_vip: "" # (Required) Options for Cloudflare Tunnel - # 1. Authenticate cloudflared to your domain - # > cloudflared tunnel login - # 2. Create the tunnel - # > cloudflared tunnel create k8s - # 3. Copy the AccountTag, TunnelID, and TunnelSecret from the tunnel configuration file and paste them below + # For a locally-managed tunnel: + # 1. Authenticate cloudflared to your domain + # > cloudflared tunnel login + # 2. Create the tunnel + # > cloudflared tunnel create k8s + # 3. Copy the AccountTag, TunnelID, and TunnelSecret from the tunnel configuration file and paste them below + # For a remotely-managed tunnel: https://one.dash.cloudflare.com/ tunnel: - # (Required) Cloudflare Account ID (cat ~/.cloudflared/*.json | jq -r .AccountTag) - account_id: "" - # (Required) Cloudflared Tunnel ID (cat ~/.cloudflared/*.json | jq -r .TunnelID) + # (Required) Cloudflared Tunnel ID + # If using locally-managed tunnel: (cat ~/.cloudflared/*.json | jq -r .TunnelID) id: "" - # (Required) Cloudflared Tunnel Secret (cat ~/.cloudflared/*.json | jq -r .TunnelSecret) + # (Required: locally-managed) Cloudflare Account ID (cat ~/.cloudflared/*.json | jq -r .AccountTag) + account_id: "" + # (Required: locally-managed) Cloudflared Tunnel Secret (cat ~/.cloudflared/*.json | jq -r .TunnelSecret) secret: "" + # (Required: remotely-managed) Cloudflared Tunnel Token + token: "" # (Required) Provide WAN access to the cluster ingresses for external ingress classes # The Load balancer IP for external ingress, choose an available IP # in your nodes host network that is NOT being used. This is announced over L2. From c968892ec0f52180e1c7bc1b6594d86a0f3a801b Mon Sep 17 00:00:00 2001 From: Devin Buhl Date: Wed, 20 Mar 2024 08:16:15 -0400 Subject: [PATCH 2/4] Update bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 --- .../kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 index cf97b857b36..0d32339dfcc 100644 --- a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 +++ b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 @@ -5,7 +5,9 @@ metadata: name: cloudflared-secret stringData: TUNNEL_ID: "#{ bootstrap_cloudflare.tunnel.id }#" + #% if bootstrap_cloudflare.tunnel.token %# TUNNEL_TOKEN: "#{ bootstrap_cloudflare.tunnel.token }#" + #% endif %# credentials.json: | { "AccountTag": "#{ bootstrap_cloudflare.tunnel.account_id }#", From a928a1f1ca95e1b06ac256fa66a934a4b52ec5e9 Mon Sep 17 00:00:00 2001 From: Jean-Francois Roy Date: Thu, 21 Mar 2024 13:13:07 -0700 Subject: [PATCH 3/4] Always include TUNNEL_TOKEN so it's easy to switch --- .../kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 | 2 -- .../kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 | 2 -- 2 files changed, 4 deletions(-) diff --git a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 index c3ca4d4b7d7..b30636e8a1e 100644 --- a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 +++ b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/helmrelease.yaml.j2 @@ -44,13 +44,11 @@ spec: secretKeyRef: name: cloudflared-secret key: TUNNEL_ID - #% if bootstrap_cloudflare.tunnel.token %# TUNNEL_TOKEN: valueFrom: secretKeyRef: name: cloudflared-secret key: TUNNEL_TOKEN - #% endif %# args: - tunnel - --config diff --git a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 index 0d32339dfcc..cf97b857b36 100644 --- a/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 +++ b/bootstrap/templates/kubernetes/apps/network/cloudflared/app/secret.sops.yaml.j2 @@ -5,9 +5,7 @@ metadata: name: cloudflared-secret stringData: TUNNEL_ID: "#{ bootstrap_cloudflare.tunnel.id }#" - #% if bootstrap_cloudflare.tunnel.token %# TUNNEL_TOKEN: "#{ bootstrap_cloudflare.tunnel.token }#" - #% endif %# credentials.json: | { "AccountTag": "#{ bootstrap_cloudflare.tunnel.account_id }#", From e9a0e61d37063d373f55ebf37602f6fb9249c4fd Mon Sep 17 00:00:00 2001 From: Jean-Francois Roy Date: Thu, 21 Mar 2024 13:13:35 -0700 Subject: [PATCH 4/4] Use CLI and dashboard naming --- config.sample.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config.sample.yaml b/config.sample.yaml index 3e387d890f6..516f701804d 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -184,22 +184,22 @@ bootstrap_cloudflare: # in your nodes host network that is NOT being used. This is announced over L2. gateway_vip: "" # (Required) Options for Cloudflare Tunnel - # For a locally-managed tunnel: + # For a locally-managed (aka CLI) tunnel: # 1. Authenticate cloudflared to your domain # > cloudflared tunnel login # 2. Create the tunnel # > cloudflared tunnel create k8s # 3. Copy the AccountTag, TunnelID, and TunnelSecret from the tunnel configuration file and paste them below - # For a remotely-managed tunnel: https://one.dash.cloudflare.com/ + # For a remotely-managed (aka dashboard) tunnel: https://one.dash.cloudflare.com/ tunnel: # (Required) Cloudflared Tunnel ID - # If using locally-managed tunnel: (cat ~/.cloudflared/*.json | jq -r .TunnelID) + # If using CLI tunnel: (cat ~/.cloudflared/*.json | jq -r .TunnelID) id: "" - # (Required: locally-managed) Cloudflare Account ID (cat ~/.cloudflared/*.json | jq -r .AccountTag) + # (Required: CLI) Cloudflare Account ID (cat ~/.cloudflared/*.json | jq -r .AccountTag) account_id: "" - # (Required: locally-managed) Cloudflared Tunnel Secret (cat ~/.cloudflared/*.json | jq -r .TunnelSecret) + # (Required: CLI) Cloudflared Tunnel Secret (cat ~/.cloudflared/*.json | jq -r .TunnelSecret) secret: "" - # (Required: remotely-managed) Cloudflared Tunnel Token + # (Required: dashboard) Cloudflared Tunnel Token token: "" # (Required) Provide WAN access to the cluster ingresses for external ingress classes # The Load balancer IP for external ingress, choose an available IP