Non root image example for kubernetes/helm

Checklist
I have read intro post: About the Installation Issues category
I have read the tutorials, help and searched for similar issues
I provide relevant information about my server (component names and versions, etc.)
I provide a copy of my logs and healthcheck
I describe the steps I have taken to trouble shoot the problem
I describe the steps on how to reproduce the issue

Hi!

I’m wondering if there is an example deployment for the non-root image on kubernetes/helm.
I have tried to use the docker compose as a base for creating a deployment with the non root image, but can’t get it to work.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: passbolt
  namespace: passbolt
  labels:
    app: passbolt
spec:
  selector:
    matchLabels:
      app: passbolt
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: passbolt
    spec:      
      containers:
        - name: passbolt-server
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 33
            runAsGroup: 33
          image: passbolt/passbolt:4.3.0-1-ce-non-root
          imagePullPolicy: IfNotPresent
          livenessProbe:
            httpGet:
              port: https
              scheme: HTTPS
              path: /healthcheck/status.json
              httpHeaders:
                - name: Host
                  value: passbolt.local
            initialDelaySeconds: 20
            periodSeconds: 10
          readinessProbe:
            httpGet:
              port: https
              scheme: HTTPS
              httpHeaders:
                - name: Host
                  value: passbolt.local
              path: /healthcheck/status.json
            initialDelaySeconds: 5
            periodSeconds: 10
          env:
            - name: APP_FULL_BASE_URL
              value: https://passbolt.domain.com
            - name: DATASOURCES_DEFAULT_DRIVER
              value: Cake\Database\Driver\Postgres
            - name: DATASOURCES_DEFAULT_ENCODING
              value: utf8
            - name: DEBUG
              value: "true"
            - name: DATASOURCES_DEFAULT_URL
              value: "postgres://passbolt:P4ssb0lt@db:5432/passbolt?schema=passbolt"
          ports:
            - name: https
              containerPort: 4433
              protocol: TCP
            - name: http
              containerPort: 8080
              protocol: TCP
          volumeMounts:
            - name: pvc-storage
              mountPath: /etc/passbolt/jwt
            - name: gpg
              mountPath: /etc/passbolt/gpg
              readOnly: true
      volumes:
        - name: pvc-storage
          persistentVolumeClaim:
            claimName: passbolt-storage
        - name: gpg
          secret: 
            secretName: passbolt-sec-gpg
      restartPolicy: Always

On container startup, the GPG keys are successfully imported but when they read I get this error gpg: error reading key: No public key

Any ideas?

Thank you

No comment at all? I can’t be the only one who wants to run the non root image on kubernetes.

Did a comparison with the root image, that also does throw the same error massage at container startup after importing the GPG keys but does continue to the installation section.
Could this be a bug on this version?

I have encountered the same issue. In my case, I aim to execute this image as a non-root user and utilize my ingress for TLS. Have you discovered a solution for this?

No, sadly didn’t find a solution to this. I instead went with vaultwarden.

It might be a permission problem with the data mounted on the volume and the fact that non-root uses a specific UID/GID.

So I ended up creating a chart myself and now it’s working perfectly for non-root images and with ssl offloading :slight_smile:

Something like that :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-passbolt
  namespace: {{ .Release.Namespace }}
spec:
  replicas: {{ default "1" .Values.passbolt.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: passbolt
  template:
    metadata:
      labels:
        app.kubernetes.io/name: passbolt
    spec:
      serviceAccountName: {{ .Release.Name }}-passbolt
      initContainers:
      - name: wait-4-mariadb
        image: busybox
        command: [ "sh", '-c', "until timeout 2 nc -zvn {{ .Release.Name }}-mariadb:{{ .Values.mariadb.primary.service.ports.mysql }}; do echo 'NO MYSQL {{ .Release.Name }}-mariadb'; done; echo {{ .Release.Name }}-mariadb ok"]
      securityContext:
        runAsUser: 33
        runAsGroup: 33
        fsGroup: 33
      containers:
      - name: passbolt
        image: {{ include "passbolt.image" . }}
        imagePullPolicy: {{ .Values.passbolt.image.pullPolicy }}
        envFrom:
        - configMapRef:
            name: {{ .Release.Name }}-env
        - secretRef:
            name: {{ .Release.Name }}-secret-env
        {{- if .Values.passbolt.keys.email }}
        - secretRef:
            name: {{ .Release.Name }}-secret-email
        {{- end }}
        livenessProbe:
          httpGet:
            port: 8080
            scheme: HTTP
            path: /healthcheck/status.json
            httpHeaders:
              - name: Host
                value: {{ .Values.passbolt.baseUrl }}
          initialDelaySeconds: 20
          periodSeconds: 10
        readinessProbe:
          httpGet:
            port: 8080
            scheme: HTTP
            httpHeaders:
              - name: Host
                value: {{ .Values.passbolt.baseUrl }}
            path: /healthcheck/status.json
          initialDelaySeconds: 5
          periodSeconds: 10
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
          requests:
            cpu: 100m
            memory: 1Gi
        volumeMounts:
        - name: gpg-secret
          mountPath: /etc/passbolt/gpg
          readOnly: true
        - name: jwt-secret
          mountPath: /etc/passbolt/jwt
          readOnly: true
        - name: session-timeout
          mountPath: /etc/php/8.2/fpm/conf.d/99-session-timeout.ini
          subPath: 99-session-timeout.ini
          readOnly: true
        {{- if .Values.passbolt.keys.subscriptionKey }}
        - name: subscription-key
          mountPath: /etc/passbolt/subscription_key.txt
          subPath: subscription_key.txt
          readOnly: true
        {{- end }}
      volumes:
      - name: gpg-secret
        secret:
          secretName: {{ .Release.Name }}-secret-gpg
      - name: jwt-secret
        secret:
          secretName: {{ .Release.Name }}-secret-jwt
      - name: session-timeout
        configMap:
          name: {{ .Release.Name }}-conf
      {{- if .Values.passbolt.keys.subscriptionKey }}
      - name: subscription-key
        secret:
          secretName: {{ .Release.Name }}-secret-subscription
      {{- end }}
      {{- with .Values.passbolt.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}

2 Likes

That is great! Could you clarify what was the specific issue? It would help other people to understand.

I will explain what I did to make it works :
In my case I wanted to run a non-root image with no SSL because my ingress already does it.

The non-root images are running uid: 33 and gid 33, so you have to put the following code in the deployment :

securityContext:
       runAsUser: 33
       runAsGroup: 33
       fsGroup: 33

For the ports, non-root images are using 8080 for http and 4433 for https so you have to bind the ports in your service.
In my case I just use http port :

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-passbolt
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    app.kubernetes.io/name: passbolt
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080

By default, passbolt image will create an SSL cert and force you to use it.
So I tried something : I pass variables to the container SSL=false and PASSBOLT_SSL_FORCE=false and it worked.

If you want to use https between the pod and the ingress, don’t set SSL=false and PASSBOLT_SSL_FORCE=false, bind the port 4433 to 433 and on your kubernetes ingress allow untrusted certificates parameter (something like that) and it should works with self-signed certificate.

Don’t forgot to check if the livenessProbe and the readinessProbe have the good port and scheme

PS: You can use the official passbolt helm chart if you want to run it as root with ssl in other case, I think it’s faster to create another chart

That’s great!! This is something to add to the official chart

And please, I pray you to update the reference : Passbolt Help | Passbolt reference environment variables

The last time it has been updated was in 2021, it could help a lot of people