Skip to content

Keyfactor/command-cert-manager-issuer

Repository files navigation

Command Issuer

Integration Status: production Release Issues GitHub Downloads (all assets, all releases)

Support · License · Related Integrations

Support

The Command Issuer is open source and community supported, meaning that there is no SLA applicable.

To report a problem or suggest a new feature, use the Issues tab. If you want to contribute actual bug fixes or proposed enhancements, use the Pull requests tab.

Overview

The Command Issuer for cert-manager is a CertificateRequest controller that issues certificates using Keyfactor Command.

Requirements

Before continuing, ensure that the following requirements are met:

Getting Started

Configuring Command

Command Issuer enrolls certificates by submitting a POST request to the Command CSR Enrollment endpoint. Before using Command Issuer, you must create or identify a Certificate Authority and Certificate Template suitable for your usecase. Additionally, you should ensure that the identity used by the Issuer/ClusterIssuer has the appropriate permissions in Command.

  1. Create or identify a Certificate Authority

    A certificate authority (CA) is an entity that issues digital certificates. Within Keyfactor Command, a CA may be a Microsoft CA, EJBCA, or a Keyfactor gateway to a cloud-based or remote CA.

    • If you haven't created a Certificate Authority before, refer to the Command documentation to learn how, or reach out to your Keyfactor support representative.

    The CA that you choose must be configured to allow CSR Enrollment.

  2. Identify a Certificate Template

    Certificate Templates in Command define properties and constraints of the certificates being issued. This includes settings like key usage, extended key usage, validity period, allowed key algorithms, and signature algorithms. They also control the type of information that end entities must provide and how that information is validated before issuing certificates.

    • If you don't have any suitable Certificate Templates, refer to the Command documentation or reach out to your Keyfactor support representative to learn more.

    The Certificate Template that you shoose must be configured to allow CSR Enrollment.

    You should make careful note of the allowed Key Types and Key Sizes on the Certificate Template. When creating cert-manager Certificates, you must make sure that the key algorithm and size are allowed by your Certificate Template in Command.

    The same goes for Enrollment RegExes and Policies defined on your Certificate Template. When creating cert-manager Certificates, you must make sure that the subject, commonName, dnsNames, etc. are allowed and/or configured correctly by your Certificate Template in Command.

  3. Configure Command Security Roles and Claims

    In Command, Security Roles define groups of users or administrators with specific permissions. Users and subjects are identified by Claims. By adding a Claim to a Security Role, you can define what actions the user or subject can perform and what parts of the system it can interact with.

    • If you haven't created Roles and Access rules before, this guide provides a primer on these concepts in Command.

    If your security policy requires fine-grain access control, Command Issuer requires the following Access Rules.

    Global Permissions
    CertificateMetadataTypes:Read
    CertificateEnrollment:EnrollCSR

Installing Command Issuer

Command Issuer is installed using a Helm chart. The chart is available in the Command cert-manager Helm repository.

  1. Verify that at least one Kubernetes node is running:

    kubectl get nodes
  2. Add the Helm repository:

    helm repo add command-issuer https://keyfactor.github.io/command-cert-manager-issuer
    helm repo update
  3. Then, install the chart:

    helm install command-cert-manager-issuer command-issuer/command-cert-manager-issuer \
        --namespace command-issuer-system \
        --create-namespace 

The Helm chart installs the Command Issuer CRDs by default. The CRDs can be installed manually with the make install target.

Authentication

Command Issuer supports authentication to Command using one of the following methods:

  • Basic Authentication (username and password)
  • OAuth 2.0 "client credentials" token flow (sometimes called two-legged OAuth 2.0)

These credentials must be configured using a Kubernetes Secret. By default, the secret is expected to exist in the same namespace as the issuer controller (command-issuer-system by default).

Command Issuer can read secrets in the Issuer namespace if --set "secretConfig.useClusterRoleForSecretAccess=true" flag is set when installing the Helm chart.

Command Issuer also supports ambient authentication, where a token is fetched from an Authorization Server using a cloud provider's auth infrastructure and passed to Command directly. The following methods are supported:

  • Managed Identity Using Azure Entra ID Workload Identity (if running in AKS)

Basic Auth

Create a kubernetes.io/basic-auth secret with the Keyfactor Command username and password:

cat <<EOF | kubectl -n command-issuer-system apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: command-secret
type: kubernetes.io/basic-auth
data:
  username: <base64 encoded (domain\\)username>
  password: <base64 encoded password>
EOF

OAuth

Create an Opaque secret containing the client ID and client secret to authenticate with Command:

