Skip to content

Latest commit

 

History

History
604 lines (452 loc) · 29.4 KB

File metadata and controls

604 lines (452 loc) · 29.4 KB

KEP-NNNN: External TLS certificate authenticator

Release Signoff Checklist

  • Enhancement issue in release milestone, which links to KEP dir in kubernetes/enhancements (not the initial KEP PR)
  • KEP approvers have approved the KEP status as implementable
  • Design details are appropriately documented
  • Test plan is in place, giving consideration to SIG Architecture and SIG Testing input
  • Graduation criteria is in place
  • "Implementation History" section is up-to-date for milestone
  • User-facing documentation has been created in kubernetes/website, for publication to kubernetes.io
  • Supporting documentation e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes

Summary

This enhancement proposes adding support for authentication via external TLS certificate signers, what would enable usage of Hardware Security Modules (HSMs) - also known as smartcards, cryptographic processors or, by a popular brand name, YubiKeys(tm) via the PKCS#11 standard. This enhancement allows developers or automation pipelines to authenticate with the Kubernetes cluster, without requiring access to the client key, hence improving compliance and security.

Motivation

Are you worried about someone getting access to your Kubernetes credentials? (For example, by extracting private keys from your laptop using malware or by performing a cold boot attack.) Do you already use a YubiKey for SSH and GPG, and wonder why you cannot use it with kubectl? If yes, then this enhancement is for you!

Highly regulated environments, such as FinTech, require delegating all digital key operations to specialized Hardware Security Modules (HSMs). Amongst others, HSMs increase security by storing digital keys without allow them to be extracted. Authentication, encryption and signing is performed via a standard such as the PKCS#11 on the HSM directly. In fact, many regulated environments already require developers and operators to store SSH and GPG keys on the YubiKey, a popular HSM connected via USB.

Unfortunately, as of today, kubectl lacks support for PKCS#11 (see Issue #64783). Indeed, kubectl requires direct access to the client key data, which can either be stored in the kubeconfig or provided via a credentials plugin. Because of this, some security bloggers have even argued for not using certificates in kubectl at all.

Goals

  • kubectl can authenticate to a Kubernetes cluster with an external TLS certificate signer, for example a PKCS#11-compatible HSM, such as SoftHSM or YubiKey
  • kubectl has no access to client key data

Non-Goals

  • HSM support on the server-side, i.e., kubernetes-apiserver (although a follow-up enhancement would be cool!)
  • Improving PKCS#11 support in the Go runtime or in a Go library

Proposal

This KEP proposes introducing a new authentication provider to enable a secure and complient usage of TLS client certificates within kubectl/client-go, by delegating digital key operations to extarnal processes, for example HSMs. The proposed authentication provider is similar to the credentials plugin in that it also uses an external process during a part of the authentication sequence, but contrary to the credentials plugin, it performs the sign operation externally, and returns only the product of it - the signature.

