Skip to main content
Skip table of contents

Tutorial - Automate EJBCA RA Deployment with Helm and ConfigDump

ENTERPRISE

In this tutorial, we will configure EJBCA as a Registration Authority (RA) instance connected to an EJBCA Certificate Authority (CA) instance in Kubernetes. We will demonstrate how to automate this deployment using the EJBCA Enterprise container, ConfigDump, and Helm charts.

Automating the deployment is particularly useful when consistent configuration is required, such as in the following scenarios:

  • Managing a large number of peers.

  • Deploying a PKI across multiple regions.

  • Maintaining multiple environments, such as test and production.

For more information on ConfigDump, see the section What is ConfigDump? in Segmenting the PKI using EJBCA Peer.

The tutorial covers these steps: 

  • Set up the environment and configure the Helm chart

  • Configure the CA-side peer connection

  • Configure the RA-side peer connection

  • Deploy the RA and access EJBCA

Prerequisites

For this tutorial, EJBCA Enterprise container version 9.2.0 was used.

This tutorial is only valid for the EJBCA Enterprise container.

Before you begin, you need:

Part 1 – Set up the Environment

The following sections cover how to prepare the environment by creating a Kubernetes namespace and secret, and setting up the database.

Step 1 – Prepare the Environment

Create a fresh namespace

In your terminal, run the following command to create a namespace:

SH
kubectl create namespace radeployment
Create image pull secret

Create your image pull secret to download the EJBCA container directly from the Keyfactor repository:

SH
kubectl create secret docker-registry keyfactor-registry \
--docker-server=keyfactor.jfrog.io \
--docker-username=<email> \
--docker-password=<credential> \
--docker-email=<email> \
--namespace radeployment

Step 2 - Set up Database

Install a database in Kubernetes for convenience. EJBCA will be connected to a supported database i.e. MariaDB, PostgreSQL, Oracle or MSSQL in a production environment. In this tutorial, having a database will allow you to simulate a similar setting.

The contents of the database will be persisted using Kubernetes volumes. This allows you to shut down your local MicroK8s cluster without losing data, so the tutorial does not need to be completed in a single session.

This example uses MariaDB as database which is heavily used with EJBCA in production environments.

You need to do the following:

  • Prepare the database credentials as Kubernetes secrets for both the database and EJBCA.

  • Deploy the database with a Helm chart and wait for the database to be up and running.

Prepare the database credential

Set up the database credentials for EJBCA and the database itself.

For MariaDB:

SH
kubectl create secret generic maria-db-credentials \
      --from-literal=mariadb-root-password="rootpass" \
      --from-literal=mariadb-password="normalpass" \
      -n radeployment

For EJBCA:

SH
kubectl create secret generic ra-db-credentials -n radeployment \
    --from-literal=DATABASE_USER=ejbcauser \
    --from-literal=DATABASE_PASSWORD=normalpass
Database deployment

Deploy the database:

SH
helm install -n radeployment mariadb \
      --set auth.database="ejbca" \
      --set auth.username="ejbcauser" \
      --set auth.existingSecret="maria-db-credentials" \
      --set primary.persistence.size="100Mi" \
      --set secondary.replicaCount=0 \
      oci://registry-1.docker.io/bitnamicharts/mariadb

Step 3 – Configure Helm Chart in values.yaml

To check the default values.yaml for the latest version of the Helm chart:

SH
helm show values oci://repo.keyfactor.com/charts/ejbca > ejbca-default-values.yaml

helm show values oci://repo.keyfactor.com/charts/ejbca --version <version>

For the initial deployment, configure the following components:

  • Configure EJBCA to:

    • Import the ConfigDump which requires configuration for all relevant EJBCA resources using the ejbca.configdumpImport section.

    • Connect EJBCA to the deployed database.

    • Use the EJBCA RA container image image.variant: ra.

  • Use NGINX as a sidecar to act as a proxy for EJBCA:

    • Enable LoadBalancer service to allow access to EJBCA from outside Kubernetes.

    • Configure the server TLS certificate as a Kubernetes secret in nginx.secretInternalNginxCert This tutorial shows how to issue this certificate via the REST API.

    • You can also use this service for internal cluster operations.

  • Additionally, you may:

    • Impose resource reservations and limits via the Helm chart.

    • Configure autoscaling through the values.yaml file. This is useful for CA and VA instances.

