How to create a limited access token for Kubernetes - authentication

I want to have a token that to use in a piece of code that has limited access to my k8s cluster and just be able to read the replicas of statefulsets and if required scale them, I don't want the person who uses that code be able to launch new stuff or delete running ones.
Is this possible? If yes how I can do it?

You need RBAC (Role Based Access Control) for this job.
Sample pod-reader role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
Bind this role to a user:
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane # Name is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
Change the apiGroup and verbs based on your requirements.

Related

Kubernetes 403 Forbidden querying API with good RBAC credentials

Final update: Apparently there was some sort of issue with the variables defined in the curl command when redefined them after closing the connection to the cluster, command started working.
The setup is simple, on learning environment.
i created ServiceAccount, Role & Rolebinding
Trying to query pods or Services, i'm getting:
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "services is forbidden: User \"system:serviceaccount:default:myscript\" cannot list resource \"services\" in API group \"\" in the namespace \"default\"",
"reason": "Forbidden",
"details": {
"kind": "services"
},
"code": 403
I don't know where i'm failing.
Originally i had only get, list and delete verbs. but even after using wildcard '*' keeps saying forbidden.
Here's some info from the cluster:
Query command: curl -X GET $SERVER/api/v1/namespaces/default/services --header "Authorization: Bearer $MYSCRIPT_TOKEN" --cacert /etc/kubernetes/pki/ca.crt
ubuntu#master:~/$ kubectl describe sa myscript
Name: myscript
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: myscript-token
Events: <none>
ubuntu#master:~/$ kubectl get role script-role
NAME CREATED AT
script-role 2022-09-04T10:44:22Z
ubuntu#master:~/$ kubectl get rolebinding script-rb -o wide
NAME ROLE AGE USERS GROUPS SERVICEACCOUNTS
script-rb Role/script-role 57m default/myscript
ubuntu#master:~/$ kubectl describe role script-role
Name: script-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [*]
services [] [] [*]
deployments.apps [] [] [get list delete]
Update:
few can-i commands that evidence RBAC should be good.
ubuntu#master:~$ kubectl auth can-i get services --as system:serviceaccount:default:myscript
yes
ubuntu#master:~$ kubectl auth can-i list services --as system:serviceaccount:default:myscript
yes
ubuntu#master:~$ kubectl auth can-i delete services --as system:serviceaccount:default:myscript
yes
ubuntu#master:~$ kubectl auth can-i delete deploy --as system:serviceaccount:default:myscript
yes
ubuntu#master:~$ kubectl auth can-i update deploy --as system:serviceaccount:default:myscript
no
ServiceAccount manifest.
ubuntu#master:~$ kubectl get sa myscript -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2022-09-04T10:35:47Z"
name: myscript
namespace: default
resourceVersion: "675592"
uid: ab3b3c20-e3b9-405a-a9e9-e4f65ac13f5c
Role manifest
ubuntu#master:~$ kubectl get role script-role -o yaml
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":"script-role","namespace":"default"},"rules":[{"apiGroups":[""],"resources":["pods","services"],"verbs":["get","list","delete"]},{"apiGroups":["apps"],"resources":["deployments"],"verbs":["get","list","delete"]}]}
creationTimestamp: "2022-09-04T10:44:22Z"
name: script-role
namespace: default
resourceVersion: "681508"
uid: a1b03864-081e-4d0a-bf54-9c69f6f6c17e
rules:
- apiGroups:
- ""
resources:
- pods
- services
verbs:
- '*'
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- delete
RoleBinding manifest
ubuntu#master:~$ kubectl get rolebinding script-rb -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"RoleBinding","metadata":{"annotations":{},"creationTimestamp":null,"name":"script-rb","namespace":"default"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"Role","name":"script-role"},"subjects":[{"kind":"ServiceAccount","name":"myscript","namespace":"default"}]}
creationTimestamp: "2022-09-04T10:46:05Z"
name: script-rb
namespace: default
resourceVersion: "676627"
uid: dbdcef8f-6a30-4cd3-8152-2626c2284c83
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: script-role
subjects:
- kind: ServiceAccount
name: myscript
namespace: default
2 questions:
Can you share the manifests of Role, RoleBinding and ServiceAccount?
Are you able to verify the working of your Role & RoleBinding with ServiceAccount using the kubectl auth can-i command?
// kubectl auth can-i <verb> <resource> -n <namespace> --as system:service:<namespace>:<service-account-name>
kubectl auth can-i get service --as system:serviceaccount:default:myscript

Metallb L2Advertisement/IPAdressPools assignments behave strangely

