I have an RBAC problem, but everything I test seems ok? - kubernetes

This is a continuation of the problem described here (How do I fix a role-based problem when my role appears to have the correct permissions?)
I have done much more testing and still do not understand the error
Error from server (Forbidden): pods is forbidden: User "dma" cannot list resource "pods" in API group "" at the cluster scope
UPDATE: Here is another hint from the API server
watch chan error: etcdserver: mvcc: required revision has been compacted
I found this thread, but I am working in the current kubernetes
How fix this error "watch chan error: etcdserver: mvcc: required revision has been compacted"?
My user exists
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
dma 77m kubernetes.io/kube-apiserver-client kubernetes-admin <none> Approved,Issued
The clusterrole exists
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"name":"kubelet-runtime"},"rules":[{"apiGroups":["","extensions","apps","argoproj.io","workflows.argoproj.io","events.argoproj.io","coordination.k8s.io"],"resources":["*"],"verbs":["*"]},{"apiGroups":["batch"],"resources":["jobs","cronjobs"],"verbs":["*"]}]}
creationTimestamp: "2021-12-16T00:24:56Z"
name: kubelet-runtime
resourceVersion: "296716"
uid: a4697d6e-c786-4ec9-bf3e-88e3dbfdb6d9
rules:
- apiGroups:
- ""
- extensions
- apps
- argoproj.io
- workflows.argoproj.io
- events.argoproj.io
- coordination.k8s.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- batch
resources:
- jobs
- cronjobs
verbs:
- '*'
The sandbox namespace exists
NAME STATUS AGE
sandbox Active 6d6h
My user has authority to operate in the kubelet cluster and the namespace "sandbox"
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"ClusterRoleBinding\",\"metadata\":{\"annotations\":{},\"name\":\"dma-kubelet-binding\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"ClusterRole\",\"name\":\"kubelet-runtime\"},\"subjects\":[{\"kind\":\"ServiceAccount\",\"name\":\"dma\",\"namespace\":\"argo\"},{\"kind\":\"ServiceAccount\",\"name\":\"dma\",\"namespace\":\"argo-events\"},{\"kind\":\"ServiceAccount\",\"name\":\"dma\",\"namespace\":\"sandbox\"}]}\n"
},
"creationTimestamp": "2021-12-16T00:25:42Z",
"name": "dma-kubelet-binding",
"resourceVersion": "371397",
"uid": "a2fb6d5b-8dba-4320-af74-71caac7bdc39"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "kubelet-runtime"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "dma",
"namespace": "argo"
},
{
"kind": "ServiceAccount",
"name": "dma",
"namespace": "argo-events"
},
{
"kind": "ServiceAccount",
"name": "dma",
"namespace": "sandbox"
}
]
}
My user has the correct permissions
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "Role",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"annotations\":{},\"name\":\"dma\",\"namespace\":\"sandbox\"},\"rules\":[{\"apiGroups\":[\"\",\"apps\",\"autoscaling\",\"batch\",\"extensions\",\"policy\",\"rbac.authorization.k8s.io\",\"argoproj.io\",\"workflows.argoproj.io\"],\"resources\":[\"pods\",\"configmaps\",\"deployments\",\"events\",\"pods\",\"persistentvolumes\",\"persistentvolumeclaims\",\"services\",\"workflows\"],\"verbs\":[\"get\",\"list\",\"watch\",\"create\",\"update\",\"patch\",\"delete\"]}]}\n"
},
"creationTimestamp": "2021-12-21T19:41:38Z",
"name": "dma",
"namespace": "sandbox",
"resourceVersion": "1058387",
"uid": "94191881-895d-4457-9764-5db9b54cdb3f"
},
"rules": [
{
"apiGroups": [
"",
"apps",
"autoscaling",
"batch",
"extensions",
"policy",
"rbac.authorization.k8s.io",
"argoproj.io",
"workflows.argoproj.io"
],
"resources": [
"pods",
"configmaps",
"deployments",
"events",
"pods",
"persistentvolumes",
"persistentvolumeclaims",
"services",
"workflows"
],
"verbs": [
"get",
"list",
"watch",
"create",
"update",
"patch",
"delete"
]
}
]
}
My user is configured correctly on all nodes
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://206.81.25.186:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: dma
name: dma#kubernetes
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin#kubernetes
current-context: kubernetes-admin#kubernetes
kind: Config
preferences: {}
users:
- name: dma
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
Based on this website, I have been searching for a watch event.
I think have rebuilt everything above the control plane but the problem persists.
The next step would be to rebuild the entire cluster, but it would be so much more satisfying to find the actual problem.
Please help.
FIX:
So the policy for the sandbox namespace was wrong. I fixed that and the problem is gone!

I think finally understand RBAC (policies and all). Thank you very much to members of the Kubernetes slack channel. These policies have passed the first set of tests for a development environment ("sandbox") for Argo workflows. Still testing.
policies.yaml file:
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: dev
namespace: sandbox
rules:
- apiGroups:
- "*"
attributeRestrictions: null
resources: ["*"]
verbs:
- get
- watch
- list
- apiGroups: ["argoproj.io", "workflows.argoproj.io", "events.argoprpj.io"]
attributeRestrictions: null
resources:
- pods
- configmaps
- deployments
- events
- pods
- persistentvolumes
- persistentvolumeclaims
- services
- workflows
- eventbus
- eventsource
- sensor
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dma-dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: dev
subjects:
- kind: User
name: dma
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: dma-admin
subjects:
- kind: User
name: dma
namespace: sandbox
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: access-nginx
namespace: sandbox
spec:
podSelector:
matchLabels:
app: nginx
ingress:
- from:
- podSelector:
matchLabels:
run: access
...

Related

Setting environment variables in kubernetes manifest using "kubectl set env"

I am trying to update a helm-deployed deployment so that it uses a secret stored as a k8s secret resource. This must be set as the STORAGE_PASSWORD environment variable in my pod.
In my case, the secret is in secrets/redis and the data item is redis-password:
$ kubectl get secret/redis -oyaml
apiVersion: v1
data:
redis-password: XXXXXXXXXXXXXXXX=
kind: Secret
metadata:
name: redis
type: Opaque
I have tried:
$ kubectl set env --from secret/redis deployment/gateway --keys=redis-password
Warning: key redis-password transferred to REDIS_PASSWORD
deployment.apps/gateway env updated
When I look in my updated deployment manifest, I see the variable has been added but (as suggested) the variable has been set to REDIS_PASSWORD:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
key: redis-password
name: redis
I have also tried kubectl patch with a replace operation, but I can't get the syntax correct to have the secret inserted.
How do I change the name of the environment variable to STORAGE_PASSWORD?
Given a deployment that looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example
spec:
replicas: 1
template:
spec:
containers:
- image: alpinelinux/darkhttpd
name: darkhttpd
args:
- --port
- "9991"
ports:
- name: http
protocol: TCP
containerPort: 9991
env:
- name: EXAMPLE_VAR
value: example value
The syntax for patching in your secret would look like:
kubectl patch deploy/example --patch='
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "darkhttpd",
"env": [
{
"name": "STORAGE_PASSWORD",
"valueFrom": {
"secretKeyRef": {
"name": "redis",
"key": "redis-password"
}
}
}
]
}
]
}
}
}
}
'
Or using a JSONPatch style patch:
kubectl patch --type json deploy/example --patch='
[
{
"op": "add",
"path": "/spec/template/spec/containers/0/env/-",
"value": {
"name": "STORAGE_PASSWORD",
"valueFrom": {
"secretKeyRef": {
"name": "redis",
"key": "redis-password"
}
}
}
}
]
'
Neither one is especially pretty because you're adding a complex nested structure to an existing complex nested structure.
you may also update resources with kubectl edit:
kubectl edit deployment gateway
then edit the yaml file
# - name: REDIS_PASSWORD
- name: STORAGE_PASSWORD
valueFrom:
secretKeyRef:
key: redis-password
name: redis
FYI: https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#kubectl-edit

Kubernetes client Job create

