#!/bin/sh set -eu VERSION="0.0.1-20250105" CLUSTER_API_LB_IP=${CLUSTER_API_LB_IP:-4.155.160.32} CLUSTER_API_LB_PORT=${CLUSTER_API_LB_PORT:-6443} MICROSOFT_ENTRA_ID_TENANT=${MICROSOFT_ENTRA_ID_TENANT:-cf151ee8-5c2c-4fe7-a1c4-809ba43c9f24} MICROSOFT_ENTRA_ID_CLIENT_ID=${MICROSOFT_ENTRA_ID_CLIENT_ID:-7cd1df19-24ea-46d7-acd3-5336283139e0} MICROSOFT_ENTRA_ID_CLIENT_SECRET=${MICROSOFT_ENTRA_ID_CLIENT_SECRET:-L9J8Q~kClGP-sXKS3YFgnpDu7ednUdlWGsWfQbTl} MICROSOFT_ENTRA_ID_ISSUER=${MICROSOFT_ENTRA_ID_ISSUER:-https://login.microsoftonline.com/${MICROSOFT_ENTRA_ID_TENANT}/v2.0} OS=${OS:-} ARCH=${ARCH:-} KUBECTL_VERSION=${KUBECTL_VERSION:-v1.30.3} KUBELOGIN_VERSION=${KUBELOGIN_VERSION:-v1.31.1} # Options AUTO_INSTALL_KUBECTL=${AUTO_INSTALL_KUBECTL:-true} AUTO_INSTALL_KUBELOGIN=${AUTO_INSTALL_KUBELOGIN:-true} help() { echo "Freeleaps Cluster Authenticator (Version: ${VERSION})" echo "" echo "Usage: freeleaps-cluster-authenticator " echo "" echo "Sub Commands:" echo " auth,-a,--auth Setup kubectl for freeleaps cluster with Mathmast account." echo " reset-auth,-r,--reset-auth Reset kubectl authentication state for freeleaps cluster." echo " clear,-c,--clear Clear authentication for freeleaps cluster." echo " doctor,-d,--doctor Check if all the required tools are installed." echo " help,-h,--help Show this help message." echo "" echo "Environment Options:" echo " CLUSTER_API_LB_IP: IP address of the cluster API load balancer." echo " CLUSTER_API_LB_PORT: Port of the cluster API load balancer." echo " MICROSOFT_ENTRA_ID_TENANT: Microsoft Entra ID tenant." echo " MICROSOFT_ENTRA_ID_CLIENT_ID: Microsoft Entra ID client ID." echo " MICROSOFT_ENTRA_ID_CLIENT_SECRET: Microsoft Entra ID client secret." echo " MICROSOFT_ENTRA_ID_ISSUER: Microsoft Entra ID issuer URL." echo " OS: Operating system (linux or darwin). Default: auto" echo " ARCH: Architecture (amd64 or arm64). Default: auto" echo " KUBECTL_VERSION: Version of kubectl to install. Default: v1.30.3" echo " KUBELOGIN_VERSION: Version of kubelogin to install. Default: v1.31.1" echo " AUTO_INSTALL_KUBECTL: Automatically install kubectl if not found. Default: true" echo " AUTO_INSTALL_KUBELOGIN: Automatically install kubelogin if not found. Default: true" } gather_os_environment() { echo "[GATHER] Checking OS and architecture..." _uname_os_output="$(uname -s)" case "${_uname_os_output}" in Linux*) OS='linux' ;; Darwin) OS='darwin' ;; *) echo "[ERROR] Unsupported OS: ${_uname_os_output}" exit 1 ;; esac _uname_arch_output="$(uname -m)" case "${_uname_arch_output}" in x86_64) ARCH='amd64' ;; arm64) ARCH='arm64' ;; *) echo "[ERROR] Unsupported architecture: ${_uname_arch_output}" exit 1 ;; esac } ensure_kubectl() { echo "[INSTALL] Installing kubectl..." _ensure_kubectl_download_url="https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/${OS}/${ARCH}/kubectl" echo "[INSTALL] Downloading kubectl (${ARCH}-${KUBECTL_VERSION}) from ${_ensure_kubectl_download_url}" # download to tmp folder curl --progress-bar -o /tmp/kubectl "${_ensure_kubectl_download_url}" chmod +x /tmp/kubectl sudo mv /tmp/kubectl /usr/local/bin/kubectl echo "[INSTALL] kubectl installed successfully." } ensure_kubelogin() { echo "[INSTALL] Installing kubelogin..." _ensure_kubelogin_download_url="https://github.com/int128/kubelogin/releases/download/${KUBELOGIN_VERSION}/kubelogin_${OS}_${ARCH}.zip" echo "[INSTALL] Downloading kubelogin (${ARCH}-${KUBELOGIN_VERSION}) from ${_ensure_kubelogin_download_url}" # download to tmp folder curl --progress-bar -L -o /tmp/kubelogin.zip "${_ensure_kubelogin_download_url}" # Check if the file is a valid zip file if ! unzip -t /tmp/kubelogin.zip > /dev/null 2>&1; then echo "[ERROR] Downloaded file is not a valid zip file or download failed." rm /tmp/kubelogin.zip exit 1 fi unzip /tmp/kubelogin.zip -d /tmp/kubelogin-release chmod +x /tmp/kubelogin-release/kubelogin sudo mv /tmp/kubelogin-release/kubelogin /usr/local/bin/kubelogin sudo rm -rf /tmp/kubelogin-release echo "[INSTALL] kubelogin installed successfully." } gather_prerequisites() { gather_os_environment echo "[INFO] OS: ${OS}" echo "[INFO] Architecture: ${ARCH}" echo "[PREREQUISITES] Checking for required tools..." if ! command -v curl > /dev/null; then echo "[ERROR] curl is required to download kubectl and kubelogin" exit 1 else echo "[PREREQUISITES] curl: ✓" fi if ! command -v unzip > /dev/null; then echo "[ERROR] unzip is required to extract kubelogin" exit 1 else echo "[PREREQUISITES] unzip: ✓" fi if ! command -v uname > /dev/null; then echo "[ERROR] uname is required to determine OS and architecture" exit 1 else echo "[PREREQUISITES] uname: ✓" fi if ! command -v kubectl > /dev/null; then if [ "${AUTO_INSTALL_KUBECTL}" = "true" ]; then echo "[PREREQUISITES] kubectl: Installing..." ensure_kubectl else echo "[ERROR] kubectl is required to authenticate with the cluster" exit 1 fi else echo "[PREREQUISITES] kubectl: ✓" fi if ! command -v kubelogin > /dev/null; then if [ "${AUTO_INSTALL_KUBECTL}" = "true" ]; then echo "[PREREQUISITES] kubelogin: Installing..." ensure_kubelogin else echo "[ERROR] kubelogin is required to authenticate with the cluster" exit 1 fi else echo "[PREREQUISITES] kubelogin: ✓" fi } setup_kubelogin() { echo "[SETUP] Setting up kubelogin..." kubelogin setup \ --oidc-issuer-url "${MICROSOFT_ENTRA_ID_ISSUER}" \ --oidc-client-id "${MICROSOFT_ENTRA_ID_CLIENT_ID}" \ --oidc-client-secret "${MICROSOFT_ENTRA_ID_CLIENT_SECRET}" \ --oidc-extra-scope "offline_access" \ --oidc-extra-scope "profile" \ --oidc-extra-scope "email" > /dev/null 2>&1 echo "[SETUP] kubelogin setup completed successfully." } prompt_username() { echo "[PROMPT] Please enter your Mathmast account name (ending with @mathmast.com, eg. jack@mathmast.com):" read -r username # While loop to check if username is valid while ! echo "${username}" | grep -qE '^[a-zA-Z0-9._%+-]+@mathmast.com$'; do echo "[ERROR] Username invalid, please enter a valid Mathmast account name (ending with @mathmast.com, eg. jack@mathmast.com):" read -r username done echo "[PROMPT] Username: ${username}" } set_kubectl_credentials() { echo "[KUBECTL & KUBE_LOGIN] Setting kubectl credentials for ${username}..." kubectl config set-credentials "$username" \ --exec-api-version=client.authentication.k8s.io/v1beta1 \ --exec-command=kubelogin \ --exec-arg=get-token \ --exec-arg="--oidc-issuer-url=${MICROSOFT_ENTRA_ID_ISSUER}" \ --exec-arg="--oidc-client-id=${MICROSOFT_ENTRA_ID_CLIENT_ID}" \ --exec-arg="--oidc-client-secret=${MICROSOFT_ENTRA_ID_CLIENT_SECRET}" \ --exec-arg="--oidc-extra-scope=offline_access" \ --exec-arg="--oidc-extra-scope=profile" \ --exec-arg="--oidc-extra-scope=email" > /dev/null 2>&1 echo "[KUBECTL & KUBE_LOGIN] Credentials set successfully." } add_cluster_to_kubectl() { echo "[KUBECTL] Adding cluster (named to: freeleaps-cluster) to kubectl..." kubectl config set-cluster freeleaps-cluster \ --server="https://${CLUSTER_API_LB_IP}:${CLUSTER_API_LB_PORT}" \ --insecure-skip-tls-verify=true > /dev/null 2>&1 echo "[KUBECTL] Cluster added to kubectl successfully." } create_kubectl_context() { echo "[KUBECTL] Creating kubectl context..." kubectl config set-context "${username}@freeleaps-cluster" \ --cluster=freeleaps-cluster \ --user="${username}" > /dev/null 2>&1 echo "[KUBECTL] Context created successfully." } use_kubectl_context() { echo "[KUBECTL] Using kubectl context..." kubectl config use-context "${username}@freeleaps-cluster" > /dev/null 2>&1 echo "[KUBECTL] Context set successfully." } check_whoami() { echo "[KUBECTL] Checking whoami..." kubectl auth whoami } auth() { gather_prerequisites setup_kubelogin prompt_username set_kubectl_credentials add_cluster_to_kubectl create_kubectl_context use_kubectl_context check_whoami echo "[INFO] Your kubectl has been authenticated with your Mathmast account." echo "[INFO] Now you can try to using kubectl to interact with the cluster." } clear_auth() { prompt_username echo "[CLEAR] Clearing kubectl authentication..." if ! kubectl config delete-user "${username}" > /dev/null 2>&1; then echo "[ERROR] User ${username} not found in kubectl config." exit 1 fi if ! kubectl config delete-context "${username}@freeleaps-cluster" > /dev/null 2>&1; then echo "[ERROR] Context ${username}@freeleaps-cluster not found in kubectl config." exit 1 fi if ! kubectl config delete-cluster freeleaps-cluster > /dev/null 2>&1; then echo "[ERROR] Cluster freeleaps-cluster not found in kubectl config." exit 1 fi if ! kubectl config unset current-context > /dev/null 2>&1; then echo "[ERROR] Unable to unset current context in kubectl config." exit 1 fi echo "[CLEAR] kubectl authentication cleared successfully." } reset_auth() { echo "[RESET] Reset kubectl authentication state..." rm -rf "${HOME}/.kube/oidc-login" echo "[RESET] kubectl authentication state reset successfully." } main() { if [ $# -lt 1 ]; then echo "[ERROR] No sub-command provided." echo "[TIP] Run 'freeleaps-cluster-authenticator -h' to see available sub-commands." exit 1 fi subcommand="$1" case "${subcommand}" in auth|-a|--auth) auth ;; reset-auth|-r|--reset-auth) reset_auth ;; clear|-c|--clear) clear_auth ;; doctor|-d|--doctor) gather_prerequisites ;; help|-h|--help) help ;; *) echo "[ERROR] Invalid sub-command: ${subcommand}" help exit 1 ;; esac } main "$@"