How To

Install and Use Cloudflare CFSSL on Linux and macOS

CFSSL is Cloudflare’s open-source PKI/TLS toolkit written in Go. It works as a certificate authority, certificate generator, and signing tool – all from the command line or through a built-in HTTP API. If you manage TLS certificates for Kubernetes clusters, internal services, or mutual TLS setups, CFSSL gives you a lightweight alternative to heavy CA solutions like OpenSSL’s CA mode or EJBCA.

Original content from computingforgeeks.com - post 61560

This guide covers installing CFSSL v1.6.5 on Linux and macOS, creating a Certificate Authority, generating server and client certificates, configuring certificate profiles, running CFSSL as an API server, and using it with Kubernetes components like etcd and the API server.

Prerequisites

Before starting, make sure you have the following ready:

  • A Linux system (Ubuntu, Debian, RHEL, Rocky Linux, AlmaLinux) or macOS
  • Root or sudo access on the target machine
  • curl or wget installed for downloading binaries
  • Go 1.22+ if you plan to install from source (optional)
  • openssl for certificate verification

Step 1: Install CFSSL on Linux and macOS

There are three ways to install CFSSL – pre-built binaries (fastest), Go install (if you already have Go), or Homebrew on macOS. Pick the method that fits your setup.

Method 1: Download Pre-built Binaries (Linux and macOS)

This is the recommended approach for most users. Download the latest v1.6.5 binaries directly from the CFSSL GitHub releases page.

For Linux (amd64), run these commands to download cfssl and cfssljson:

CFSSL_VERSION="1.6.5"
curl -sLo cfssl "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl_${CFSSL_VERSION}_linux_amd64"
curl -sLo cfssljson "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssljson_${CFSSL_VERSION}_linux_amd64"
curl -sLo cfssl-certinfo "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl-certinfo_${CFSSL_VERSION}_linux_amd64"
chmod +x cfssl cfssljson cfssl-certinfo
sudo mv cfssl cfssljson cfssl-certinfo /usr/local/bin/

For macOS (Intel), swap out the architecture:

CFSSL_VERSION="1.6.5"
curl -sLo cfssl "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl_${CFSSL_VERSION}_darwin_amd64"
curl -sLo cfssljson "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssljson_${CFSSL_VERSION}_darwin_amd64"
curl -sLo cfssl-certinfo "https://github.com/cloudflare/cfssl/releases/download/v${CFSSL_VERSION}/cfssl-certinfo_${CFSSL_VERSION}_darwin_amd64"
chmod +x cfssl cfssljson cfssl-certinfo
sudo mv cfssl cfssljson cfssl-certinfo /usr/local/bin/

For Apple Silicon Macs, use the darwin_arm64 binary for cfssl. Note that cfssljson and cfssl-certinfo do not currently have arm64 builds – the amd64 versions work fine under Rosetta 2.

Method 2: Install with Go

If you have Go 1.22 or newer installed, you can build from source:

go install github.com/cloudflare/cfssl/cmd/cfssl@latest
go install github.com/cloudflare/cfssl/cmd/cfssljson@latest
go install github.com/cloudflare/cfssl/cmd/cfssl-certinfo@latest

The binaries land in $GOPATH/bin (usually ~/go/bin). Make sure that directory is in your PATH.

Method 3: Install with Homebrew (macOS)

On macOS with Homebrew, a single command installs everything:

brew install cfssl

After installing through any method, verify the installation works:

cfssl version

The output confirms the installed version and build details:

Version: 1.6.5
Runtime: go1.22.0

Step 2: Create a Certificate Authority (CA)

Every PKI setup starts with a Certificate Authority. The CA signs all other certificates, and clients trust any certificate the CA has signed. Create a directory to hold your CA files:

mkdir -p ~/cfssl-pki && cd ~/cfssl-pki

Create a CA Certificate Signing Request (CSR) configuration file. This defines the CA identity:

cat > ca-csr.json

Add the following JSON content to the file:

{
  "CN": "My Organization CA",
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US",
      "ST": "California",
      "L": "San Francisco",
      "O": "My Organization",
      "OU": "Infrastructure"
    }
  ],
  "ca": {
    "expiry": "87600h"
  }
}

