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:
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.
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"
}
]
}'
```
The dockerfile does not specifiy a user. From the documentation:
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
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:
To run as non root user, it would need to change:
scratch
image does not have the commands to do this. Then switch to the new user in the next phase.0.0.0.0:80
and0.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