I'm using metallb 0.13.4 L2, I have below IP advertisements and pools. Nginx grabs the right IP address and metallb speakers announce it properly. So IP addresses are correctly assigned.
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: external-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- external-pool
nodeSelectors:
- matchLabels:
kubernetes.io/os: linux
kubernetes.io/arch: amd64
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: internal-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- internal-pool
nodeSelectors:
- matchLabels:
kubernetes.io/os: linux
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: external-pool
namespace: metallb-system
spec:
addresses:
- x.x.x.204/32
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: internal-pool
namespace: metallb-system
spec:
addresses:
- x.x.x.203/32
Nginx configs
....
controller:
annotations:
metallb.universe.tf/address-pool: external-pool
....
---
....
controller:
annotations:
metallb.universe.tf/address-pool: internal-pool
....
and from nginx controller events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal nodeAssigned 4m6s (x1173 over 19h) metallb-speaker announcing from node [redacted] with protocol "layer2"
See the (x1173 over 19h) so weird ? And when I look at the Ingresses their IP addresses change constantly but they are assigned to either internal or external nginx classes.
$ kl get ingressclass
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 5d6h
nginx-internal k8s.io/ingress-nginx <none> 5d6h
Although Ingress IPs constantly change between x.x.x.203 and x.x.x.204???, they always responds on the assigned IP address!!! This definitely looks very strange.
Note: I wasn't sure about the help in metallb project, that's why I'm creating the issue here.
The problem was the annotations on the controller, they should be at controller.service; Here is the working configuration;
controller:
service:
externalTrafficPolicy: Local
type: LoadBalancer
loadBalancerIP: x.x.x.203
annotations:
metallb.universe.tf/address-pool: "internal-pool"
Additionally, service must be type LoadBalancer and IP is specified.

How to create service account for Spinnaker

I want to automate pipeline triggers by using fiat service account. So I follow the Spinnaker doc: https://www.spinnaker.io/setup/security/authorization/service-accounts/ Then i have trouble to run the curl command. Where should I run it? I tried to run in local machine which is installed halyard and fiat pod in Kubernetes. However, I got cannot resolve http://front50.url:8080.
Create Role for spinnaker with role name spinnaker-role you can edit role as per you need
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: spinnaker-role
namespace: default
rules:
- apiGroups: [""]
resources: ["namespaces", "configmaps", "events", "replicationcontrollers", "serviceaccounts", "pods/logs"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods", "services", "secrets"]
verbs: ["*"]
- apiGroups: ["autoscaling"]
resources: ["horizontalpodautoscalers"]
verbs: ["list", "get"]
- apiGroups: [“apps”]
resources: [“controllerrevisions”, "statefulsets"]
verbs: [“list”]
- apiGroups: ["extensions", "app"]
resources: ["deployments", "replicasets", "ingresses"]
verbs: ["*"]
Service account for spinnaker
apiVersion: v1
kind: ServiceAccount
metadata:
name: spinnaker-service-account
namespace: default
Main part role binding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: spinnaker-role-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: spinnaker-role
subjects:
- namespace: default
kind: ServiceAccount
name: spinnaker-service-account
You can edit it as per your need changing statefulset adding deployments
This url is just an example and won't work. You need to access it using the service that exposes front50. If you installed using Halyard, probably the service is exposed as spin-front50:8080
I ran it in halyard and used the URL
(I know its really long time after your question :), I just happened to see this and it's better late than never.)
You have to port-forward into the pod, and curl your localhost with the port created for that pod, during port-forwarding.

Istio Authorization with JWT

I am running isio 1.0.2 and am unable to configure service authorization based on JWT claims against Azure AD.
I have succesfully configured and validated Azure AD oidc jwt end user authentication and it works fine.
Now I'd like to configure RBAC Authorization using request.auth.claims["preferred_username"] attribute.
I've created a ServiceRoleBinding like below:
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
name: service-reader
namespace: default
spec:
rules:
- services: ["myservice.default.svc.cluster.local"]
methods: ["GET"]
paths: ["*/products"]
---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
name: service-reader-binding
namespace: default
spec:
subjects:
- properties:
source.principal: "*"
request.auth.claims["preferred_username"]: "user#company.com"
roleRef:
kind: ServiceRole
name: "service-reader"
However, I keep getting 403 Forbidden from the service proxy, even though preferred_username claim from Authentication header is correct.
If I comment out request.auth.claims["preferred_username"]: "user#company.com" line the request succeeds.
Can anyone point me in the right direction regarding configuring authorization based on oidc and jwt?
Never mind. I found the problem.
I was missing user: "*" check to allow all users.
so under subjects it should say:
subjects:
- user: "*"
properties:
source.principal: "*"
request.auth.claims["preferred_username"]: "user#company.com"
That fixes it.

Kubernetes PersistentVolume and PersistentVolumeClaim could be causing issues for my pod which crashes while copying logs

I have a PersistentVolume that I specified as the following:
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv-shared
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 5Gi
hostPath:
path: /data/mypv-shared/
Then I created a PersistentVolumeClaim with the following specifications:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypv-shared-claim
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
But when I create the PVC, running kubectl get pv shows that it is bound to a randomly generated PV
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-38c77920-a223-11e7-89cc-08002719b642 5Gi RWX Delete Bound default/mypv-shared standard 16m
I believe this is causing issues for my pods when running tests because I am not sure if the pod is correctly mounting the specified directory. My pods crash at the end of the test when trying to copy over the test logs at the end of the run.
Is the cause really the persistentVolume/Claim or should I be looking into something else? Thanks!
Creating the PVC dynamically provisioned a PV instead of using the one you created manually with the hostpath. On the PVC simply set .spec.storageClassName to and an empty string ("")
From the documentation:
A PVC with its storageClassName set equal to "" is always interpreted to be requesting a PV with no class, so it can only be bound to PVs with no class (no annotation or one set equal to ""). A PVC with no storageClassName is not quite the same ...
So create something like this (I've also added labels and selectors to make sure that the intended PV is paired up the PVC; you might not need that constraint):
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv-shared
labels:
name: mypv-shared
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 5Gi
hostPath:
path: /data/mypv-shared/
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypv-shared-claim
spec:
storageClassName: ""
selector:
matchLabels:
name: mypv-shared
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi