K8S ConfigMap Merging Controller

ConfigMap Merging Controller (cmmc)

cmmc is a k8s operator that allows for the merging of ConfigMap resources with data validation.

Why?

The impetus for building this is to have a GitOps friendly solution to manage kube-system/aws-auth, a ConfigMap that binds AWS roles to K8S Roles in EKS (link).

Instead of solving the problem directly, our approach was to ask the question: If another tool existed that would make this problem trivial to solve that wasn't specific to this specific use-case, what would it be?

Features

  • Watch specific keys of ConfigMaps and merge their results into a target.
  • JSON Schema Validation for the target ConfigMap (possibly add other validation in the future).
  • Fully Configurable source/target selectors mix and match
  • Changes to resources are non-destructive and recoverable
  • Permissions gated by namespace selectors (if desired)
  • Metrics exposed for how many resources are being watched/updated, and their states.

How

The operator is built on the kubebuilder library and has two controllers/reconcilers, one for the MergeSource resource and one for MergeTarget.

Resources & Configuration

MergeSource

apiVersion: config.cmmc.k8s.cash.app/v1beta1
kind: MergeSource
metadata:
  name: merge-map-roles-aws-auth
spec:
  selector: 
    cmmc.k8s.cash.app/merge: "something"
  source:
    data: someKey
  target: 
    name: our-merge-target
    data: someKey
  • A MergeSource describes what ConfigMap resource we are watching with its selector field.
    So any ConfigMap with a label that matches spec.selector will be watched.
  • The controller will read data from the source.data field on a matching ConfigMap
  • The MergeSource will annotate the watched CMs so they know they are being watched.
  • This resource/controller does no mutatations of the data on any of the resources outside of the annotation!
  • Annotations are cleaned up when the resource is deleted.
  • The MergeTarget at spec.target.name will watch for MergeSource resources with it as the target and read their aggregated states to attempt to write to the target ConfigMap.

MergeTarget

apiVersion: config.cmmc.k8s.cash.app/v1beta1
kind: MergeTarget
metadata:
  name: our-merge-target
spec:
  target: some-ns/some-resource-name # a configMap
  data:
    someKey: 
      init: ''
      jsonSchema: |
        { … }
  • A MergeTarget describes the resource we are managing, in this case it is some-ns/some-resource-name.
  • We can configure this resource with the keys that we care about managing on the target, above its someKey.
  • Each data[$key]
    • Can have an initial value that we'll inject if the data was not present the key was missing or empty
    • Can have an optional jsonSchema that we use to validate the data before it is persisted.
  • Creates the ConfigMap if it doesn't exist.
  • Uses annotations to make sure there is only one MergeTarget per spec.target
  • Clean up after itself when it is deleted.
    • If it didn't eist, it will be removed
    • If it did exist, the data will be reset back to what it was before.

Demo (aws-auth)

Let's build a solution for managing kube-system/aws-auth.

1. Create a MergeTarget

A MergeTarget describes the target ConfigMap that want to write the data to.

cat <<EOF | kubectl apply -f -
apiVersion: config.cmmc.k8s.cash.app/v1beta1
kind: MergeTarget
metadata:
  name: kube-system-aws-auth
spec:
  target: kube-system/aws-auth
  data:
    mapRoles: {}
    mapUsers: {}
EOF

This says that we want to write/merge data to the mapRoles and mapUsers keys of kube-system/aws-auth. Note, there is no auth, or initial value for these keys in this example, but we can add this later on.

2. Create a MergeSource for mapRoles

A MergeSource describes what ConfigMaps we are watching to write to the target. This one specifically looks for ConfigMap resources with the label: cmmc:k8s.cash.app/merge: "aws-auth-map-roles".

target.name refers to the MergeTarget we created earlier.

cat <<EOF | kubectl apply -f -
apiVersion: config.cmmc.k8s.cash.app/v1beta1
kind: MergeSource
metadata:
  name: aws-auth-map-roles
spec:
  selector:
    cmmc.k8s.cash.app/merge: "aws-auth-map-roles"
  source:
    data: mapRoles
  target:
    name: kube-system-aws-auth 
    data: mapRoles
EOF