token_url="<token url>"
client_id="<client id>"
client_secret="<client secret>"
audience="<audience>"
scopes="<scopes>" # comma separated list of scopes

kubectl -n command-issuer-system create secret generic command-secret \
    "--from-literal=tokenUrl=$token_url" \
    "--from-literal=clientId=$client_id" \
    "--from-literal=clientSecret=$client_secret" \
    "--from-literal=audience=$audience" \
    "--from-literal=scopes=$scopes"

Audience and Scopes are optional

Managed Identity Using Azure Entra ID Workload Identity (AKS)

Azure Entra ID workload identity in Azure Kubernetes Service (AKS) allows Command Issuer to exchange a Kubernetes ServiceAccount Token for an Azure Entra ID access token, which is then used to authenticate to Command.

  1. Reconfigure the AKS cluster to enable workload identity federation.

    az aks update \
        --name ${CLUSTER} \
        --enable-oidc-issuer \
        --enable-workload-identity

    The Azure Workload Identity extension can be installed on non-AKS or self-managed clusters if you're not using AKS.

    Refer to the AKS documentation for more information on the --enable-workload-identity feature.

  2. Reconfigure or deploy Command Issuer with extra labels for the Azure Workload Identity webhook, which will result in the Command Issuer controller Pod having an extra volume containing a Kubernetes ServiceAccount token which it will exchange for a token from Azure.

    helm install command-cert-manager-issuer command-issuer/command-cert-manager-issuer \
        --namespace command-issuer-system \
        --create-namespace \
        --set "fullnameOverride=$chart_name" \
        --set-string "podLabels.azure\.workload\.identity/use=true" \
        --set-string "serviceAccount.labels.azure\.workload\.identity/use=true"
        # --set-string "serviceAccount.annotations.azure\.workload\.identity/client-id=<managed identity client ID>" # May be necessary, but is usually not.

    If successful, the Command Issuer Pod will have new environment variables and the Azure WI ServiceAccount token as a projected volume:

    kubectl -n command-issuer-system describe pod
    Containers:
      command-cert-manager-issuer:
        ...
        Environment:
          AZURE_CLIENT_ID:             <GUID>
          AZURE_TENANT_ID:             <GUID>
          AZURE_FEDERATED_TOKEN_FILE:  /var/run/secrets/azure/tokens/azure-identity-token
          AZURE_AUTHORITY_HOST:        https://login.microsoftonline.com/
        Mounts:
          /var/run/secrets/azure/tokens from azure-identity-token (ro)
          /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-6rmzz (ro)
    ...
    Volumes:
      ...
      azure-identity-token:
        Type:                    Projected (a volume that contains injected data from multiple sources)
        TokenExpirationSeconds:  3600

    Refer to Azure Workload Identity docs more information on the role of the Mutating Admission Webhook.

  3. Create a User Assigned Managed Identity in Azure.

    export IDENTITY_NAME=command-issuer
    az identity create --name "${IDENTITY_NAME}"

    Read more about the az identity command.

  4. Associate a Federated Identity Credential (FIC) with the User Assigned Managed Identity. The FIC allows Command Issuer to act on behalf of the Managed Identity by telling Azure to expect:

    • The iss claim of the ServiceAccount token to match the cluster's OIDC Issuer. Azure will also use the Issuer URL to download the JWT signing certificate.
    • The sub claim of the ServiceAccount token to match the ServiceAccount's name and namespace.
    export SERVICE_ACCOUNT_NAME=command-cert-manager-issuer # This is the default Kubernetes ServiceAccount used by the Command Issuer controller.
    export SERVICE_ACCOUNT_NAMESPACE=command-issuer-system # This is the default namespace for Command Issuer used in this doc.
    export SERVICE_ACCOUNT_ISSUER=$(az aks show --resource-group $AZURE_DEFAULTS_GROUP --name $CLUSTER --query "oidcIssuerProfile.issuerUrl" -o tsv)
    az identity federated-credential create \
        --name "command-issuer" \
        --identity-name "${IDENTITY_NAME}" \
        --issuer "${SERVICE_ACCOUNT_ISSUER}" \
        --subject "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}"

    Read more about Workload Identity federation in the Entra ID documentation.

    Read more about the az identity federated-credential command.

  5. Add Microsoft Entra ID as an Identity Provider in Command, and add the Managed Identity's Client ID as an oid claim to the Security Role created/identified earlier.

CA Bundle

If the Command API is configured to use a self-signed certificate or with a certificate whose issuer isn't widely trusted, the CA certificate must be provided as a Kubernetes secret.

kubectl -n command-issuer-system create secret generic command-ca-secret --from-file=ca.crt