The ca.expiry field sets the CA certificate lifetime to 10 years (87600 hours). ECDSA P-256 is used here for faster operations and smaller certificates compared to RSA. You can use "algo": "rsa", "size": 2048 if you need broader compatibility.

Generate the CA certificate and private key:

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

This produces three files in the current directory:

  • ca.pem – the CA certificate (distribute to all clients and servers)
  • ca-key.pem – the CA private key (keep this secret and secure)
  • ca.csr – the Certificate Signing Request (can be discarded)

Verify the CA certificate was generated correctly:

openssl x509 -in ca.pem -text -noout | head -15

You should see the CA subject fields matching your CSR configuration, with CA:TRUE in the basic constraints:

Certificate:
    Data:
        Version: 3 (0x2)
        Issuer: C = US, ST = California, L = San Francisco, O = My Organization, OU = Infrastructure, CN = My Organization CA
        Subject: C = US, ST = California, L = San Francisco, O = My Organization, OU = Infrastructure, CN = My Organization CA
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE

Lock down the CA private key permissions immediately:

chmod 600 ca-key.pem

Step 3: Generate Server Certificates

Server certificates authenticate your services to clients. Each service (web server, API endpoint, database) gets its own certificate signed by the CA you just created.

Create a server CSR configuration file:

cat > server-csr.json

Add the server identity details. The hosts field defines which domain names and IPs this certificate is valid for:

{
  "CN": "server.example.com",
  "hosts": [
    "server.example.com",
    "*.example.com",
    "10.0.1.10",
    "127.0.0.1",
    "localhost"
  ],
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US",
      "ST": "California",
      "L": "San Francisco",
      "O": "My Organization",
      "OU": "Infrastructure"
    }
  ]
}

Generate the server certificate signed by your CA:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem server-csr.json | cfssljson -bare server

This creates server.pem (certificate), server-key.pem (private key), and server.csr. Verify the Subject Alternative Names (SANs) are correct:

openssl x509 -in server.pem -text -noout | grep -A1 "Subject Alternative Name"

The output should list all hostnames and IPs you specified in the hosts field:

            X509v3 Subject Alternative Name:
                DNS:server.example.com, DNS:*.example.com, IP Address:10.0.1.10, IP Address:127.0.0.1, DNS:localhost

Step 4: Generate Client Certificates

Client certificates are used for mutual TLS (mTLS) where both the server and client authenticate each other. This is common in microservices, database connections, and internal API communication.

Create a client CSR configuration:

cat > client-csr.json

Add the client identity. Notice no hosts field is needed for client certificates – clients are identified by their CN and organization, not by hostname:

{
  "CN": "client-app-01",
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US",
      "ST": "California",
      "L": "San Francisco",
      "O": "My Organization",
      "OU": "Client Applications"
    }
  ]
}

Generate the client certificate:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem client-csr.json | cfssljson -bare client

Verify the client certificate is signed by your CA:

openssl verify -CAfile ca.pem client.pem

A successful verification returns:

client.pem: OK

Step 5: Create Certificate Profiles

Certificate profiles let you define different signing policies for different use cases – server certificates, client certificates, and peer certificates all have different key usage requirements. Create a CA configuration file with multiple profiles:

cat > ca-config.json

Add the following profile definitions:

{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "server": {
        "usages": [
          "signing",
          "digital signature",
          "key encipherment",
          "server auth"
        ],
        "expiry": "8760h"
      },
      "client": {
        "usages": [
          "signing",
          "digital signature",
          "key encipherment",
          "client auth"
        ],
        "expiry": "8760h"
      },
      "peer": {
        "usages": [
          "signing",
          "digital signature",
          "key encipherment",
          "server auth",
          "client auth"
        ],
        "expiry": "8760h"
      }
    }
  }
}

Each profile controls what the signed certificate can do:

  • server – includes server auth for TLS server authentication. Use for web servers, API endpoints, databases
  • client – includes client auth for client-side mTLS authentication. Use for service-to-service communication
  • peer – includes both server auth and client auth. Use for cluster members that both serve and connect to each other (etcd nodes, Consul agents)

Now use a specific profile when signing certificates:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server-csr.json | cfssljson -bare server

The -profile=server flag applies the server profile’s key usage and expiry settings to the generated certificate.

Step 6: Run CFSSL as an API Server

CFSSL includes a built-in HTTP API server that lets you request and sign certificates over the network. This is useful for automated certificate provisioning in CI/CD pipelines or service meshes.

Start the CFSSL API server with your CA credentials and config:

cfssl serve \
  -address=0.0.0.0 \
  -port=8888 \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json

The server listens on port 8888. In production, bind to a specific interface IP instead of 0.0.0.0 and restrict access with firewall rules. Open the port if needed:

sudo firewall-cmd --add-port=8888/tcp --permanent
sudo firewall-cmd --reload

On Ubuntu/Debian with ufw:

sudo ufw allow 8888/tcp

Test the API by checking its health endpoint:

curl -s http://localhost:8888/api/v1/cfssl/health

A healthy server returns:

{"success":true,"result":{"healthy":true},"errors":[],"messages":[]}

Request a new certificate through the API by sending a CSR as JSON:

curl -s -X POST http://localhost:8888/api/v1/cfssl/newcert \
  -H "Content-Type: application/json" \
  -d '{
    "request": {
      "CN": "api.example.com",
      "hosts": ["api.example.com", "10.0.1.20"],
      "key": {"algo": "ecdsa", "size": 256},
      "names": [{"O": "My Organization"}]
    },
    "profile": "server"
  }' | python3 -m json.tool

The API returns a JSON response containing the certificate, private key, and CSR. Pipe it through cfssljson to extract the files.

To run the API server as a systemd service for persistent operation, create a service unit file:

sudo vi /etc/systemd/system/cfssl-api.service

Add the following service configuration (adjust paths to match your CA file locations):

[Unit]
Description=CFSSL Certificate Authority API Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/cfssl serve \
  -address=127.0.0.1 \
  -port=8888 \
  -ca=/etc/cfssl/ca.pem \
  -ca-key=/etc/cfssl/ca-key.pem \
  -config=/etc/cfssl/ca-config.json
Restart=on-failure
User=cfssl
Group=cfssl

[Install]
WantedBy=multi-user.target

Create the service user and enable the service:

sudo useradd -r -s /usr/sbin/nologin cfssl
sudo mkdir -p /etc/cfssl
sudo cp ca.pem ca-key.pem ca-config.json /etc/cfssl/
sudo chown -R cfssl:cfssl /etc/cfssl
sudo chmod 600 /etc/cfssl/ca-key.pem
sudo systemctl daemon-reload
sudo systemctl enable --now cfssl-api.service

Confirm the service is running:

sudo systemctl status cfssl-api.service

Step 7: Use cfssljson to Extract Certificates

The cfssljson tool takes CFSSL’s JSON output and splits it into separate PEM files. Every CFSSL command that generates certificates outputs JSON to stdout – cfssljson handles the extraction.

The basic pattern pipes cfssl output directly to cfssljson:

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

The -bare flag controls the output file naming. With -bare ca, cfssljson produces ca.pem, ca-key.pem, and ca.csr. Without -bare, it adds a -cert suffix instead.

You can also save the JSON output first and process it later. This is useful when you request certificates from the API server:

curl -s http://localhost:8888/api/v1/cfssl/newcert \
  -d '{"request":{"CN":"test.example.com","hosts":["test.example.com"],"key":{"algo":"ecdsa","size":256}}}' \
  > cert-response.json
cfssljson -f cert-response.json -bare test

The -f flag reads from a file instead of stdin. This produces test.pem, test-key.pem, and test.csr from the saved JSON.

Step 8: Use CFSSL with Kubernetes

CFSSL is widely used to generate TLS certificates for Kubernetes clusters – particularly for etcd and the Kubernetes API server. Both components require valid TLS certificates for encrypted communication.

Generate etcd Certificates

Etcd cluster nodes need peer certificates (for node-to-node communication) and server certificates (for client-to-server communication). Create an etcd CSR that includes all cluster member IPs:

cat > etcd-csr.json

Add the etcd node details. List every etcd node IP and hostname in the hosts field:

{
  "CN": "etcd",
  "hosts": [
    "10.0.1.11",
    "10.0.1.12",
    "10.0.1.13",
    "etcd-01",
    "etcd-02",
    "etcd-03",
    "127.0.0.1",
    "localhost"
  ],
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US",
      "ST": "California",
      "O": "Kubernetes",
      "OU": "etcd"
    }
  ]
}

Generate the etcd peer certificates using the peer profile:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer etcd-csr.json | cfssljson -bare etcd

Copy the certificate files to each etcd node:

for node in 10.0.1.11 10.0.1.12 10.0.1.13; do
  scp ca.pem etcd.pem etcd-key.pem root@${node}:/etc/etcd/pki/
done

In the etcd configuration, reference these certificates:

--cert-file=/etc/etcd/pki/etcd.pem
--key-file=/etc/etcd/pki/etcd-key.pem
--peer-cert-file=/etc/etcd/pki/etcd.pem
--peer-key-file=/etc/etcd/pki/etcd-key.pem
--trusted-ca-file=/etc/etcd/pki/ca.pem
--peer-trusted-ca-file=/etc/etcd/pki/ca.pem

Generate Kubernetes API Server Certificates

The Kubernetes API server needs a certificate that covers its service IP, hostname, and all node IPs that clients might use to connect. Create the API server CSR:

cat > kube-apiserver-csr.json

Include the Kubernetes service IP (first IP in the service CIDR), the API server hostname, and any load balancer addresses:

{
  "CN": "kube-apiserver",
  "hosts": [
    "10.96.0.1",
    "10.0.1.10",
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster.local",
    "127.0.0.1",
    "localhost"
  ],
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "US",
      "ST": "California",
      "O": "Kubernetes",
      "OU": "API Server"
    }
  ]
}

Generate the API server certificate with the server profile:

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server kube-apiserver-csr.json | cfssljson -bare kube-apiserver

These certificates go into the API server’s --tls-cert-file and --tls-private-key-file flags, or into the kubeadm certificate directory at /etc/kubernetes/pki/.

Step 9: Verify Certificates with OpenSSL

Always verify your certificates after generation. OpenSSL provides several commands to inspect and validate certificate details.

Check the full certificate details including issuer, validity period, and extensions:

openssl x509 -in server.pem -text -noout

Verify a certificate was signed by your CA:

openssl verify -CAfile ca.pem server.pem

A valid certificate returns:

server.pem: OK

Check when a certificate expires:

openssl x509 -in server.pem -noout -dates

The output shows the validity window:

notBefore=Mar 22 10:00:00 2026 GMT
notAfter=Mar 22 10:00:00 2027 GMT

Verify the Subject Alternative Names match your intended hostnames:

openssl x509 -in server.pem -noout -ext subjectAltName

Use cfssl-certinfo for a more detailed, JSON-formatted view of any certificate:

cfssl-certinfo -cert server.pem

This returns the full certificate data as JSON, which is useful for scripting and automation.

CFSSL Tools Reference

CFSSL ships as a suite of tools, each handling a specific part of the certificate lifecycle. The table below summarizes every binary included in the v1.6.5 release:

ToolPurpose
cfsslCore tool – generates keys, CSRs, certificates, and runs the API server
cfssljsonExtracts PEM certificates and keys from CFSSL’s JSON output
cfssl-certinfoDisplays certificate details in JSON format from PEM files or running servers
cfssl-bundleCreates optimal certificate bundles by finding the shortest chain to a trusted root
cfssl-newkeyGenerates a new key and CSR without contacting a CA
cfssl-scanScans a TLS server and reports certificate chain and configuration details
mkbundleBuilds certificate pool bundles from individual certificate files
multirootcaCertificate authority server that handles multiple signing keys for multi-tenant setups

Conclusion

CFSSL provides a clean, scriptable approach to managing TLS certificates across your infrastructure. From a single CA on your workstation to an API-driven certificate server for automated provisioning, it handles the full certificate lifecycle without the complexity of heavier PKI solutions.

For production use, store your CA private key on an encrypted volume or hardware security module, set up certificate expiry monitoring with Prometheus alerting, and automate certificate renewal before expiry hits. If you run a Kubernetes cluster with RKE2, integrate CFSSL into your cluster bootstrapping workflow for consistent certificate management across all components.

Related Articles

Security Set Default Login Shell on SSSD for AD users using FreeIPA Proxmox How To Secure Proxmox VE Server With Let’s Encrypt SSL Networking Install Metasploit Framework on Arch | Manjaro | Garuda Linux Security Configure Centralized SSH for Linux systems using ShellHub

Leave a Comment

Press ESC to close