Example values.yaml:

YAML
image:
  variant: ra
imagePullSecrets: [{ name: keyfactor-registry }]
ejbca:
  configdumpImport: ####
    enabled: true
    configMapName: ejbca-ra-init-configmap
    configMapKey: configdump.json
  env:
    LOG_LEVEL_APP: DEBUG
    DATABASE_JDBC_URL: "jdbc:mariadb://mariadb:3306/ejbca?characterEncoding=utf8"
  envFrom:
    - secretRef:
   name: ra-db-credentials
################################################################
nginx:
  enabled: true
  host: "ejbcara.testdomain.se"
  mountInternalNginxCert: true
  secretInternalNginxCert: "ejbcara-testdomain-se-secret"
  service:
    type: LoadBalancer

Part 2 - Configure the CA Side Peer Connection

Step 4 – Prepare Server Certificate

Before configuring the peer connection, the peer server certificate must be issued and stored as a Kubernetes secret.

To enroll a server certificate for the peer, you can use the REST API and the provided script enroll_server_certificate.sh.

Example:

SH
# ./enroll_server_certificate.sh <CA IP Address or Domain> <Domain Name for K8s external access> 
# <Domain Name for K8s internal access> <SuperAdmin token file> <password>

./enroll_server_certificate.sh 192.168.122.59 ejbcara.testdomain.se ejbca-ra-nginx.radeployment SuperAdmin.p12 foo123

Note:

  • <Domain Name for K8s external access> is also configured in values.yaml at nginx.host attribute

  • <Domain Name for K8s internal access> is of the form {helm deployment name}-nginx.{namespace of peer deployment}

Next, create a Kubernetes secret which will be configured in values.yaml to provide the TLS credential to EJBCA.

Note that the file names are expected in the following format:

  • {domain-name}.pem (certificate)

  • {domain-name}-Key.pem (private key)

  • {domain-name}-CA.pem (CA certificate)

SH
kubectl create secret generic ejbcara-testdomain-se-secret -n radeployment \
 --from-file=ejbcara.testdomain.se.pem=ejbcara.testdomain.se.pem \
 --from-file=ejbcara.testdomain.se-Key.pem=ejbcara.testdomain.se.key \
 --from-file=ejbcara.testdomain.se-CA.pem=ManagementCA.cacert.pem

Note that the name of the secret is mentioned in secretInternalNginxCert in the nginx block in values.yaml:

YAML
nginx:
  enabled: true
  host: "ejbcara.testdomain.se"
  mountInternalNginxCert: true
  secretInternalNginxCert: "ejbcara-testdomain-se-secret"
  service:
    type: LoadBalancer

Now that you have a database for the RA deployed, the server TLS credentials for your RA, and a fully configured values.yaml for your RA referring to the TLS credential and the deployed database, you can continue to the next step to configure your CA for your peer connection.

Step 5 – Configure Peer in CA

This section shows how to configure the CA instance to find the Peer, that is, the RA. For the Peer connection to function correctly, the resources highlighted in green must be configured on the CA.

peer_config_in_ca.png

These configurations initiate an mTLS connection to the RA and properly authenticate and authorize the RA(s). This configuration can be performed either before or after deploying the RA.

For more information about peer configurations in EJBCA deployments, see Segmenting the PKI using EJBCA Peer.

Below is the configuration used in this example (add_ra_in_ca_configdump.json), which also serves as a comprehensive example:

