Skip to main content
Skip table of contents

Tutorial - Integrate EJBCA with SPIFFE SPIRE Server

This tutorial teaches you how to run SPIFFE SPIRE configured to use the EJBCA UpstreamAuthority Plugin. The EJBCA UpstreamAuthority plugin enables SPIRE to issue workload identities as part of a trusted PKI using EJBCA.

You should follow this tutorial if you want to:

  • Use the SPIRE implementation of the SPIFFE framework to provide workload identity.

  • Issue workload identity certificates as part of an EJBCA PKI.

In this tutorial, you will:

  • Prepare EJBCA

  • Compile SPIRE Server and SPIRE Agent

  • Configure SPIRE Server using the EJBCA UpstreamAuthority plugin

  • Start SPIRE Server

  • Attest a SPIRE Agent to the Server using a join token

  • Fetch an x509-SVID from SPIRE Agent over the SPIFFE Workload API

image-20241101-192157.png

Prerequisites

EJBCA Community Docker container version 8.3.2 was used in this tutorial.

This tutorial is valid for both the EJBCA EJBCA Community and Enterprise containers. For more information on the EJBCA Enterprise container, see EJBCA & SignServer Enterprise Container documentation.

Before you begin, you need a running EJBCA container with:

  • A Client Certificate and corresponding Private Key with permissions to use the EJBCA REST API.

  • Access to the EJBCA Admin Web UI.

  • At least one configured Certificate Authority.

You can follow the EJBCA - Getting started with Kubernetes guide to deploy EJBCA in Kubernetes on your computer.

Additionally, you need:

  • A 64-bit Linux or macOS environment

  • The openssl command line tool

  • Go 1.13 or higher to build SPIRE. See https://golang.org/dl/ or run brew install golang.

  • The Make command line tool

Step 1 - Prepare EJBCA

Follow these steps to prepare and configure EJBCA.

Enable EJBCA Certificate Management REST API

To prepare EJBCA, you must first enable the EJBCA REST Certificate Management.

  1. In the EJBCA Admin UI, under System Configuration, click System Configuration.

  2. Click on Protocol Configuration.

  3. Click Enable next to REST Certificate Management. You should see ✅ Enabled.

Create a Certificate Profile

Next, you will create a Certificate Profile capable of issuing intermediate certificates for SPIRE Server. SPIRE Server uses an intermediate (CA) certificate to sign workload identity certificates.

  1. In the EJBCA Admin UI, under CA Functions, click Certificate Profiles.

  2. In the text entry at the bottom of the List of Certificate Profiles table, type spireIntermediateCA and click Add.

  3. Click Edit next to the spireIntermediateCA entry in the table.

  4. Configure the following fields:

    • Type: Sub CA

    • Available Key Algorithms: ECDSA

    • Available ECDSA curves: P-256 / prime256v1

    • Signature Algorithm: SHA256WithRSA

    • Validity or end date of the certificate: 2d

    • Available CAs: Your Issuing CA

  5. Click Save.

Create an End Entity Profile

Next, you will create an End Entity Profile that defines the properties and constraints of the SPIRE intermediate certificates being issued.

  1. In the EJBCA Admin UI, under RA Functions, click End Entity Profiles.

  2. Under Add End Entity Profile, enter spireIntermediateCA, and click Add.

  3. Click spireIntermediateCA in the List of End Entity Profiles table, and click Edit.

  4. Configure the following fields:

    • Subject DN Attributes

      • serialNumber, Serial number (in DN) [modifiable]

      • O, Organization [modifiable]

      • C, Country (ISO 3166) [modifiable]

    • Other Subject Attributes

      • Uniform Resource Identifier (URI) [modifiable]

    • Available Certificate Profiles: Ensure spireIntermediateCA is selected.

    • Available CAs: Your Issuing CA.

  5. Click Save.

Step 2 - Build SPIRE

To build SPIRE, clone the source code and use the build Makefile target.

  1. Clone SPIRE locally to your machine by running the following commands:

    BASH
    git clone --single-branch --branch v1.11.0 https://github.com/spiffe/spire.git
    cd spire
  2. Build SPIRE Server and Agent by running the following command:

    BASH
    make build

Step 3 - Configure SPIRE with the EJBCA UpstreamAuthority plugin

