penguin/monok8s

k8s image for Mono Gateway Dev Kit

devtools/create-join-token.sh

raw ยท 4799 bytes

#!/bin/sh
set -eu

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(realpath "$SCRIPT_DIR/..")"
LIB_DIR="$ROOT_DIR/scripts"
CLUSTER_ENV_WORK="${CLUSTER_ENV_WORK:-$ROOT_DIR/configs/cluster.env.work}"

KUBECTL="${KUBECTL:-kubectl}"
TTL_HOURS="${TTL_HOURS:-24}"
WAIT_SECONDS="${WAIT_SECONDS:-30}"

need() {
    command -v "$1" >/dev/null 2>&1 || {
        echo "missing required command: $1" >&2
        exit 1
    }
}

rfc3339_after_hours() {
    hours="$1"

    # GNU date
    if date -u -d "+${hours} hours" '+%Y-%m-%dT%H:%M:%SZ' >/dev/null 2>&1; then
        date -u -d "+${hours} hours" '+%Y-%m-%dT%H:%M:%SZ'
        return
    fi

    # BSD/macOS date
    if date -u -v+"${hours}"H '+%Y-%m-%dT%H:%M:%SZ' >/dev/null 2>&1; then
        date -u -v+"${hours}"H '+%Y-%m-%dT%H:%M:%SZ'
        return
    fi

    echo "cannot compute expiration time with this date(1). Set EXPIRATION manually." >&2
    exit 1
}

decode_base64_to_file() {
    input="$1"
    output="$2"

    if printf '%s' "$input" | base64 -d >"$output" 2>/dev/null; then
        return
    fi

    if printf '%s' "$input" | base64 -D >"$output" 2>/dev/null; then
        return
    fi

    if printf '%s' "$input" | openssl base64 -d -A >"$output" 2>/dev/null; then
        return
    fi

    echo "failed to decode certificate-authority-data" >&2
    exit 1
}

need "$KUBECTL"
need openssl
need awk
need sed

TOKEN_ID="${TOKEN_ID:-$(openssl rand -hex 3)}"
TOKEN_SECRET="${TOKEN_SECRET:-$(openssl rand -hex 8)}"
TOKEN="${TOKEN_ID}.${TOKEN_SECRET}"
SECRET_NAME="bootstrap-token-${TOKEN_ID}"

if [ "${TTL_HOURS}" = "0" ]; then
    EXPIRATION=""
else
    EXPIRATION="${EXPIRATION:-$(rfc3339_after_hours "$TTL_HOURS")}"
fi

echo "Creating bootstrap token Secret: ${SECRET_NAME}" >&2

{
    cat <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: ${SECRET_NAME}
  namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
  description: "Join token created with kubectl"
  token-id: "${TOKEN_ID}"
  token-secret: "${TOKEN_SECRET}"
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"
  auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
EOF

    if [ -n "$EXPIRATION" ]; then
        printf '  expiration: "%s"\n' "$EXPIRATION"
    fi
} | "$KUBECTL" apply -f -

TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT INT TERM

CA_FILE="$TMPDIR/ca.crt"

CA_DATA="$("$KUBECTL" config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')"

if [ -z "$CA_DATA" ]; then
    echo "could not find certificate-authority-data in current kubeconfig" >&2
    echo "token was created, but cannot print a safe kubeadm join command" >&2
    echo "token: ${TOKEN}"
    exit 0
fi

decode_base64_to_file "$CA_DATA" "$CA_FILE"

CA_HASH="$(
    openssl x509 -in "$CA_FILE" -pubkey -noout |
        openssl pkey -pubin -outform der 2>/dev/null |
        openssl dgst -sha256 -hex |
        awk '{print $2}'
)"

SERVER="$("$KUBECTL" config view --raw --minify -o jsonpath='{.clusters[0].cluster.server}')"
JOIN_ENDPOINT="$(printf '%s\n' "$SERVER" | sed -E 's#^https?://##')"

echo "Waiting for cluster-info signature for token ${TOKEN_ID}..." >&2

i=0
signed="false"
while [ "$i" -lt "$WAIT_SECONDS" ]; do
    template="{{ index .data \"jws-kubeconfig-${TOKEN_ID}\" }}"
    sig="$("$KUBECTL" -n kube-public get configmap cluster-info -o "go-template=${template}" 2>/dev/null || true)"

    if [ -n "$sig" ]; then
        signed="true"
        break
    fi

    i=$((i + 1))
    sleep 1
done

echo
echo "Token:"
echo "  ${TOKEN}"

if [ -n "$EXPIRATION" ]; then
    echo
    echo "Expires:"
    echo "  ${EXPIRATION}"
fi

echo
echo "Join command:"
echo "  kubeadm join ${JOIN_ENDPOINT} --token ${TOKEN} --discovery-token-ca-cert-hash sha256:${CA_HASH}"

TMP_ENV="$(mktemp)"
trap 'rm -f "$TMP_ENV"; rm -rf "$TMPDIR"' EXIT INT TERM

cat >"$TMP_ENV" <<EOF
MKS_API_SERVER_ENDPOINT=${JOIN_ENDPOINT}
MKS_BOOTSTRAP_TOKEN=${TOKEN}
MKS_DISCOVERY_TOKEN_CA_CERT_HASH=sha256:${CA_HASH}
EOF

echo
echo "cluster-config:"
cat "$TMP_ENV"

if [ ! -x "$LIB_DIR/merge-env.sh" ]; then
    echo "merge-env.sh not found or not executable: $LIB_DIR/merge-env.sh" >&2
    exit 1
fi

"$LIB_DIR/merge-env.sh" "$TMP_ENV" "$CLUSTER_ENV_WORK"

echo
echo "Merged into:"
echo "  $CLUSTER_ENV_WORK"
echo
echo "Try"
cat <<EOF
make cluster-config \\
    MKS_HOSTNAME=monok8s-worker \\
    MKS_CLUSTER_ROLE=worker \\
    MKS_INIT_CONTROL_PLANE=no \\
    MKS_MGMT_ADDRESS=10.0.0.10/24 \\
    MKS_APISERVER_ADVERTISE_ADDRESS=10.0.0.10 \\
    MKS_CNI_PLUGIN=none
EOF


if [ "$signed" != "true" ]; then
    echo >&2
    echo "warning: cluster-info was not signed within ${WAIT_SECONDS}s." >&2
    echo "If kubeadm join fails discovery, check that kube-controller-manager enables bootstrapsigner." >&2
fi