OpenShift: Accessing mounted file-system as non-root

1/3/2020

I am trying to run Chart Museum as a non-root user in OpenShift. Here is a snapshot of my YAML.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: chart-museum
  namespace: demo
spec:
  selector:
    matchLabels:
      app: chart-museum
  replicas: 1
  template:
    metadata:
      labels:
        app: chart-museum
    spec:
      volumes:
        - name: pvc-charts
          persistentVolumeClaim:
            claimName: pvc-charts      
      containers:
        - name: chart-museum
          securityContext:
            fsGroup: 1000
          image: chartmuseum/chartmuseum:latest
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: chart-museum
          volumeMounts:
            - name: pvc-charts
              mountPath: "/charts"

As you can see, I have set spec.containers.securityContext.fsGroup to 1000 which is same as the user ID in the Chart Museum Dockerfile as shown below.

FROM alpine:3.10.3
RUN apk add --no-cache cifs-utils ca-certificates \
    && adduser -D -u 1000 chartmuseum
COPY bin/linux/amd64/chartmuseum /chartmuseum
USER 1000
ENTRYPOINT ["/chartmuseum"]

And, yet, when I try to upload a chart, I get a permission denied message for /charts. How do I get around this issue?

-- cogitoergosum
dockerfile
kubernetes
kubernetes-helm
openshift
persistent-volumes

3 Answers

1/3/2020

Add the chmod/chown lines in your dockerfile:

FROM alpine:3.10.3
RUN apk add --no-cache cifs-utils ca-certificates \
    && adduser -D -u 1000 chartmuseum
COPY bin/linux/amd64/chartmuseum /chartmuseum
RUN chmod +xr /chartmuseum
RUN chown 1000:1000 /chartmuseum
USER 1000
ENTRYPOINT ["/chartmuseum"]

Modify your spec.template.spec.containers.securityContext to ensure the user and group will be enforced.

      containers:
        - name: chart-museum
          securityContext:
            runAsUser: 1000
            runAsGroup: 1000
            fsGroup: 1000
-- willrof
Source: StackOverflow

1/3/2020

It's related to Kubernetes and how the given Persistent Volume is defined. You can check all the discussion and possible workarounds in the related GH Issue.

-- RafaƂ Leszko
Source: StackOverflow

1/4/2020

This is how I solved the issue.

  1. Download the binary curl -LO https://s3.amazonaws.com/chartmuseum/release/latest/bin/linux/amd64/chartmuseum.
  2. Change permissions chmod +xr chartmuseum
  3. Create a new Dockerfile as shown below. Basically, use the user name instead of ID for chown commands so that the binary and the storage location are owned by chartmuseum user and not root.
FROM alpine:3.10.3
RUN apk add --no-cache cifs-utils ca-certificates \
    && adduser -D -u 1000 chartmuseum
COPY chartmuseum /chartmuseum
RUN chown chartmuseum:chartmuseum /chartmuseum
RUN chown chartmuseum:chartmuseum /charts
USER chartmuseum
ENTRYPOINT ["/chartmuseum"]
  1. Build and push the resulting Docker image to e.g. somerepo/chartmuseum:0.0.0.
  2. Use the k8s manifest as shown below. Edit the namespace as required. Note, creation of PersistentVolumeClaim is not covered here.
kind: ConfigMap
apiVersion: v1
metadata:
  name: chart-museum
  namespace: demo
data:
  DEBUG: 'true'
  STORAGE: local
  STORAGE_LOCAL_ROOTDIR: "/charts"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chart-museum
  namespace: demo
spec:
  selector:
    matchLabels:
      app: chart-museum
  replicas: 1
  template:
    metadata:
      labels:
        app: chart-museum
    spec:
      volumes:
        - name: pvc-charts
          persistentVolumeClaim:
              claimName: pvc-charts
      containers:
        - name: chart-museum
          image: somerepo/chartmuseum:0.0.0
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: chart-museum
          volumeMounts:
            - mountPath: "/charts"
              name: pvc-charts
          resources:
            limits:
              memory: "128Mi"
              cpu: "500m"
      imagePullSecrets:
        - name: us.icr.io.secret
---              
apiVersion: v1
kind: Service
metadata:
  labels:
    app: chart-museum
  name: chart-museum
  namespace: demo
spec:
  type: ClusterIP
  ports:
    - name: 8080-tcp
      port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: chart-museum
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  labels:
    app: chart-museum
  name: chart-museum
  namespace: demo
spec:
  port:
    targetPort: 8080-tcp
  tls:
    insecureEdgeTerminationPolicy: Redirect
    termination: edge
  to:
    kind: Service
    name: chart-museum

The manifest creates a ConfigMap object and uses a PersistentVolumeClaim to 'replicate' the command to run Chart Museum locally (as described at https://chartmuseum.com/)

 docker run --rm -it \
  -p 8080:8080 \
  -v $(pwd)/charts:/charts \
  -e DEBUG=true \
  -e STORAGE=local \
  -e STORAGE_LOCAL_ROOTDIR=/charts \
  chartmuseum/chartmuseum:latest

The Service and Route in the manifest expose the repo to the external world.

  1. After the objects are created, enter the HOST/PORT value in oc get route/chart-museum -n demo with https in address bar and hit enter. You should see a welcome page for Chart Museum. This means the installation is successful.
-- cogitoergosum
Source: StackOverflow