nats-io / k8s

NATS on Kubernetes with Helm Charts
Apache License 2.0
436 stars 299 forks source link

[feature request] nats in security context of non root user #904

Open lachnerd opened 1 month ago

lachnerd commented 1 month ago

What motivated this proposal?

Security Issues throughout k8s deployments to run any pod possibly as nonRootUser

What is the proposed change?

Add possibility in chart and nats app to run nats pods with e.g.

securityContext:
    runAsNonRoot: true
    runAsUser: 1001

Who benefits from this change?

everyone ?

What alternatives have you evaluated?

No response

keriisan commented 1 month ago

What motivated this proposal?

All our k8s environments with k8s version >= 1.25 run with Pod Security Admission (PSA) Pod Security Standard (PSS) set to RESTRICTED and ENFORCING. Exceptions to this can be made, but only if there is a convincing justification.

The current helm charts (1.1.12) violate the RESTRICTED PSS and fail to deploy. There is no provision to configure the securityContexts.

In the previous 0.x.y versions, we could override the securityContexts, so this is a loss of functionality.

The RESTRICTED PSS requires the PodSecurityContext and container SecurityContext be populated. The recommended values are as follows (note: user, group, and fsGroup are arbitrary as long as they are not root (0)).

(Pod)

securityContext:
  runAsUser: 1000
  runAsGroup: 2000
  fsGroup: 3000
  runAsNonRoot: true 
  seccompProfile:
    type: RuntimeDefault
automountServiceAccountToken: false

(Container)

securityContext:
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL                                                                                                                                                                                                                                                                                                                           
  privileged: false
  readOnlyRootFilesystem: true

What is the proposed change?

See attached diff to the nats-1.1.12 helm chart. It adds the pod and container security contexts to the templates using the same "loadMergePatch" approach used by the rest of the helm chart, allowing the security context for individual pods and containers to be tailored.

In addition to this, the nats-box:0.14.3-nonroot is required and should, in my opinion, be the default.

Finally, the nats-box container writes to the container's root file system, which is not allowed by the above security contexts and is, in my opinion, NOT best practice. Anything written to the root file system is ephemeral, so "emptyDir" can be used if required.

helm-nats-1.1.12-securityContext.diff.gz

Who benefits from this change?

Everyone. NATS does not require elevated privilege to function correctly. These settings are actually best practices and specifically adhere to the concept of least privilege.

What alternatives have you evaluated?

Unfortunately, there are no options in this case. NATS does not require elevated privilege to function correctly, so it will not receive an exception in our environment.

keriisan commented 2 weeks ago

The Nats helm chart defines "merge" and "patch" operations for most templates generating Kubernetes resources. The merge operation can be used to set the pod and container security policies on the nats and natsBox. The patch operation can be used to add an "emptyDir" mount the the /nsc/.config where the natsBox container attempts to modify the root container, which is not allowed by policy. Hope the following yaml can assist anyone trying to run Nats with a restricted profile.

global:
  image:
    registry: <our offline registry>

config:
  cluster:
    enabled: true
    replicas: 3

  jetstream:
    enabled: true

# defined this in the previous helm version -- do we need it now?
#   domain: <domain>

    fileStore:
      pvc:
        size: 500Mi
#        storageClassName: flash

container:
  merge: 
    securityContext: &sc
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL                                                                                                                                                                                                                                                                                                                           
      privileged: false
      readOnlyRootFilesystem: true

reloader:
  merge: 
    securityContext: *sc

promExporter:
  merge: 
    securityContext: *sc

service:
  merge:
    metadata:
      annotations:
        config.linkerd.io/opaque-ports: "4222,6222"  

headlessService:
  merge:
    metadata:
      annotations:
        config.linkerd.io/opaque-ports: "4222,6222"        

podTemplate:
  merge:
    spec: 
      securityContext: &psc
        runAsUser: 1000
        runAsGroup: 2000
        fsGroup: 3000
        runAsNonRoot: true 
        seccompProfile:
          type: RuntimeDefault
      automountServiceAccountToken: false

serviceAccount:
  enabled: true

natsBox:
  contexts:
    default:

  container:
    image:
      tag: 0.14.3-nonroot
    merge:
      securityContext: *sc
    patch:
    - op: add
      path: /volumeMounts/-
      value:
        name: nsc-config
        mountPath: /nsc/.config

  podTemplate:
    merge:
      spec:
        securityContext: *psc
    patch:
    - op: add
      path: /spec/volumes/-
      value:
        name: nsc-config
        emptyDir:
          sizeLimit: "100Mi"`