In order to achieve a general solution, which can support various HSMs with different protocols, this KEP proposes to split the authenticator into two components and establish an API for the communication between them. The internal component provides only the general primitives for the authentication process, while leaving the implementation of a particular HSM protocol (for example PKCS#11) out of kubectl/client-go. Support of a specific protocol can be enabled by providing an external plugin that implements the proposed API.

User Stories

Story 1

As a developer or an operator, I want to be able to authenticate my API requests using a client certificate without a need of providing direct access to my private key data so that I can improve compliance and security of the whole system.

To authenticate against the API:

  • The user issues a kubectl command, for example kubectl get pods.
  • Authentication provider calls the external plugin to obtain a client certificate and signature.
  • External plugin prompts the user for a PIN to perform signing operation (if the PIN is not provided in the configuration file).
  • External plugin returns a client certificate and a signature to client-go via the authentication provider.
  • API server verifies the signature and processes the request.

Notes/Constraints/Caveats

It was requested by the sig-auth community to come up with a solution that does not require CGO during the build process and preferably does not add new dependencies to kubectl.

Risks and Mitigations

Design Details

The complete solution for external TLS certificate signing includes two components:

  • an authentication provider within kubectl/client-go, and
  • an external plugin, which provides the actual certificate and signing services.

UML sequence diagram

The main role of the authentication provider is to pass along requests for obtaining a TLS certificate and signing to the external plugin, retrieve the responses and return them to the initial caller.

Users would be required to install an external plugin (possibly along with dependencies) on their workstation.

Configuration

Configuration of the both components (authentication provider and external plugin) is specified in the kubeconfig files as part of the user fields. The authentication provider is responsible for reading the configuration from the kubeconfig file and exposing the configuration parameters to the external plugin.

The internal authentication provider requires only a single parameter, the path to the external plugin pathExec.

The remaining of the parameters are specific for the external plugin and protocol in use. In order to increase flexibility of the solution and support for multiple protocols, the authentication provider will pass all the configuration parameters specified in the kubeconfig file to the external plugin in a form of string-to-string mapping (key-value pairs).

In case of the PKCS#11 protocol, the following parameters are in use:

  • pathLib - a path to the library used by the authentication protocol (mandatory),
  • slotId - an identifier of the slot (mandatory),
  • objectId - an identifier of the object (mandatory),
  • pin - a PIN code used when accessing the private key during the signing operation (optional, if not provided, the user will be asked to provide the PIN during each signing operation).

An excerpt from an exemplary kubeconfig file:

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    auth-provider:
      name: externalSigner
      config:
        pathExec: /path/to/externalSigner  
        pathLib: /path/to/library.so        # library used by the external plugin
        objectId: "2"                       # PKCS#11 specific configuration
        slotId: "0"
        pin: "123456"                       # (optional)

API specs

Communication between the authentication provider and the external plugin is bidirectional. The authentication provider initiates the communication by sending a request for performing an operation (obtaining a client certificate or signing) to the external plugin and the external plugin replies by sending a response with a product of the respective operation (a client certificate or signature).

All messages (requests and responses) are in the JSON format. The resources (certificates and signatures) are Base64 encoded.

A request message is passed from the authentication provider to the external plugin as a program argument. The external plugin is expected to return the response message by printing them to stdout. Moreover, the external plugin has access to stdin for interacting with the user (for example providing a PIN) and stderr for printing diagnostic information.

Obtaining a certificate

Certificate request

The authentication provider sends a request message in the JSON format of CertificateRequest kind containing the plugin configuration parameters as a program argument.

{
  "apiVersion":"external-signer.authentication.k8s.io/v1alpha1",
  "kind":"CertificateRequest",
  "configuration":{
    "objectId":"2",
    "pathExec":"/path/to/externalSigner",
    "pathLib":"/path/to/library.so",
    "pin":"123456",
    "slotId":"0"
  }
}
Certificate response

The external plugin returns a response massage in the JSON format of CertificateResponse kind containing a Base64-encoded client certificate by printing it to stdout.

{
  "apiVersion":"external-signer.authentication.k8s.io/v1alpha1",
  "kind":"CertificateResponse",
  "certificate":"(CERTIFICATE BASE64 ENCODED)"
}

k8s.io/client-go uses the returned client certificate in the certificate.

Signing

Sign request

The authentication provider sends a request message as a program argument in the JSON format of SignRequest kind containing:

  • the digest,
  • plugin configuration parameters,
  • the type of signer options (rsa.PSSOptions by default),
  • signer options as a key-value pairs (SaltLength and Hash by default).
{
  "apiVersion":"external-signer.authentication.k8s.io/v1alpha1",
  "kind":"SignRequest",
  "digest":"TqRUvJjLvlp3g9B3elpfzfgrSbukXBP5txkBLIkCSs4=",
  "configuration":{
    "objectId":"2",
    "pathExec":"/path/to/externalSigner",
    "pathLib":"/path/to/library.so",
    "pin":"123456",
    "slotId":"0"
    },
  "signerOptsType":"*rsa.PSSOptions",
  "signerOpts":"{\"SaltLength\":-1,\"Hash\":5}"
}
Sign response

The external plugin returns a response massage in the JSON format of SignResponse kind containing a Base64-encoded signature by printing it to stdout.

{
  "apiVersion":"external-signer.authentication.k8s.io/v1alpha1",
  "kind":"SignResponse",
  "signature":"(SIGNATURE BASE64 ENCODED)"
}

k8s.io/client-go authenticates against the Kubernetes API using the signed certificate returned in the signature.

Test Plan

This enhancement includes unit and integration tests:

  • Unit tests in pkg/client/auth/externalsigner to:

    • test that the external singer authentication provider APIs follow the format defined in the specification,
    • test the internal mechanisms of the authentication provider, such as caching, and
    • test handling of certificates and signatures data (including (un)marshalling the messages and en/decoding values).
  • Integration tests in test/integration/auth/externalsigner_test.go to:

    • attempt an execution of a kubectl command with authentication using the external singer authentication provider.

Graduation Criteria

Alpha -> Beta Graduation

  • Gather feedback regarding the API from developers of external plugins

Upgrade / Downgrade Strategy

Version Skew Strategy

Implementation History

Drawbacks

Alternatives

External signer vs existing authenticators using TLS certificates

First of all, it should be noted that Kubernetes already offers authentication using client certificates, enabled by passing the --client-ca-file=SOMEFILE option to API server, or by using the client-go credential plugins (exec), which executes an arbitrary command that returns clientCertificateData and clientKeyData. However, both of these alternatives require kubectl to obtain a direct access to the client key data, what can be considered unacceptable in some regulated environments and therefore does not fullfil our requirements.

Monolithic vs modular architecture

An alternative approach to enable HSM support within kubectl would be to create a monolithic authentication provider, which implements the PKCS#11 protocol. The drawback of this approach is that the support of PKCS#11 by definition requires making C calls to the .so library, which makes CGO mandatory during the build process of the whole kubectl. Such a dependency has been considered unacceptable by the sig-auth community (see the Slack thread).

RPC vs exec

Similarly as in case of external credential providers, external plugin could be exposed as a network endpoint. Instead of executing a binary and passing request/response over arguments/stdout, client could open a network connection and send request/response over that.

The downsides of this approach compared to exec model are:

  • if external signer is remote, design for client authentication is required (aka "chicken-and-egg problem"),
  • external signer must constantly run, consuming resources; clients refresh their credentials infrequently.

Independent external plugin configuration vs passing configuration parameters from kubectl/client-go

The external plugin may require configuration with some protocol specific parameters, for example the path to a library implementing the communication with an HMS, an identifier of HMS unit, etc. This communication could be stored and loaded outside of the kubectl/client-go process, directly by the external provider. However, that would require adding additional logic to the external provider, which is already available within kubectl/client-go, as well as, would spread configuration of authentication process into multiple files.

Stdin vs environment variables vs program arguments

The authentication provider within the kubectl/client-go process sends to the external plugin two categories of data:

  • general configuration of the external plugin (as described above), and
  • parameters for the sign operation (digest and signer options).

These data could be passed in various ways, for example:

  • writing directly to the stdin of the external plugin,
  • as environment variables set by the authentication provider and read by the external plugin,
  • as program arguments set when spawning the extenal plugin process by the authentication provider.

We have decided to reserve stdin for the interactive usage of the external plugin (for example, entering a PIN code). Since some of the configuration parameters have a complex structure (maps) we have decided to marshal them to JSON format and pass as a single program argument, instead of creating multiple environment variables. Moreover, this approach fits well also the case of sign operation parameters, which are coming directly from within kubectl/client-go.

Infrastructure Needed (optional)