Important Announcement!

This deprecated version of TLS Protect for Kubernetes, originally known as Jetstack Secure, will be PERMANENTLY SHUTDOWN on May 19, 2025. If you're still using this version, please work with your CyberArk/Venafi account team to transition to the current version of TLS Protect for Kubernetes.

Installing isolated-issuer

Getting Started With isolated-issuer

isolated-issuer is intended to be simple to run and use, and setup should be equally easy.

This document describes everything you need to do to set up isolated-issuer to sign cert-manager CertificateRequests.

Step 0: Assumptions

We assume you've already got a Kubernetes cluster up and running with cert-manager installed, and a valid kubeconfig file which works for connecting to the cluster.

In short: If you can run kubectl get certificates.cert-manager.io locally, then isolated-issuer should work on your machine; by default isolated-issuer looks for kubeconfig in the same place kubectl does, and this is configurable through the --kubeconfig flag, which we'll use in this example.

If you want to test on a throwaway cluster, you can create a kind cluster locally by following the Developing with Kind guide on the cert-manager website.

Step 1: Get isolated-issuer

Currently, isolated-issuer is available as a container image with an optional FIPS version.

šŸ“¢ If there's an alternative distribution method which you'd prefer, please get in touch!

Container Image

isolated-issuer is distributed as a Docker image which can be run on most cloud providers, on a local VM or on bare metal.

  • eu.gcr.io/jetstack-secure-enterprise/isolated-issuer - preferred version, available on amd64, arm64 and armv7

There are also FIPS compliant Docker images available at eu.gcr.io/jetstack-secure-enterprise/isolated-issuer-fips and these have the same version tags as the main Docker images.

Configure access to the enterprise registry

šŸ”‘ Follow the instructions in Access to enterprise components to enable access to the artifacts required for this component. Use jetstack-secure as the namespace.

Container Working Directory

For this example, we'll run all of our commands in a temporary directory and share that directory with the docker container running isolated-issuer.

We'd suggest creating this directory in your $HOME, by running the following commands:

mkdir ~/isolated-issuer-example
cd ~/isolated-issuer-example
Copy to clipboard

Step 2: Install the CRD + RBAC Role

Because isolated-issuer runs outside a Kubernetes cluster but interacts with resources inside the cluster, cert-manager needs to be made aware of the presence of isolated-issuer.

If you miss this step, you might see confusing error messages when trying to approve CertificateRequest resources in your cluster.

There's also an RBAC role which is required.

In total, you'll need to install:

  • a simple Custom Resource Definition (CRD)
  • an optional RBAC role to allow cert-manager's default approver to approve isolated-issuer referencing requests

You can install everything required by running the following commands:

kubectl apply -f https://platform.jetstack.io/documentation/installation/isolated-issuer/cert-manager/approve/v0.1.0/cert-manager-isolated-issuer-crd.yaml
kubectl apply -f https://platform.jetstack.io/documentation/installation/isolated-issuer/cert-manager/approve/v0.1.0/cert-manager-isolated-issuer-rbac.yaml
Copy to clipboard

There are a few lines in those manifests which you might want to change for a production deployment, but they should be usable as they are for this example.

As-is, the files will set the "group" for the isolated-issuer signer to isolated-issuer.jetstack.io. We'll assume that that group is set in the rest of this guide.

Step 2.1: Set up a Test Issuer for cert-manager

āš ļø This is just for the example; you can configure your bootstrap how you want in production. If you're not using a cert-manager bootstrap, you don't need to do this. āš ļø

We'll use a self-signed issuer to issue a root certificate, and then we'll create a CA issuer using that root.

The CA issuer will allow isolated-issuer to issue an intermediate certificate, which it will then use for signing. We'll save the public part of the root for isolated-issuer to use, too.

Again, this is just for the example.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: isolated-issuer-test
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: isolated-issuer-selfsigned
namespace: isolated-issuer-test
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: isolated-issuer-root-certificate
namespace: isolated-issuer-test
spec:
isCA: true
commonName: isolated-issuer-root
secretName: isolated-issuer-root-secret
privateKey:
algorithm: ECDSA
issuerRef:
name: isolated-issuer-selfsigned
kind: Issuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: isolated-issuer-root-ca-issuer
namespace: isolated-issuer-test
spec:
ca:
secretName: isolated-issuer-root-secret
EOF
Copy to clipboard

We should be sure to wait for the resources to be ready:

kubectl wait --for=condition=ready -n isolated-issuer-test issuers.cert-manager.io isolated-issuer-root-ca-issuer
# waiting for a line like this: issuer.cert-manager.io/isolated-issuer-root-ca-issuer condition met
Copy to clipboard

