Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stop giving root access and start securing your...

tiffany jernigan
May 09, 2024
150

Stop giving root access and start securing your Kubernetes clusters instead

tiffany jernigan

May 09, 2024
Tweet

Transcript

  1. Stop giving root access and start securing your Kubernetes clusters

    instead Tiffany Jernigan tiffanyfayj www. tiffanyfay.dev
  2. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V AUTHENTICATION & AUTHORIZATION • AUTHN (authentication): who are you? • AUTHZ (authorization): what are you allowed to do?
  3. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V AUTHENTICATION & USER PROVISIONING k8s.io/docs/reference/access-authn-authz/authentication/
  4. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V PROVISIONING USERS At least three possibilities: • certificates ◦ can use your own CA (e.g. Vault), or Kubernetes' ◦ warning: Kubernetes API server doesn't support revocation, so you need short-lived certs • OIDC tokens ◦ can use an auth provider of your choice (e.g. okta, keycloak…) or something linked to your cloud's IAM • (ab)use serviceaccounts to provision users (a service account is really just a user named system:serviceaccount:<namespace>:<serviceaccountname>)
  5. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V PROVISIONING USERS • Humans: TLS, OIDC, Service Accounts, etc. • Robots: use Service Accounts
  6. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V • Example creation with OpenSSL: # Generate key and CSR for our user openssl genrsa 4096 > user.key openssl req -new -key user.key \ -subj /CN=ada.lovelace/O=devs/O=ops > user.csr • After that, transfer the CSR to the CA CERTIFICATES
  7. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V # Copy the CSR to the CA # (for instance, a kubeadm-deployed control plane node) # Then generate the cert: sudo openssl x509 -req \ -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key \ -in user.csr -days 1 -set_serial 1234 > user.crt # Copy certificate (user.crt) back to the user! CERTIFICATES – SELF-HOSTED
  8. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V • Or, we can use Kubernetes CA through the CSR API • The Kubernetes cluster admin can submit the CSR like this: kubectl apply -f - <<EOF apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest metadata: name: user=ada.lovelace spec: #expirationSeconds: 3600 request: $(base64 -w0 < user.csr) signerName: kubernetes.io/kube-apiserver-client usages: - digital signature - key encipherment - client auth EOF CERTIFICATES – THROUGH THE CSR API (1)
  9. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V CERTIFICATES – THROUGH THE CSR API (2) • Then approve it: kubectl certificate approve user=ada.lovelace • And retrieve the certificate like this: kubectl get csr user=ada.lovelace -o jsonpath={.status.certificate} | base64 -d > user.crt • Now give back the user.crt file to the user!
  10. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V ONCE WE HAVE OUR CERTIFICATE… # Add the key and cert to our kubeconfig file like this: kubectl config set-credentials ada.lovelace \ --client-key=user.key --client-certificate=user.crt
  11. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V • OIDC is conceptually similar to TLS (but different set of protocols) • On self-hosted clusters, you'd need to add a few command-line flags to API server: ◦ --oidc-issuer-url → URL of the OpenID provider ◦ --oidc-client-id → OpenID app requesting the authentication (=our cluster) • More details on k8s.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens • On managed clusters, there may be ways to achieve the same results, e.g. on EKS: docs.aws.amazon.com/eks/latest/userguide/authenticate-oidc-identity-provider.html TOKENS: OIDC
  12. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V TOKENS: SERVICE ACCOUNT • A Service Account is just a user with a funny name: "system:serviceaccount:<namespace>:<serviceaccountname>" • "Service Account Tokens" are JWT generated by the Kubernetes control plane • By default, in each container, Kubernetes will automatically place a token in that file: /var/run/secrets/kubernetes.io/serviceaccount/token • That token is a token for the Service Account of the Pod that the container belongs to • Kubernetes client libraries know to automatically detect and use that token • We're going to see that in practice!
  13. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V ROLE-BASED ACCESS CONTROL (RBAC)
  14. • authZ: mostly RBAC (perhaps have the other stuff quickly

    on a slide just to mention them, but who cares!) • quick explainer of the concept: "you put permissions in ROLES then you bind the roles to users/groups/serviceaccounts" • example: we gonna [give someone access to a namespace] or [set up permissions for an autoscaler] (or something else if you feel like these examples have been done too many times?) • DEMO FLOW! • then at the end, mention the tools (kubectl auth can-i, access matrix, etc?)
  15. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V RBAC High level idea on Kubernetes: 1. Define a ROLE (or ClusterRole), which is a collection of permissions ("things that can be done") e.g. list pods 2. Bind the ROLE to a USER or GROUP or SERVICEACCOUNT (with a RoleBinding or ClusterRoleBinding)
  16. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V RBAC • kubectl get --raw /api/v1 (core resources with apiVersion: v1) • kubectl get --raw /apis/<group>/<version> (for other resources)
  17. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V Example Role apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: get-pods namespace: devoxx rules: - apiGroups: - "" resources: - pods verbs: - get - list
  18. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V Example RoleBinding apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: get-pods namespace: devoxx roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: get-pods subjects: - kind: ServiceAccount name: default namespace: devoxx
  19. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V RBAC AUDITING After setting permissions, audit them: • kubectl auth can-i --list • kubectl who-can / kubectl-who-can by Aqua Security • kubectl access-matrix / Rakkess (Review Access) by Cornelius Weig • kubectl rbac-lookup / RBAC Lookup by FairwindsOps • kubectl rbac-tool / RBAC Tool by insightCloudSec
  20. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V DEMO
  21. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V k create ns devoxx kubens devoxx k create deploy nginx --image=nginx:1.24.0 k create deploy web --image=nginx:1.24.0 k get pods k run -it tester --rm --image=nixery.dev/shell/kubectl/curl/jq -- sh #check that if we "kubectl get pods" in the pod (it won't work) kubectl get pods kubectl auth can-i --list WINDOW 2 WINDOW 1 DEMO P0
  22. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #create a role that can get pods k create role get-pods \ --verb=get --verb=list \ --resource=pods #bind role (create RoleBinding) to the NS default SA k create rolebinding get-pods --role=get-pods --serviceaccount=devoxx:default kubectl get pods #now "kubectl get pods -v6" so we see the req URL kubectl get pods -v6 #-k, --insecure Allow insecure server connections when using SSL Basically I don't care about the cert shown to me by the Kubernetes API server. I trust that I am talking to my cluster and not some impersonator curl https://$IP:443/api/v1/namespaces/devoxx/ pods -k WINDOW 2 WINDOW 1 DEMO: USING DEFAULT NS SA
  23. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V cat /var/run/secrets/kubernetes.io/serviceaccount/token #copy it and paste it in jwt.io to see what it shows TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) We can also find the HOST:PORT in env as $KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT env curl https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/devoxx/pods -k -H "Authorization: Bearer $TOKEN" | jq .items[].metadata.name #Gets API server certificate curl https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/devoxx/pods -H "Authorization: Bearer $TOKEN" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | jq .items[].metadata.name WINDOW 2 DEMO: USING DEFAULT NS SA
  24. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V k run -it pirate --rm --image=nixery.dev/shell/kubectl/curl/jq -- sh TOKEN="<ctrl-v>" #Gets API server certificate curl https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/devoxx/pods -H "Authorization: Bearer $TOKEN" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | jq .items[].metadata.name #since the token is gone this should fail curl https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api/v1/namespaces/devoxx/pods -H "Authorization: Bearer $TOKEN" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | jq exit WINDOW 1 #In window 2 type exit and hit enter in the pod and wait for it to be completely gone DEMO: BOUND SERVICE ACCOUNT TOKENS
  25. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #create new sa called scaler k create sa scaler #create pod using this new service account k run -it scaler --rm --image=nixery.dev/shell/kubectl/curl/jq --overrides='{ "spec": { "serviceAccount": "scaler" } }' -- sh WINDOW 2 WINDOW 1 DEMO: CREATING A NEW SA
  26. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #add ability to view resources using an existing cluster role k create clusterrolebinding scaler-view --clusterrole=view --serviceaccount=devoxx:scaler #verify it works kubectl get all #you only have view, so this should fail kubectl scale deployment nginx --replicas=2 WINDOW 2 WINDOW 1 DEMO: FACTORY ROLES
  27. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #create a role that can scale deployments k create role scaler --verb=patch --resource=deployments/scale --resource-name=nginx #bind role (create RoleBinding) to the scaler SA k create rolebinding scaler --role=scaler --serviceaccount=devoxx:scaler kubectl scale deployment nginx --replicas=2 #will fail since it is tied to nginx kubectl scale deployment web --replicas=2 kubectl delete deployment nginx WINDOW 2 WINDOW 1 DEMO: SCALER
  28. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #remove scaler-view clusterrolebinding k delete clusterrolebinding scaler-view #will fail since you can't get the deployment anymore kubectl scale deployment nginx --replicas=2 WINDOW 2 WINDOW 1 DEMO: SCALER
  29. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V #edit scaler role to add get to the nginx deployment k edit role scaler #Add another apigroup under the rules like this: rules: - apiGroups: - apps resourceNames: - nginx resources: - deployments verbs: - get kubectl scale deploy nginx --replicas=1 #will fail since you can't get any other resources kubectl get deploy kubectl get deploy nginx exit k delete ns devoxx WINDOW 2 WINDOW 1 DEMO: SCALER
  30. T I F F A N Y F A Y

    J W W W . T I F F A N Y F A Y . D E V Feedback Link: https://forms.gle/3B58uuxxwxqDHkDX6