Validation Webhook
The Splunk Operator includes an optional validation webhook that validates Splunk Enterprise Custom Resource (CR) specifications before they are persisted to the Kubernetes API server. This provides immediate feedback when invalid configurations are submitted.
Overview
The validation webhook intercepts CREATE and UPDATE operations on Splunk Enterprise CRDs and validates the spec fields according to predefined rules. If validation fails, the request is rejected with a descriptive error message.
Supported CRDs
The webhook validates the following Custom Resource Definitions:
- Standalone
- IndexerCluster
- SearchHeadCluster
- ClusterManager
- LicenseManager
- MonitoringConsole
Enabling the Validation Webhook
The validation webhook is disabled by default and must be explicitly enabled. This is an opt-in feature for the v4 API.
Prerequisites
Before enabling the webhook, you need TLS certificates for the webhook server. You have two options:
Option A: Use cert-manager (Recommended)
Install cert-manager to automatically manage TLS certificates:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
kubectl wait --for=condition=Available --timeout=300s deployment/cert-manager -n cert-manager
kubectl wait --for=condition=Available --timeout=300s deployment/cert-manager-webhook -n cert-manager
Option B: Use Your Own Certificates
If you prefer not to use cert-manager, you can provide your own TLS certificates:
- Generate certificates for the webhook service. The certificate must have:
- Common Name (CN):
splunk-operator-webhook-service.splunk-operator.svc - Subject Alternative Names (SANs):
splunk-operator-webhook-service.splunk-operator.svcsplunk-operator-webhook-service.splunk-operator.svc.cluster.local
Example using OpenSSL:
# Generate CA openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt -subj "/CN=splunk-webhook-ca" # Generate server key and CSR openssl genrsa -out tls.key 2048 openssl req -new -key tls.key -out server.csr -subj "/CN=splunk-operator-webhook-service.splunk-operator.svc" \ -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:splunk-operator-webhook-service.splunk-operator.svc,DNS:splunk-operator-webhook-service.splunk-operator.svc.cluster.local")) # Sign the certificate openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -days 365 \ -extensions SAN -extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:splunk-operator-webhook-service.splunk-operator.svc,DNS:splunk-operator-webhook-service.splunk-operator.svc.cluster.local")) - Common Name (CN):
- Create the webhook certificate Secret:
kubectl create secret tls webhook-server-cert \ --cert=tls.crt \ --key=tls.key \ -n splunk-operator - Inject the CA bundle into the ValidatingWebhookConfiguration:
# Get base64-encoded CA certificate CA_BUNDLE=$(cat ca.crt | base64 | tr -d '\n') # Patch the webhook configuration kubectl patch validatingwebhookconfiguration splunk-operator-validating-webhook-configuration \ --type='json' -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value': '${CA_BUNDLE}'}]" - Deploy without cert-manager: Use the
config/default-with-webhookoverlay but skip the certmanager components, or manually deploy the webhook components.
Deployment Options
Option 1: Enable via Helm Feature Gates
If deploying with Helm, enable the feature gate through the splunkOperator.featureGates value:
helm install splunk-operator splunk/splunk-operator \
--set splunkOperator.featureGates.ValidationWebhook=true
Or in your values file:
splunkOperator:
featureGates:
ValidationWebhook: true
Note: This requires the webhook Kubernetes resources (Service, ValidatingWebhookConfiguration, TLS certificates) to be deployed separately.
Option 2: Use the Webhook-Enabled Kustomize Overlay
To learn more about the <required value> for the SPLUNK_GENERAL_TERMS variable, please see the README or Splunk Enterprise 10.x image’s README.
Deploy using the config/default-with-webhook overlay which includes all necessary webhook components and enables the ValidationWebhook feature gate automatically:
make deploy IMG=<your-image> ENVIRONMENT=default-with-webhook \
SPLUNK_GENERAL_TERMS="<required value>"
This uses the same make deploy target as the standard deployment, which substitutes the WATCH_NAMESPACE, SPLUNK_ENTERPRISE_IMAGE, and SPLUNK_GENERAL_TERMS placeholder values before running kustomize build.
Option 3: Enable via Feature Gate on Existing Deployment
If you already have the operator deployed with the webhook Kubernetes resources (Service, ValidatingWebhookConfiguration, TLS certificates), enable the feature gate by patching the container args:
kubectl patch deployment splunk-operator-controller-manager -n splunk-operator \
--type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--feature-gates=ValidationWebhook=true"}]'
Note: This requires the webhook service, ValidatingWebhookConfiguration, and TLS certificates to already be deployed. Use Option 1 for a complete deployment.
Option 4: Modify Default Kustomization
To learn more about the <required value> for the SPLUNK_GENERAL_TERMS variable, please see the README or Splunk Enterprise 10.x image’s README.
Edit config/default/kustomization.yaml to uncomment the webhook-related sections:
- Uncomment
- ../webhookin thebasessection - Uncomment
- ../certmanagerin thebasessection - Uncomment
- manager_webhook_patch.yamlinpatchesStrategicMerge - Uncomment
- webhookcainjection_patch.yamlinpatchesStrategicMerge - Uncomment the
varssection for certificate injection
Then deploy:
make deploy IMG=<your-image> SPLUNK_GENERAL_TERMS="<required value>"
Legacy: ENABLE_VALIDATION_WEBHOOK Environment Variable
Deprecated: The
ENABLE_VALIDATION_WEBHOOKenvironment variable is deprecated and will be removed in a future release. Use the--feature-gates=ValidationWebhook=trueflag instead.
For backwards compatibility, setting ENABLE_VALIDATION_WEBHOOK=true as an environment variable on the operator container will still enable the validation webhook. The operator logs a deprecation warning when this method is used.
When both the --feature-gates=ValidationWebhook=... CLI flag and the ENABLE_VALIDATION_WEBHOOK env var are set, the CLI flag takes precedence. The env var is applied at startup before flag parsing, so the CLI value overwrites it.
Validated Fields
The webhook validates the following spec fields:
Common Fields (All CRDs)
| Field | Validation Rule | Error Message |
|---|---|---|
spec.etcVolumeStorageConfig.storageCapacity | Must match format ^[0-9]+Gi$ (e.g., “10Gi”, “100Gi”) | must be in Gi format (e.g., ‘10Gi’, ‘100Gi’) |
spec.varVolumeStorageConfig.storageCapacity | Must match format ^[0-9]+Gi$ | must be in Gi format (e.g., ‘10Gi’, ‘100Gi’) |
spec.etcVolumeStorageConfig.storageClassName | Required when ephemeralStorage=false and storageCapacity is set | storageClassName is required when using persistent storage |
spec.varVolumeStorageConfig.storageClassName | Required when ephemeralStorage=false and storageCapacity is set | storageClassName is required when using persistent storage |
spec.etcVolumeStorageConfig.ephemeralStorage | Mutually exclusive with storageClassName and storageCapacity | storageClassName/storageCapacity cannot be set when ephemeralStorage is true |
spec.varVolumeStorageConfig.ephemeralStorage | Mutually exclusive with storageClassName and storageCapacity | storageClassName/storageCapacity cannot be set when ephemeralStorage is true |
spec.extraEnv[*].name | Must be unique across all entries | duplicate environment variable |
spec.imagePullSecrets[*].name | Must be unique across all entries | duplicate secret reference |
spec.imagePullSecrets[*].name | Must reference an existing Secret in the namespace | not found |
spec.livenessProbe.initialDelaySeconds | Must be ≥ 0 | must be non-negative |
spec.readinessProbe.initialDelaySeconds | Must be ≥ 0 | must be non-negative |
spec.startupProbe.initialDelaySeconds | Must be ≥ 0 | must be non-negative |
spec.resources.requests.cpu | Must be ≤ limits.cpu | request must be less than or equal to limit |
spec.resources.requests.memory | Must be ≤ limits.memory | request must be less than or equal to limit |
CRD-Specific Fields
| CRD | Field | Validation Rule |
|---|---|---|
| Standalone | spec.replicas | Must be ≥ 0 |
| IndexerCluster | spec.replicas | Must be ≥ 3 |
| SearchHeadCluster | spec.replicas | Must be ≥ 3 |
SmartStore Validation (Standalone, ClusterManager)
SmartStore configuration is validated only when provided:
| Field | Validation Rule |
|---|---|
spec.smartstore.volumes[*].name | Required (non-empty) |
spec.smartstore.volumes[*] | Either endpoint or path must be specified |
spec.smartstore.indexes[*].name | Required (non-empty) |
spec.smartstore.indexes[*].volumeName | Required (non-empty) |
AppFramework Validation (Standalone, ClusterManager, SearchHeadCluster)
AppFramework configuration is validated only when provided:
| Field | Validation Rule |
|---|---|
spec.appRepo.appSources[*].name | Required (non-empty) |
spec.appRepo.appSources[*].location | Required (non-empty) |
spec.appRepo.appSources[*] | Combination of location + scope must be unique across all appSources |
spec.appRepo.appSources[*].premiumAppsProps | Required when scope=premiumApps |
spec.appRepo.appsRepoPollIntervalSeconds | Must be ≥ 0 |
spec.appRepo.volumes[*].name | Required (non-empty) |
Example Validation Errors
Invalid Replicas
apiVersion: enterprise.splunk.com/v4
kind: Standalone
metadata:
name: example
spec:
replicas: -1 # Invalid: negative value
Error:
The Standalone "example" is invalid: .spec.replicas: Invalid value: -1: should be a non-negative integer
Invalid Storage Configuration
apiVersion: enterprise.splunk.com/v4
kind: Standalone
metadata:
name: example
spec:
etcVolumeStorageConfig:
storageCapacity: "10GB" # Invalid: must use Gi suffix
Error:
The Standalone "example" is invalid: spec.etcVolumeStorageConfig.storageCapacity: Invalid value: "10GB": must be in Gi format (e.g., '10Gi', '100Gi')
Missing SmartStore Volume Name
apiVersion: enterprise.splunk.com/v4
kind: Standalone
metadata:
name: example
spec:
smartstore:
volumes:
- name: "" # Invalid: empty name
endpoint: "s3://bucket"
Error:
The Standalone "example" is invalid: spec.smartstore.volumes[0].name: Required value: volume name is required
Non-Existent ImagePullSecret
apiVersion: enterprise.splunk.com/v4
kind: Standalone
metadata:
name: example
namespace: splunk
spec:
imagePullSecrets:
- name: my-registry-secret # Invalid: secret does not exist in namespace
Error:
The Standalone "example" is invalid: spec.imagePullSecrets[0].name: Not found: "my-registry-secret"
Ephemeral Storage with StorageClassName
apiVersion: enterprise.splunk.com/v4
kind: Standalone
metadata:
name: example
spec:
etcVolumeStorageConfig:
ephemeralStorage: true
storageClassName: "standard" # Invalid: cannot set with ephemeralStorage=true
Error:
The Standalone "example" is invalid: spec.etcVolumeStorageConfig.storageClassName: Invalid value: "standard": storageClassName cannot be set when ephemeralStorage is true
Verifying Webhook Deployment
Check Webhook Pod is Running
kubectl get pods -n splunk-operator
# Expected: splunk-operator-controller-manager-xxx 1/1 Running
Check Certificate is Ready
kubectl get certificate -n splunk-operator
# Expected: splunk-operator-serving-cert True webhook-server-cert
Check Webhook is Registered
kubectl get validatingwebhookconfiguration splunk-operator-validating-webhook-configuration
Check Operator Logs
kubectl logs -n splunk-operator deployment/splunk-operator-controller-manager | grep -i webhook
# Look for: "Validation webhook enabled"
# Look for: "Starting webhook server" {"port": 9443}
Troubleshooting
Webhook Not Being Called
- Verify the ValidatingWebhookConfiguration exists:
kubectl get validatingwebhookconfiguration splunk-operator-validating-webhook-configuration -o yaml - Check that the CA bundle is injected:
kubectl get validatingwebhookconfiguration splunk-operator-validating-webhook-configuration \ -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | head -1 # Should show: -----BEGIN CERTIFICATE----- - Verify webhook service endpoints:
kubectl get endpoints -n splunk-operator splunk-operator-webhook-service # Should show an IP address
Certificate Issues
If using cert-manager:
- Check cert-manager logs:
kubectl logs -n cert-manager deployment/cert-manager - Check certificate status:
kubectl describe certificate -n splunk-operator splunk-operator-serving-cert - Check issuer:
kubectl get issuer -n splunk-operator
If using custom certificates:
- Verify the Secret exists and contains valid data:
kubectl get secret webhook-server-cert -n splunk-operator -o yaml - Verify the certificate is valid and not expired:
kubectl get secret webhook-server-cert -n splunk-operator -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout - Verify the CA bundle in the webhook configuration matches your CA:
kubectl get validatingwebhookconfiguration splunk-operator-validating-webhook-configuration \ -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | openssl x509 -text -noout - Ensure the certificate SANs include the webhook service DNS name:
splunk-operator-webhook-service.splunk-operator.svc
Webhook Disabled
If you see “Validation webhook disabled” in the logs, ensure:
- The
--feature-gates=ValidationWebhook=trueflag is set on the operator container args (or the legacyENABLE_VALIDATION_WEBHOOK=trueenv var is set) - You’re using the correct kustomize overlay (
config/default-with-webhook)
Architecture
The validation webhook consists of:
| Component | Description |
|---|---|
| Webhook Server | HTTP server listening on port 9443 with TLS |
| Validator Registry | Maps CRD types to their validation functions |
| ValidatingWebhookConfiguration | Kubernetes resource that registers the webhook |
| Certificate | TLS certificate (managed by cert-manager or provided manually) |
| Service | Kubernetes service exposing the webhook endpoint |
Request Flow
- User submits a CREATE/UPDATE request for a Splunk CRD
- Kubernetes API server intercepts the request
- API server sends an AdmissionReview to the webhook service
- Webhook server validates the spec fields
- Webhook returns Allowed/Denied response
- If allowed, the resource is persisted; if denied, user receives error
Adding a New CRD to the Webhook
For a step-by-step guide on extending the webhook to support a new CRD, see the Webhook Development guide.
Disabling the Webhook
To disable the webhook after it has been enabled, remove the --feature-gates=ValidationWebhook=true flag from the container args (or remove the ENABLE_VALIDATION_WEBHOOK env var if using the legacy method).
To learn more about the <required value> for the SPLUNK_GENERAL_TERMS variable, please see the README or Splunk Enterprise 10.x image’s README.
Or redeploy using the default kustomization (without webhook):
make deploy IMG=<your-image> SPLUNK_GENERAL_TERMS="<required value>"