To configure SPIRE server to use the EJBCA UpstreamAuthority plugin, you will locate your client certificate and key used to authenticate with EJBCA and configure the Server conf file with the values you prepared in Step 1 - Prepare EJBCA.

  1. Locate your EJBCA client certificate and private key, and export their paths as environment variables.

    BASH
    export EJBCA_CLIENT_CERT_PATH=/path/to/superadmin.pem
    export EJBCA_CLIENT_CERT_KEY_PATH=/path/to/superadmin.key
  2. Locate your EJBCA server CA certificate and export its path as an environment variable. This is the certificate that you used to secure the Ingress route to EJBCA.

    BASH
    export EJBCA_CA_CERT_PATH=/path/to/ingress-ca-cert.pem
  3. Create the SPIRE Server configuration file.

    HCL
    cat <<EOF > server.conf
    server {
      bind_address = "127.0.0.1"
      bind_port    = "8081"
      trust_domain = "example.org"
      data_dir     = "./.data"
      log_level    = "DEBUG"
    }
    
    plugins {
      DataStore "sql" {
        plugin_data {
          database_type     = "sqlite3"
          connection_string = "./.data/datastore.sqlite3"
        }
      }
    
      NodeAttestor "join_token" {
        plugin_data {
        }
      }
    
      KeyManager "memory" {
        plugin_data = {}
      }
    
      UpstreamAuthority "ejbca" {
        plugin_data {
          hostname                 = "localhost"
          ca_cert_path             = "$EJBCA_CA_CERT_PATH"
          client_cert_path         = "$EJBCA_CLIENT_CERT_PATH"
          client_key_path          = "$EJBCA_CLIENT_CERT_KEY_PATH"
          ca_name                  = "Sub-CA"
          end_entity_profile_name  = "spireIntermediateCA"
          certificate_profile_name = "spireIntermediateCA"
          end_entity_name          = ""
          account_binding_id       = ""
        }
      }
    }
    EOF

Next, configure the SPIRE Agent to trust the CA that will issue intermediate CA certificates used to sign workload identity certificates.

  1. In the EJBCA RA UI, click CA Certificates and CRLs.

  2. Download the PEM certificate chain for the CA that you will be using to issue Intermediate CAs for SPIRE.

  3. Move this certificate to a known location and export its path as an environment variable.

    CODE
    export EJBCA_ISSUING_CA_CERT_PATH=/path/to/issuing-cacert.pem
  4. Create the SPIRE Agent configuration file.

    CODE
    cat <<EOF > agent.conf
    agent {
      data_dir          = "./.data"
      log_level         = "DEBUG"
      server_address    = "127.0.0.1"
      server_port       = "8081"
      socket_path       = "/tmp/spire-agent/public/api.sock"
      trust_bundle_path = "$EJBCA_ISSUING_CA_CERT_PATH"
      trust_domain      = "example.org"
    }
    
    plugins {
      NodeAttestor "join_token" {
        plugin_data {
        }
      }
      KeyManager "disk" {
        plugin_data {
          directory = "./.data"
        }
      }
      WorkloadAttestor "unix" {
        plugin_data {
        }
      }
    }
    EOF

Step 4 - Start SPIRE Server and generate a token to attest a SPIRE Agent

