Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions modules/kubernetes_cluster/gke/1.0/facets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ title: GKE Cluster Control Plane
description: Creates a GKE cluster control plane without default node pool. Use kubernetes_node_pool
module to add node pools.
intentDetails:
type: Cloud & Infrastructure
description: GKE cluster control plane without default node pool for flexible node management
displayName: Kubernetes Cluster
iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/kubernetes_cluster.svg
type: kubernetes
description: GKE cluster control plane without default node pool for flexible node
management
displayName: GKE
spec:
type: object
properties:
Expand Down
81 changes: 81 additions & 0 deletions modules/kubernetes_cluster/gke/1.0/gke-exec-auth.sh.tftpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# GKE exec-auth credential helper.
#
# Why this exists: GKE's standard kubeconfig auth path requires
# `gke-gcloud-auth-plugin` (or the community `gke-auth-plugin`) on every
# machine that talks to the cluster. The Facets runner image doesn't ship
# that binary and installing release artifacts at apply time is fragile.
# This script performs the same OAuth2 JWT-bearer exchange the plugin
# performs (RFC 7523), using only tools already on the runner: bash, curl,
# openssl, and standard text utilities.
#
# Terraform's templatefile() substitutes $${credentials} below with the raw
# GCP service-account JSON before the script is handed to `bash -c`.
# stdout is the Kubernetes ExecCredential JSON that the kubernetes provider
# consumes; everything else goes to stderr.

set -euo pipefail

creds_file=$(mktemp -t gke-creds.XXXXXX)
key_file=$(mktemp -t gke-key.XXXXXX)
trap 'rm -f "$creds_file" "$key_file"' EXIT

# Single-quoted heredoc — credentials are written verbatim with no shell
# expansion. templatefile() injects the JSON before bash ever sees it.
cat > "$creds_file" <<'__CREDS_EOF__'
${credentials}
__CREDS_EOF__

# --- 1. Parse service-account JSON ------------------------------------------
# Avoiding jq because it isn't guaranteed on the runner. The private_key
# field stores literal "\n" escape sequences; openssl needs real newlines.
client_email=$(grep -o '"client_email" *: *"[^"]*"' "$creds_file" \
| head -1 \
| cut -d'"' -f4)

sed -n '/"private_key"/s/.*"private_key" *: *"//p' "$creds_file" \
| sed 's/",*$//' \
| sed 's/\\n/\n/g' \
> "$key_file"

if [[ -z "$client_email" || ! -s "$key_file" ]]; then
echo "gke-exec-auth: failed to parse client_email / private_key from credentials JSON" >&2
exit 1
fi

# --- 2. Build and sign a JWT, exchange it for an access token --------------
b64url() {
openssl base64 | tr -d '=\n' | tr '/+' '_-'
}

now=$(date +%s)
exp=$((now + 3600))

header=$(printf '{"alg":"RS256","typ":"JWT"}' | b64url)
claim=$(printf \
'{"iss":"%s","scope":"https://www.googleapis.com/auth/cloud-platform","aud":"https://oauth2.googleapis.com/token","iat":%d,"exp":%d}' \
"$client_email" "$now" "$exp" \
| b64url)
signature=$(printf '%s.%s' "$header" "$claim" \
| openssl dgst -sha256 -sign "$key_file" -binary \
| b64url)
assertion="$header.$claim.$signature"

if ! response=$(curl -sf -X POST https://oauth2.googleapis.com/token \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
--data-urlencode "assertion=$assertion"); then
echo "gke-exec-auth: token endpoint request failed" >&2
exit 1
fi

access_token=$(echo "$response" | grep -o '"access_token" *: *"[^"]*"' | head -1 | cut -d'"' -f4)
if [[ -z "$access_token" ]]; then
echo "gke-exec-auth: no access_token in oauth2 response" >&2
exit 1
fi

# --- 3. Emit ExecCredential -------------------------------------------------
# Schema reference: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins
printf '{"apiVersion":"client.authentication.k8s.io/v1","kind":"ExecCredential","status":{"token":"%s"}}\n' \
"$access_token"
15 changes: 11 additions & 4 deletions modules/kubernetes_cluster/gke/1.0/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
locals {
# GKE exec-auth helper: runs the OAuth2 JWT-bearer flow in pure bash and
# emits an ExecCredential. See gke-exec-auth.sh.tftpl for the script.
exec_bash_command = templatefile(
"${path.module}/gke-exec-auth.sh.tftpl",
{ credentials = local.credentials }
)

output_attributes = {
# Cluster identification
cluster_id = google_container_cluster.primary.id
Expand All @@ -10,9 +17,9 @@ locals {
cluster_endpoint = "https://${google_container_cluster.primary.endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth[0].cluster_ca_certificate)
kubernetes_provider_exec = {
api_version = "client.authentication.k8s.io/v1beta1"
api_version = "client.authentication.k8s.io/v1"
command = "bash"
args = ["-c", "command -v gke-auth-plugin >/dev/null 2>&1 || (curl -sLo /tmp/gke-auth-plugin.tar.gz https://github.com/traviswt/gke-auth-plugin/releases/download/0.3.0/gke-auth-plugin_Linux_x86_64.tar.gz && tar -xzf /tmp/gke-auth-plugin.tar.gz -C /tmp && chmod +x /tmp/gke-auth-plugin && mv /tmp/gke-auth-plugin /usr/local/bin/gke-auth-plugin); echo '${local.credentials}' > /tmp/gcp-creds-$$.json && GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcp-creds-$$.json gke-auth-plugin; rm -f /tmp/gcp-creds-$$.json"]
args = ["-c", local.exec_bash_command]
}

# Project and region details
Expand Down Expand Up @@ -49,9 +56,9 @@ locals {
host = "https://${google_container_cluster.primary.endpoint}"
cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth[0].cluster_ca_certificate)
kubernetes_provider_exec = {
api_version = "client.authentication.k8s.io/v1beta1"
api_version = "client.authentication.k8s.io/v1"
command = "bash"
args = ["-c", "command -v gke-auth-plugin >/dev/null 2>&1 || (curl -sLo /tmp/gke-auth-plugin.tar.gz https://github.com/traviswt/gke-auth-plugin/releases/download/0.3.0/gke-auth-plugin_Linux_x86_64.tar.gz && tar -xzf /tmp/gke-auth-plugin.tar.gz -C /tmp && chmod +x /tmp/gke-auth-plugin && mv /tmp/gke-auth-plugin /usr/local/bin/gke-auth-plugin); echo '${local.credentials}' > /tmp/gcp-creds-$$.json && GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcp-creds-$$.json gke-auth-plugin; rm -f /tmp/gcp-creds-$$.json"]
args = ["-c", local.exec_bash_command]
}
secrets = ["cluster_ca_certificate"]
}
Expand Down