Security in the Drupal Operator

This document describes security-related aspects of the Drupal Operator and how you can configure or harden them.

It focuses on:

  • Access control and RBAC
  • Container security (securityContext, user IDs, capabilities)
  • Secrets and sensitive data
  • Network exposure (Services)
  • Operational safety (CRDs and data deletion)

Access control and RBAC

The Drupal Operator installs RBAC roles and bindings that allow it to:

  • Watch and reconcile Drupal custom resources
  • Create and manage:
  • Deployments, StatefulSets, Jobs, CronJobs
  • Services, ConfigMaps, Secrets, PVCs, Pods
  • Status fields on custom resources

Key points:

  • The operator runs as a cluster-wide controller.
  • RBAC files are under config/rbac/ and install/rbac/install.yaml.
  • The operator service account is typically:

    ServiceAccount/drupal-operator (in your operator namespace)

Recommendations:

  • Use a dedicated namespace for the operator (for example drupal-operator-system).
  • Restrict who can create or modify Drupal CRs; these are effectively “infrastructure as code” for entire sites.
  • If needed, review and tailor the RBAC manifests in config/rbac/ to your organization’s policies (for example, restricting namespaces).

Container security

The operator and the Drupal-related workloads follow best-effort hardening practices, which you can extend.

User and group IDs

Defaults in roles/setup/vars/main.yaml:

  • drupal_run_as_user: 1000
  • drupal_run_as_group: 2000
  • drupal_fsgroup: 2000

These values are used to configure:

  • securityContext.runAsUser
  • securityContext.runAsGroup
  • securityContext.fsGroup

This ensures:

  • The main Drupal container does not run as root by default.
  • Volumes are accessible with the configured group.

You can change these IDs to match your cluster’s policy, PodSecurity standards, or existing conventions.

SecurityContext snippets

The operator defines securityContext snippets for PHP-FPM and Apache containers:

  • drupal_php_fpm_container_security_context:

    allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"]

  • drupal_apache_container_security_context:

    allowPrivilegeEscalation: false

These are applied in the templated manifests and provide:

  • No privilege escalation
  • Dropped Linux capabilities (for PHP-FPM)
  • Read-only root filesystem (for PHP-FPM), where applicable

You can extend or override these snippets in your own overlays to:

  • Add seccompProfile definitions
  • Enforce runAsNonRoot: true
  • Adjust filesystem or capability requirements

Sidecars and custom workloads

When you add sidecars via drupal_sidecars, those containers:

  • Are not automatically hardened by the operator’s securityContext snippets, unless your templates explicitly apply them.

Recommendations:

  • Always specify appropriate securityContext for each sidecar.
  • Run sidecars as non-root where possible.
  • Drop unnecessary capabilities and set readOnlyRootFilesystem: true when feasible.

Secrets and sensitive data

The Drupal Operator manages sensitive information primarily via Kubernetes Secrets:

  • Database credentials (Drupal and MariaDB)
  • Drupal admin user and password
  • Backup credentials (via drupal_backup_secret)

Relevant defaults:

  • drupal_backup_secret: "backup-secret"

The Ansible role roles/setup/tasks/main.yaml:

  • Creates Secrets using templates:
  • templates/db-secrets.yaml.j2
  • templates/drupal-secrets.yaml.j2
  • References them from Deployments and Jobs.

Recommendations:

  • Use strong, unique values for:
  • drupal_db_pass
  • drupal_password
  • Any credentials stored in drupal_backup_secret
  • Store the backing values in a secure secret management solution or use sealed-secrets / external secret operators where appropriate.
  • Ensure kubectl/helm users with permission to read these Secrets are limited.

Network exposure

By default, the operator can expose Drupal via:

  • ClusterIP (default)
  • NodePort Service (if drupal_nodeport: true)
  • LoadBalancer Service (if drupal_loadbalancer: true)

Example fields:

  • drupal_nodeport: true (default)
  • drupal_loadbalancer: false (default)
  • drupal_loadbalancer_port: 80
  • drupal_loadbalancer_target_port: 80

Security considerations:

  • A NodePort is reachable on every node in the cluster; ensure:
  • Firewall rules or security groups around node ports are appropriate.
  • A LoadBalancer may receive a public IP on cloud environments; ensure:
  • TLS termination (via Ingress or other means).
  • Proper WAF or reverse proxy rules, as needed.

Recommendations:

  • For production, consider using:
  • An Ingress controller and HTTPS.
  • Locked-down firewall rules or private LoadBalancers.
  • Limit the use of NodePort to development or internal clusters.

Data persistence and cleanup

The operator creates:

  • PersistentVolumeClaims (PVCs) for:
  • Database (db_pvc, db_pvc_size)
  • Files (files_pvc, files_pvc_size)

Deleting a Drupal resource:

  • Will typically delete the Deployment/StatefulSet and some K8s resources.
  • May or may not delete PVCs depending on the reclaimPolicy of your StorageClass and how the templates are written.

You should:

  • Understand your storage class behavior (Retain vs Delete).
  • Treat PVCs as containing sensitive data (DB + file system).
  • Apply appropriate storage encryption at rest where supported by your infrastructure (for example, encrypted volumes).

Operator uninstall and data loss

Running:

make uninstall

will:

  • Remove the CRDs installed by the operator.
  • Trigger Kubernetes garbage collection of all custom resources and any resources that depend on them, depending on ownerReferences.

The README and docs highlight:

  • Never run make uninstall on a live/production cluster.

Because:

  • Removing CRDs makes all Drupal resources and potentially associated Sites, Databases, and Jobs subject to deletion.

Recommendation:

  • Only use make uninstall in disposable or dedicated test clusters.
  • For production, remove individual Drupal resources or namespaces, not the CRD itself.

Logging, auditing, and observability

For security investigations and auditing:

  • Operator logs:

    kubectl logs -f deploy/drupal-operator-controller-manager -n drupal-operator-system

  • Resource events:

    kubectl get events -A kubectl describe drupal -n

Recommendations:

  • Forward logs to a central logging solution (for example, EFK, Loki, or a cloud logging service).
  • Enable audit logging on your Kubernetes cluster and restrict access to audit logs.

Summary checklist

  • Run the operator in a dedicated namespace with least-privilege RBAC.
  • Protect Drupal CRs and Secrets via Kubernetes RBAC.
  • Use non-root containers and hardened securityContext where possible.
  • Limit and secure external exposure (NodePort, LoadBalancer, Ingress).
  • Understand storage retention and encryption for PVCs.
  • Never use make uninstall on a cluster with data you want to keep.
  • Collect and centralize logs for auditing and incident response.