3. Create some sample ConfigMap sources

Let's create a sample configuration for two services/namespaces, service-a and service-b, which need some role binding from AWS to K8S.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: service-a
---
apiVersion: v1
kind: Namespace
metadata:
  name: service-b
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-roles-mapping
  namespace: service-a
  labels:
    cmmc.k8s.cash.app/merge: "aws-auth-map-roles"
data:
  mapRoles: |
    - arn: arn:aws:iam::111122223333:role/external-user-service-a
      username: service-a-external
      groups:
      - service-a
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-roles-mapping
  namespace: service-b
  labels:
    cmmc.k8s.cash.app/merge: "aws-auth-map-roles"
data:
  mapRoles: |
    - arn: arn:aws:iam::111122223333:role/external-user-service-b
      username: service-b-external
      groups:
      - service-b
EOF

4. Check the resources

Target
kubectl get cm -n kube-system aws-auth -o yaml
apiVersion: v1
data:
  mapRoles: |
    - arn: arn:aws:iam::111122223333:role/external-user-service-b
      username: service-b-external
      groups:
      - service-b
    - arn: arn:aws:iam::111122223333:role/external-user-service-a
      username: service-a-external
      groups:
      - service-a
kind: ConfigMap
metadata:
  annotations:
    config.cmmc.k8s.cash.app/managed-by-merge-target: default/kube-system-aws-auth
  name: aws-auth
  namespace: kube-system
Statuses
# kubectl get mergetarget
NAME                   TARGET                 READY   STATUS                         VALIDATION
kube-system-aws-auth   kube-system/aws-auth   True    Target ConfigMap up to date.   1 MergeSources reporting valid data
# kubectl get mergesource
NAME                 READY   STATUS
aws-auth-map-roles   True    Data from 2 ConfigMap(s)

Cleanup

kubectl delete ns service-a
kubectl delete ns service-b
kubectl delete mergesource aws-auth-map-roles
kubectl delete mergetarget kube-system-aws-auth
Comments
  • CMMC fails merging `mapRoles` and `mapUsers` at the same time

    CMMC fails merging `mapRoles` and `mapUsers` at the same time

    I have a config map with mapRoles and mapUsers define that I want CMMC to merge in the aws-auth configmap. Unfortunately, it only merge the mapUsers and ignore the mapRoles.


    Here is my configmap CMMC will grab

    apiVersion: v1
    data:
      mapRoles: |
        - "groups":
          - "system:masters"
          "rolearn": "arn:aws:iam::261357321482:role/shuffle-labs-atlantis-dev-ecs_task_execution"
          "username": "atlantis"
      mapUsers: |
        - "groups":
          - "system:masters"
          "userarn": "arn:aws:iam::261357321482:user/ben"
          "username": "ben"
    kind: ConfigMap
    metadata:
      annotations:
        config.cmmc.k8s.cash.app/watched-by-merge-source: kube-system/aws-auth-map-users
      labels:
        cmmc.k8s.cash.app/merge: aws-auth-map
      name: aws-auth-mapping-cmmc
      namespace: kube-system
    

    Here is the merged aws-auth configmap

    apiVersion: v1
    data:
      mapRoles: |
        - groups:
          - system:bootstrappers
          - system:nodes
          rolearn: arn:aws:iam::261357321482:role/shuffle-labs-private_eks-dev-eks-node
          username: system:node:{{EC2PrivateDNSName}}
      mapUsers: |
        - "groups":
          - "system:masters"
          "userarn": "arn:aws:iam::261357321482:user/ben"
          "username": "ben"
    kind: ConfigMap
    metadata:
      annotations:
        config.cmmc.k8s.cash.app/managed-by-merge-target: kube-system/kube-system-aws-auth
      name: aws-auth
      namespace: kube-system
    
  • docs/feature: How to deploy

    docs/feature: How to deploy

    I wanna use cmmc for managing my eks auth but I did not find any information explaining how to deploy it on a kubernetes cluster. Is there any guide I can follow? Thanks

  • feat: Allow multiple mergesources

    feat: Allow multiple mergesources

    Append multiple mergesource's names to the annotation in the configmap (separated with ,) in order to allow multiple mergesource to target the same configmap.

    The use case is mapRoles and mapUsers for managing the aws-auth config map.

    Fix https://github.com/cashapp/cmmc/issues/7

    (I tested it locally and bappr/cmmc:v0.0.2 is running in my infra)

  • Removing a data key from a `MergeTarget` does not revert the data.

    Removing a data key from a `MergeTarget` does not revert the data.

    A simple workaround for this right now is to remove the MergeTarget and then create it without the missing key.

    Ideally the behavior is that the key is reverted as if ^ the above action is taken.

  • feat: adding flags for MaxConcurrentReconciles

    feat: adding flags for MaxConcurrentReconciles

    Adding flags:

    • merge-source-max-concurrent-reconciles (default 1)
    • merge-target-max-concurrent-reconciles (default 1)
    • help (prints help/usage)

    Closes #6.

  • Cleanup test-suite

    Cleanup test-suite

    Closes #11

    This patch:

    • updates the gomega libs
    • uses the primitives a bit more idiomatically
    • does a bunch of cleanup/DRYing up the tests so it's easier to add assertions/etc
  • Controller test cleanup

    Controller test cleanup

    After https://github.com/cashapp/cmmc/pull/10, the controller tests are fairly brittle and hard to change/read. Need to invest some time into cleaning them up so they are easier to understand.

  • docs: Metrics

    docs: Metrics

    Document the custom metrics that are emitted by CMMC.

    https://github.com/cashapp/cmmc/blob/b3d8bd9cc47996c8b5422db9c6c22904a5d338aa/util/metrics/metrics.go#L17-L32

  • jsonSchemaFromRef

    jsonSchemaFromRef

    Currently, we support adding JSONSchema as a field that is an inlined string, it would be nice to also be able to reference a ConfigMap here so it can be managed separately.

    This is not super important, but can be a nice to have.

  • Permissive mode for MergeTarget?

    Permissive mode for MergeTarget?

    Currently, once a MergeTarget manages a ConfigMap, any changes to the managed resource will be overwritten by CMMC. It might be useful to allow a permissive mode to the MergeTarget (probably per key), so that a change to the target, as long as it doesn't intersect with anything that CMMC is writing, will be persisted.

    Rough notes here on how this could work during reconciliation:

    • we do all target CM data manipulation in MergeTarget.ReduceDataState
    • While the output of the target config map is deterministic generally, the order of the merge depends on the order of the MergeSourceList, and we can make sure that this has a deterministic order as well (a subtask).
    • If that merge order is deterministic, we should be able to do some simpler diffing to see what features has been added/removed given the prior Init state.
    • We probably want to update the Init state to reflect this.
    • If someone appends to the target CM, we currently have only one initial state, not a bunch of patches, so we would (in the current implementation), reorder the target. Does this matter? Would we be able to maintain this order going forward? Probably not since someone can then change contributing sources to add things so we might want to keep the simple thing here.
  • bug: YAML-decode is implicit with the JSONSchema Validator

    bug: YAML-decode is implicit with the JSONSchema Validator

    This should either probably be resolved, or included in the design for:

    • #3

    Because JSON is a subset of YAML, we do YAML parse of everything that has a JSONSchema. https://github.com/cashapp/cmmc/blob/b3d8bd9cc47996c8b5422db9c6c22904a5d338aa/util/validator/validator.go#L12

    1. This should not be implicit
    2. There should be options for this
  • feature: Custom Validators (webhooks)

    feature: Custom Validators (webhooks)

    Currently, cmmc only supports doing JSONSchema validation.

    It would be nice to support something like a custom validators via some syntax in the MergeTarget like:

       # ....
       data: 
         key:
           validator:
             url: <...>
    

    For this to be usable, this should also have a proto definition of a request/response spec of a sort.

A handy utility to generate configmap and values.yaml of your application for helmifying them

Helmfig Are you tired of writing values.yaml for configmap of your project when you are helmifying them? Helmfig is a handy tool that can generate the

Dec 14, 2022
Image clone controller is a kubernetes controller to safe guard against the risk of container images disappearing

Image clone controller image clone controller is a kubernetes controller to safe guard against the risk of container images disappearing from public r

Oct 10, 2021
A Controller written in kubernetes sample-controller style which watches a custom resource named Bookstore

bookstore-sample-controller A Controller written in kubernetes sample-controller style which watches a custom resource named Bookstore. A resource cre

Jan 20, 2022
Simple wrapper around multiple fs.FS instances, recursively merging them together dynamically.

go-layerfs This is a simple wrapper around multiple fs.FS instances, recursively merging them together dynamically. If you have two directories, of wh

Aug 9, 2022
A controller to create K8s Ingresses for Openshift routes.

route-to-ingress-operator A controller to create corresponding ingress.networking.k8s.io/v1 resources for route.openshift.io/v1 TODO int port string p

Jan 7, 2022
The k8s-generic-webhook is a library to simplify the implementation of webhooks for arbitrary customer resources (CR) in the operator-sdk or controller-runtime.

k8s-generic-webhook The k8s-generic-webhook is a library to simplify the implementation of webhooks for arbitrary customer resources (CR) in the opera

Nov 24, 2022
K8s controller implementing Multi-Cluster Services API based on AWS Cloud Map.

AWS Cloud Map MCS Controller for K8s Introduction AWS Cloud Map multi-cluster service discovery for Kubernetes (K8s) is a controller that implements e

Dec 17, 2022
Ejemplo de un k8s custom controller para un CRD nuevo

Clonado de kubernetes/sample-controller Para pruebas de un CRD nuevo This repository implements a simple controller for watching Foo resources as defi

Nov 3, 2021
Controller-check - Run checks against K8s controllers to verify if they meets certain conventions

controller-check Run checks against K8s controllers to verify if they meets cert

Jan 4, 2022
K8s-delete-protection - Kubernetes admission controller to avoid deleteing master nodes

k8s-delete-protection Admission Controller If you want to make your Kubernetes c

Nov 2, 2022
K8s-cinder-csi-plugin - K8s Pod Use Openstack Cinder Volume

k8s-cinder-csi-plugin K8s Pod Use Openstack Cinder Volume openstack volume list

Jul 18, 2022
K8s-ingress-health-bot - A K8s Ingress Health Bot is a lightweight application to check the health of the ingress endpoints for a given kubernetes namespace.

k8s-ingress-health-bot A K8s Ingress Health Bot is a lightweight application to check the health of qualified ingress endpoints for a given kubernetes

Jan 2, 2022
K8s-go-structs - All k8s API Go structs

k8s-api go types Why? Its nice to have it all in a single package. . |-- pkg |

Jul 17, 2022
Annotated and kubez-autoscaler-controller will maintain the HPA automatically for kubernetes resources.

Kubez-autoscaler Overview kubez-autoscaler 通过为 deployment / statefulset 添加 annotations 的方式,自动维护对应 HorizontalPodAutoscaler 的生命周期. Prerequisites 在 kuber

Jan 2, 2023
network-node-manager is a kubernetes controller that controls the network configuration of a node to resolve network issues of kubernetes.
network-node-manager is a kubernetes controller that controls the network configuration of a node to resolve network issues of kubernetes.

Network Node Manager network-node-manager is a kubernetes controller that controls the network configuration of a node to resolve network issues of ku

Dec 18, 2022
A Kubernetes Terraform Controller
A Kubernetes Terraform Controller

Terraform Controller Terraform Controller is a Kubernetes Controller for Terraform, which can address the requirement of Using Terraform HCL as IaC mo

Jan 2, 2023
Carrier is a Kubernetes controller for running and scaling game servers on Kubernetes.
Carrier is a Kubernetes controller for running and scaling game servers on Kubernetes.

Carrier is a Kubernetes controller for running and scaling game servers on Kubernetes. This project is inspired by agones. Introduction Genera

Nov 25, 2022
A fluxcd controller for managing remote manifests with kubecfg

kubecfg-operator A fluxcd controller for managing remote manifests with kubecfg This project is in very early stages proof-of-concept. Only latest ima

Nov 1, 2022
the simplest testing framework for Kubernetes controller.

KET(Kind E2e Test framework) KET is the simplest testing framework for Kubernetes controller. KET is available as open source software, and we look fo

Dec 10, 2022