JSON
{
  "peer-connectors": {
    "ejbca-ra1": {
      "Object Type": "Peer Connector",
      "Version": 1,
      "Name": "ejbca-ra1",
      "Peer Enabled": true,
      "URL": "<https://ejbca-ra-nginx.radeployment/ejbca/peer/v1>",
      "Long Hanging Connections Enabled": true,
      "Min Long Hanging Connections": 10,
      "Max Long Hanging Connections": 20,
      "Authentication Key Binding": "peer-key"
    }
  },
  "internal-key-bindings": {
    "peer-key": {
      "Object Type": "Internal Key Binding",
      "Version": 1,
      "Name": "peer-key",
      "Type": "AuthenticationKeyBinding",
      "Status": "ACTIVE",
      "Crypto Token": "PeerToken",
      "Key Pair Alias": "peerkey",
      "Next Key Pair Alias": null,
      "Signature Algorithm": "SHA256WithRSA",
      "Enrollment info: Key Binding SubjectDN": "CN=peer-key",
      "Enrollment info: Signing CA SubjectDN": "CN=ManagementCA,O=EJBCA Sample,C=SE",
      "Enrollment info: Certificate profile": "ENDUSER",
      "Enrollment info: End entity profile": "EMPTY",
      "Enrollment info: Key spec or curve": "RSA2048",
      "Trusted certificates": {},
      "Signed on behalf of CAs": {},
      "Properties": {
   "Protocol and Cipher Suite": "TLSv1.2;TLS_RSA_WITH_AES_256_CBC_SHA256"
      }
    }
  },
  "admin-roles": {
    "RA-Peer-Connection": {
      "Object Type": "Role",
      "Version": 1,
      "Name": "RA-Peer-Connection",
      "Role Members": [
   {
     "Token Type": "CertificateAuthenticationToken",
     "Issuer": "ManagementCA",
     "Match With": "WITH_COMMONNAME",
     "Match Value": "ejbcara.testdomain.se"
   }
      ],
      "Namespace": "",
      "RA Style Id": 0,
      "Access Rules": {
   "/administrator/": "Allow",
   "/ca/": "Allow",
   "/ca_functionality/create_certificate/": "Allow",
   "/ca_functionality/use_approval_request_id/": "Allow",
   "/ca_functionality/use_username/": "Allow",
   "/ca_functionality/view_ca/": "Allow",
   "/ca_functionality/view_certificate/": "Allow",
   "/endentityprofilesrules/": "Allow",
   "/endentityprofilesrules/EMPTY/": "Allow",
   "/protocol/acme/": "Allow",
   "/protocol/cmp/": "Allow",
   "/protocol/est/": "Allow",
   "/protocol/rest/": "Allow",
   "/protocol/scep/": "Allow",
   "/protocol/web_services/": "Allow",
   "/ra_functionality/approve_end_entity/": "Allow",
   "/ra_functionality/create_end_entity/": "Allow",
   "/ra_functionality/delete_end_entity/": "Allow",
   "/ra_functionality/edit_end_entity/": "Allow",
   "/ra_functionality/revoke_end_entity/": "Allow",
   "/ra_functionality/view_approvals/": "Allow",
   "/ra_functionality/view_end_entity/": "Allow",
   "/ra_functionality/view_end_entity_history/": "Allow",
   "/ra_functionality/view_end_entity_profiles/": "Allow",
   "/ra_master/invoke_api/": "Allow"
      }
    }
  }
}

Note the following:

  • A "peer-connectors" section that describes the peer instance, including its URL. The URL should reference the ClusterIP access point, which typically follows the format: {helm-deployment-name}-nginx.{namespace}.

  • Authentication and authorization rules in the "admin-roles" section.

    • The Common Name used in the RA’s server TLS certificate must match the DNS Name defined in the NGINX block of the values.yaml file.

  • An "internal-key-bindings" section for the Remote Authenticator. The Remote Authenticator connects to the RA and creates an mTLS authenticated connection pool. The following arguments are used to enroll a certificate:

    • A key pair named peerkey in the crypto token PeerToken

    • With the subject DN "CN=peer-key" issued by ManagementCA which is identified by Signing CA SubjectDN

    • The ENDUSER Certificate profile and EMPTY End entity profile are used.

JSON
"Crypto Token": "PeerToken",
"Key Pair Alias": "peerkey",
"Enrollment info: Key Binding SubjectDN": "CN=peer-key",
"Enrollment info: Signing CA SubjectDN": "CN=ManagementCA,O=EJBCA Sample,C=SE",
"Enrollment info: Certificate profile": "ENDUSER",
"Enrollment info: End entity profile": "EMPTY"

To add additional peers, replicate the "peer-connectors" section with a new name and URL (such as, ejbca-ra2). The remaining configuration can remain the same.

