...

Text file src/k8s.io/kubernetes/test/cmd/authentication.sh

Documentation: k8s.io/kubernetes/test/cmd

     1#!/usr/bin/env bash
     2
     3# Copyright 2020 The Kubernetes Authors.
     4#
     5# Licensed under the Apache License, Version 2.0 (the "License");
     6# you may not use this file except in compliance with the License.
     7# You may obtain a copy of the License at
     8#
     9#     http://www.apache.org/licenses/LICENSE-2.0
    10#
    11# Unless required by applicable law or agreed to in writing, software
    12# distributed under the License is distributed on an "AS IS" BASIS,
    13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14# See the License for the specific language governing permissions and
    15# limitations under the License.
    16
    17set -o errexit
    18set -o nounset
    19set -o pipefail
    20
    21run_exec_credentials_tests() {
    22  run_exec_credentials_tests_version client.authentication.k8s.io/v1beta1
    23  run_exec_credentials_tests_version client.authentication.k8s.io/v1
    24}
    25
    26run_exec_credentials_tests_version() {
    27  set -o nounset
    28  set -o errexit
    29
    30  local -r apiVersion="$1"
    31
    32  kube::log::status "Testing kubectl with configured ${apiVersion} exec credentials plugin"
    33
    34  cat > "${TMPDIR:-/tmp}"/invalid_exec_plugin.yaml << EOF
    35apiVersion: v1
    36clusters:
    37- cluster:
    38  name: test
    39contexts:
    40- context:
    41    cluster: test
    42    user: invalid_token_user
    43  name: test
    44current-context: test
    45kind: Config
    46preferences: {}
    47users:
    48- name: invalid_token_user
    49  user:
    50    exec:
    51      apiVersion: ${apiVersion}
    52      # Any invalid exec credential plugin will do to demonstrate
    53      command: ls
    54      interactiveMode: IfAvailable
    55EOF
    56
    57  ### Provided --token should take precedence, thus not triggering the (invalid) exec credential plugin
    58  # Pre-condition: Client certificate authentication enabled on the API server
    59  kube::util::test_client_certificate_authentication_enabled
    60  # Command
    61  output=$(kubectl "${kube_flags_with_token[@]:?}" --kubeconfig="${TMPDIR:-/tmp}"/invalid_exec_plugin.yaml get namespace kube-system -o name || true)
    62
    63  if [[ "${output}" == "namespace/kube-system" ]]; then
    64    kube::log::status "exec credential plugin not triggered since kubectl was called with provided --token"
    65  else
    66    kube::log::status "Unexpected output when providing --token for authentication - exec credential plugin likely triggered. Output: ${output}"
    67    exit 1
    68  fi
    69  # Post-condition: None
    70
    71  ### Without provided --token, the exec credential plugin should be triggered
    72  # Pre-condition: Client certificate authentication enabled on the API server - already checked by positive test above
    73
    74  # Command
    75  output2=$(kubectl "${kube_flags_without_token[@]:?}" --kubeconfig="${TMPDIR:-/tmp}"/invalid_exec_plugin.yaml get namespace kube-system -o name 2>&1 || true)
    76
    77  if [[ "${output2}" =~ "json parse error" ]]; then
    78    kube::log::status "exec credential plugin triggered since kubectl was called without provided --token"
    79  else
    80    kube::log::status "Unexpected output when not providing --token for authentication - exec credential plugin not triggered. Output: ${output2}"
    81    exit 1
    82  fi
    83  # Post-condition: None
    84
    85  cat >"${TMPDIR:-/tmp}"/valid_exec_plugin.yaml <<EOF
    86apiVersion: v1
    87clusters:
    88- cluster:
    89  name: test
    90contexts:
    91- context:
    92    cluster: test
    93    user: valid_token_user
    94  name: test
    95current-context: test
    96kind: Config
    97preferences: {}
    98users:
    99- name: valid_token_user
   100  user:
   101    exec:
   102      apiVersion: ${apiVersion}
   103      command: echo
   104      args:
   105        - '{"apiVersion":"${apiVersion}","status":{"token":"admin-token"}}'
   106      interactiveMode: IfAvailable
   107EOF
   108
   109  ### Valid exec plugin should authenticate user properly
   110  # Pre-condition: Client certificate authentication enabled on the API server - already checked by positive test above
   111
   112  # Command
   113  output3=$(kubectl "${kube_flags_without_token[@]:?}" --kubeconfig="${TMPDIR:-/tmp}"/valid_exec_plugin.yaml get namespace kube-system -o name 2>&1 || true)
   114
   115  if [[ "${output3}" == "namespace/kube-system" ]]; then
   116    kube::log::status "exec credential plugin triggered and provided valid credentials"
   117  else
   118    kube::log::status "Unexpected output when using valid exec credential plugin for authentication. Output: ${output3}"
   119    exit 1
   120  fi
   121  # Post-condition: None
   122
   123  ### Provided --username/--password should take precedence, thus not triggering the (valid) exec credential plugin
   124  # Pre-condition: Client certificate authentication enabled on the API server - already checked by positive test above
   125
   126  # Command
   127  output4=$(kubectl "${kube_flags_without_token[@]:?}" --username bad --password wrong --kubeconfig="${TMPDIR:-/tmp}"/valid_exec_plugin.yaml get namespace kube-system -o name 2>&1 || true)
   128
   129  if [[ "${output4}" =~ "Unauthorized" ]]; then
   130    kube::log::status "exec credential plugin not triggered since kubectl was called with provided --username/--password"
   131  else
   132    kube::log::status "Unexpected output when providing --username/--password for authentication - exec credential plugin likely triggered. Output: ${output4}"
   133    exit 1
   134  fi
   135  # Post-condition: None
   136
   137  ### Provided --client-certificate/--client-key should take precedence on the cli, thus not triggering the (invalid) exec credential plugin
   138  # contained in the kubeconfig.
   139
   140  # Use CSR to get a valid certificate
   141  cat <<EOF | kubectl create -f -
   142apiVersion: certificates.k8s.io/v1
   143kind: CertificateSigningRequest
   144metadata:
   145  name: testuser
   146spec:
   147  request: $(base64 < hack/testdata/auth/testuser.csr | tr -d '\n')
   148  signerName: kubernetes.io/kube-apiserver-client
   149  usages: [client auth]
   150EOF
   151
   152  kube::test::wait_object_assert 'csr/testuser' '{{range.status.conditions}}{{.type}}{{end}}' ''
   153  kubectl certificate approve testuser
   154  kube::test::wait_object_assert 'csr/testuser' '{{range.status.conditions}}{{.type}}{{end}}' 'Approved'
   155  # wait for certificate to not be empty
   156  kube::test::wait_object_assert 'csr/testuser' '{{.status.certificate}}' '.+'
   157  kubectl get csr testuser -o jsonpath='{.status.certificate}' | base64 -d > "${TMPDIR:-/tmp}"/testuser.crt
   158
   159  output5=$(kubectl  "${kube_flags_without_token[@]:?}" --client-certificate="${TMPDIR:-/tmp}"/testuser.crt --client-key="hack/testdata/auth/testuser.key" --kubeconfig="${TMPDIR:-/tmp}"/invalid_exec_plugin.yaml get namespace kube-system -o name)
   160  if [[ "${output5}" =~ "Unauthorized" ]]; then
   161    kube::log::status "Unexpected output when providing --client-certificate/--client-key for authentication - exec credential plugin likely triggered. Output: ${output5}"
   162    exit 1
   163  else
   164    kube::log::status "exec credential plugin not triggered since kubectl was called with provided --client-certificate/--client-key"
   165  fi
   166
   167    ### Provided --client-certificate/--client-key should take precedence in the kubeconfig, thus not triggering the (invalid) exec credential plugin.
   168  cat >"${TMPDIR:-/tmp}"/invalid_execcredential.sh <<EOF
   169#!/bin/bash
   170echo '{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","status":{"clientKeyData":"bad","clientCertificateData":"bad"}}'
   171EOF
   172  chmod +x "${TMPDIR:-/tmp}"/invalid_execcredential.sh
   173
   174  kubectl config set-credentials testuser --client-certificate="${TMPDIR:-/tmp}"/testuser.crt --client-key="hack/testdata/auth/testuser.key" --exec-api-version=client.authentication.k8s.io/v1beta1 --exec-command=/tmp/invalid_execcredential.sh
   175  output6=$(kubectl  "${kube_flags_without_token[@]:?}" --user testuser get namespace kube-system -o name)
   176  if [[ "${output6}" =~ "Unauthorized" ]]; then
   177    kube::log::status "Unexpected output when kubeconfig was configured with --client-certificate/--client-key for authentication - exec credential plugin likely triggered. Output: ${output6}"
   178    exit 1
   179  else
   180    kube::log::status "exec credential plugin not triggered since kubeconfig was configured with --client-certificate/--client-key for authentication"
   181  fi
   182
   183  kubectl delete csr testuser
   184  rm "${TMPDIR:-/tmp}"/invalid_execcredential.sh
   185  rm "${TMPDIR:-/tmp}"/invalid_exec_plugin.yaml
   186  rm "${TMPDIR:-/tmp}"/valid_exec_plugin.yaml
   187
   188  set +o nounset
   189  set +o errexit
   190}
   191
   192run_exec_credentials_interactive_tests() {
   193  run_exec_credentials_interactive_tests_version client.authentication.k8s.io/v1beta1
   194  run_exec_credentials_interactive_tests_version client.authentication.k8s.io/v1
   195}
   196
   197run_exec_credentials_interactive_tests_version() {
   198  set -o nounset
   199  set -o errexit
   200
   201  local -r apiVersion="$1"
   202
   203  kube::log::status "Testing kubectl with configured ${apiVersion} interactive exec credentials plugin"
   204
   205  cat >"${TMPDIR:-/tmp}"/always_interactive_exec_plugin.yaml <<EOF
   206apiVersion: v1
   207clusters:
   208- cluster:
   209  name: test
   210contexts:
   211- context:
   212    cluster: test
   213    user: always_interactive_token_user
   214  name: test
   215current-context: test
   216kind: Config
   217preferences: {}
   218users:
   219- name: always_interactive_token_user
   220  user:
   221    exec:
   222      apiVersion: ${apiVersion}
   223      command: echo
   224      args:
   225        - '{"apiVersion":"${apiVersion}","status":{"token":"admin-token"}}'
   226      interactiveMode: Always
   227EOF
   228
   229  ### The exec credential plugin should not be run if it kubectl already uses standard input
   230  # Pre-condition: The kubectl command requires standard input
   231
   232  some_resource='{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"some-resource"}}'
   233
   234  # Declare map from kubectl command to standard input data
   235  declare -A kubectl_commands
   236  kubectl_commands["apply -f -"]="$some_resource"
   237  kubectl_commands["set env deployment/some-deployment -"]="SOME_ENV_VAR_KEY=SOME_ENV_VAR_VAL"
   238  kubectl_commands["replace -f - --force"]="$some_resource"
   239
   240  failure=
   241  for kubectl_command in "${!kubectl_commands[@]}"; do
   242    # Use a separate bash script for the command here so that script(1) will not get confused with kubectl flags
   243    script_file="${TMPDIR:-/tmp}/test-cmd-exec-credentials-script-file.sh"
   244    cat <<EOF >"$script_file"
   245#!/usr/bin/env bash
   246set -o errexit
   247set -o nounset
   248set -o pipefail
   249kubectl ${kube_flags_without_token[*]:?} --kubeconfig=${TMPDIR:-/tmp}/always_interactive_exec_plugin.yaml ${kubectl_command} 2>&1 || true
   250EOF
   251    chmod +x "$script_file"
   252
   253    # Run kubectl as child of script(1) so kubectl will always run with a PTY
   254    # Dynamically build script(1) command so that we can conditionally add flags on Linux
   255    script_command="script -q /dev/null"
   256    if [[ "$(uname)" == "Linux" ]]; then script_command="${script_command} -c"; fi
   257    script_command="${script_command} ${script_file}"
   258
   259    # Specify SHELL env var when we call script(1) since it is picky about the format of the env var
   260    shell="$(which bash)"
   261
   262    kube::log::status "Running command '$script_command' (kubectl command: '$kubectl_command') with input '${kubectl_commands[$kubectl_command]}'"
   263    output=$(echo "${kubectl_commands[$kubectl_command]}" | SHELL="$shell" $script_command)
   264
   265    if [[ "${output}" =~ "used by stdin resource manifest reader" ]]; then
   266      kube::log::status "exec credential plugin not run because kubectl already uses standard input"
   267    else
   268      kube::log::status "Unexpected output when running kubectl command that uses standard input. Output: ${output}"
   269      failure=yup
   270    fi
   271  done
   272
   273  if [[ -n "$failure" ]]; then
   274    exit 1
   275  fi
   276  # Post-condition: None
   277
   278  cat >"${TMPDIR:-/tmp}"/missing_interactive_exec_plugin.yaml <<EOF
   279apiVersion: v1
   280clusters:
   281- cluster:
   282  name: test
   283contexts:
   284- context:
   285    cluster: test
   286    user: missing_interactive_token_user
   287  name: test
   288current-context: test
   289kind: Config
   290preferences: {}
   291users:
   292- name: missing_interactive_token_user
   293  user:
   294    exec:
   295      apiVersion: ${apiVersion}
   296      command: echo
   297      args:
   298        - '{"apiVersion":"${apiVersion}","status":{"token":"admin-token"}}'
   299EOF
   300
   301  ### The kubeconfig will fail to be loaded if a v1 exec credential plugin is missing an interactiveMode field, otherwise it will default to IfAvailable
   302  # Pre-condition: The exec credential plugin is missing an interactiveMode setting
   303
   304  output2=$(kubectl "${kube_flags_without_token[@]:?}" --kubeconfig="${TMPDIR:-/tmp}"/missing_interactive_exec_plugin.yaml get namespace kube-system -o name 2>&1 || true)
   305
   306  if [[ "$apiVersion" == "client.authentication.k8s.io/v1" ]]; then
   307    if [[ "${output2}" =~ "error: interactiveMode must be specified for missing_interactive_token_user to use exec authentication plugin" ]]; then
   308      kube::log::status "kubeconfig was not loaded successfully because ${apiVersion} exec credential plugin is missing interactiveMode"
   309    else
   310      kube::log::status "Unexpected output when running kubectl command that uses a ${apiVersion} exec credential plugin without an interactiveMode. Output: ${output2}"
   311      exit 1
   312    fi
   313  else
   314    if [[ "${output2}" == "namespace/kube-system" ]]; then
   315      kube::log::status "${apiVersion} exec credential plugin triggered and provided valid credentials"
   316    else
   317      kube::log::status "Unexpected output when using valid exec credential plugin for authentication. Output: ${output2}"
   318      exit 1
   319    fi
   320  fi
   321  # Post-condition: None
   322
   323  rm "${TMPDIR:-/tmp}"/always_interactive_exec_plugin.yaml
   324  rm "${TMPDIR:-/tmp}"/missing_interactive_exec_plugin.yaml
   325
   326  set +o nounset
   327  set +o errexit
   328}

View as plain text