From 49cb1024b74a56557e519e8493b68ce2017feb6b Mon Sep 17 00:00:00 2001 From: Robert Bailey Date: Thu, 15 Feb 2018 00:54:36 -0800 Subject: [PATCH] Move code only used by gce out of common.sh and into gce/util.sh. --- cluster/common.sh | 828 -------------------------------------------- cluster/gce/util.sh | 798 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 798 insertions(+), 828 deletions(-) diff --git a/cluster/common.sh b/cluster/common.sh index 083ae8feea..819aa02522 100755 --- a/cluster/common.sh +++ b/cluster/common.sh @@ -264,15 +264,6 @@ function load-or-gen-kube-basicauth() { fi } -function load-or-gen-kube-bearertoken() { - if [[ ! -z "${KUBE_CONTEXT:-}" ]]; then - get-kubeconfig-bearertoken - fi - if [[ -z "${KUBE_BEARER_TOKEN:-}" ]]; then - gen-kube-bearertoken - fi -} - # Sets KUBE_VERSION variable to the proper version number (e.g. "v1.0.6", # "v1.2.0-alpha.1.881+376438b69c7612") or a version' publication of the form # / (e.g. "release/stable",' "ci/latest-1"). @@ -297,53 +288,6 @@ function set_binary_version() { fi } -# Figure out which binary use on the server and assure it is available. -# If KUBE_VERSION is specified use binaries specified by it, otherwise -# use local dev binaries. -# -# Assumed vars: -# KUBE_VERSION -# KUBE_RELEASE_VERSION_REGEX -# KUBE_CI_VERSION_REGEX -# Vars set: -# KUBE_TAR_HASH -# SERVER_BINARY_TAR_URL -# SERVER_BINARY_TAR_HASH -function tars_from_version() { - local sha1sum="" - if which sha1sum >/dev/null 2>&1; then - sha1sum="sha1sum" - else - sha1sum="shasum -a1" - fi - - if [[ -z "${KUBE_VERSION-}" ]]; then - find-release-tars - upload-server-tars - elif [[ ${KUBE_VERSION} =~ ${KUBE_RELEASE_VERSION_REGEX} ]]; then - SERVER_BINARY_TAR_URL="https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz" - # TODO: Clean this up. - KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}" - KUBE_MANIFESTS_TAR_HASH=$(curl ${KUBE_MANIFESTS_TAR_URL} --silent --show-error | ${sha1sum} | awk '{print $1}') - elif [[ ${KUBE_VERSION} =~ ${KUBE_CI_VERSION_REGEX} ]]; then - SERVER_BINARY_TAR_URL="https://storage.googleapis.com/kubernetes-release-dev/ci/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz" - # TODO: Clean this up. - KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}" - KUBE_MANIFESTS_TAR_HASH=$(curl ${KUBE_MANIFESTS_TAR_URL} --silent --show-error | ${sha1sum} | awk '{print $1}') - else - echo "Version doesn't match regexp" >&2 - exit 1 - fi - if ! SERVER_BINARY_TAR_HASH=$(curl -Ss --fail "${SERVER_BINARY_TAR_URL}.sha1"); then - echo "Failure trying to curl release .sha1" - fi - - if ! curl -Ss --head "${SERVER_BINARY_TAR_URL}" >&/dev/null; then - echo "Can't find release at ${SERVER_BINARY_TAR_URL}" >&2 - exit 1 - fi -} - # Search for the specified tarball in the various known output locations, # echoing the location if found. # @@ -386,703 +330,6 @@ function find-release-tars() { fi } -# Discover the git version of the current build package -# -# Assumed vars: -# KUBE_ROOT -# Vars set: -# KUBE_GIT_VERSION -function find-release-version() { - KUBE_GIT_VERSION="" - if [[ -f "${KUBE_ROOT}/version" ]]; then - KUBE_GIT_VERSION="$(cat ${KUBE_ROOT}/version)" - fi - if [[ -f "${KUBE_ROOT}/_output/release-stage/full/kubernetes/version" ]]; then - KUBE_GIT_VERSION="$(cat ${KUBE_ROOT}/_output/release-stage/full/kubernetes/version)" - fi - - if [[ -z "${KUBE_GIT_VERSION}" ]]; then - echo "!!! Cannot find release version" - exit 1 - fi -} - -# Quote something appropriate for a yaml string. -# -# TODO(zmerlynn): Note that this function doesn't so much "quote" as -# "strip out quotes", and we really should be using a YAML library for -# this, but PyYAML isn't shipped by default, and *rant rant rant ... SIGH* -function yaml-quote { - echo "'$(echo "${@:-}" | sed -e "s/'/''/g")'" -} - -# Builds the RUNTIME_CONFIG var from other feature enable options (such as -# features in alpha) -function build-runtime-config() { - # There is nothing to do here for now. Just using this function as a placeholder. - : -} - -# Writes the cluster name into a temporary file. -# Assumed vars -# CLUSTER_NAME -function write-cluster-name { - cat >"${KUBE_TEMP}/cluster-name.txt" << EOF -${CLUSTER_NAME} -EOF -} - -function write-master-env { - # If the user requested that the master be part of the cluster, set the - # environment variable to program the master kubelet to register itself. - if [[ "${REGISTER_MASTER_KUBELET:-}" == "true" && -z "${KUBELET_APISERVER:-}" ]]; then - KUBELET_APISERVER="${MASTER_NAME}" - fi - if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then - KUBERNETES_MASTER_NAME="${MASTER_NAME}" - fi - - build-kube-env true "${KUBE_TEMP}/master-kube-env.yaml" - build-kube-master-certs "${KUBE_TEMP}/kube-master-certs.yaml" -} - -function write-node-env { - if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then - KUBERNETES_MASTER_NAME="${MASTER_NAME}" - fi - - build-kube-env false "${KUBE_TEMP}/node-kube-env.yaml" -} - -function build-kube-master-certs { - local file=$1 - rm -f ${file} - cat >$file <$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file </dev/null 2>&1; then - sha1sum "$1" | awk '{ print $1 }' - else - shasum -a1 "$1" | awk '{ print $1 }' - fi -} - -# Create certificate pairs for the cluster. -# $1: The public IP for the master. -# -# These are used for static cert distribution (e.g. static clustering) at -# cluster creation time. This will be obsoleted once we implement dynamic -# clustering. -# -# The following certificate pairs are created: -# -# - ca (the cluster's certificate authority) -# - server -# - kubelet -# - kubecfg (for kubectl) -# -# TODO(roberthbailey): Replace easyrsa with a simple Go program to generate -# the certs that we need. -# -# Assumed vars -# KUBE_TEMP -# MASTER_NAME -# -# Vars set: -# CERT_DIR -# CA_CERT_BASE64 -# MASTER_CERT_BASE64 -# MASTER_KEY_BASE64 -# KUBELET_CERT_BASE64 -# KUBELET_KEY_BASE64 -# KUBECFG_CERT_BASE64 -# KUBECFG_KEY_BASE64 -function create-certs { - local -r primary_cn="${1}" - - # Determine extra certificate names for master - local octets=($(echo "${SERVICE_CLUSTER_IP_RANGE}" | sed -e 's|/.*||' -e 's/\./ /g')) - ((octets[3]+=1)) - local -r service_ip=$(echo "${octets[*]}" | sed 's/ /./g') - local sans="" - for extra in $@; do - if [[ -n "${extra}" ]]; then - sans="${sans}IP:${extra}," - fi - done - sans="${sans}IP:${service_ip},DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.${DNS_DOMAIN},DNS:${MASTER_NAME}" - - echo "Generating certs for alternate-names: ${sans}" - - setup-easyrsa - PRIMARY_CN="${primary_cn}" SANS="${sans}" generate-certs - AGGREGATOR_PRIMARY_CN="${primary_cn}" AGGREGATOR_SANS="${sans}" generate-aggregator-certs - - # By default, linux wraps base64 output every 76 cols, so we use 'tr -d' to remove whitespaces. - # Note 'base64 -w0' doesn't work on Mac OS X, which has different flags. - CA_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n') - CA_CERT_BASE64=$(cat "${CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n') - MASTER_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" | base64 | tr -d '\r\n') - MASTER_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/${MASTER_NAME}.key" | base64 | tr -d '\r\n') - KUBELET_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kubelet.crt" | base64 | tr -d '\r\n') - KUBELET_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kubelet.key" | base64 | tr -d '\r\n') - KUBECFG_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kubecfg.crt" | base64 | tr -d '\r\n') - KUBECFG_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kubecfg.key" | base64 | tr -d '\r\n') - KUBEAPISERVER_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kube-apiserver.crt" | base64 | tr -d '\r\n') - KUBEAPISERVER_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kube-apiserver.key" | base64 | tr -d '\r\n') - - # Setting up an addition directory (beyond pki) as it is the simplest way to - # ensure we get a different CA pair to sign the proxy-client certs and which - # we can send CA public key to the user-apiserver to validate communication. - AGGREGATOR_CA_KEY_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n') - REQUESTHEADER_CA_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n') - PROXY_CLIENT_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" | base64 | tr -d '\r\n') - PROXY_CLIENT_KEY_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" | base64 | tr -d '\r\n') -} - -# Set up easy-rsa directory structure. -# -# Assumed vars -# KUBE_TEMP -# -# Vars set: -# CERT_DIR -# AGGREGATOR_CERT_DIR -function setup-easyrsa { - local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") - # Note: This was heavily cribbed from make-ca-cert.sh - (set -x - cd "${KUBE_TEMP}" - curl -L -O --connect-timeout 20 --retry 6 --retry-delay 2 https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz - tar xzf easy-rsa.tar.gz - mkdir easy-rsa-master/kubelet - cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/kubelet - mkdir easy-rsa-master/aggregator - cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/aggregator) &>${cert_create_debug_output} || true - CERT_DIR="${KUBE_TEMP}/easy-rsa-master/easyrsa3" - AGGREGATOR_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/aggregator" - if [ ! -x "${CERT_DIR}/easyrsa" -o ! -x "${AGGREGATOR_CERT_DIR}/easyrsa" ]; then - # TODO(roberthbailey,porridge): add better error handling here, - # see https://github.com/kubernetes/kubernetes/issues/55229 - cat "${cert_create_debug_output}" >&2 - echo "=== Failed to setup easy-rsa: Aborting ===" >&2 - exit 2 - fi -} - -# Runs the easy RSA commands to generate certificate files. -# The generated files are IN ${CERT_DIR} -# -# Assumed vars -# KUBE_TEMP -# MASTER_NAME -# CERT_DIR -# PRIMARY_CN: Primary canonical name -# SANS: Subject alternate names -# -# -function generate-certs { - local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") - # Note: This was heavily cribbed from make-ca-cert.sh - (set -x - cd "${CERT_DIR}" - ./easyrsa init-pki - # this puts the cert into pki/ca.crt and the key into pki/private/ca.key - ./easyrsa --batch "--req-cn=${PRIMARY_CN}@$(date +%s)" build-ca nopass - ./easyrsa --subject-alt-name="${SANS}" build-server-full "${MASTER_NAME}" nopass - ./easyrsa build-client-full kube-apiserver nopass - - kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl" - - # make the config for the signer - echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json" - # create the kubelet client cert with the correct groups - echo '{"CN":"kubelet","names":[{"O":"system:nodes"}],"hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare kubelet - mv "kubelet-key.pem" "pki/private/kubelet.key" - mv "kubelet.pem" "pki/issued/kubelet.crt" - rm -f "kubelet.csr" - - # Make a superuser client cert with subject "O=system:masters, CN=kubecfg" - ./easyrsa --dn-mode=org \ - --req-cn=kubecfg --req-org=system:masters \ - --req-c= --req-st= --req-city= --req-email= --req-ou= \ - build-client-full kubecfg nopass) &>${cert_create_debug_output} || true - local output_file_missing=0 - local output_file - for output_file in \ - "${CERT_DIR}/pki/private/ca.key" \ - "${CERT_DIR}/pki/ca.crt" \ - "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" \ - "${CERT_DIR}/pki/private/${MASTER_NAME}.key" \ - "${CERT_DIR}/pki/issued/kubelet.crt" \ - "${CERT_DIR}/pki/private/kubelet.key" \ - "${CERT_DIR}/pki/issued/kubecfg.crt" \ - "${CERT_DIR}/pki/private/kubecfg.key" \ - "${CERT_DIR}/pki/issued/kube-apiserver.crt" \ - "${CERT_DIR}/pki/private/kube-apiserver.key" - do - if [[ ! -s "${output_file}" ]]; then - echo "Expected file ${output_file} not created" >&2 - output_file_missing=1 - fi - done - if (( $output_file_missing )); then - # TODO(roberthbailey,porridge): add better error handling here, - # see https://github.com/kubernetes/kubernetes/issues/55229 - cat "${cert_create_debug_output}" >&2 - echo "=== Failed to generate master certificates: Aborting ===" >&2 - exit 2 - fi -} - -# Runs the easy RSA commands to generate aggregator certificate files. -# The generated files are in ${AGGREGATOR_CERT_DIR} -# -# Assumed vars -# KUBE_TEMP -# AGGREGATOR_MASTER_NAME -# AGGREGATOR_CERT_DIR -# AGGREGATOR_PRIMARY_CN: Primary canonical name -# AGGREGATOR_SANS: Subject alternate names -# -# -function generate-aggregator-certs { - local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") - # Note: This was heavily cribbed from make-ca-cert.sh - (set -x - cd "${KUBE_TEMP}/easy-rsa-master/aggregator" - ./easyrsa init-pki - # this puts the cert into pki/ca.crt and the key into pki/private/ca.key - ./easyrsa --batch "--req-cn=${AGGREGATOR_PRIMARY_CN}@$(date +%s)" build-ca nopass - ./easyrsa --subject-alt-name="${AGGREGATOR_SANS}" build-server-full "${AGGREGATOR_MASTER_NAME}" nopass - ./easyrsa build-client-full aggregator-apiserver nopass - - kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl" - - # make the config for the signer - echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json" - # create the aggregator client cert with the correct groups - echo '{"CN":"aggregator","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare proxy-client - mv "proxy-client-key.pem" "pki/private/proxy-client.key" - mv "proxy-client.pem" "pki/issued/proxy-client.crt" - rm -f "proxy-client.csr" - - # Make a superuser client cert with subject "O=system:masters, CN=kubecfg" - ./easyrsa --dn-mode=org \ - --req-cn=proxy-clientcfg --req-org=system:aggregator \ - --req-c= --req-st= --req-city= --req-email= --req-ou= \ - build-client-full proxy-clientcfg nopass) &>${cert_create_debug_output} || true - local output_file_missing=0 - local output_file - for output_file in \ - "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" \ - "${AGGREGATOR_CERT_DIR}/pki/ca.crt" \ - "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" \ - "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" - do - if [[ ! -s "${output_file}" ]]; then - echo "Expected file ${output_file} not created" >&2 - output_file_missing=1 - fi - done - if (( $output_file_missing )); then - # TODO(roberthbailey,porridge): add better error handling here, - # see https://github.com/kubernetes/kubernetes/issues/55229 - cat "${cert_create_debug_output}" >&2 - echo "=== Failed to generate aggregator certificates: Aborting ===" >&2 - exit 2 - fi -} - # Run the cfssl command to generates certificate files for etcd service, the # certificate files will save in $1 directory. # @@ -1206,81 +453,6 @@ EOF popd } -# -# Using provided master env, extracts value from provided key. -# -# Args: -# $1 master env (kube-env of master; result of calling get-master-env) -# $2 env key to use -function get-env-val() { - local match=`(echo "${1}" | grep -E "^${2}:") || echo ""` - if [[ -z ${match} ]]; then - echo "" - fi - echo ${match} | cut -d : -f 2 | cut -d \' -f 2 -} - -# Load the master env by calling get-master-env, and extract important values -function parse-master-env() { - # Get required master env vars - local master_env=$(get-master-env) - KUBE_PROXY_TOKEN=$(get-env-val "${master_env}" "KUBE_PROXY_TOKEN") - NODE_PROBLEM_DETECTOR_TOKEN=$(get-env-val "${master_env}" "NODE_PROBLEM_DETECTOR_TOKEN") - CA_CERT_BASE64=$(get-env-val "${master_env}" "CA_CERT") - CA_KEY_BASE64=$(get-env-val "${master_env}" "CA_KEY") - KUBEAPISERVER_CERT_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_CERT") - KUBEAPISERVER_KEY_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_KEY") - EXTRA_DOCKER_OPTS=$(get-env-val "${master_env}" "EXTRA_DOCKER_OPTS") - KUBELET_CERT_BASE64=$(get-env-val "${master_env}" "KUBELET_CERT") - KUBELET_KEY_BASE64=$(get-env-val "${master_env}" "KUBELET_KEY") - MASTER_CERT_BASE64=$(get-env-val "${master_env}" "MASTER_CERT") - MASTER_KEY_BASE64=$(get-env-val "${master_env}" "MASTER_KEY") - AGGREGATOR_CA_KEY_BASE64=$(get-env-val "${master_env}" "AGGREGATOR_CA_KEY") - REQUESTHEADER_CA_CERT_BASE64=$(get-env-val "${master_env}" "REQUESTHEADER_CA_CERT") - PROXY_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_CERT") - PROXY_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_KEY") - ENABLE_LEGACY_ABAC=$(get-env-val "${master_env}" "ENABLE_LEGACY_ABAC") -} - -# Update or verify required gcloud components are installed -# at minimum required version. -# Assumed vars -# KUBE_PROMPT_FOR_UPDATE -function update-or-verify-gcloud() { - local sudo_prefix="" - if [ ! -w $(dirname `which gcloud`) ]; then - sudo_prefix="sudo" - fi - # update and install components as needed - if [[ "${KUBE_PROMPT_FOR_UPDATE}" == "y" ]]; then - ${sudo_prefix} gcloud ${gcloud_prompt:-} components install alpha - ${sudo_prefix} gcloud ${gcloud_prompt:-} components install beta - ${sudo_prefix} gcloud ${gcloud_prompt:-} components update - else - local version=$(gcloud version --format=json) - python -c' -import json,sys -from distutils import version - -minVersion = version.LooseVersion("1.3.0") -required = [ "alpha", "beta", "core" ] -data = json.loads(sys.argv[1]) -rel = data.get("Google Cloud SDK") -if rel != "HEAD" and version.LooseVersion(rel) < minVersion: - print("gcloud version out of date ( < %s )" % minVersion) - exit(1) -missing = [] -for c in required: - if not data.get(c): - missing += [c] -if missing: - for c in missing: - print ("missing required gcloud component \"{0}\"".format(c)) - exit(1) - ' """${version}""" - fi -} - # Check whether required client and server binaries exist, prompting to download # if missing. # If KUBERNETES_SKIP_CONFIRM is set to y, we'll automatically download binaries diff --git a/cluster/gce/util.sh b/cluster/gce/util.sh index 8893f7ac69..6ff255a5c0 100755 --- a/cluster/gce/util.sh +++ b/cluster/gce/util.sh @@ -391,6 +391,62 @@ function detect-master() { echo "Using master: $KUBE_MASTER (external IP: $KUBE_MASTER_IP)" >&2 } +function load-or-gen-kube-bearertoken() { + if [[ ! -z "${KUBE_CONTEXT:-}" ]]; then + get-kubeconfig-bearertoken + fi + if [[ -z "${KUBE_BEARER_TOKEN:-}" ]]; then + gen-kube-bearertoken + fi +} + +# Figure out which binary use on the server and assure it is available. +# If KUBE_VERSION is specified use binaries specified by it, otherwise +# use local dev binaries. +# +# Assumed vars: +# KUBE_VERSION +# KUBE_RELEASE_VERSION_REGEX +# KUBE_CI_VERSION_REGEX +# Vars set: +# KUBE_TAR_HASH +# SERVER_BINARY_TAR_URL +# SERVER_BINARY_TAR_HASH +function tars_from_version() { + local sha1sum="" + if which sha1sum >/dev/null 2>&1; then + sha1sum="sha1sum" + else + sha1sum="shasum -a1" + fi + + if [[ -z "${KUBE_VERSION-}" ]]; then + find-release-tars + upload-server-tars + elif [[ ${KUBE_VERSION} =~ ${KUBE_RELEASE_VERSION_REGEX} ]]; then + SERVER_BINARY_TAR_URL="https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz" + # TODO: Clean this up. + KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}" + KUBE_MANIFESTS_TAR_HASH=$(curl ${KUBE_MANIFESTS_TAR_URL} --silent --show-error | ${sha1sum} | awk '{print $1}') + elif [[ ${KUBE_VERSION} =~ ${KUBE_CI_VERSION_REGEX} ]]; then + SERVER_BINARY_TAR_URL="https://storage.googleapis.com/kubernetes-release-dev/ci/${KUBE_VERSION}/kubernetes-server-linux-amd64.tar.gz" + # TODO: Clean this up. + KUBE_MANIFESTS_TAR_URL="${SERVER_BINARY_TAR_URL/server-linux-amd64/manifests}" + KUBE_MANIFESTS_TAR_HASH=$(curl ${KUBE_MANIFESTS_TAR_URL} --silent --show-error | ${sha1sum} | awk '{print $1}') + else + echo "Version doesn't match regexp" >&2 + exit 1 + fi + if ! SERVER_BINARY_TAR_HASH=$(curl -Ss --fail "${SERVER_BINARY_TAR_URL}.sha1"); then + echo "Failure trying to curl release .sha1" + fi + + if ! curl -Ss --head "${SERVER_BINARY_TAR_URL}" >&/dev/null; then + echo "Can't find release at ${SERVER_BINARY_TAR_URL}" >&2 + exit 1 + fi +} + # Reads kube-env metadata from master # # Assumed vars: @@ -407,6 +463,748 @@ function get-master-env() { 'http://metadata/computeMetadata/v1/instance/attributes/kube-master-certs'" 2>/dev/null } +# Quote something appropriate for a yaml string. +# +# TODO(zmerlynn): Note that this function doesn't so much "quote" as +# "strip out quotes", and we really should be using a YAML library for +# this, but PyYAML isn't shipped by default, and *rant rant rant ... SIGH* +function yaml-quote { + echo "'$(echo "${@:-}" | sed -e "s/'/''/g")'" +} + +# Writes the cluster name into a temporary file. +# Assumed vars +# CLUSTER_NAME +function write-cluster-name { + cat >"${KUBE_TEMP}/cluster-name.txt" << EOF +${CLUSTER_NAME} +EOF +} + +function write-master-env { + # If the user requested that the master be part of the cluster, set the + # environment variable to program the master kubelet to register itself. + if [[ "${REGISTER_MASTER_KUBELET:-}" == "true" && -z "${KUBELET_APISERVER:-}" ]]; then + KUBELET_APISERVER="${MASTER_NAME}" + fi + if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then + KUBERNETES_MASTER_NAME="${MASTER_NAME}" + fi + + build-kube-env true "${KUBE_TEMP}/master-kube-env.yaml" + build-kube-master-certs "${KUBE_TEMP}/kube-master-certs.yaml" +} + +function write-node-env { + if [[ -z "${KUBERNETES_MASTER_NAME:-}" ]]; then + KUBERNETES_MASTER_NAME="${MASTER_NAME}" + fi + + build-kube-env false "${KUBE_TEMP}/node-kube-env.yaml" +} + +function build-kube-master-certs { + local file=$1 + rm -f ${file} + cat >$file <$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file <>$file </dev/null 2>&1; then + sha1sum "$1" | awk '{ print $1 }' + else + shasum -a1 "$1" | awk '{ print $1 }' + fi +} + +# Create certificate pairs for the cluster. +# $1: The public IP for the master. +# +# These are used for static cert distribution (e.g. static clustering) at +# cluster creation time. This will be obsoleted once we implement dynamic +# clustering. +# +# The following certificate pairs are created: +# +# - ca (the cluster's certificate authority) +# - server +# - kubelet +# - kubecfg (for kubectl) +# +# TODO(roberthbailey): Replace easyrsa with a simple Go program to generate +# the certs that we need. +# +# Assumed vars +# KUBE_TEMP +# MASTER_NAME +# +# Vars set: +# CERT_DIR +# CA_CERT_BASE64 +# MASTER_CERT_BASE64 +# MASTER_KEY_BASE64 +# KUBELET_CERT_BASE64 +# KUBELET_KEY_BASE64 +# KUBECFG_CERT_BASE64 +# KUBECFG_KEY_BASE64 +function create-certs { + local -r primary_cn="${1}" + + # Determine extra certificate names for master + local octets=($(echo "${SERVICE_CLUSTER_IP_RANGE}" | sed -e 's|/.*||' -e 's/\./ /g')) + ((octets[3]+=1)) + local -r service_ip=$(echo "${octets[*]}" | sed 's/ /./g') + local sans="" + for extra in $@; do + if [[ -n "${extra}" ]]; then + sans="${sans}IP:${extra}," + fi + done + sans="${sans}IP:${service_ip},DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.${DNS_DOMAIN},DNS:${MASTER_NAME}" + + echo "Generating certs for alternate-names: ${sans}" + + setup-easyrsa + PRIMARY_CN="${primary_cn}" SANS="${sans}" generate-certs + AGGREGATOR_PRIMARY_CN="${primary_cn}" AGGREGATOR_SANS="${sans}" generate-aggregator-certs + + # By default, linux wraps base64 output every 76 cols, so we use 'tr -d' to remove whitespaces. + # Note 'base64 -w0' doesn't work on Mac OS X, which has different flags. + CA_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n') + CA_CERT_BASE64=$(cat "${CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n') + MASTER_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" | base64 | tr -d '\r\n') + MASTER_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/${MASTER_NAME}.key" | base64 | tr -d '\r\n') + KUBELET_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kubelet.crt" | base64 | tr -d '\r\n') + KUBELET_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kubelet.key" | base64 | tr -d '\r\n') + KUBECFG_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kubecfg.crt" | base64 | tr -d '\r\n') + KUBECFG_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kubecfg.key" | base64 | tr -d '\r\n') + KUBEAPISERVER_CERT_BASE64=$(cat "${CERT_DIR}/pki/issued/kube-apiserver.crt" | base64 | tr -d '\r\n') + KUBEAPISERVER_KEY_BASE64=$(cat "${CERT_DIR}/pki/private/kube-apiserver.key" | base64 | tr -d '\r\n') + + # Setting up an addition directory (beyond pki) as it is the simplest way to + # ensure we get a different CA pair to sign the proxy-client certs and which + # we can send CA public key to the user-apiserver to validate communication. + AGGREGATOR_CA_KEY_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n') + REQUESTHEADER_CA_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n') + PROXY_CLIENT_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" | base64 | tr -d '\r\n') + PROXY_CLIENT_KEY_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" | base64 | tr -d '\r\n') +} + +# Set up easy-rsa directory structure. +# +# Assumed vars +# KUBE_TEMP +# +# Vars set: +# CERT_DIR +# AGGREGATOR_CERT_DIR +function setup-easyrsa { + local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") + # Note: This was heavily cribbed from make-ca-cert.sh + (set -x + cd "${KUBE_TEMP}" + curl -L -O --connect-timeout 20 --retry 6 --retry-delay 2 https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz + tar xzf easy-rsa.tar.gz + mkdir easy-rsa-master/kubelet + cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/kubelet + mkdir easy-rsa-master/aggregator + cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/aggregator) &>${cert_create_debug_output} || true + CERT_DIR="${KUBE_TEMP}/easy-rsa-master/easyrsa3" + AGGREGATOR_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/aggregator" + if [ ! -x "${CERT_DIR}/easyrsa" -o ! -x "${AGGREGATOR_CERT_DIR}/easyrsa" ]; then + # TODO(roberthbailey,porridge): add better error handling here, + # see https://github.com/kubernetes/kubernetes/issues/55229 + cat "${cert_create_debug_output}" >&2 + echo "=== Failed to setup easy-rsa: Aborting ===" >&2 + exit 2 + fi +} + +# Runs the easy RSA commands to generate certificate files. +# The generated files are IN ${CERT_DIR} +# +# Assumed vars +# KUBE_TEMP +# MASTER_NAME +# CERT_DIR +# PRIMARY_CN: Primary canonical name +# SANS: Subject alternate names +# +# +function generate-certs { + local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") + # Note: This was heavily cribbed from make-ca-cert.sh + (set -x + cd "${CERT_DIR}" + ./easyrsa init-pki + # this puts the cert into pki/ca.crt and the key into pki/private/ca.key + ./easyrsa --batch "--req-cn=${PRIMARY_CN}@$(date +%s)" build-ca nopass + ./easyrsa --subject-alt-name="${SANS}" build-server-full "${MASTER_NAME}" nopass + ./easyrsa build-client-full kube-apiserver nopass + + kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl" + + # make the config for the signer + echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json" + # create the kubelet client cert with the correct groups + echo '{"CN":"kubelet","names":[{"O":"system:nodes"}],"hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare kubelet + mv "kubelet-key.pem" "pki/private/kubelet.key" + mv "kubelet.pem" "pki/issued/kubelet.crt" + rm -f "kubelet.csr" + + # Make a superuser client cert with subject "O=system:masters, CN=kubecfg" + ./easyrsa --dn-mode=org \ + --req-cn=kubecfg --req-org=system:masters \ + --req-c= --req-st= --req-city= --req-email= --req-ou= \ + build-client-full kubecfg nopass) &>${cert_create_debug_output} || true + local output_file_missing=0 + local output_file + for output_file in \ + "${CERT_DIR}/pki/private/ca.key" \ + "${CERT_DIR}/pki/ca.crt" \ + "${CERT_DIR}/pki/issued/${MASTER_NAME}.crt" \ + "${CERT_DIR}/pki/private/${MASTER_NAME}.key" \ + "${CERT_DIR}/pki/issued/kubelet.crt" \ + "${CERT_DIR}/pki/private/kubelet.key" \ + "${CERT_DIR}/pki/issued/kubecfg.crt" \ + "${CERT_DIR}/pki/private/kubecfg.key" \ + "${CERT_DIR}/pki/issued/kube-apiserver.crt" \ + "${CERT_DIR}/pki/private/kube-apiserver.key" + do + if [[ ! -s "${output_file}" ]]; then + echo "Expected file ${output_file} not created" >&2 + output_file_missing=1 + fi + done + if (( $output_file_missing )); then + # TODO(roberthbailey,porridge): add better error handling here, + # see https://github.com/kubernetes/kubernetes/issues/55229 + cat "${cert_create_debug_output}" >&2 + echo "=== Failed to generate master certificates: Aborting ===" >&2 + exit 2 + fi +} + +# Runs the easy RSA commands to generate aggregator certificate files. +# The generated files are in ${AGGREGATOR_CERT_DIR} +# +# Assumed vars +# KUBE_TEMP +# AGGREGATOR_MASTER_NAME +# AGGREGATOR_CERT_DIR +# AGGREGATOR_PRIMARY_CN: Primary canonical name +# AGGREGATOR_SANS: Subject alternate names +# +# +function generate-aggregator-certs { + local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX") + # Note: This was heavily cribbed from make-ca-cert.sh + (set -x + cd "${KUBE_TEMP}/easy-rsa-master/aggregator" + ./easyrsa init-pki + # this puts the cert into pki/ca.crt and the key into pki/private/ca.key + ./easyrsa --batch "--req-cn=${AGGREGATOR_PRIMARY_CN}@$(date +%s)" build-ca nopass + ./easyrsa --subject-alt-name="${AGGREGATOR_SANS}" build-server-full "${AGGREGATOR_MASTER_NAME}" nopass + ./easyrsa build-client-full aggregator-apiserver nopass + + kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl" + + # make the config for the signer + echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json" + # create the aggregator client cert with the correct groups + echo '{"CN":"aggregator","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare proxy-client + mv "proxy-client-key.pem" "pki/private/proxy-client.key" + mv "proxy-client.pem" "pki/issued/proxy-client.crt" + rm -f "proxy-client.csr" + + # Make a superuser client cert with subject "O=system:masters, CN=kubecfg" + ./easyrsa --dn-mode=org \ + --req-cn=proxy-clientcfg --req-org=system:aggregator \ + --req-c= --req-st= --req-city= --req-email= --req-ou= \ + build-client-full proxy-clientcfg nopass) &>${cert_create_debug_output} || true + local output_file_missing=0 + local output_file + for output_file in \ + "${AGGREGATOR_CERT_DIR}/pki/private/ca.key" \ + "${AGGREGATOR_CERT_DIR}/pki/ca.crt" \ + "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" \ + "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" + do + if [[ ! -s "${output_file}" ]]; then + echo "Expected file ${output_file} not created" >&2 + output_file_missing=1 + fi + done + if (( $output_file_missing )); then + # TODO(roberthbailey,porridge): add better error handling here, + # see https://github.com/kubernetes/kubernetes/issues/55229 + cat "${cert_create_debug_output}" >&2 + echo "=== Failed to generate aggregator certificates: Aborting ===" >&2 + exit 2 + fi +} + +# +# Using provided master env, extracts value from provided key. +# +# Args: +# $1 master env (kube-env of master; result of calling get-master-env) +# $2 env key to use +function get-env-val() { + local match=`(echo "${1}" | grep -E "^${2}:") || echo ""` + if [[ -z ${match} ]]; then + echo "" + fi + echo ${match} | cut -d : -f 2 | cut -d \' -f 2 +} + +# Load the master env by calling get-master-env, and extract important values +function parse-master-env() { + # Get required master env vars + local master_env=$(get-master-env) + KUBE_PROXY_TOKEN=$(get-env-val "${master_env}" "KUBE_PROXY_TOKEN") + NODE_PROBLEM_DETECTOR_TOKEN=$(get-env-val "${master_env}" "NODE_PROBLEM_DETECTOR_TOKEN") + CA_CERT_BASE64=$(get-env-val "${master_env}" "CA_CERT") + CA_KEY_BASE64=$(get-env-val "${master_env}" "CA_KEY") + KUBEAPISERVER_CERT_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_CERT") + KUBEAPISERVER_KEY_BASE64=$(get-env-val "${master_env}" "KUBEAPISERVER_KEY") + EXTRA_DOCKER_OPTS=$(get-env-val "${master_env}" "EXTRA_DOCKER_OPTS") + KUBELET_CERT_BASE64=$(get-env-val "${master_env}" "KUBELET_CERT") + KUBELET_KEY_BASE64=$(get-env-val "${master_env}" "KUBELET_KEY") + MASTER_CERT_BASE64=$(get-env-val "${master_env}" "MASTER_CERT") + MASTER_KEY_BASE64=$(get-env-val "${master_env}" "MASTER_KEY") + AGGREGATOR_CA_KEY_BASE64=$(get-env-val "${master_env}" "AGGREGATOR_CA_KEY") + REQUESTHEADER_CA_CERT_BASE64=$(get-env-val "${master_env}" "REQUESTHEADER_CA_CERT") + PROXY_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_CERT") + PROXY_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "PROXY_CLIENT_KEY") + ENABLE_LEGACY_ABAC=$(get-env-val "${master_env}" "ENABLE_LEGACY_ABAC") +} + +# Update or verify required gcloud components are installed +# at minimum required version. +# Assumed vars +# KUBE_PROMPT_FOR_UPDATE +function update-or-verify-gcloud() { + local sudo_prefix="" + if [ ! -w $(dirname `which gcloud`) ]; then + sudo_prefix="sudo" + fi + # update and install components as needed + if [[ "${KUBE_PROMPT_FOR_UPDATE}" == "y" ]]; then + ${sudo_prefix} gcloud ${gcloud_prompt:-} components install alpha + ${sudo_prefix} gcloud ${gcloud_prompt:-} components install beta + ${sudo_prefix} gcloud ${gcloud_prompt:-} components update + else + local version=$(gcloud version --format=json) + python -c' +import json,sys +from distutils import version + +minVersion = version.LooseVersion("1.3.0") +required = [ "alpha", "beta", "core" ] +data = json.loads(sys.argv[1]) +rel = data.get("Google Cloud SDK") +if rel != "HEAD" and version.LooseVersion(rel) < minVersion: + print("gcloud version out of date ( < %s )" % minVersion) + exit(1) +missing = [] +for c in required: + if not data.get(c): + missing += [c] +if missing: + for c in missing: + print ("missing required gcloud component \"{0}\"".format(c)) + exit(1) + ' """${version}""" + fi +} + # Robustly try to create a static ip. # $1: The name of the ip to create # $2: The name of the region to create the ip in.