You can also send a separate REST API request at a later stage to create additional peers as needed.

Next, do the following:

  • Create a Crypto Token in the CA named PeerToken of type SOFT. For more information about creating crypto tokens, see Managing Crypto Tokens.
    (info) If you are using a software-backed crypto token while creating the PeerToken, ConfigDump will automatically create the key pair. If not, you must create the key pair in advance. This example uses the key pair named peerkey.

  • Send a ConfigDump REST request to the CA using the provided script configdump-rest.sh:

SH
# <CA IP Address or Domain> <JSON file with configdump> <SuperAdmin token file> <password>
./configdump-rest.sh <CA IP Address or Domain> add_ra_in_ca_configdump.json SuperAdmin.p12 foo123

Ensure that you use initialize=true when importing the ConfigDump. Without this parameter, EJBCA will not generate the Remote Authentication certificate.

The script will create and/or configure all necessary resources. To verify, navigate to the EJBCA Admin user interface and verify that a peer connection is created on the /ejbca/adminweb/peerconnector/peerconnectors.xhtml page.

capeer.png

Part 3 - Configure the RA Side Peer Connection

Step 6 – Configure Peer in RA

This section shows how to prepare configdump with the necessary configurations which RA will import during the installation. For the Peer connection to work, the RA resources highlighted in green must be configured.

peer_config_in_peer.png

For more information about peer configurations in EJBCA deployments, see Segmenting the PKI using EJBCA Peer.

Notice that:

  • A "peer-connectors"."global-peer-configuration" to allow incoming connection.

  • Authentication and authorization rules in "admin-roles" section to:

    • Authorize the certificate issued to Remote Authenticator in the CA.

    • Use the same SuperAdmin token for the CA to access the RA.

  • A "certification-authorities" to import the CAs present in the CA instance:

    • Import the ManagementCA, that is, the CA issuing the Admin(SuperAdmin) token.

    • Import the CAs that will be issuing certificates, in this example, named IssuerCA.

  • An "available-protocols" section to enable protocols such as ACME, CMP, and a subset of the REST API.

The same ConfigDump content can be reused to configure multiple RAs of the same type.

You need to prepare the "certification-authorities" section. However, you can automate this process with scripts or take advantage of EJBCA features introduced in version 9.3.

As of EJBCA 9.3, use the query parameter exportCasForPeerImport in the ConfiDump REST API to automatically retrieve the "certification-authorities" content.

SH
# <CA IP Address or Domain> <SuperAdmin token file> <password>.
/configdump-rest-get.sh 192.168.122.59 SuperAdmin.p12 foo123
  • Remove the "CA Token" section for each CA.

  • Download and format the Certificate Chain for each CA.

  • Set the certificate as a single line in "Certificate Chain" attribute as shown in the following example.

Configuration example:

JSON
{
  "certification-authorities": {
   "ManagementCA": {
     "Object Type": "Certification Authority",
     "Version": 2,
     "Name": "ManagementCA",
     "Type of CA": "X.509",
     "Description": "CA created by certificate import.",
     "Serial Number Octet Size": 20,
     "Pre-produce OCSP Responses": false,
     "Microsoft CA Compatible Mode Used": false,
     "Store responses on-demand": false,
     "Pre-produce OCSP Responses Upon certificate issuance/revocation": false,
     "Certificate Profile": "ROOTCA",
     "Default Certificate Profile": "Not used",
     "Use Append-Only Table": false,
     "Certificate Chain": [
       "MIID......truncated........b8ubneo="
     ],
     "Enforce Unique Public Keys": true,
     "Enforce key renewal": false,
     "Enforce Unique DN": true,
     "User Storage": true,
     "Certificate Storage": true,
     "Accept Revocations for Non-Existing Entries": false,
     "Subject DN": "CN=ManagementCA,O=EJBCA Sample,C=SE",
     "Signed By": "Self Signed",
     "Validity": "0d",
     "Subject Alternative Name": null,
     "Use UTF-8 in Policy Notice Text": true,
     "LDAP DN Order": true,
     "Authority Key Id Used": true,
     "CRL Number Used": true,
     "Partitioned CRL Used": false,
     "CRL Expiration Period": "1d",
     "CRL Issue Interval": "0m",
     "CRL Overlap Time": "10m",
     "Delta CRL Period": "0m",
     "Generate CRL Upon Revocation": false,
     "Allow Changing Revocation Reason": false,
     "Finish User": true,
     "CA Healthcheck Enabled": true,
     "Request Processor": null
   },
   "IssuerCA": {
     "Object Type": "Certification Authority",
     "Version": 2,
     "Name": "IssuerCA",
     "Type of CA": "X.509",
     "Description": "CA created by certificate import.",
     "Serial Number Octet Size": 20,
     "Pre-produce OCSP Responses": false,
     "Microsoft CA Compatible Mode Used": false,
     "Store responses on-demand": false,
     "Pre-produce OCSP Responses Upon certificate issuance/revocation": false,
     "Certificate Profile": "ROOTCA",
     "Default Certificate Profile": "Not used",
     "Use Append-Only Table": false,
     "Certificate Chain": [
       "MIIDFzCCAf+g......truncated........KPCEvEre"
     ],
     "Enforce Unique Public Keys": true,
     "Enforce key renewal": false,
     "Enforce Unique DN": true,
     "User Storage": true,
     "Certificate Storage": true,
     "Accept Revocations for Non-Existing Entries": false,
     "Subject DN": "CN=IssuerCA",
     "Signed By": "Self Signed",
     "Validity": "0d",
     "Subject Alternative Name": null,
     "Use UTF-8 in Policy Notice Text": true,
     "LDAP DN Order": true,
     "Authority Key Id Used": true,
     "CRL Number Used": true,
     "Partitioned CRL Used": false,
     "CRL Expiration Period": "1d",
     "CRL Issue Interval": "0m",
     "CRL Overlap Time": "10m",
     "Delta CRL Period": "0m",
     "Generate CRL Upon Revocation": false,
     "Allow Changing Revocation Reason": false,
     "Finish User": true,
     "CA Healthcheck Enabled": true,
     "Request Processor": null
   }
  },
  "available-protocols": {
   "available-protocol-configuration": {
     "Object Type": "Available Protocols",
     "Version": 1,
     "Name": "available-protocol-configuration",
     "ACME": true,
     "Certstore": true,
     "CMP": true,
     "CRLstore": true,
     "EST": true,
     "MSAE": true,
     "OCSP": false,
     "SCEP": true,
     "RA Web": true,
     "REST CA Management": false,
     "REST Certificate Management": false,
     "REST Coap Management": false,
     "REST Crypto Token Management": false,
     "REST End Entity Management": true,
     "REST End Entity Management V2": true,
     "REST Configdump": true,
     "REST Certificate Management V2": true,
     "REST SSH V1": true,
     "REST System V1": false,
     "Webdist": true,
     "Web Service": true,
     "ITS Certificate Management": false,
     "Custom header name for REST calls from browser": "X-Keyfactor-Requested-With"
   }
  },
  "peer-connectors": {
   "global-peer-configuration": {
     "Object Type": "Peer Global Configuration",
     "Version": 1,
     "Allow incoming connections": true,
     "Allow outgoing connections": false,
     "Max wait by caller (ms)": 25000,
     "Max wait by peer (ms)": 15000,
     "Max age for requests (ms)": 16000
   }
  },
  "admin-roles": {
   "Super Administrator Role": {
     "Object Type": "Role",
     "Version": 1,
     "Name": "Super Administrator Role",
     "Role Members": [
       {
         "Token Type": "CertificateAuthenticationToken",
         "Issuer": "ManagementCA",
         "Match With": "WITH_COMMONNAME",
         "Match Value": "SuperAdmin"
       },
       {
         "Token Type": "CliAuthenticationToken",
         "Issuer": null,
         "Match With": "USERNAME",
         "Match Value": "ejbca"
       }
     ],
     "Namespace": "",
     "RA Style Id": 0,
     "Access Rules": {
       "/": "Allow"
     }
   },
   "raIncomingCaPeer": {
     "Object Type": "Role",
     "Version": 1,
     "Name": "raIncomingCaPeer",
     "Role Members": [
       {
         "Token Type": "CertificateAuthenticationToken",
         "Issuer": "ManagementCA",
         "Match With": "WITH_COMMONNAME",
         "Match Value": "peer-key"
       }
     ],
     "Namespace": "",
     "RA Style Id": 0,
     "Access Rules": {
       "/ca/": "Allow",
       "/peerincoming/": "Allow",
       "/ra_slave/manage/": "Allow"
     }
   }
 }
}

Part 4 - Finalize RA Deployment

Step 7 – Deploy the RA

Before deploying the RA, you need to upload the ConfigDump JSON just created to Kubernetes. To do this, create a ConfigMap in Kubernetes:

BASH
kubectl apply -f ejbca-ra-init-configmap.yaml -n radeployment 

Ensure that the name of the ConfigMap matches the value of configMapName under the ejbca.configdumpImport section in your values.yaml file:

CODE
ejbca:
  configdumpImport: ####
    enabled: true
    configMapName: ejbca-ra-init-configmap
    configMapKey: configdump.json

This allows the EJBCA RA instance to read and import the required resources.

Deploy RA instance

To deploy the RA by using the Helm chart:

  1. Copy the contents of the values.yaml mentioned above to a file named ra_deployment_values.yaml, or download and use the provided ra_deployment_values.yaml.

  2. Run the following command to deploy the EJBCA RA instance:

    CODE
    helm install ejbca-ra -n radeployment -f ra_deployment_values.yaml \
                    oci://repo.keyfactor.com/charts/ejbca
  3. To verify that EJBCA is operational, run:

    CODE
    kubectl get all,pvc,cm,secrets -n radeployment
  4. The output will be similar to the following:

radeployed.png

Note the following expected behavior:

  • You have only one pod of MariaDB and EJBCA RA. Currently, each RA instance requires a separate Helm installation. All RAs connect to a single shared database instance, that is, there is no need for database replication.

  • The EJBCA RA pod contains two containers:

    • ejbca: the application itself.

    • nginx

  • Services:

    • mariadb: internal service used by EJBCA to access the database.

    • ejbca-ra-nginx: a LoadBalancer service for accessing EJBCA both from within and outside the Kubernetes cluster.

  • There is a service mariadb which is used by EJBCA to talk to the database.

  • The service ejbca-ra-nginx is used to connect to EJBCA both inside and outside the cluster as it is a LoadBalancer service. The EXTERNAL_IP of this service will be used in a later step to access EJBCA from your browser.

Wait until pod/ejbca-ra-0 shows status READY 2/2.

Access EJBCA RA

Access EJBCA by navigating to:

CODE
https://<IP Address of ejbca-ra-nginx>

When prompted for certificate authentication, select the same SuperAdmin certificate.

You should be automatically redirected to the EJBCA RA Web, displaying Logged in as SuperAdmin at the top.

raraweb.png

If the error RA is not connected to any CA is displayed, go to https://<IP Address of ejbca-ra-nginx>/ejbca/adminweb/peerconnector/peerconnectors.xhtml. Check if the CA instance is listed as peer:

  • If listed, wait 1–2 minutes and try accessing the RA web interface again.

  • If not listed, review the earlier configuration steps to ensure proper peer setup.

Now you can search for already enrolled certificates by clicking Search>Certificates.

Test the setup

To enroll a certificate to verify the setup, do the following:

  1. Select Enroll > Make New Request and specify the following:

    • For Certificate Type, select EMPTY or any listed End Entity Profile.

    • For Certificate subtype, select ENDUSER or any listed Certificate Profile.

    • For CA (if prompted), select IssuerCA or ManagementCA.

    • For Key-pair generation, select By the CA.

    • For Key algorithm, select for example, RSA-2048, or P-256.

    • For the Required Subject DN Attributes, specify CN, Common Name and enter a unique value.

    • For Provide User Credentials, enter a username and password. The user name should be unique in EJBCA.

  2. Click Download PEM to download the certificate in PEM format.

Now you have verified that your RA is working properly.

Next steps

In this tutorial, you learned how to automate the deployment of peer connections between Certificate Authorities (CAs) and Registration Authorities (RAs) in EJBCA Enterprise using ConfigDump, Helm charts, and Kubernetes-native automation tools.

Here are some next steps we recommend:

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.