After the issuer is ready, we also need to grab the public part of the root CA for isolated-issuer to use, and store it in a file in our temporary directory:

kubectl get -n isolated-issuer-test secrets isolated-issuer-root-secret -ogo-template='{{index .data "tls.crt"}}' | base64 -d | tee root.pem
Copy to clipboard

Step 3: Ensure kubeconfig is Available to isolated-issuer

Since isolated-issuer is going to run inside a container, we'll need to make the kubeconfig for your Kubernetes cluster is available inside the container.

One option is to copy your current kubeconfig into the temporary directory that we're working in, which we'll later mount into the container:

cp ~/.kube/config ./kubeconfig
chmod +r ./kubeconfig
Copy to clipboard

If you want to get your kubeconfig into the container in a different way, bear in mind that this guide will assume that a working kubeconfig is available at /work/kubeconfig inside the container.

Step 4: Write isolated-issuer configuration

We'll use the cert-manager issuer we created earlier, and we'll store the intermediate CA entirely in-memory, meaning it'll be lost on a restart but that it'll be harder for it to end up being written to disk where it might be easier to steal.

In production environments your configuration will vary based on how you want to actually bootstrap your intermediate issuing CA and how you want to store that issuing CA. For this guide we'll keep it simple, but do take a look at the other configuration options later!

cat << EOF > isolated-config.yaml
bootstrap:
remote:
# first, we need to configure our CSR for our intermediate
csr:
duration: 48h
commonName: isolated-issuer-getting-started
privateKey:
algorithm: ECDSA
size: 256
subject:
organizations:
- example.com
# next, we need to specify the issuer we'll use to generate our intermediate
# this part will change depending on how you're going to bootstrap your cert
cert-manager:
issuerName: isolated-issuer-root-ca-issuer
issuerKind: Issuer
namespace: isolated-issuer-test
signer:
# we use the in-memory signer, which is the simplest and requires no further configuration
inMemory: true
controller:
# finally, we configure our controller. note that the groupName must match the
# one which was defined in the CRD and RBAC role
cert-manager:
groupName: "isolated-issuer.jetstack.io"
caFile: /work/root.pem
EOF
Copy to clipboard

We can verify that our configuration is correct using isolated-issuer itself:

docker run -it --rm --network=host -v `pwd`:/work eu.gcr.io/jetstack-secure-enterprise/isolated-issuer validate --config /work/isolated-config.yaml --kubeconfig /work/kubeconfig
Copy to clipboard

Which should produce output ending with a line saying that the configuration is valid".

Step 5: Run isolated-issuer and Sign Certificates

Our cluster is set up and our configuration is valid; we're ready to issue our first certificates.

First we'll run isolated issuer:

docker run -it --network=host --rm -v `pwd`:/work eu.gcr.io/jetstack-secure-enterprise/isolated-issuer run --disable-mlock --config /work/isolated-config.yaml --kubeconfig /work/kubeconfig
Copy to clipboard

There will be a bunch of log output here, but it should start up successfully and then seem to wait. That means isolated-issuer is watching for CertificateRequest resources which match it, so we'll ask cert-manager to create one for isolated-issuer to sign, via a Certificate.

In a different terminal to the one in which you ran isolated-issuer, run:

cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-first-isolated-cert
namespace: isolated-issuer-test
spec:
isCA: false
commonName: my-first-isolated-cert
privateKey:
algorithm: ECDSA
duration: 1h
secretName: my-first-isolated-cert-secret
issuerRef:
name: "anythingatall"
group: "isolated-issuer.jetstack.io"
dnsNames:
- foo.example.com
- bar.example.com
EOF
Copy to clipboard

And then shortly, we should be able to see our signed certificate, which we can inspect using openssl:

kubectl get -n isolated-issuer-test secrets my-first-isolated-cert-secret -ogo-template='{{index .data "tls.crt"}}' | base64 -d | openssl x509 -noout -text
Copy to clipboard

The output should be similar to the following:

Certificate:
Data:
Version: 3 (0x2)
Serial Number: ...
Signature Algorithm: ecdsa-with-SHA256
Issuer: O = example.com, CN = isolated-issuer-getting-started
Validity
Not Before: ...
Not After : ...
Subject: CN = my-first-isolated-cert
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
...
X509v3 extensions:
...
X509v3 Subject Alternative Name:
DNS:foo.example.com, DNS:bar.example.com
Signature Algorithm: ecdsa-with-SHA256
Copy to clipboard

That's it! We just issued a certificate using a CA stored outside the cluster, and that certificate is now available as a secure machine identity inside the cluster.

Next steps

On this page