Skip to content
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# v2.5.0
## Features
- Add support to specify a ConfigMap for CA trust bundles in Issuer / ClusterIssuer resources via the `caBundleConfigMapName` specification.
- Add support for specifying a key on a Secret / ConfigMap resource for the CA trust bundle via the `caBundleKey` specification on an Issuer / ClusterIssuer resource.
- Add a timeout when fetching ambient Azure credentials to move onto other ambient credential methods.
- Ability to specify environment variables on issuer deployment to set additional configuration options (i.e. HTTP proxy settings, etc.)

## Chores
- Add documentation for how to configure command-cert-manager-issuer with ambient credentials on Google Kubernetes Engine (GKE).
- Add documentation for configuring CA trust bundles via Secret and ConfigMap resources using trust-manager.

# v2.4.0
## Features
- Add a `healthcheck` specification to Issuer / ClusterIssuer resources, allowing flexibility in the health check interval.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ test: manifests generate fmt vet envtest ## Run tests.
# Utilize Kind or modify the e2e tests to load the image locally, enabling compatibility with other vendors.
.PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up.
test-e2e:
go test ./test/e2e/ -v -ginkgo.v
cd e2e && source .env && ./run_tests.sh

.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter & yamllint
Expand Down
36 changes: 27 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,25 @@ Command Issuer is installed using a Helm chart. The chart is available in the [C
--create-namespace
```

> For all possible configuration values for the command-cert-manager-issuer Helm chart, please refer to [this list](./deploy/charts/command-cert-manager-issuer/README.md#configuration)
You can also install a specific version of the command-cert-manager-issuer Helm chart:

```shell
helm search repo command-issuer/command-cert-manager-issuer --versions
```

```shell
helm install command-cert-manager-issuer command-issuer/command-cert-manager-issuer \
--namespace command-issuer-system \
--version 2.4.0
--create-namespace
```

> For all possible configuration values for the command-cert-manager-issuer Helm chart, please refer to [this list](./deploy/charts/command-cert-manager-issuer/README.md#configuration)

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

> A list of configurable Helm chart parameters can be found [in the Helm chart docs](./deploy/charts/command-cert-manager-issuer/README.md#configuration)

# Authentication

## Explicit Credentials
Expand All @@ -166,6 +181,7 @@ These credentials must be configured using a Kubernetes Secret. By default, the
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](./docs/ambient-providers/azure.md) (if running in [AKS](https://azure.microsoft.com/en-us/products/kubernetes-service))
- [Workload Identity Using Google Kubernetes Engine](./docs/ambient-providers/google.md) (if running in [GKE](https://cloud.google.com/kubernetes-engine))

If you are running your Kubernetes workload in a cloud provider not listed above, you can use workload identity federation with [Azure AD](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation).

Expand Down Expand Up @@ -212,11 +228,7 @@ This section has moved. Please refer to [this link](./docs/ambient-providers/azu

# 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.

```shell
kubectl -n command-issuer-system create secret generic command-ca-secret --from-file=ca.crt
```
This section has been moved. Please refer to the new [CA Bundle docs](./docs/ca-bundle/README.md) documentation regarding CA trust with command-cert-manager-issuer.

# Creating Issuer and ClusterIssuer resources

Expand All @@ -243,7 +255,9 @@ For example, ClusterIssuer resources can be used to issue certificates for resou
| hostname | The hostname of the Command API Server. |
| apiPath | (optional) The base path of the Command REST API. Defaults to `KeyfactorAPI`. |
| commandSecretName | (optional) The name of the Kubernetes secret containing basic auth credentials or OAuth 2.0 credentials. Omit if using ambient 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. |
| caSecretName | (optional) The name of the Kubernetes secret containing the CA certificate trust chain. See the [CA Bundle docs](./docs/ca-bundle/README.md) for more information. |
| caBundleConfigMapName | (optional) The name of the Kubernetes ConfigMap containing the CA certificate trust chain. See the [CA Bundle docs](./docs/ca-bundle/README.md) for more information. |
| caBundleKey | (optional) The name of the key in the ConfigMap or Secret specified by `caSecretName` or `caBundleConfigMapName` that contains the CA bundle. If omitted, the last key of the ConfigMap / Secret resource will be used. |
| 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. |
| enrollmentPatternId | The ID of the [Enrollment Pattern](https://software.keyfactor.com/Core-OnPrem/Current/Content/ReferenceGuide/Enrollment-Patterns.htm) to use when this Issuer/ClusterIssuer enrolls CSRs. **Supported by Keyfactor Command 25.1 and above**. If `certificateTemplate` and `enrollmentPatternId` are both specified, the enrollment pattern parameter will take precedence. If `enrollmentPatternId` and `enrollmentPatternName` are both specified, `enrollmentPatternId` will take precedence. Enrollment will fail if the specified certificate template is not compatible with the enrollment pattern. |
Expand Down Expand Up @@ -276,7 +290,9 @@ For example, ClusterIssuer resources can be used to issue certificates for resou
hostname: "$HOSTNAME"
apiPath: "/KeyfactorAPI" # Preceding & trailing slashes are handled automatically
commandSecretName: "command-secret" # references the secret created above. Omit if using ambient credentials.
caSecretName: "command-ca-secret" # references the secret created above
# caSecretName: "command-ca-secret" # references a secret containing the CA trust chain (see CA Bundle docs for more info)
# caBundleConfigMapName: "command-ca-configmap" # references a configmap containing the CA trust chain (see CA Bundle docs for more info)
# caBundleKey: "ca.crt" # references the key in the secret/configmap containing the CA trust chain (see CA Bundle docs for more info)

# certificateAuthorityHostname: "$COMMAND_CA_HOSTNAME" # Uncomment if required
certificateAuthorityLogicalName: "$COMMAND_CA_LOGICAL_NAME"
Expand Down Expand Up @@ -309,7 +325,9 @@ For example, ClusterIssuer resources can be used to issue certificates for resou
hostname: "$HOSTNAME"
apiPath: "/KeyfactorAPI" # Preceding & trailing slashes are handled automatically
commandSecretName: "command-secret" # references the secret created above. Omit if using ambient credentials.
caSecretName: "command-ca-secret" # references the secret created above
# caSecretName: "command-ca-secret" # references a secret containing the CA trust chain (see CA Bundle docs for more info)
# caBundleConfigMapName: "command-ca-configmap" # references a configmap containing the CA trust chain (see CA Bundle docs for more info)
# caBundleKey: "ca.crt" # references the key in the secret/configmap containing the CA trust chain (see CA Bundle docs for more info)

# certificateAuthorityHostname: "$COMMAND_CA_HOSTNAME" # Uncomment if required
certificateAuthorityLogicalName: "$COMMAND_CA_LOGICAL_NAME"
Expand Down
16 changes: 15 additions & 1 deletion api/v1alpha1/issuer_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,24 @@ type IssuerSpec struct {

// The name of the secret containing the CA bundle to use when verifying
// Command's server certificate. If specified, the CA bundle will be added to
// the client trust roots for the Command issuer.
// the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
// are specified, caBundleConfigMapName will take precedence.
// +optional
CaSecretName string `json:"caSecretName,omitempty"`

// The name of the ConfigMap containing the CA bundle to use when verifying
// Command's server certificate. If specified, the CA bundle will be added to
// the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
// are specified, caBundleConfigMapName will take precedence.
// +optional
CaBundleConfigMapName string `json:"caBundleConfigMapName,omitempty"`

// The key in the Secret or ConfigMap containing the CA certificate bundle.
// Applies to both caSecretName and caBundleConfigMapName.
// If unspecified, the last key alphabetically in the Secret or ConfigMap data will be used.
// +optional
CaBundleKey string `json:"caBundleKey,omitempty"`

// A list of comma separated scopes used when requesting a Bearer token from an ambient token provider implied
// by the environment, rather than by commandSecretName. For example, could be set to
// api://{tenant ID}/.default when requesting an access token for Entra ID (DefaultAzureCredential). Has no
Expand Down
36 changes: 27 additions & 9 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func main() {
var clusterResourceNamespace string
var disableApprovedCheck bool
var secretAccessGrantedAtClusterLevel bool
var configMapAccessGrantedAtClusterLevel bool

flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
Expand All @@ -88,6 +89,8 @@ func main() {
"Disables waiting for CertificateRequests to have an approved condition before signing.")
flag.BoolVar(&secretAccessGrantedAtClusterLevel, "secret-access-granted-at-cluster-level", false,
"Set this flag to true if the secret access is granted at cluster level. This will allow the controller to access secrets in any namespace. ")
flag.BoolVar(&configMapAccessGrantedAtClusterLevel, "configmap-access-granted-at-cluster-level", false,
"Set this flag to true if the config map access is granted at cluster level. This will allow the controller to access config maps in any namespace. ")
opts := zap.Options{
Development: true,
}
Expand Down Expand Up @@ -130,16 +133,31 @@ func main() {
}

var cacheOpts cache.Options
if secretAccessGrantedAtClusterLevel {
setupLog.Info("expecting SA to have Get+List+Watch permissions for corev1 Secret resources at cluster level")
} else {
setupLog.Info(fmt.Sprintf("expecting SA to have Get+List+Watch permissions for corev1 Secret resources in the %q namespace", clusterResourceNamespace))

// Build the ByObject map if either resource is namespace-scoped
if !secretAccessGrantedAtClusterLevel || !configMapAccessGrantedAtClusterLevel {
byObject := make(map[client.Object]cache.ByObject)

if !secretAccessGrantedAtClusterLevel {
setupLog.Info(fmt.Sprintf("expecting SA to have Get+List+Watch permissions for corev1 Secret resources in the %q namespace", clusterResourceNamespace))
byObject[&corev1.Secret{}] = cache.ByObject{
Namespaces: map[string]cache.Config{clusterResourceNamespace: {}},
}
} else {
setupLog.Info("expecting SA to have Get+List+Watch permissions for corev1 Secret resources at cluster level")
}

if !configMapAccessGrantedAtClusterLevel {
setupLog.Info(fmt.Sprintf("expecting SA to have Get+List+Watch permissions for corev1 ConfigMap resources in the %q namespace", clusterResourceNamespace))
byObject[&corev1.ConfigMap{}] = cache.ByObject{
Namespaces: map[string]cache.Config{clusterResourceNamespace: {}},
}
} else {
setupLog.Info("expecting SA to have Get+List+Watch permissions for corev1 ConfigMap resources at cluster level")
}

cacheOpts = cache.Options{
ByObject: map[client.Object]cache.ByObject{
&corev1.Secret{}: {
Namespaces: map[string]cache.Config{clusterResourceNamespace: cache.Config{}},
},
},
ByObject: byObject,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,25 @@ spec:
the URL of your Command environment.Has no effect on OAuth 2.0 Client Credential configuration - please specify
the audience for this method in an Opaque secret.
type: string
caBundleConfigMapName:
description: |-
The name of the ConfigMap containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
caBundleKey:
description: |-
The key in the Secret or ConfigMap containing the CA certificate bundle.
Applies to both caSecretName and caBundleConfigMapName.
If unspecified, the last key alphabetically in the Secret or ConfigMap data will be used.
type: string
caSecretName:
description: |-
The name of the secret containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer.
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
certificateAuthorityHostname:
description: |-
Expand Down
16 changes: 15 additions & 1 deletion config/crd/bases/command-issuer.keyfactor.com_issuers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,25 @@ spec:
the URL of your Command environment.Has no effect on OAuth 2.0 Client Credential configuration - please specify
the audience for this method in an Opaque secret.
type: string
caBundleConfigMapName:
description: |-
The name of the ConfigMap containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
caBundleKey:
description: |-
The key in the Secret or ConfigMap containing the CA certificate bundle.
Applies to both caSecretName and caBundleConfigMapName.
If unspecified, the last key alphabetically in the Secret or ConfigMap data will be used.
type: string
caSecretName:
description: |-
The name of the secret containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer.
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
certificateAuthorityHostname:
description: |-
Expand Down
1 change: 1 addition & 0 deletions deploy/charts/command-cert-manager-issuer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@ The following table lists the configurable parameters of the `command-cert-manag
| `resources` | CPU/Memory resource requests/limits | `{}` (with commented out options) |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `env` | Environmental variables set for pod | `{}` |
| `secretConfig.useClusterRoleForSecretAccess` | Specifies if the ServiceAccount should be granted access to the Secret resource using a ClusterRole | `false` |
| `defaultHealthCheckInterval` | Specifies the default health check interval for issuers | `""` (uses the default in the code which is 60s) |
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,25 @@ spec:
description: APIPath is the base path of the Command API. KeyfactorAPI
by default
type: string
caBundleConfigMapName:
description: |-
The name of the ConfigMap containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
caBundleKey:
description: |-
The key in the Secret or ConfigMap containing the CA certificate bundle.
Applies to both caSecretName and caBundleConfigMapName.
If unspecified, the last key alphabetically in the Secret or ConfigMap data will be used.
type: string
caSecretName:
description: |-
The name of the secret containing the CA bundle to use when verifying
Command's server certificate. If specified, the CA bundle will be added to
the client trust roots for the Command issuer.
the client trust roots for the Command issuer. If both caSecretName and caBundleConfigMapName
are specified, caBundleConfigMapName will take precedence.
type: string
certificateAuthorityHostname:
description: |-
Expand Down
Loading
Loading