To start SPIRE Server, run the compiled binary as a background process. Then, generate a join token that the SPIRE Agent will use to attest (authenticate) with the server. Join tokens are one-time-use.

  1. Start the SPIRE Server as a background process.

    CODE
    bin/spire-server run -config server.conf &
    BASH
    INFO[0000] X509 CA prepared                              expiration="2034-10-30 12:49:48 -0700 MST" issued_at="2024-11-01 14:06:57.580516 -0700 MST" local_authority_id=a2ff2082452a002e88907c923821c7df02c88c36 self_signed=false slot=A subsystem_name=ca_manager upstream_authority_id=74a171872a501e46fecf2db6e1d640545f315025
    INFO[0000] X509 CA activated                             expiration="2034-10-30 12:49:48 -0700 MST" issued_at="2024-11-01 14:06:57.580516 -0700 MST" local_authority_id=a2ff2082452a002e88907c923821c7df02c88c36 slot=A subsystem_name=ca_manager upstream_authority_id=74a171872a501e46fecf2db6e1d640545f315025
    INFO[0000] Creating a new CA journal entry               subsystem_name=ca_manager
    DEBU[0000] Successfully stored CA journal entry in datastore  ca_journal_id=1 local_authority_id=a2ff2082452a002e88907c923821c7df02c88c36 subsystem_name=ca_manager
    DEBU[0000] Successfully rotated X.509 CA                 subsystem_name=ca_manager trust_domain_id="spiffe://example.org" ttl=3.15355370192041e+08
    ...
    INFO[0000] Starting Server APIs                          address="127.0.0.1:8081" network=tcp subsystem_name=endpoints
    INFO[0000] Starting Server APIs                          address=/tmp/spire-server/private/api.sock network=unix subsystem_name=endpoints

    Verify that SPIRE Server is running:

    CODE
    bin/spire-server healthcheck
    CODE
    Server is healthy.
  2. Generate a join token for the SPIRE Agent, and export it as an environment variable.

    CODE
    export TOKEN=$(exec bin/spire-server token generate -spiffeID spiffe://example.org/host | awk '{print $2}')
    echo $TOKEN
    CODE
    fce80504-500e-4b3c-bc2b-409302c440aa

Step 5 - Start a SPIRE Agent

To start a SPIRE Agent, run the compiled binary as a background process using the one-time-use join token created earlier.

  1. Start SPIRE Agent as a background process using the join token.

    CODE
    bin/spire-agent run -config agent.conf -joinToken "$TOKEN" &
    CODE
    INFO[0000] Starting agent with data directory: "./.data"
    ...
    INFO[0000] Creating X509-SVID                            entry_id=2863dd03-050e-4d7a-bf32-b6c1e45558d9 spiffe_id="spiffe://example.org/host" subsystem_name=manager
    DEBU[0502] Signed X509 SVID                              authorized_as=agent authorized_via=datastore caller_addr="127.0.0.1:62205" caller_id="spiffe://example.org/spire/agent/join_token/fce80504-500e-4b3c-bc2b-409302c440aa" entry_id=2863dd03-050e-4d7a-bf32-b6c1e45558d9 expiration="2024-11-01T22:15:19Z" method=BatchNewX509SVID request_id=60b9bcf5-8705-4ee2-a6c5-76c923476a6a revision_number=0 serial_num=86991583435265654855032218279933190658 service=svid.v1.SVID spiffe_id="spiffe://example.org/host" subsystem_name=api
    DEBU[0000] SVID updated                                  entry=2863dd03-050e-4d7a-bf32-b6c1e45558d9 spiffe_id="spiffe://example.org/host" subsystem_name=cache_manager

Step 6 - Create a registration entry

For SPIRE to identify a workload, the workload needs to be registered with the SPIRE Server. For this tutorial, we create a registration entry based on the current Unix UID. Refer to the SPIRE docs for a complete list of workload attention methods.

  1. Create a registration entry based on the current Unix UID.

    CODE
    bin/spire-server entry create -parentID spiffe://example.org/host \
        -spiffeID spiffe://example.org/myservice -selector unix:uid:$(id -u)
    CODE
    Entry ID         : 66aa8f5b-b152-4acc-a748-7f152b37156f
    SPIFFE ID        : spiffe://example.org/myservice
    Parent ID        : spiffe://example.org/host
    Revision         : 0
    X509-SVID TTL    : default
    JWT-SVID TTL     : default
    Selector         : unix:uid:502

Step 7 - Fetch and view an x509 SVID

Fetch an x509 SVID from the SPIRE Agent. This process replicates how a workload would fetch an identity from the SPIRE Agent to, for example, authenticate the workload to another workload.

  1. Fetch an x509 SVID from the SPIRE Agent.

    CODE
    bin/spire-agent api fetch x509 -write .
    CODE
    Received 1 svid after 254.109375ms
    
    SPIFFE ID:              spiffe://example.org/myservice
    SVID Valid After:       2024-11-01 21:22:57 +0000 UTC
    SVID Valid Until:       2024-11-01 22:23:07 +0000 UTC
    Intermediate #1 Valid After:    2024-11-01 20:56:57 +0000 UTC
    Intermediate #1 Valid Until:    2034-10-30 19:49:48 +0000 UTC
    Intermediate #2 Valid After:    2024-11-01 19:50:32 +0000 UTC
    Intermediate #2 Valid Until:    2034-10-30 19:49:48 +0000 UTC
    CA #1 Valid After:      2024-11-01 19:49:49 +0000 UTC
    CA #1 Valid Until:      2034-10-30 19:49:48 +0000 UTC
    
    Writing SVID #0 to file svid.0.pem.
    Writing key #0 to file svid.0.key.
    Writing bundle #0 to file bundle.0.pem.
  2. Use the openssl command to view the contents of the SVID:

    CODE
    openssl x509 -in svid.0.pem -text -noout
    CODE
    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                fb:47:9f:4c:dc:ba:64:cd:8f:e3:4f:f5:5b:b5:2e:0c
            Signature Algorithm: ecdsa-with-SHA256
            Issuer: serialNumber=166101901659418739104792428097135422316, O=SPIFFE, C=US
            Validity
                Not Before: Nov  1 21:22:57 2024 GMT
                Not After : Nov  1 22:23:07 2024 GMT
            Subject: C=US, O=SPIRE
            Subject Public Key Info:
    ...

Cleanup

To clean up the tutorial, delete the SPIRE directory and exit the terminal session to kill the Server and Agent processes running in the background.

  1. Remove the spire directory.

    CODE
    cd ..
    rm -rf spire
  2. Terminate the shell.

    CODE
    exit 0

Next steps

In this tutorial, you learned how to configure SPIRE Server with the EJBCA UpstreamAuthority plugin.

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.