Creating Issuer and ClusterIssuer resources

The command-issuer.keyfactor.com/v1alpha1 API version supports Issuer and ClusterIssuer resources. The Issuer resource is namespaced, while the ClusterIssuer resource is cluster-scoped.

For example, ClusterIssuer resources can be used to issue certificates for resources in multiple namespaces, whereas Issuer resources can only be used to issue certificates for resources in the same namespace.

  1. Prepare the spec

    export HOSTNAME="<hostname>"
    export COMMAND_CA_HOSTNAME="<certificateAuthorityName>" # Only required for non-HTTPS CA types
    export COMMAND_CA_LOGICAL_NAME="<certificateAuthorityName>"
    export CERTIFICATE_TEMPLATE_SHORT_NAME="<certificateTemplateShortName>"

    The spec field of both the Issuer and ClusterIssuer resources use the following fields:

    Field Name Description
    hostname The hostname of the Command API Server.
    apiPath (optional) The base path of the Command REST API. Defaults to KeyfactorAPI.
    commandSecretName The name of the Kubernetes secret containing basic auth credentials or OAuth 2.0 credentials
    caSecretName (optional) The name of the Kubernetes secret containing the CA certificate. Required if the Command API uses a self-signed certificate or it was signed by a CA that is not widely trusted.
    certificateAuthorityLogicalName The logical name of the Certificate Authority to use in Command. For example, Sub-CA
    certificateAuthorityHostname (optional) The hostname of the Certificate Authority specified by certificateAuthorityLogicalName. This field is usually only required if the CA in Command is a DCOM (MSCA-like) CA.
    certificateTemplate The Short Name of the Certificate Template to use when this Issuer/ClusterIssuer enrolls CSRs.

    If a different combination of hostname/certificate authority/certificate template is required, a new Issuer or ClusterIssuer resource must be created. Each resource instantiation represents a single configuration.

  2. Create an Issuer or ClusterIssuer

    • Issuer

      Create an Issuer resource using the environment variables prepared in step 1.

      cat <<EOF > ./issuer.yaml
      apiVersion: command-issuer.keyfactor.com/v1alpha1
      kind: Issuer
      metadata:
        name: issuer-sample
        namespace: default
      spec:
        hostname: "$HOSTNAME"
        apiPath: "/KeyfactorAPI" # Preceding & trailing slashes are handled automatically
        commandSecretName: "command-secret" # references the secret created above
        caSecretName: "command-ca-secret" # references the secret created above
      
        # certificateAuthorityHostname: "$COMMAND_CA_HOSTNAME" # Uncomment if required
        certificateAuthorityLogicalName: "$COMMAND_CA_LOGICAL_NAME"
        certificateTemplate: "$CERTIFICATE_TEMPLATE_SHORT_NAME"
      EOF
      
      kubectl -n default apply -f issuer.yaml
    • ClusterIssuer

      Create a ClusterIssuer resource using the environment variables prepared in step 1.

      cat <<EOF > ./clusterissuer.yaml
      apiVersion: command-issuer.keyfactor.com/v1alpha1
      kind: ClusterIssuer
      metadata:
        name: clusterissuer-sample
      spec:
        hostname: "$HOSTNAME"
        apiPath: "/KeyfactorAPI" # Preceding & trailing slashes are handled automatically 
        commandSecretName: "command-secret" # references the secret created above
        caSecretName: "command-ca-secret" # references the secret created above
      
        # certificateAuthorityHostname: "$COMMAND_CA_HOSTNAME" # Uncomment if required
        certificateAuthorityLogicalName: "$COMMAND_CA_LOGICAL_NAME"
        certificateTemplate: "$CERTIFICATE_TEMPLATE_SHORT_NAME"
      EOF
      
      kubectl apply -f clusterissuer.yaml

Creating a Certificate

Once an Issuer or ClusterIssuer resource is created, they can be used to issue certificates using cert-manager. The two most important concepts are Certificate and CertificateRequest resources.

  1. Certificate resources represent a single X.509 certificate and its associated attributes. cert-manager maintains the corresponding certificate, including renewal when appropriate.
  2. When Certificate resources are created, cert-manager creates a corresponding CertificateRequest that targets a specific Issuer or ClusterIssuer to actually issue the certificate.

To learn more about cert-manager, see the cert-manager documentation.

The following is an example of a Certificate resource. This resource will create a corresponding CertificateRequest resource, and will use the issuer-sample Issuer resource to issue the certificate. Once issued, the certificate will be stored in a Kubernetes secret named command-certificate.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: command-certificate
spec:
  issuerRef:
    name: issuer-sample
    group: command-issuer.keyfactor.com
    kind: Issuer
  commonName: example.com
  secretName: command-certificate

