Skip to content

Instantly share code, notes, and snippets.

@maelvls
Created July 25, 2024 18:57
Show Gist options
  • Save maelvls/adf680ae01612ff79658872c7dca013f to your computer and use it in GitHub Desktop.
Save maelvls/adf680ae01612ff79658872c7dca013f to your computer and use it in GitHub Desktop.
kind-tailscale makes your Kind cluster's API server available to the internet, including the OpenID Configuration and JWKS endpoints.
#!/bin/bash
# shellcheck disable=SC2059
set -eo pipefail
help() {
cat <<EOF
A script to expose your Kind cluster's API server to the internet using
Tailscale Funnel. To do that, the API server's --service-account-issuer and
--service-account-jwks-uri are updated, and the API server's certificate SANs
are updated to include the funnel's hostname.
Usage: kind-tailscale
Options:
--name cluster The name of the Kind cluster to configure. Default: kind.
EOF
}
yel=$(tput setaf 3)
gray=$(tput setaf 8)
red=$(tput setaf 1)
end=$(tput sgr0)
# color "$yel"
color() {
# Let's prevent accidental interference from programs that also print colors.
# Caveat: does only work on lines that end with \n. Lines that do not end with
# \n are discarded.
sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[mGK]//g" | while read -r line; do
printf "${1}%s${end}\n" "$line"
done
}
name=kind
while [ $# -gt 0 ]; do
case "$1" in
--name)
name="$2"
shift
;;
--help)
help
exit 0
;;
-*)
printf "${red}error${end}: unknown option: $1\n"
;;
*)
printf "${red}error${end}: unknown argument: $1\n"
;;
esac
shift
done
docker exec "$name-control-plane" kubectl create clusterrolebinding oidc-reviewer --clusterrole=system:service-account-issuer-discovery --group=system:unauthenticated --dry-run=client -o yaml \
| docker exec -i "$name-control-plane" kubectl apply -f - | color "$gray"
hostname=$(tailscale status --json | jq -r .Self.DNSName | sed 's/\.$//')
# Tailscale Funnel only supports 443, 8443, and 10000.
addr_oidc_discovery=$hostname:8443
addr_kubeconfig=$hostname:10000
docker exec -i "$name-control-plane" bash <<EOF 2>&1 | color "$gray" || exit 1
if ! command -v yq >/dev/null; then
curl -LO https://github.com/mikefarah/yq/releases/download/v4.43.1/yq_linux_arm64
install yq_linux_arm64 /usr/local/bin/yq
fi
yq 'select(.kind == "ClusterConfiguration").apiServer.certSANs = ["localhost", "127.0.0.1", "$hostname"]' -i /kind/kubeadm.conf
yq 'del(select(.kind == "ClusterConfiguration").apiServer.extraArgs | select(."service-account-issuer"))' -i /kind/kubeadm.conf
yq 'del(select(.kind == "ClusterConfiguration").apiServer.extraArgs | select(."service-account-jwks-uri"))' -i /kind/kubeadm.conf
yq 'select(.kind == "ClusterConfiguration").apiServer.extraArgs += {"service-account-jwks-uri": "https://$addr_oidc_discovery/openid/v1/jwks"}' -i /kind/kubeadm.conf
yq 'select(.kind == "ClusterConfiguration").apiServer.extraArgs += {"service-account-issuer": "https://$addr_oidc_discovery"}' -i /kind/kubeadm.conf
kubeadm init phase control-plane apiserver --config /kind/kubeadm.conf
rm -f /etc/kubernetes/pki/apiserver.key /etc/kubernetes/pki/apiserver.crt
kubeadm init phase certs apiserver --config /kind/kubeadm.conf
EOF
localport=$(docker inspect "$name-control-plane" | jq -r '.[].HostConfig.PortBindings[][].HostPort')
tailscale funnel --bg --https "${addr_oidc_discovery/*:/}" "https+insecure://127.0.0.1:$localport" 2>&1 | color "$gray"
tailscale funnel --bg --tcp "${addr_kubeconfig/*:/}" "tcp://127.0.0.1:$localport" 2>&1 | color "$gray"
cat <<EOF
The OIDC discovery endpoint is: ${yel}https://$addr_oidc_discovery/.well-known/openid-configuration${end}
You can now share your cluster by sharing the kubeconfig with the following
command (the second command tests that the kubeconfig works):
${yel}
kind get kubeconfig -n $name | sed "s/127.0.0.1:[0-9]*/$addr_kubeconfig/" >kubeconfig
kubectl --kubeconfig kubeconfig get nodes
${end}
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment