Deploy EJBCA as CA with automation with SoftHSM2
ENTERPRISE
This outlines how to deploy a Certificate Authority (CA) using the EJBCA Enterprise configuration export/import tool EJBCA ConfigDump and a SoftHSM2 sidecar.
Prerequisites
Before you begin, you should be familiar with how to deploy EJBCA CA in Kubernetes.
EJBCA installation with ConfigDump import does not support replication during the installation stage. Instead, use helm install
with ConfigDump and replicaCount: 1
. After installation, perform a helm upgrade
to the desired replicaCount
.
Prepare database and other credentials
Prepare a database for EJBCA in Kubernetes or externally
Create secrets containing the username and password
Configure these essential values in values.yaml
The database URL
The database credential secrets
Ingress URL
Create slots and keys in HSM
Create slots in the HSM before installing or deploying EJBCA. The EJBCA container will load the slots as configured from ConfigDump and load the keys into those slots.
This example uses a SoftHSM2, which is intended for non-production use, and slots and keys will be created automatically when running the Helm install command. To persist key materials across application restarts, utilize a Kubernetes PersistentVolumeClaim
(PVC).
To create a PVC named softhsm-pvc
, run:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: softhsm-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 250Mi
EOF
Prepare configuration dump
ConfigDump objects used in the following examples were created using the EJBCA ConfigDump REST Interface. The provided samples will demonstrate how to initialize an EJBCA instance with:
Crypto Tokens: Two Hardware Security Module (HSM) backed crypto tokens with authentication codes provided using Kubernetes secrets.
Certification Authorities (CAs): Two CAs created using keys from the HSM and supplied subjectDN, validity, etc.
End Entity Profile: An End Entity Profile for issuing server certificates with up to four DNS Names.
Roles and Role Members: Roles used for certificate authentication with CN (common name) SuperAdmin and AutomationAdmin.
Protocols: Configuration that enables REST, ACME, and other protocols.
Separate confidential data and create Kubernetes secrets
Sensitive data in ConfigDump exports can be replaced with references to Kubernetes Secret keys.
For example, given a configuration dump snippet with an Authentication Code supperSecretPassword
:
"crypto-tokens": {
"ManagementCA": {
"Object Type": "Crypto Token",
"Authentication Code": "supperSecretPassword",
...
}
}
You can first create a secret to store the Authentication Code using an arbitrary key, for example, AUTHENTICATION_CODE
:
kubectl create secret generic configdump-secrets \
--from-literal=AUTHENTICATION_CODE=supperSecretPassword
Then, reference this key in an inline or mounted ConfigDump file when deploying EJBCA:
"crypto-tokens": {
"ManagementCA": {
"Object Type": "Crypto Token",
"Authentication Code": "${AUTHENTICATION_CODE}",
...
}
}
Subsequent sections of this guide will use a Kubernetes secret with the following content:
kubectl create secret generic configdump-secrets \
--from-literal=CA_TOKEN_PASSWORD=foo123 \
--from-literal=PEER_TOKEN_PASSWORD=bar123
Prepare configuration dump as a Kubernetes ConfigMap
Prepare a Kubernetes ConfigMap with your EJBCA configuration dump file.
Example filename: ejbca-ca-init-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ejbca-ca-init-configmap
data:
initialize-hsm-slots-softhsm2.sh: |
#!/bin/bash
cp --preserve --recursive /opt/keyfactor/p11proxy-client/* /mnt/driver/
slot_count=$(softhsm2-util --show-slots | grep -cE "Slot [0-9]+")
if [ "$slot_count" -gt "1" ]; then
exit 0
fi
softhsm2-util --init-token --free --label CA_SLOT --so-pin "$CA_TOKEN_PASSWORD" --pin "$CA_TOKEN_PASSWORD"
softhsm2-util --init-token --free --label IKB_SLOT --so-pin "$CA_TOKEN_PASSWORD" --pin "$PEER_TOKEN_PASSWORD"
after-deployed-pre.sh: |
#!/bin/bash
echo "web.reqcert=false" >> /opt/keyfactor/ejbca/conf/web.properties
common_args="--password $CA_TOKEN_PASSWORD --lib-file /opt/keyfactor/p11proxy-client/p11proxy-client.so --slot-ref SLOT_LABEL --slot CA_SLOT"
if p11ng-cli.sh listkeypairs $common_args | grep -q "signkey001"; then
return 0
fi
p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias signkey001
p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias defaultkey001 --key-usage SIGN_ENCRYPT
p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias signkey002
p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias defaultkey002 --key-usage SIGN_ENCRYPT
p11ng-cli.sh generatekeypair $common_args --key-spec RSA2048 --alias testkey
configdump.json: |
{
"crypto-tokens": {
"CaToken001": {
"Object Type": "Crypto Token",
"Version": 2,
"Name": "CaToken001",
"Used": true,
"PKCS11 Library": "/opt/keyfactor/p11proxy-client/p11proxy-client.so",
"PKCS11 Reference Type": "SLOT_LABEL",
"PKCS11 Reference": "CA_SLOT",
"PKCS11 Attribute File": "",
"Key Pair Info": [],
"Authentication Code": "${CA_TOKEN_PASSWORD}",
"Type": "Pkcs11NgCryptoToken",
"Active": true,
"Auto Activation": true
},
"PeerToken": {
"Object Type": "Crypto Token",
"Version": 2,
"Name": "PeerToken",
"Used": true,
"PKCS11 Library": "/opt/keyfactor/p11proxy-client/p11proxy-client.so",
"PKCS11 Reference Type": "SLOT_LABEL",
"PKCS11 Reference": "IKB_SLOT",
"PKCS11 Attribute File": "",
"Key Pair Info": [],
"Authentication Code": "${PEER_TOKEN_PASSWORD}",
"Type": "Pkcs11NgCryptoToken",
"Active": true,
"Auto Activation": true
}
},
"certification-authorities": {
"ManagementCA": {
"Object Type": "Certification Authority",
"Version": 2,
"Name": "ManagementCA",
"Type of CA": "X.509",
"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,
"CA Token": {
"Signature Algorithm": "SHA256WithRSA",
"Encryption Algorithm": "SHA256WithRSA",
"Crypto Token": "CaToken001",
"Default Key": "defaultkey001",
"Certificate Signing Key": "signkey001",
"CRL Signing Key": "signkey001",
"Key Encryption Key": "defaultkey001",
"Test Key": "testkey",
"Key Sequence Format": "Numeric",
"Key Sequence": "00000"
},
"Enforce Unique Public Keys": false,
"Enforce key renewal": false,
"Enforce Unique DN": false,
"User Storage": true,
"Certificate Storage": true,
"Accept Revocations for Non-Existing Entries": false,
"Subject DN": "CN=ManagementCA,OU=SomeOrg",
"Signed By": "Self Signed",
"Validity": "20y",
"Subject Alternative Name": "None",
"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
},
"RootCA": {
"Object Type": "Certification Authority",
"Version": 2,
"Name": "RootCA",
"Type of CA": "X.509",
"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,
"CA Token": {
"Signature Algorithm": "SHA256WithRSA",
"Encryption Algorithm": "SHA256WithRSA",
"Crypto Token": "CaToken001",
"Default Key": "defaultkey002",
"Certificate Signing Key": "signkey002",
"CRL Signing Key": "signkey002",
"Key Encryption Key": "defaultkey002",
"Test Key": "testkey",
"Key Sequence Format": "Numeric",
"Key Sequence": "00000"
},
"Enforce Unique Public Keys": false,
"Enforce key renewal": false,
"Enforce Unique DN": false,
"User Storage": true,
"Certificate Storage": true,
"Accept Revocations for Non-Existing Entries": false,
"Subject DN": "CN=RootCA,C=SE",
"Signed By": "Self Signed",
"Validity": "20y",
"Subject Alternative Name": "None",
"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
}
},
"end-entity-profiles": {
"eep001": {
"Object Type": "End Entity Profile",
"Version": 1,
"Name": "ServerEndEntityProfile",
"Description": "",
"Default CA": "ManagementCA",
"Available CAs": ["ManagementCA", "RootCA"],
"Default Certificate Profile": "SERVER",
"Available Certificate Profiles": ["SERVER"],
"Default Token Type": "PEM File",
"Available Token Types": [
"User Generated",
"PKCS12 File",
"BCFKS File",
"JKS File",
"PEM File"
],
"Subject DN": {
"CN": [
{
"Required": true
}
]
},
"Subject Alternative Name": {
"DNSNAME": [
{
"Required": false
},
{
"Required": false
},
{
"Required": false
},
{
"Required": false
}
]
}
}
},
"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": true,
"SCEP": true,
"RA Web": true,
"REST CA Management": true,
"REST Certificate Management": true,
"REST Coap Management": true,
"REST Crypto Token Management": true,
"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": true,
"Webdist": true,
"Web Service": true,
"ITS Certificate Management": false,
"Custom header name for REST calls from browser": "X-Keyfactor-Requested-With"
}
},
"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": "AutomationAdmin"
},
{
"Token Type": "CertificateAuthenticationToken",
"Issuer": "ManagementCA",
"Match With": "WITH_COMMONNAME",
"Match Value": "SuperAdmin"
},
{
"Token Type": "CliAuthenticationToken",
"Issuer": null,
"Match With": "USERNAME",
"Match Value": "ejbca"
},
{
"Token Type": "PublicAccessAuthenticationToken",
"Issuer": null,
"Match With": "TRANSPORT_CONFIDENTIAL",
"Match Value": ""
}
],
"Namespace": "",
"RA Style Id": 0,
"Access Rules": {
"/": "Allow"
}
}
}
}
Create the ConfigMap:
kubectl apply -f ejbca-ca-init-configmap.yaml
Configure Deployment
The following shows an example Helm chart values file modified to deploy the EJBCA CA utilizing a SoftHSM sidecar.
Example filename: ejbca-ca-values.yaml
ejbca:
env:
DATABASE_JDBC_URL: "jdbc:mariadb://mariadb:3306/ejbca?characterEncoding=utf8"
DATABASE_USER: ejbca
LOG_AUDIT_TO_DB: true
envRaw:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: mariadb-passwords
key: mariadb-password
envFrom:
- secretRef:
name: configdump-secrets
configdumpImport:
enabled: true
initialize: true
configMapName: ejbca-ca-init-configmap
configMapKey: configdump.json
# More convenient way to integrate with HSM will be available soon
# Extra init containers to be added to the deployment
initContainers:
- name: hsm-driver-init
image: registry.primekey.com/primekey/hsm-driver-softhsm:1.0.9
command:
["sh", "-c", "/opt/keyfactor/init/initialize-hsm-slots-softhsm2.sh"]
volumeMounts:
- name: p11proxy-client
mountPath: /mnt/driver/
- name: hsm-slot-init
mountPath: /opt/keyfactor/init/initialize-hsm-slots-softhsm2.sh
subPath: initialize-hsm-slots-softhsm2.sh
- name: tokens
mountPath: /mnt/tokens
env:
- name: SOFTHSM2_CREATE_TOKENS
value: "false"
envFrom:
- secretRef:
name: configdump-secrets
# Extra sidecar containers to be added to the deployment
sidecarContainers:
- name: hsm
image: registry.primekey.com/primekey/hsm-driver-softhsm:1.0.9
imagePullPolicy: IfNotPresent
env:
- name: SOFTHSM2_CREATE_TOKENS
value: "false"
- name: SOFTHSM2_LOG_LEVEL
value: INFO
volumeMounts:
- name: tokens
mountPath: /mnt/tokens
# Extra volumes to be added to the deployment
volumes:
- name: p11proxy-client
emptyDir: {}
- name: tokens
persistentVolumeClaim:
claimName: softhsm-pvc
- name: after-deployed-pre
configMap:
name: ejbca-ca-init-configmap
defaultMode: 0777
items:
- key: "after-deployed-pre.sh"
path: "after-deployed-pre.sh"
- name: hsm-slot-init
configMap:
name: ejbca-ca-init-configmap
defaultMode: 0777
items:
- key: "initialize-hsm-slots-softhsm2.sh"
path: "initialize-hsm-slots-softhsm2.sh"
# Extra volume mounts to be added to the deployment
volumeMounts:
- name: p11proxy-client
mountPath: /opt/keyfactor/p11proxy-client
- name: after-deployed-pre
mountPath: /opt/keyfactor/bin/internal/after-deployed-pre.sh
subPath: after-deployed-pre.sh
# needed to make softhsm volume mount to work
podSecurityContext:
fsGroup: 10001
ingress:
enabled: true
hosts:
- host: "ejbca.example.com"
paths:
- path: /
pathType: Prefix
#imagePullSecrets:
# - name: keyfactor-registry
Install EJBCA
Deploy EJBCA to a Kubernetes cluster using the previously prepared Helm chart values file:
helm install ejbca-ca -f ejbca-ca-values.yaml \
oci://repo.keyfactor.com/charts/ejbca --version <version>
Secure EJBCA
Once EJBCA is deployed, enroll a Super Administrator certificate, create a TLS server certificate, and configure EJBCA to use them with client certificate authentication.
Issue SuperAdmin certificate
To issue a certificate for the SuperAdmin:
In EJBCA, click RA Web to access the EJBCA RA user interface.
Select Enroll > Make New Request and specify the following:
For Certificate Type, i applicable, select EMPTY.
For Certificate subtype, select ENDUSER.
For CA, select ManagementCA.
For Key-pair generation, select By the CA.
For Key algorithm, select for example, RSA-2048.
For the Required Subject DN Attributes, Common Name (CN), specify SuperAdmin.
For Provide User Credentials, enter a username and password.
Click Download PKCS#12 to download the certificate.
Your certificate is saved as a SuperAdmin.p12 file.
Configure TLS and client certificate authentication
Steps describing how to secure access to EJBCA using a TLS server certificate with client certificate authentication are outlined on the Deploy CA in Kubernetes page.
To configure EJBCA:
Access EJBCA Admin Web by navigating your browser to the host configured in
ejbca-ca-values.yaml
.On the CA Structure & CRLs page, click Download PEM file to download the
ManagementCA.cacert.pem
file.Proceed to the Deploy CA in Kubernetes page and follow the guide starting from section Enroll server TLS certificate.