I am using kubernetes client (https://github.com/kubernetes-client/javascript) to create job and I have set up service account for the pod which is creating the job. However, I am getting this error when executing the job creation.
body: {
kind: 'Status',
apiVersion: 'v1',
metadata: {},
status: 'Failure',
message: 'Job.batch "compiler-job" is invalid: spec.template.spec.containers: Required value',
reason: 'Invalid',
details: {
name: 'compiler-job',
group: 'batch',
kind: 'Job',
causes: [Array]
},
code: 422
},
I am sure there is something wrong with the body options that i am passing to k8sBatchV1Api.createNamespacedJob() but I am not sure what I am doing wrong. Here is the snippet of the manifest.
const kc = new k8s.KubeConfig();
kc.loadFromCluster();
const k8sBatchV1Api = kc.makeApiClient(k8s.BatchV1Api);
k8sBatchV1Api.createNamespacedJob('default', {
apiVersion: 'batch/v1',
kind: 'Job',
metadata: {
name: 'compiler-job'
},
spec: {
template: {
metadata: {
name: 'compiler-job'
},
spec: {
containers: {
image: 'perl',
name: 'compiler-job',
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
},
restartPolicy: "OnFailure"
}
}
}
}).catch((e: any) => console.log(e));
Here is the serviceaccount.yaml file
apiVersion: v1
kind: ServiceAccount
metadata:
name: create-job
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: create-job-role
rules:
- apiGroups: [ "batch", "extensions" ]
resources: [ "jobs" ]
verbs: [ "get", "list", "watch", "create", "update", "patch", "delete" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: create-job-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: create-job
namespace: default
roleRef:
kind: ClusterRole
name: create-job-role
apiGroup: rbac.authorization.k8s.io
Been spending almost a week and have no clue.
I think that might be because containers should be a list, can you try this?
const kc = new k8s.KubeConfig();
kc.loadFromCluster();
const k8sBatchV1Api = kc.makeApiClient(k8s.BatchV1Api);
k8sBatchV1Api.createNamespacedJob('default', {
apiVersion: 'batch/v1',
kind: 'Job',
metadata: {
name: 'compiler-job'
},
spec: {
template: {
metadata: {
name: 'compiler-job'
},
spec: {
containers: [{
image: 'perl',
name: 'compiler-job',
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
}],
restartPolicy: "OnFailure"
}
}
}
}).catch((e: any) => console.log(e));

What configuration can be done on prometheus adapter in order to get the sum of cpu_usage_seconds_total accross all replicas of a container?

I have a Kubernetes cluster and Prometheus/Prometheus adapter installed.
This is the prometheus adapter configuration rules:
rules:
custom:
- seriesQuery: '{__name__=~"container_cpu_usage_seconds_total"}'
resources:
overrides:
template: "<<.Resource>>"
# namespace:
# resource: namespace
# pod:
# resource: pod
name:
matches: "container_cpu_usage_seconds_total"
as: "my_custom_metric"
metricsQuery: sum(<<.Series>>{container="php-apache"}) by (<<.GroupBy>>)
And this is my hpa configuration:
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 6
metrics:
- type: Pods
pods:
metric:
name: my_custom_metric
target:
type: Value
averageValue: 250 //limit
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
The problem here is that I want to scale based on the summary of the replicas that container=php-apache use and not with the Average Value of them.
This is the value that is returned from the Prometheus Adapter:
{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/%2A/malakas"
},
"items": [
{
"describedObject": {
"kind": "Pod",
"namespace": "default",
"name": "php-apache-d4cf67d68-8ddbx",
"apiVersion": "/v1"
},
"metricName": "my_custom_metric",
"timestamp": "2021-04-16T10:52:02Z",
"value": "331827m",
"selector": null
},
{
"describedObject": {
"kind": "Pod",
"namespace": "default",
"name": "php-apache-d4cf67d68-zxkrd",
"apiVersion": "/v1"
},
"metricName": "my_custom_metric",
"timestamp": "2021-04-16T10:52:02Z",
"value": "44478m",
"selector": null
}
]
}
In this example, there are 2 replicas.
I want to get one result (the sum of these two) and not two results just like above in order to pass the result to hpa and scale accordingly.
How can I achieve that?
You should use metrics from the service not from the pod:
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/services/*/my_custom_metric"

Jenkins installed via helm not exposed when using static IP for the nginx ingress on GKE

I am setting jenkins on GKE using the official helm chart.
I am also installing the nginx ingress also via its stable helm chart and after creating a static IP on GCE as follows:
gcloud compute addresses create jenkins-static-ip --global --ip-version IPV4
I am passing the above IP to the nginx-ingress helm values:
helm upgrade --force --tls --install nginx-ingress stable/nginx-ingress --set rbac.create=true --set controller.service.loadBalancerIP=<jenkins-static-ip>
I am also using the prefix /jenkins for accessing the jenkins service.
Both the ingress controller and the ingress resource are created as below:
kubectl get svc nginx-ingress-controller -n default -o json
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"creationTimestamp": "2020-02-12T09:54:50Z",
"labels": {
"app": "nginx-ingress",
"chart": "nginx-ingress-1.29.6",
"component": "controller",
"heritage": "Tiller",
"release": "nginx-ingress"
},
"name": "nginx-ingress-controller",
"namespace": "default",
"resourceVersion": "35664441",
"selfLink": "/api/v1/namespaces/default/services/nginx-ingress-controller",
"uid": "b6029772-4d7d-11ea-aa7f-42010a79fa0b"
},
"spec": {
"clusterIP": "10.19.150.227",
"externalTrafficPolicy": "Cluster",
"loadBalancerIP": "13.579.24.68",
"ports": [
{
"name": "http",
"nodePort": 30508,
"port": 80,
"protocol": "TCP",
"targetPort": "http"
},
{
"name": "https",
"nodePort": 32756,
"port": 443,
"protocol": "TCP",
"targetPort": "https"
}
],
"selector": {
"app": "nginx-ingress",
"component": "controller",
"release": "nginx-ingress"
},
"sessionAffinity": "None",
"type": "LoadBalancer"
},
"status": {
"loadBalancer": {}
}
}
kubectl get ingress my-jenkins -n jenkins -o yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
creationTimestamp: "2020-02-12T09:54:54Z"
generation: 1
labels:
app.kubernetes.io/component: jenkins-master
app.kubernetes.io/instance: my-jenkins
app.kubernetes.io/managed-by: Tiller
app.kubernetes.io/name: jenkins
helm.sh/chart: jenkins-1.9.16
name: my-jenkins
namespace: jenkins
resourceVersion: "35664516"
selfLink: /apis/extensions/v1beta1/namespaces/jenkins/ingresses/my-jenkins
uid: b8604a30-4d7d-11ea-aa7f-42010a79fa0b
spec:
rules:
- http:
paths:
- backend:
serviceName: my-jenkins
servicePort: 8080
path: /jenkins
status:
loadBalancer: {}
However I am unable to access jenkins in any of the following urls:
http://13.579.24.68
https://13.579.24.68
http://13.579.24.68/jenkins
https://13.579.24.68/jenkins
edit: I have added the annotation to my ingress resource but the result remains the same:
▶ k get ingress my-jenkins -n jenkins -o yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
creationTimestamp: "2020-02-12T16:00:40Z"
generation: 1
labels:
app.kubernetes.io/component: jenkins-master
app.kubernetes.io/instance: my-jenkins
app.kubernetes.io/managed-by: Tiller
app.kubernetes.io/name: jenkins
helm.sh/chart: jenkins-1.9.16
name: my-jenkins
namespace: jenkins
resourceVersion: "35776360"
selfLink: /apis/extensions/v1beta1/namespaces/jenkins/ingresses/my-jenkins
uid: d1405c90-4db0-11ea-aa7f-42010a79fa0b
spec:
rules:
- http:
paths:
- backend:
serviceName: my-jenkins
servicePort: 8080
path: /jenkins
status:
loadBalancer: {}
Any idea why is this happening?
You are missing the kubernetes.io/ingress.class: nginx annotation on your Ingress object for Jenkins.
From the nginx-ingress Helm Chart's documentation:
To use, add the kubernetes.io/ingress.class: nginx annotation to your Ingress resources.

Forbidden to access Kubernetes API Server

I have defined a ClusterRole for Prometheus:
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: prometheus
labels:
k8s-app: prometheus
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- endpoints
- services
- nodes
- pods
verbs:
- get
- watch
- list
- nonResourceURLs:
- /metrics
- /api/*
verbs:
- get
Prometheus is able to access the API-Servers /metrics route:
https://10.0.1.104:443/metrics
https://10.0.2.112:443/metrics
But I get "server returned HTTP status 403 Forbidden" on
https://kubernetes.default.svc:443/api/v1/nodes/ip-10-0-0-219.eu-west-1.compute.internal/proxy/metrics
and
https://kubernetes.default.svc:443/api/v1/nodes/ip-10-0-0-219.eu-west-1.compute.internal/proxy/metrics/cadvisor
I thought I had this covered by
- nonResourceURLs:
- /api/*
What am I missing?
I tried this myself and yes nodes/proxy is missing. (it works for me after adding it)
rules:
- apiGroups: [""]
resources:
- namespaces
- endpoints
- services
- nodes
- nodes/proxy <===
- pods
# From my K8s master
$ curl -k -H 'Authorization: Bearer <redacted>' https://localhost:6443/api/v1/nodes/ip-x-x-x-x.us-west-2.compute.internal/proxy/stats/summary
{
"node": {
"nodeName": "ip-x-x-x-x.us-west-2.compute.internal",
"systemContainers": [
{
"name": "kubelet",
"startTime": "2018-10-19T21:02:19Z",
"cpu": {
"time": "2018-11-09T23:51:15Z",
"usageNanoCores": 30779949,
"usageCoreNanoSeconds": 59446529195638
},
....
Removing it:
$ curl -k -H 'Authorization: Bearer <redacted>' https://localhost:6443/api/v1/nodes/ip-x-x-x-x.us-west-2.compute.internal/proxy/stats/summary
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "nodes \"ip-x-x-x-x.us-west-2.compute.internal\" is forbidden: User \"system:serviceaccount:default:prometheus-k8s\" cannot get resource \"nodes/proxy\" in API group \"\" at the cluster scope",
"reason": "Forbidden",
"details": {
"name": "ip-x-x-x-x.us-west-2.compute.internal",
"kind": "nodes"
},
"code": 403
}
For those two endpoints, the rules may be missing nodes/metrics and nodes/proxy for (sub)resources, and possibly the proxy verb.
If acceptable from security standpoint, it will be much easier to assign the cluster-reader role to the prometheus' service account.