Skip to content

Allow more customization for security contexts - #160

Open
albundy83 wants to merge 1 commit into
n8n-io:mainfrom
albundy83:improve-securitycontext
Open

Allow more customization for security contexts#160
albundy83 wants to merge 1 commit into
n8n-io:mainfrom
albundy83:improve-securitycontext

Conversation

@albundy83

@albundy83 albundy83 commented Jun 24, 2026

Copy link
Copy Markdown

Pull Request

Allow a more flexible way to configure security context accross the differents containers.
Allow also to achieve the hardening recommandation here

Description

Brief description of the changes and their purpose.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Example/configuration update
  • CI/CD improvements

Related Issues

Fixes # (issue)
Relates to # (issue)

Changes Made

Remove SecurityContext and split it in podSecurityContext and containerSecurityContext

Testing Performed

I have fully deployed it and also be able to set custom user and group id for runners.

Chart Validation

  • helm lint charts/n8n passes
  • ./scripts/validate-examples.sh passes
  • Template rendering works with all examples

Deployment Testing (if applicable)

  • Tested with minimal configuration
  • Tested with production configuration
  • Tested upgrade path from previous version
  • All pods start successfully
  • Application is accessible

Specific Testing for Changes

Describe any specific testing you performed for your changes:

  • Deploy the chart using the following values file:
image:
  repository: docker.n8n.io/n8nio/n8n
  tag: "stable"

fullnameOverride: easi-n8n

queueMode:
  enabled: true
  workerReplicaCount: 5
  workerConcurrency: 10

strategy:
  type: Recreate

database:
  type: postgresdb
  useExternal: true
  host: n8n-rw
  port: 5432
  database: n8n
  schema: public
  user: n8n
  passwordSecret:
    name: n8n-app
    key: password

redis:
  enabled: true
  useExternal: true
  host: valkey
  port: 6379
  username: "
  passwordSecret:
    name: valkey-auth
    key: default-password
  tls: false
  healthCheck:
    enabled: false
    port: 5678

taskRunners:
  enabled: true
  image:
    repository: n8nio/runners
    tag: "2.26.9-distroless"
  authToken:
    existingSecret: n8n-encryption-key
    existingSecretKey: N8N_TASK_RUNNERS_AUTH_TOKEN
  # Needed for distroless deployment
  # Ref: https://docs.n8n.io/deploy/host-n8n/configure-n8n/security/harden-task-runners#use-the-distroless-image
  containerSecurityContext:
    allowPrivilegeEscalation: false
    capabilities:
      drop:
        - ALL
    readOnlyRootFilesystem: true
    runAsUser: 65532
    runAsGroup: 65532
    seccompProfile:
      type: RuntimeDefault

secretRefs:
  existingSecret: n8n-encryption-key

config:
  timezone: Europe/Paris
  extraEnvFrom:
    - configMapRef:
        name: n8n-env
  extraEnv:
    - name: N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS
      value: "true"
    - name: N8N_PROXY_HOPS
      value: "1"

executions:
  concurrency:
    productionLimit: 50
  timeout: 3600 # 1 hour
  data:
    saveOnError: "all"
    saveOnSuccess: "all"
    saveOnProgress: false
    saveManualExecutions: true
  pruning:
    enabled: true
    maxAge: 336 # 14 days in hours
    maxCount: 10000
    hardDeleteBuffer: 1 # 1 hour buffer before hard delete
    hardDeleteInterval: 15 # minutes between hard delete runs
    softDeleteInterval: 60 # minutes between soft delete runs

serviceAccount:
  name: easi-n8n
  automountServiceAccountToken: false

dnsConfig:
  options:
    - name: ndots
      value: "1"

resources:
  main:
    limits:
      cpu: "2"
      ephemeral-storage: 1Gi
      memory: 4Gi
    requests:
      cpu: 500m
      ephemeral-storage: "0"
      memory: 512Mi

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: easi-n8n.youpi.fr
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: n8n-tls
      hosts:
        - easi-n8n.youpi.fr

pdb:
  enabled: false

# /tmp as mount
# # Needed for distroless deployment
# Ref: https://docs.n8n.io/deploy/host-n8n/configure-n8n/security/harden-task-runners#use-the-distroless-image
extraVolumes:
  - name: tmp
    emptyDir: {}

extraVolumeMounts:
  - name: tmp
    mountPath: /tmp

# This could be defined but that the new default values
# containerSecurityContext:
#   allowPrivilegeEscalation: false
#   capabilities:
#     drop:
#       - ALL
#   readOnlyRootFilesystem: false
#   runAsUser: 1000
#   runAsGroup: 1000
#   seccompProfile:
#     type: RuntimeDefault

# # -- Security Context for the whole pod.
# podSecurityContext:
#   fsGroup: 1000
#   runAsNonRoot: true
#   runAsUser: 1000
#   runAsGroup: 1000
#   seccompProfile:
#     type: RuntimeDefault

Breaking Changes

If this includes breaking changes, describe what they are and provide migration instructions:
Yes. The old SecurityContext has been split into podSecurityContext and containerSecurityContext.

The best approach is to remove SecurityContext, as it will be silently ignored, and replace it with podSecurityContext and/or containerSecurityContext, which provide more granular configuration and customization capabilities.
Or let the default values do the job.. :)

Documentation Updates

  • Updated Chart.yaml version (if needed)
  • Updated CHANGELOG.md
  • Updated README.md (if needed)
  • Updated examples (if needed)
  • Updated CONTRIBUTING.md (if needed)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have added examples that demonstrate the changes (if applicable)
  • All new and existing tests pass

Screenshots (if applicable)

Add screenshots to help explain your changes.

Additional Notes

Any additional information that reviewers should know.


Summary by cubic

Split the old securityContext into pod-level and container-level settings across main, worker, webhook-processor, and task-runner sidecars to enable stricter hardening and support distroless runners.

  • New Features

    • Added .Values.podSecurityContext and .Values.containerSecurityContext for main and worker.
    • Added .Values.webhookProcessor.podSecurityContext and .Values.webhookProcessor.containerSecurityContext.
    • Added .Values.taskRunners.containerSecurityContext.
    • Templates now render via toYaml for full control of seccompProfile, runAs*, capabilities, and readOnlyRootFilesystem.
    • Defaults in values.yaml mirror previous behavior; readOnlyRootFilesystem remains false. Examples updated to use podSecurityContext with runAsGroup and seccompProfile.
  • Migration

    • Replace securityContext.* (including enabled) with:
      • podSecurityContext and containerSecurityContext (main/worker)
      • webhookProcessor.podSecurityContext and .containerSecurityContext
      • taskRunners.containerSecurityContext
    • Review runAs*, capabilities.drop: [ALL], and seccompProfile: RuntimeDefault.
    • For distroless task runners, set runAsUser/runAsGroup to 65532.

Written for commit 2bc91bd. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="charts/n8n/templates/deployment-worker.yaml">

<violation number="1" location="charts/n8n/templates/deployment-worker.yaml:38">
P1: SecurityContext key rename has no backward-compatibility shim, so existing user-provided security settings under the old `securityContext` key are silently dropped during chart upgrades.</violation>
</file>

<file name="charts/n8n/values.schema.json">

<violation number="1" location="charts/n8n/values.schema.json:229">
P2: HPA settings were moved under `hpa.main`, but the `hpa` schema object remains open so legacy top-level keys can still validate and silently become no-ops.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Helm as Helm Values
    participant Render as Helm Template Engine
    participant Dep as Deployment (Main)
    participant WorkerDep as Deployment (Worker)
    participant WebhookDep as Deployment (Webhook Processor)
    participant Sidecar as TaskRunner Sidecar Container
    participant KubePod as Kubernetes Pod
    participant KubeContainer as Container Runtime

    Note over Helm,KubeContainer: Security Context Configuration Flow

    Helm->>Render: NEW: .Values.podSecurityContext (map)
    Helm->>Render: NEW: .Values.containerSecurityContext (map)

    Helm->>Render: NEW: .Values.webhookProcessor.podSecurityContext (map)
    Helm->>Render: NEW: .Values.webhookProcessor.containerSecurityContext (map)

    Helm->>Render: NEW: .Values.taskRunners.containerSecurityContext (map)

    Render->>Render: toYaml: pod-level contexts
    Render->>Dep: podSecurityContext rendered
    Render->>WorkerDep: podSecurityContext rendered
    Render->>WebhookDep: podSecurityContext rendered

    Render->>Render: toYaml: container-level contexts
    Render->>Dep: containerSecurityContext rendered
    Render->>WorkerDep: containerSecurityContext rendered
    Render->>WebhookDep: containerSecurityContext rendered
    Render->>Sidecar: taskRunners.containerSecurityContext rendered

    Dep->>KubePod: Apply podSecurityContext (fsGroup, runAsUser, runAsGroup, seccompProfile)
    KubePod->>KubeContainer: Apply containerSecurityContext (capabilities.drop, readOnlyRootFS, runAsUser)

    WorkerDep->>KubePod: Same pattern: podSecurityContext
    KubePod->>KubeContainer: Same pattern: containerSecurityContext

    WebhookDep->>KubePod: Same pattern: webhookProcessor.podSecurityContext
    KubePod->>KubeContainer: Same pattern: webhookProcessor.containerSecurityContext

    Sidecar->>KubeContainer: Apply taskRunners.containerSecurityContext (distroless-ready)

    Note over KubeContainer: NEW: Each component independently configurable<br/>Allows per-component seccomp, runAsGroup, capabilities
Loading

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread charts/n8n/templates/deployment-worker.yaml
"type": { "type": "string" },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
"annotations": { "type": "object", "additionalProperties": true },
"main": {

@cubic-dev-ai cubic-dev-ai Bot Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: HPA settings were moved under hpa.main, but the hpa schema object remains open so legacy top-level keys can still validate and silently become no-ops.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At charts/n8n/values.schema.json, line 229:

<comment>HPA settings were moved under `hpa.main`, but the `hpa` schema object remains open so legacy top-level keys can still validate and silently become no-ops.</comment>

<file context>
@@ -200,6 +226,18 @@
         "type": { "type": "string" },
         "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
         "annotations": { "type": "object", "additionalProperties": true },
+        "main": {
+          "type": "object",
+          "properties": {
</file context>
Fix with cubic

Signed-off-by: Grégoire Bellon-Gervais <gregoire.bellon-gervais@docaposte.fr>
@albundy83
albundy83 force-pushed the improve-securitycontext branch from fab2879 to 2bc91bd Compare July 7, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant