SparebankenVest / azure-key-vault-to-kubernetes

Azure Key Vault to Kubernetes (akv2k8s for short) makes it simple and secure to use Azure Key Vault secrets, keys and certificates in Kubernetes.
https://akv2k8s.io
Apache License 2.0
437 stars 97 forks source link

[DOCUMENTATION] Best practices #532

Open tspearconquest opened 1 year ago

tspearconquest commented 1 year ago

Is your documentation request related to a problem? Please describe. I noticed last week that when watchAllNamespaces and env_injector.enabled are set to true in the helm chart, AKV2K8S installs a cluster role binding which allows every service account in the cluster to query the API for AKVS resources. See https://github.com/SparebankenVest/public-helm-charts/blob/master/stable/akv2k8s/templates/env-injector-rbac.yaml#L84:

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

The issue I'm raising is that this group includes the default service account in every namespace; whose use is discouraged by best practices. Since this group includes every service account in the cluster, this cluster role binding is granting privileges to all service accounts in the cluster to query AKVS resources from any namespace to any other namespace in the cluster when those service accounts normally would have no privileges to the API at all.

This is a weak configuration which should be eliminated through more granular access controls.

Describe the solution you'd like There should be a best practices page in the documentation which outlines exactly what best practices should be followed for deploying AKV2K8S. In this case, when deploying the environment injector with watchAllNamespaces set to true, the best practice is to explicitly name the service accounts in the cluster who need access to query AKVS resources, and apply a role binding on the namespace to the service accounts which binds them to the cluster role so that they can only query the AKVS resources in the same namespace.

In order to accomplish this, it becomes necessary to modify the helm chart as well. The chart should allow for this setup to be configured easily, maybe by a production: true value or some such.

Describe alternatives you've considered In my environment, I'm modifying the rendered chart with kustomize to remove from the akv2k8s deployment the default cluster role binding which is created due to setting watchAllNamespaces: true, and then I am having to centrally manage the service accounts with our own home-made chart that can dynamically add the service accounts and role bindings to the akv2k8s cluster role as necessary.

tspearconquest commented 1 year ago

Fixes for the helm chart to make supporting this easier have been merged:

These are available in chart version 2.4.2

With the first PR, I can change the subjects for the env-injector in the values.yaml:

akv2k8s:
  env_injector:
    rbacSubjects:
    - kind: ServiceAccount
      name: fooServiceAccount
      namespace: someNamespace

Note: controller was not updated -- we don't use the controller in my env, so I set this up only for the env-injector, but if someone wants to refactor this to make it part of global with overrides in each of the 2 service-specific blocks, please feel free.

With the second PR, I can make this helm chart a dependency in my "central management" project and set the following values to deploy the RBAC only for the controller and env-injector:

akv2k8s:
  global:
    rbac:
      create: true
  controller:
    enabled: false
  env_injector:
    enabled: false

Or I can set the below to deploy the RBAC only for the env-injector:

akv2k8s:
  global:
    rbac:
      create: false
  controller:
    enabled: false
  env_injector:
    enabled: false
    rbac:
      create: true

Or I can set the below to deploy the RBAC only for the controller:

akv2k8s:
  global:
    rbac:
      create: false
  controller:
    enabled: false
    rbac:
      create: true
  env_injector:
    enabled: false

These examples will deploy the default rbac subjects still, but since we can also override the subject, we can do something like:

akv2k8s:
  global:
    rbac:
      create: false
  controller:
    enabled: false
  env_injector:
    enabled: false
    rbac:
      create: true
    rbacSubjects:
    - kind: ServiceAccount
      name: fooServiceAccount
      namespace: someNamespace