Certificate resources support many more fields than the above example. See the Certificate resource documentation for more information.

Similarly, a CertificateRequest resource can be created directly. The following is an example of a CertificateRequest resource.

apiVersion: cert-manager.io/v1
kind: CertificateRequest
metadata:
  name: command-certificate
spec:
  issuerRef:
    name: issuer-sample
    group: command-issuer.keyfactor.com
    kind: Issuer
  request: <csr>

All fields in Command Issuer and ClusterIssuer spec can be overridden by applying Kubernetes Annotations to Certificates and CertificateRequests. See runtime customization for more

Approving Certificate Requests

Unless the cert-manager internal approver automatically approves the request, newly created CertificateRequest resources will be in a Pending state until they are approved. CertificateRequest resources can be approved manually by using cmctl. The following is an example of approving a CertificateRequest resource named command-certificate.

cmctl approve command-certificate

Once a certificate request has been approved, the certificate will be issued and stored in the secret specified in the CertificateRequest resource. The following is an example of retrieving the certificate from the secret.

kubectl get secret command-certificate -o jsonpath='{.data.tls\.crt}' | base64 -d

To learn more about certificate approval and RBAC configuration, see the cert-manager documentation.

Overriding the Issuer/ClusterIssuer spec using Kubernetes Annotations on CertificateRequest Resources

Command Issuer allows you to override the certificateAuthorityHostname, certificateAuthorityLogicalName, and certificateTemplate by setting Kubernetes Annotations on CertificateRequest resources. This may be useful if certain enrollment scenarios require a different Certificate Authority or Certificate Template, but you don't want to create a new Issuer/ClusterIssuer.

  • command-issuer.keyfactor.com/certificateAuthorityHostname overrides certificateAuthorityHostname
  • command-issuer.keyfactor.com/certificateAuthorityLogicalName overrides certificateAuthorityLogicalName
  • command-issuer.keyfactor.com/certificateTemplate overrides certificateTemplate

cert-manager copies Annotations set on Certificate resources to the corresponding CertificateRequest.

How to Apply Annotations

Notes

To apply these annotations, include them in the metadata section of your Certificate/CertificateRequest resource:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  annotations:
    command-issuer.keyfactor.com/certificateTemplate: "Ephemeral2day"
    command-issuer.keyfactor.com/certificateAuthorityLogicalName: "InternalIssuingCA1"
    metadata.command-issuer.keyfactor.com/ResponsibleTeam: "theResponsibleTeam@example.com"
    # ... other annotations
spec:
# ... the rest of the spec

Certificate Metadata

Keyfactor Command allows users to attach custom metadata to certificates that can be used to tag certificates with additional information. Command Issuer can attach Certificate Metadata upon enrollment.

  • Pre-defined Certificate Metadata

    If all of the following metadata fields are defined in Command, Command Issuer will populate the fields upon certificate enrollment. All of the metadata fields are String types. Please refer to the Command docs to define these metadata fields in Command if you would like Command Issuer to populate these fields on certificates upon enrollment.

    Field Name Description
    Issuer-Namespace The namespace that the Issuer resource was created in. Is always empty for ClusterIssuers.
    Controller-Reconcile-Id The GUID of the reconciliation run that corresponded to the issuance of this certificate.
    Certificate-Signing-Request-Namespace The namespace that the CertificateRequest resource was created in.
    Controller-Namespace The namespace that the controller container is running in.
    Controller-Kind The issuer type - Issuer or ClusterIssuer.
    Controller-Resource-Group-Name The group name of the Command Issuer CRD. Is always command-issuer.keyfactor.com.
    Issuer-Name The name of the K8s Issuer/ClusterIssuer resource.

    You don't need to re-create the Issuer/ClusterIssuer when metadata fields are added/removed in Command. Command Issuer automatically detects the presence of these fields and tracks the state in the SupportsMetadata resource condition.

  • Custom Certificate Metadata

    You can also configure Command Issuer to attach Certificate Metadata by annotating Certificate/CertificateRequest resources. Command Issuer does not check for the presence of custom metadata fields configured in Annotations, and you should take special care that fields defined in annotations exist in Command prior to use. Certificate issuance will fail if any of the metadata fields specified aren't configured in Command. The syntax for specifying metadata is as follows:

    metadata.command-issuer.keyfactor.com/<metadata-field-name>: <metadata-value>

License

Apache License 2.0, see LICENSE.

Related Integrations

See all Keyfactor integrations.