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-examplecd ~/isolated-issuer-example
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.yamlkubectl apply -f https://platform.jetstack.io/documentation/installation/isolated-issuer/cert-manager/approve/v0.1.0/cert-manager-isolated-issuer-rbac.yaml
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: v1kind: Namespacemetadata:name: isolated-issuer-test---apiVersion: cert-manager.io/v1kind: Issuermetadata:name: isolated-issuer-selfsignednamespace: isolated-issuer-testspec:selfSigned: {}---apiVersion: cert-manager.io/v1kind: Certificatemetadata:name: isolated-issuer-root-certificatenamespace: isolated-issuer-testspec:isCA: truecommonName: isolated-issuer-rootsecretName: isolated-issuer-root-secretprivateKey:algorithm: ECDSAissuerRef:name: isolated-issuer-selfsignedkind: Issuergroup: cert-manager.io---apiVersion: cert-manager.io/v1kind: Issuermetadata:name: isolated-issuer-root-ca-issuernamespace: isolated-issuer-testspec:ca:secretName: isolated-issuer-root-secretEOF
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
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
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 ./kubeconfigchmod +r ./kubeconfig
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.yamlbootstrap:remote:# first, we need to configure our CSR for our intermediatecsr:duration: 48hcommonName: isolated-issuer-getting-startedprivateKey:algorithm: ECDSAsize: 256subject: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 certcert-manager:issuerName: isolated-issuer-root-ca-issuerissuerKind: Issuernamespace: isolated-issuer-testsigner:# we use the in-memory signer, which is the simplest and requires no further configurationinMemory: truecontroller:# finally, we configure our controller. note that the groupName must match the# one which was defined in the CRD and RBAC rolecert-manager:groupName: "isolated-issuer.jetstack.io"caFile: /work/root.pemEOF
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
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
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/v1kind: Certificatemetadata:name: my-first-isolated-certnamespace: isolated-issuer-testspec:isCA: falsecommonName: my-first-isolated-certprivateKey:algorithm: ECDSAduration: 1hsecretName: my-first-isolated-cert-secretissuerRef:name: "anythingatall"group: "isolated-issuer.jetstack.io"dnsNames:- foo.example.com- bar.example.comEOF
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
The output should be similar to the following:
Certificate:Data:Version: 3 (0x2)Serial Number: ...Signature Algorithm: ecdsa-with-SHA256Issuer: O = example.com, CN = isolated-issuer-getting-startedValidityNot Before: ...Not After : ...Subject: CN = my-first-isolated-certSubject Public Key Info:Public Key Algorithm: id-ecPublicKey...X509v3 extensions:...X509v3 Subject Alternative Name:DNS:foo.example.com, DNS:bar.example.comSignature Algorithm: ecdsa-with-SHA256
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.