Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kubeclient does not support certificate authorities with intermediate certificate #460

Open
jperville opened this issue Sep 1, 2020 · 5 comments
Labels

Comments

@jperville
Copy link
Contributor

jperville commented Sep 1, 2020

The issue

We tried to run a Ruby application (using the kubeclient gem) on a kubernetes cluster which uses a custom CA (the cluster's CA itself is signed by another custom CA, hence the need for intermediate certificates).

Kubeclient (initialized from the KUBECONFIG file) fails with SSL verify errors.

How to reproduce

First, we create a KUBECONFIG file in the container by executing the following script:

write_client_kubeconfig() {
  KUBECTL=${1:?please provide the KUBECTL environment variable}

  # only needed for writing a kubeconfig:
  master_url=${MASTER_URL:-https://kubernetes.default.svc.cluster.local:443}
  master_ca=${MASTER_CA:-/var/run/secrets/kubernetes.io/serviceaccount/ca.crt}
  token_file=${TOKEN_FILE:-/var/run/secrets/kubernetes.io/serviceaccount/token}

  # set up configuration for openshift client
  if [ -n "${WRITE_KUBECONFIG:-''}" ]; then
      # craft a kubeconfig, usually at $KUBECONFIG location
      ${KUBECTL} config set-cluster master \
            --certificate-authority="${master_ca}" \
            --server="${master_url}"
      ${KUBECTL} config set-credentials account \
            --token="$(cat ${token_file})"
      ${KUBECTL} config set-context current \
            --cluster=master \
            --user=account \
            --namespace="${infra_project}"
      ${KUBECTL} config use-context current
  fi
}

write_client_kubeconfig kubectl

Then we try listing services cluster-wide using the kubectl binary:

kubectl get services --all-namespaces

This should work, assuming that the current service account is allowed to list services cluster-wide.

Finally, we try listing the same services in Ruby using kubeclient:

mkdir -p /tmp/test
cd /tmp/test

cat <<EOF | tee Gemfile
source 'https://rubygems.org'
gem 'kubeclient', '~> 4.8'
EOF

bundle install --path .bundle

cat <<EOF | tee test.rb
require 'kubeclient'

config = Kubeclient::Config.read(ENV.fetch('KUBECONFIG'))
context = config.context
ssl_options = context.ssl_options
auth_options = context.auth_options

client = Kubeclient::Client.new(
    context.api_endpoint, 'v1',
    ssl_options: ssl_options, auth_options: auth_options
)

services_names = client.get_services.map { |svc| svc.metadata.name }
puts services_names.inspect
EOF

bundle exec ruby test.rb

On clusters where the kubernetes CA has been signed by an intermediate CA, kubeclient fails to verify the kubernetes API certificate, even if the cacert in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt contains the intermediate certificates.

We see the following stacktrace:

 Kubeclient::HttpError: SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get issuer certificate)
/opt/deploy/.bundle/ruby/2.6.0/gems/kubeclient-4.6.0/lib/kubeclient/common.rb:130:in `rescue in handle_exception'
/opt/deploy/.bundle/ruby/2.6.0/gems/kubeclient-4.6.0/lib/kubeclient/common.rb:120:in `handle_exception'
/opt/deploy/.bundle/ruby/2.6.0/gems/kubeclient-4.6.0/lib/kubeclient/common.rb:567:in `fetch_entities'
/opt/deploy/.bundle/ruby/2.6.0/gems/kubeclient-4.6.0/lib/kubeclient/common.rb:554:in `load_entities'
/opt/deploy/.bundle/ruby/2.6.0/gems/kubeclient-4.6.0/lib/kubeclient/common.rb:134:in `discover'

The explanation

The reason of this behavior is the use of OpenSSL::X509::Store#add_cert in https://github.com/abonas/kubeclient/blob/v4.9.1/lib/kubeclient/config.rb#L58 .

Per the documentation:

add_cert(cert)
Adds the OpenSSL::X509::Certificate cert to the certificate store.

If we had used the add_file method instead of add_cert every certificate included in the cacert file would have been loaded.

Documentation of add_file:

add_file(file) → self
Adds the certificates in file to the certificate store. file is the path to the file, and the file contains one or more certificates in PEM format concatenated together.

I will submit a PR next

jperville added a commit to PerfectMemory/kubeclient that referenced this issue Sep 1, 2020
This will load all certificates concatenated in the cacert file,
instead of only the first one (ManageIQ#460).
@cben
Copy link
Collaborator

cben commented Feb 28, 2021

Closing, assuming #461 fixed this.

@cben cben closed this as completed Feb 28, 2021
@DocX
Copy link
Contributor

DocX commented Oct 12, 2021

Hello, is there an ETA when this fix will be released in the client release? AFAIK it is not in the latest v4.9.2 cc @cben

cben added a commit to cben/kubeclient that referenced this issue Mar 22, 2022
Test sandwitches the real CA cert between two other CA certs
(another-ca1.pem, another-ca2.pem, simply copied from two runs of update_certs_k0s.rb).

Fails before the backport of ManageIQ#461:

  1) Failure:
KubeclientConfigTest#test_concatenated_ca [/home/beni/kubeclient/test/test_config.rb:196]:
Expected false to be truthy.

passes with the fix.
cben added a commit to cben/kubeclient that referenced this issue Mar 22, 2022
Test sandwitches the real CA cert between two unrelated CA certs
(another-ca1.pem, another-ca2.pem, simply copied from two runs of
update_certs_k0s.rb).
No test for root+intermediate scenario.

Fails before the backport of ManageIQ#461:

KubeclientConfigTest#test_concatenated_ca [/home/beni/kubeclient/test/test_config.rb:196]:
Expected false to be truthy.

(some experimenting with order suggests only first cert is honored.)
Passes with the fix.
cben added a commit to cben/kubeclient that referenced this issue Mar 22, 2022
cben added a commit to cben/kubeclient that referenced this issue Mar 22, 2022
Test sandwitches the real CA cert between two unrelated CA certs
(another-ca1.pem, another-ca2.pem, simply copied from two runs of
update_certs_k0s.rb).
No test for root+intermediate scenario.

Fails before the backport of ManageIQ#461:

KubeclientConfigTest#test_concatenated_ca [/home/beni/kubeclient/test/test_config.rb:196]:
Expected false to be truthy.

(some experimenting with order suggests only first cert is honored.)
Passes with the fix.
cben added a commit to cben/kubeclient that referenced this issue Mar 22, 2022
@cben
Copy link
Collaborator

cben commented Mar 22, 2022

reopening:

@cben cben reopened this Mar 22, 2022
@cben
Copy link
Collaborator

cben commented Mar 22, 2022

interesting, in lostisland/faraday#371 (opened on faraday but actaully not specific to faraday at all) people had possibly related findings 👀

  • Also, in ca_file you have to have all certificates in the chain in correct order.

  • The most important here is -subject_hash and -subject_hash_old - it wasn't working because of different OpenSSL versions used to prepare hashes and to compile Ruby etc.

  • I also had this issue on OSX.
    Make sure your Ruby OpenSSL is a similar version as your system openssl!

EDIT: turns out the failure was my mistake, not Mac-specific 🎉. And those discussion of "hashes" IIUC are only interesting for OpenSSL::X509::Store#add_path, not relevant for #add_file.

cben added a commit to cben/kubeclient that referenced this issue Mar 23, 2022
Test sandwitches the real CA cert between two unrelated CA certs
(another-ca1.pem, another-ca2.pem, simply copied from two runs of
update_certs_k0s.rb).
No test for root+intermediate scenario.

Fails before the backport of ManageIQ#461:

KubeclientConfigTest#test_concatenated_ca [/home/beni/kubeclient/test/test_config.rb:196]:
Expected false to be truthy.

(some experimenting with order suggests only first cert is honored.)
Passes with the fix.
cben added a commit to cben/kubeclient that referenced this issue Mar 23, 2022
cben added a commit to cben/kubeclient that referenced this issue Mar 23, 2022
@cben cben mentioned this issue Mar 23, 2022
@cben
Copy link
Collaborator

cben commented Mar 23, 2022

@DocX certificate-authority fix released in gem version 4.9.3 (together with other Config security fixes — see #554 and #555, recommend updating ASAP!)

Keeping issue open for certificate-authority-data.

@cben cben added the bug label Jul 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants