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.
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 authfor TLS server authentication. Use for web servers, API endpoints, databases - client – includes
client authfor client-side mTLS authentication. Use for service-to-service communication - peer – includes both
server authandclient 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:
| Tool | Purpose |
|---|---|
cfssl | Core tool – generates keys, CSRs, certificates, and runs the API server |
cfssljson | Extracts PEM certificates and keys from CFSSL’s JSON output |
cfssl-certinfo | Displays certificate details in JSON format from PEM files or running servers |
cfssl-bundle | Creates optimal certificate bundles by finding the shortest chain to a trusted root |
cfssl-newkey | Generates a new key and CSR without contacting a CA |
cfssl-scan | Scans a TLS server and reports certificate chain and configuration details |
mkbundle | Builds certificate pool bundles from individual certificate files |
multirootca | Certificate 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.