Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce EJBCA UpstreamAuthority plugin for SPIRE Server #5378

Merged
merged 15 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions conf/server/server_full.conf
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,39 @@ plugins {
# }
# }

# UpstreamAuthority "ejbca": Uses a connected EJBCA to sign SPIRE server
# intermediate certificates
UpstreamAuthority "ejbca" {
plugin_data {
# The hostname of the connected EJBCA server.
hostname = "ejbca.example.com"
# (optional) The path to the CA certificate file used to validate the
# EJBCA server's certificate. Certificates must be in PEM format.
ca_cert_path = "/path/to/ca_cert.pem"
# The path to the client certificate (public key only) used
# to authenticate to EJBCA. Must be in PEM format.
client_cert_path = "/path/to/client_cert.pem"
# The path to the client key matching `client_cert` used to
# authenticate to EJBCA. Must be in PEM format.
client_cert_key_path = "/path/to/client_key.pem"
# The name of a CA in the connected EJBCA instance that will
# issue the intermediate signing certificates.
ca_name = "Fake-Sub-CA"
# The name of an end entity profile in the connected EJBCA
# instance that is configured to issue SPIFFE certificates.
end_entity_profile_name = "fakeSpireIntermediateCAEEP"
# The name of a certificate profile in the connected EJBCA instance
# that is configured to issue intermediate CA certificates.
certificate_profile_name = "fakeSubCACP"
# (optional) The name of the end entity, or configuration for how
# the EJBCA UpstreamAuthority should determine the end entity name.
end_entity_name = ""
# (optional) An account binding ID in EJBCA to associate with issued certificates.
account_binding_id = "abc123"
}
}


# BundlePublisher "aws_s3": A bundle publisher that puts the current trust
# bundle of the server in a designated Amazon S3 bucket, keeping it updated.
# BundlePublisher "aws_s3" {
Expand Down
82 changes: 82 additions & 0 deletions doc/plugin_server_upstreamauthority_ejbca.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Server plugin: UpstreamAuthority "ejbca"

The `ejbca` UpstreamAuthority plugin uses a connected [EJBCA](https://www.ejbca.org/) to issue intermediate signing certificates for the SPIRE server. The plugin authenticates to EJBCA using mTLS (client certificate).

> The EJBCA UpstreamAuthority plugin uses only the `/ejbca-rest-api/v1/certificate/pkcs10enroll` REST API endpoint, and is compatible with both [EJBCA Community](https://www.ejbca.org/) and [EJBCA Enterprise](https://www.keyfactor.com/products/ejbca-enterprise/).

## Requirements

* EJBCA [Community](https://www.ejbca.org/) or EJBCA [Enterprise](https://www.keyfactor.com/products/ejbca-enterprise/)
* The "REST Certificate Management" protocol must be enabled under System Configuration > Protocol Configuration.

> It's important that the EJBCA Certificate Profile and End Entity Profile are properly configured before using this plugin. The plugin does not attempt to configure these profiles. Please refer to the [EJBCA Sub CA End Entity Profile & Certificate Profile Configuration](#ejbca-sub-ca-end-entity-profile--certificate-profile-configuration) section for more information.

## Configuration

The EJBCA UpstreamAuthority Plugin accepts the following configuration options.

| Configuration | Description | Default from Environment Variables |
|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------|
| `hostname` | The hostname of the connected EJBCA server. | |
| `ca_cert_path` | (optional) The path to the CA certificate file used to validate the EJBCA server's certificate. Certificates must be in PEM format. | `EJBCA_CA_CERT_PATH` |
| `client_cert_path` | The path to the client certificate (public key only) used to authenticate to EJBCA. Must be in PEM format. | `EJBCA_CLIENT_CERT_PATH` |
| `client_cert_key_path` | The path to the client key matching `client_cert` used to authenticate to EJBCA. Must be in PEM format. | `EJBCA_CLIENT_CERT_KEY_PATH` |
| `ca_name` | The name of a CA in the connected EJBCA instance that will issue the intermediate signing certificates. | |
| `end_entity_profile_name` | The name of an end entity profile in the connected EJBCA instance that is configured to issue SPIFFE certificates. | |
| `certificate_profile_name` | The name of a certificate profile in the connected EJBCA instance that is configured to issue intermediate CA certificates. | |
| `end_entity_name` | (optional) The name of the end entity, or configuration for how the EJBCA UpstreamAuthority should determine the end entity name. See [End Entity Name Customization](#ejbca-end-entity-name-customization-leaf-certificates) for more info. | |
| `account_binding_id` | (optional) An account binding ID in EJBCA to associate with issued certificates. | |

> Configuration parameters that have an override from Environment Variables will always override the provided value from the SPIRE configuration with the values in the environment.
>
> If all configuration parameters for the selected auth method are specified by environment variables, an empty block still must exist to select the auth method.

```hcl
UpstreamAuthority "ejbca" {
m8rmclaren marked this conversation as resolved.
Show resolved Hide resolved
plugin_data {
hostname = "ejbca.example.com"
ca_cert_path = "/path/to/ca_cert.pem"
client_cert_path = "/path/to/client_cert.pem"
client_cert_key_path = "/path/to/client_key.pem"
ca_name = "Fake-Sub-CA"
end_entity_profile_name = "fakeSpireIntermediateCAEEP"
certificate_profile_name = "fakeSubCACP"
end_entity_name = "cn"
account_binding_id = "foo123"
}
}
```

## EJBCA Sub CA End Entity Profile & Certificate Profile Configuration

The connected EJBCA instance must have at least one Certificate Profile and at least one End Entity Profile capable of issuing SPIFFE certificates. The Certificate Profile must be of type `Sub CA`, and must be able to issue certificates with the ECDSA prime256v1 algorithm, at a minimum. The SPIRE Server configuration may require additional fields.

The End Entity Profile must have the following Subject DN Attributes:

* `serialNumber, Serial number (in DN)` [modifiable]
* `O, Organization` [modifiable]
* `C, Country (ISO 3166)` [modifiable]

And the following Other Subject Attributes:

* `Uniform Resource Identifier (URI)` [modifiable]

## EJBCA End Entity Name Customization (leaf certificates)

The EJBCA UpstreamAuthority plugin allows users to determine how the End Entity Name is selected at runtime. Here are the options you can use for `end_entity_name`:

* **`cn`:** Uses the Common Name from the CSR's Distinguished Name.
* **`dns`:** Uses the first DNS Name from the CSR's Subject Alternative Names (SANs).
* **`uri`:** Uses the first URI from the CSR's Subject Alternative Names (SANs).
* **`ip`:** Uses the first IP Address from the CSR's Subject Alternative Names (SANs).
* **Custom Value:** Any other string will be directly used as the End Entity Name.

By default, SPIRE issues certificates with no DN and only the SPIFFE ID in the SANs. If you want to use the SPIFFE ID as the End Entity Name, you can usually leave this field blank or set it to `uri`.

If the endEntityName field is not explicitly set, the EJBCA UpstreamAuthority plugin will attempt to determine the End Entity Name using the following default behavior:

* **First, it will try to use the Common Name:** It looks at the Common Name from the CSR's Distinguished Name.
* **If the Common Name is not available, it will use the first DNS Name:** It looks at the first DNS Name from the CSR's Subject Alternative Names (SANs).
* **If the DNS Name is not available, it will use the first URI:** It looks at the first URI from the CSR's Subject Alternative Names (SANs).
* **If the URI is not available, it will use the first IP Address:** It looks at the first IP Address from the CSR's Subject Alternative Names (SANs).
* **If none of the above are available, it will return an error.**
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.0
github.com/Keyfactor/ejbca-go-client-sdk v1.0.2
github.com/Microsoft/go-winio v0.6.2
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129
github.com/aws/aws-sdk-go-v2 v1.30.4
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dX
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.0 h1:gl5KGBBLKXc4BVKkyOJW9w6B890gUkoDkG/pYkTTQHE=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.37.0/go.mod h1:43xFPKNglOf/bHDR1DbcGTp4Erza2TiUJaU+X9L+1AI=
github.com/Keyfactor/ejbca-go-client-sdk v1.0.2 h1:pPnXCFfIFAwCjJrg1BtYlzoF8oHQ52sPOMs/uZ9uvZA=
github.com/Keyfactor/ejbca-go-client-sdk v1.0.2/go.mod h1:4Sv/KGVgRV4VXKko1ajfTaJwqJ5Aiw0VrDI9S7IcQ1g=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
Expand Down
2 changes: 2 additions & 0 deletions pkg/server/catalog/upstreamauthority.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/awssecret"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/certmanager"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/disk"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/ejbca"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/gcpcas"
spireplugin "github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/spire"
"github.com/spiffe/spire/pkg/server/plugin/upstreamauthority/vault"
Expand Down Expand Up @@ -39,6 +40,7 @@ func (repo *upstreamAuthorityRepository) BuiltIns() []catalog.BuiltIn {
spireplugin.BuiltIn(),
disk.BuiltIn(),
certmanager.BuiltIn(),
ejbca.BuiltIn(),
}
}

Expand Down
Loading
Loading