gubernator-io / gubernator

High Performance Rate Limiting MicroService and Library - Developed at Mailgun
Apache License 2.0
103 stars 10 forks source link

Allow running as non root user #27

Closed constanca-m closed 1 month ago

constanca-m commented 1 month ago

The dockerfile does not specifiy a user. From the documentation:

When the user doesn't have a primary group then the image (or the next instructions) will be run with the root group.

We can also see from this blogpost from Docker that it is a best practice to run as non root user.

I have tried to do it by adding

spec:
  securityContext:
    runAsNonRoot: true

to my gubernator pod.

You can see the complete manifest here. ```yaml --- apiVersion: v1 kind: ServiceAccount metadata: name: gubernator --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: get-endpoints rules: - apiGroups: - "" resources: - endpoints verbs: - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: get-endpoints roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: get-endpoints subjects: - kind: ServiceAccount name: gubernator --- apiVersion: v1 kind: Pod metadata: name: gubernator labels: app: gubernator spec: securityContext: runAsNonRoot: true serviceAccountName: gubernator containers: - image: ghcr.io/gubernator-io/gubernator:latest imagePullPolicy: IfNotPresent ports: - name: grpc-port containerPort: 81 - name: http-port containerPort: 80 name: gubernator env: - name: GUBER_K8S_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: GUBER_K8S_POD_IP valueFrom: fieldRef: fieldPath: status.podIP # Must set the GRPC and HTTP addresses, as gubernator # defaults to listening on localhost only - name: GUBER_GRPC_ADDRESS value: 0.0.0.0:81 - name: GUBER_HTTP_ADDRESS value: 0.0.0.0:80 # Use the k8s API for peer discovery - name: GUBER_PEER_DISCOVERY_TYPE value: "k8s" # This should match the port number GRPC is listening on # as defined by `containerPort` - name: GUBER_K8S_POD_PORT value: "81" # The selector used when listing endpoints. This selector # should only select gubernator peers. - name: GUBER_K8S_ENDPOINTS_SELECTOR value: "app=gubernator" # Gubernator can watch 'endpoints' for changes to the peers # or it can watch 'pods' (Defaults to 'endpoints') # - name: GUBER_K8S_WATCH_MECHANISM # value: "endpoints" # Enable debug for diagnosing issues - name: GUBER_DEBUG value: "true" # Defines the max age of a client connection # Default is infinity # - name: GUBER_GRPC_MAX_CONN_AGE_SEC # value: "30" restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: gubernator labels: app: gubernator spec: type: LoadBalancer ports: - targetPort: 80 port: 80 selector: app: gubernator ```

This does not work. It fails with:

Error: container has runAsNonRoot and image will run as root (pod: "gubernator-794fb7b76f-6vntp_default(8e4c444e-68ec-4800-9dd5-668a593a7b65)", container: gubernator)

To run as non root user, it would need to change:

  1. Create a non root user in the Dockerfile. This would be ideally done in the build phase, since scratch image does not have the commands to do this. Then switch to the new user in the next phase.
  2. I think the defaults would have to change since they use 0.0.0.0:80 and 0.0.0.0:81, which seems to be by default ports for root users. I could not find documentation on this, but I have tried running it like this and it did not work.
Here is an example script to test these changes in a local K8s cluster. ```sh # delete local cluster if exists kind delete cluster # expose load balancer cloud-provider-kind > cloud-provider-kind.log 2>&1 & kind create cluster docker build -t custom-image . kind load docker-image custom-image:latest # ensure gubernator pod is using image: custom-image:latest kubectl apply -f k8s-deployment.yaml # wait until gubernator is ready kubectl wait --for=jsonpath='{.status.loadBalancer.ingress}' service/gubernator kubectl wait --for=condition=Ready pod/gubernator --timeout=5m # check the pod and service kubectl get all # make a request IP=$(kubectl get service/gubernator -o=jsonpath='{.status.loadBalancer.ingress[0].ip}') ENDPOINT="http://$IP:80/v1/GetRateLimits" echo "ENDPOINT IS $ENDPOINT" curl $ENDPOINT \ --header 'Content-Type: application/json' \ --data '{ "requests": [ { "name": "requests_per_sec", "uniqueKey": "account:12345", "hits": "1", "limit": "10", "duration": "1000" } ] }' ```

PR: https://github.com/gubernator-io/gubernator/pull/28