Skip to content

Specification for binding services to k8s applications

License

Notifications You must be signed in to change notification settings

baijum/service-binding-specification

 
 

Service Binding Specification for Kubernetes

Today in Kubernetes, the exposure of secrets for connecting applications to external services such as REST APIs, databases, event buses, and more is both manual and bespoke. Each service provider suggests a different way to access their secrets and each application developer consumes those secrets in a way that is custom to their applications. While there is a good deal of value to this level of flexibility, large development teams lose overall velocity dealing with each unique solution. To combat this, we already see teams adopting internal patterns for how to achieve this application-to-service linkage.

The goal of this specification is to create a Kubernetes-wide specification for communicating service secrets to applications in an automated way. It aims to create a mechanism that is widely applicable, but without excluding other strategies for systems that it does not fit easily. The benefit of a Kubernetes-wide specification is that all of the actors in an ecosystem can work towards a clearly defined abstraction at the edge of their expertise and depend on other parties to complete the chain.

  • Application Developers expect secrets to be exposed in a consistent and predictable way
  • Service Providers expect their secrets to be collected and exposed to users in a consistent and predictable way
  • Platforms expect to retrieve secrets from Service Providers and expose them to Application Developers in a consistent and predictable way

The pattern of Service Binding has prior art in non-Kubernetes platforms. Heroku pioneered this model with Add-ons and Cloud Foundry adopted similar ideas with their Services. Other open source projects like the Open Service Broker aim to help with this pattern on those non-Kubernetes platforms. In the Kubernetes ecosystem, the CNCF Sandbox Cloud Native Buildpacks project has proposed a buildpack-specific specification exclusively addressing the application developer portion of this pattern.

Community

The Service Binding Specification for Kubernetes project is a community lead effort. A bi-weekly working group call is open to the public. Discussions occur here on GitHub and on the #bindings-discuss channel in the Kubernetes Sack.

Behavior within the project is governed by the Contributor Covenant Code of Conduct.

If you catch an error in the specification’s text, or if you write an implementation, please let us know by opening an issue or pull request at our GitHub repository.



Notational Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in RFC 2119.

The key words "unspecified", "undefined", and "implementation-defined" are to be interpreted as described in the rationale for the C99 standard.

An implementation is not compliant if it fails to satisfy one or more of the MUST, MUST NOT, REQUIRED, SHALL, or SHALL NOT requirements for the protocols it implements. An implementation is compliant if it satisfies all the MUST, MUST NOT, REQUIRED, SHALL, and SHALL NOT requirements for the protocols it implements.

Terminology definition

Duck Type
Any type that meets the contract defined in a specification, without being an instance of a specific concrete type. For example, for specification that requires a given key on status, any resource that has that key on its status regardless of its kind would be considered a duck type of the specification.
Service
Any software that exposes functionality. Examples include a database, a message broker, an application with REST endpoints, an event stream, an Application Performance Monitor, or a Hardware Security Module.
Application
Any process, running within a container. Examples include a Spring Boot application, a NodeJS Express application, or a Ruby Rails application. Note: This is different than an umbrella application as defined by the Kubernetes SIG, which refers to a set of micro-services.
Service Binding
The act of or representation of the action of providing information about a Service to an Application
ConfigMap
A Kubernetes ConfigMap
Secret
A Kubernetes Secret

Provisioned Service

A Provisioned Service resource MUST define a .status.binding.name which is a LocalObjectReference-able to a Secret. The Secret MUST be in the same namespace as the resource. The Secret SHOULD contain a type entry with a value that identifies the abstract classification of the binding. It is RECOMMENDED that the Secret also contain a provider entry with a value that identifies the provider of the binding. The Secret MAY contain any other entry.

Extensions and implementations MAY define additional mechanisms to consume a Provisioned Service that does not conform to the duck type.

Resource Type Schema

status:
  binding:
    name:  # string

Example Resource

...
status:
  ...
  binding:
    name: production-db-secret

Well-known Secret Entries

Other than the recommended type and provider entries, there are no other reserved Secret entries. In the interests of consistency, if a Secret includes any of the following entry names, the entry value MUST meet the specified requirements:

Name Requirements
host A DNS-resolvable host name or IP address
port A valid port number
uri A valid URI as defined by RFC3986
username A string-based username credential
password A string-based password credential
certificates A collection of PEM-encoded X.509 certificates, representing a certificate chain used in mTLS client authentication
private-key A PEM-encoded private key used in mTLS client authentication

Secret entries that do not meet these requirements MUST use different entry names.

Example Secret

apiVersion: v1
kind: Secret
metadata:
  name: production-db
stringData:
  type: mysql
  provider: bitnami
  host: localhost
  port: 3306
  username: root
  password: root

Application Projection

A Binding Secret MUST be volume mounted into a container at $SERVICE_BINDING_ROOT/<binding-name> with directory names matching the name of the binding. Binding names MUST match [a-z0-9\-\.]{1,253}. The $SERVICE_BINDING_ROOT environment variable MUST be declared and can point to any valid file system location.

The Secret MUST contain a type entry with a value that identifies the abstract classification of the binding. It is RECOMMENDED that the Secret also contain a provider entry with a value that identifies the provider of the binding. The Secret MAY contain any other entry.

The name of a secret entry file name SHOULD match [a-z0-9\-\.]{1,253}. The contents of a secret entry may be anything representable as bytes on the file system including, but not limited to, a literal string value (e.g. db-password), a language-specific binary (e.g. a Java KeyStore with a private key and X.509 certificate), or an indirect pointer to another system for value resolution (e.g. vault://production-database/password).

The collection of files within the directory MAY change between container launches. The collection of files within the directory SHOULD NOT change during the lifetime of the container.

Example Directory Structure

$SERVICE_BINDING_ROOT
├── account-database
│   ├── type
│   ├── provider
│   ├── uri
│   ├── username
│   └── password
└── transaction-event-stream
    ├── type
    ├── connection-count
    ├── uri
    ├── certificates
    └── private-key

Service Binding

A Service Binding describes the connection between a Provisioned Service and an Application Projection. It is codified as a concrete resource type. Multiple Service Bindings can refer to the same service. Multiple Service Bindings can refer to the same application. An exemplar CRD can be found here.

Restricting service binding to resources within the same namespace is strongly RECOMMENDED. Cross-namespace service binding SHOULD be secured appropriately by the implementor to prevent attacks like privilege escalation and secret enumeration.

A Service Binding resource MUST define a .spec.application which is an ObjectReference-like declaration to a PodSpec-able resource. A ServiceBinding MAY define the application reference by-name or by-label selector. A name and selector MUST NOT be defined in the same reference. A Service Binding resource MUST define a .spec.service which is an ObjectReference-like declaration to a Provisioned Service-able resource.

A Service Binding Resource MAY define a .spec.mappings which is an array of Mapping objects. A Mapping object MUST define name and value entries. The value of a Mapping MUST be handled as a Go Template exposing binding Secret keys for substitution. The executed output of the template MUST be added to the Secret exposed to the resource represented by application as the key specified by the name of the Mapping.

A Service Binding Resource MAY define a .spec.env which is an array of EnvVar. An EnvVar object MUST define name and key entries. The key of an EnvVar MUST refer to a binding Secret key name including any key defined by a Mapping. The value of this Secret entry MUST be configured as an environment variable on the resource represented by application.

A Service Binding resource MUST define a .status.conditions which is an array of Condition objects. A Condition object MUST define type, status, and lastTransitionTime entries. At least one condition containing a type of Ready MUST be defined. The status of the Ready condition MUST have a value of True, False, or Unknown. The lastTransitionTime MUST contain the last time that the condition transitioned from one status to another. A Service Binding resource MAY define reason and message entries to describe the last status transition. As label selectors are inherently queries that return zero-to-many resources, it is RECOMMENDED that ServiceBinding authors use a combination of labels that yield a single resource, but implementors MUST handle each matching resource as if it was specified by name in a distinct ServiceBinding resource. Partial failures MUST be aggregated and reported on the binding status's Ready condition. A Service Binding resource SHOULD reflect the secret projected into the application as .status.binding.name.

Resource Type Schema

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name:                 # string
  generation:           # int64, defined by the Kubernetes control plane
  ...
spec:
  name:                 # string, optional, default: .metadata.name
  type:                 # string, optional
  provider:             # string, optional

  application:          # PodSpec-able resource ObjectReference-like
    apiVersion:         # string
    kind:               # string
    name:               # string, mutually exclusive with selector
    selector:           # metav1.LabelSelector, mutually exclusive with name
    containers:         # []intstr.IntOrString, optional

  service:              # Provisioned Service-able resource ObjectReference-like
    apiVersion:         # string
    kind:               # string
    name:               # string

  mappings:             # []Mapping, optional
  - name:               # string
    value:              # string

  env:                  # []EnvVar, optional
  - name:               # string
    key:                # string

status:
  binding:              # LocalObjectReference, optional
    name:               # string
  conditions:           # []Condition containing at least one entry for `Ready`
  - type:               # string
    status:             # string
    lastTransitionTime: # Time
    reason:             # string
    message:            # string
  observedGeneration:   # int64

Minimal Example Resource

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking

  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service

status:
  conditions:
  - type:   Ready
    status: 'True'

Label Selector Example Resource

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: online-banking-frontend-to-account-service
spec:
  name: account-service

  application:
    apiVersion: apps/v1
    kind:       Deployment
    selector:
      matchLabels:
        app.kubernetes.io/part-of: online-banking
        app.kubernetes.io/component: frontend

  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service

status:
  conditions:
  - type:   Ready
    status: 'True'

Mappings Example Resource

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking

  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service

  mappings:
  - name:  accountServiceUri
    value: https://{{ urlquery .username }}:{{ urlquery .password }}@{{ .host }}:{{ .port }}/{{ .path }}

status:
  binding:
    name: prod-account-service-projection
  conditions:
  - type:   Ready
    status: 'True'

Environment Variables Example Resource

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking

  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service

  mappings:
  - name:  accountServiceUri
    value: https://{{ urlquery .username }}:{{ urlquery .password }}@{{ .host }}:{{ .port }}/{{ .path }}

  env:
  - name: ACCOUNT_SERVICE_HOST
    key:  host
  - name: ACCOUNT_SERVICE_USERNAME
    key:  username
  - name: ACCOUNT_SERVICE_PASSWORD
    key:  password
  - name: ACCOUNT_SERVICE_URI
    key:  accountServiceUri

status:
  binding:
    name: prod-account-service-projection
  conditions:
  - type:   Ready
    status: 'True'

Reconciler Implementation

A Reconciler implementation for the ServiceBinding type is responsible for binding the Provisioned Service binding Secret into an Application. The Secret referred to by .status.binding.name on the resource represented by service MUST be mounted as a volume on the resource represented by application.

If a .spec.name is set, the directory name of the volume mount MUST be its value. If a .spec.name is not set, the directory name of the volume mount SHOULD be the value of .metadata.name.

If the $SERVICE_BINDING_ROOT environment variable has already been configured on the resource represented by application, the Provisioned Service binding Secret MUST be mounted relative to that location. If the $SERVICE_BINDING_ROOT environment variable has not been configured on the resource represented by application, the $SERVICE_BINDING_ROOT environment variable MUST be set and the Provisioned Service binding Secret MUST be mounted relative to that location. A RECOMMENDED value to use is /bindings.

The $SERVICE_BINDING_ROOT environment variable MUST NOT be reset if it is already configured on the resource represented by application.

If a .spec.type is set, the type entry in the binding Secret MUST be set to its value overriding any existing value. If a .spec.provider is set, the provider entry in the binding Secret MUST be set to its value overriding any existing value.

Ready Condition Status

If the modification of the Application resource is completed successfully, the Ready condition status MUST be set to True. If the modification of the Application resource is not completed successfully the Ready condition status MUST NOT be set to True.

Extensions

Extensions are optional additions to the core specification as defined above. Implementation and support of these specifications are not required in order for a platform to be considered compliant. However, if the features addressed by these specifications are supported a platform MUST be in compliance with the specification that governs that feature.

Custom Projection

There are scenarios where the Reconciler that processes a ServiceBinding (hereinafter referred to as "Reconciler A") is different than the Reconciler that will project the binding into the Application (hereinafter referred to as "Reconciler B"). To transfer the projection responsibility from Reconciler A to Reconciler B the ServiceBinding author MUST set the projection.service.binding/type annotation to Custom. An exemplar CRD can be found here.

Reconciler A reacts to this annotation by creating a ServiceBindingProjection which includes the necessary information for Reconciler B. Reconciler B takes responsibility for the ServiceBindingProjection resource to perform the projection. Reconciler A is responsible for updating the ServiceBindingProjection's .spec upon corresponding changes to ServiceBinding.

Reconciler B MUST set the ServiceBindingProjection's Ready condition according to the rules set in Ready Condition Status. Reconciler A MUST reflect ServiceBindingProjection's Ready condition with an additional ProjectionReady condition on ServiceBinding. The Ready condition of ServiceBinding MUST NOT be True unless ProjectionReady is True.

Custom Projection Service Binding Example Resource

apiVersion: service.binding/v1alpha2
kind: ServiceBinding
metadata:
  name: account-service
  annotations:
    projection.service.binding/type: "Custom"
spec:
  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking

  service:
    apiVersion: com.example/v1alpha1
    kind:       AccountService
    name:       prod-account-service

status:
  binding:
    name: prod-account-service-projection
  conditions:
  - type:   Ready
    status: 'True'
  - type:   ProjectionReady
    status: 'True'

Resource Type Schema

apiVersion: internal.service.binding/v1alpha2
kind: ServiceBindingProjection
metadata:
  name:                 # string
  generation:           # int64, defined by the Kubernetes control plane
  ...
spec:
  name:                 # string
  binding:              # LocalObjectReference
  application:          # ObjectReference-like
    apiVersion:         # string
    kind:               # string
    name:               # string, mutually exclusive with selector
    selector:           # metav1.LabelSelector, mutually exclusive with name
    containers:         # []intstr.IntOrString, optional

  env:                  # []EnvVar, optional
  - name:               # string
    key:                # string

status:
  conditions:           # []Condition containing at least one entry for `Ready`
  - type:               # string
    status:             # string
    lastTransitionTime: # Time
    reason:             # string
    message:            # string
  observedGeneration:   # int64

Service Binding Projection Example Resource

apiVersion: internal.service.binding/v1alpha2
kind: ServiceBindingProjection
metadata:
  name: account-service
spec:
  binding:  prod-account-service-projection

  application:
    apiVersion: apps/v1
    kind:       Deployment
    name:       online-banking

status:
  conditions:
  - type:   Ready
    status: 'True'

Binding Secret Generation Strategies

Many services, especially initially, will not be Provisioned Service-compliant. These services will expose the appropriate binding Secret information, but not in the way that the specification or applications expect. Users should have a way of describing a mapping from existing data associated with arbitrary resources and CRDs to a representation of a binding Secret.

To handle the majority of existing resources and CRDs, Secret generation needs to support the following behaviors:

  1. Extract a string from a resource
  2. Extract an entire ConfigMap/Secret refrenced from a resource
  3. Extract a specific entry in a ConfigMap/Secret referenced from a resource
  4. Extract entries from a collection of objects, mapping keys and values from entries in a ConfigMap/Secret referenced from a resource
  5. Map each value to a specific key

While the syntax of the generation strategies are specific to the system they are annotating, they are based on a common data model.

Model Description
path A template represention of the path to an element in a Kubernetes resource. The value of path is specified as JSONPath. Required.
objectType Specifies the type of the object selected by the path. One of ConfigMap, Secret, or string (default).
elementType Specifies the type of object in an array selected by the path. One of sliceOfMaps, sliceOfStrings, string (default).
sourceKey Specifies a particular key to select if a ConfigMap or Secret is selected by the path. Specifies a value to use for the key for an entry in a binding Secret when elementType is sliceOfMaps.
sourceValue Specifies a particular value to use for the value for an entry in a binding Secret when elementType is sliceOfMaps

OLM Operator Descriptors

OLM Operators are configured by setting the specDescriptor and statusDescriptor entries in the ClusterServiceVersion with mapping descriptors.

Descriptor Examples

The following examples refer to this resource definition.

apiVersion: apps.kube.io/v1beta1
kind: Database
metadata:
  name: my-cluster
spec:
  ...

status:
  bootstrap:
  - type: plain
    url: myhost2.example.com
    name: hostGroup1
  - type: tls
    url: myhost1.example.com:9092,myhost2.example.com:9092
    name: hostGroup2
  data:
    dbConfiguration: database-config     # ConfigMap
    dbCredentials: database-cred-Secret  # Secret
    url: db.stage.ibm.com
  1. Mount an entire Secret as the binding Secret

    - path: data.dbCredentials
      x-descriptors:
      - urn:alm:descriptor:io.kubernetes:Secret
      - service.binding
  2. Mount an entire ConfigMap as the binding Secret

    - path: data.dbConfiguration
      x-descriptors:
      - urn:alm:descriptor:io.kubernetes:ConfigMap
      - service.binding
  3. Mount an entry from a ConfigMap into the binding Secret

    - path: data.dbConfiguration
      x-descriptors:
      - urn:alm:descriptor:io.kubernetes:ConfigMap
      - service.binding:certificate:sourceKey=certificate
  4. Mount an entry from a ConfigMap into the binding Secret with a different key

    - path: data.dbConfiguration
      x-descriptors:
      - urn:alm:descriptor:io.kubernetes:ConfigMap
      - servicebinding:timeout:sourceKey=db_timeout
  5. Mount a resource definition value into the binding Secret

    - path: data.uri
      x-descriptors:
      - service.binding:uri
  6. Mount a resource definition value into the binding Secret with a different key

    - path: data.connectionURL
      x-descriptors:
      - service.binding:uri
  7. Mount the entries of a collection into the binding Secret selecting the key and value from each entry

    - path: bootstrap
      x-descriptors:
      - service.binding:endpoints:elementType=sliceOfMaps:sourceKey=type:sourceValue=url

Non-OLM Operator and Resource Annotations

Non-OLM Operators are configured by adding annotations to the Operator's CRD with mapping configuration. All Kubernetes resources are configured by adding annotations to the resource.

Annotation Examples

The following examples refer to this resource definition.

apiVersion: apps.kube.io/v1beta1
kind: Database
metadata:
  name: my-cluster
spec:
  ...

status:
  bootstrap:
  - type: plain
    url: myhost2.example.com
    name: hostGroup1
  - type: tls
    url: myhost1.example.com:9092,myhost2.example.com:9092
    name: hostGroup2
  data:
    dbConfiguration: database-config     # ConfigMap
    dbCredentials: database-cred-Secret  # Secret
    url: db.stage.ibm.com
  1. Mount an entire Secret as the binding Secret
    “service.binding":
      ”path={.status.data.dbCredentials},objectType=Secret”
    
  2. Mount an entire ConfigMap as the binding Secret
    service.binding”:
      "path={.status.data.dbConfiguration},objectType=ConfigMap”
    
  3. Mount an entry from a ConfigMap into the binding Secret
    “service.binding/certificate”:
      "path={.status.data.dbConfiguration},objectType=ConfigMap,sourceKey=certificate"
    
  4. Mount an entry from a ConfigMap into the binding Secret with a different key
    “service.binding/timeout”:
      “path={.status.data.dbConfiguration},objectType=ConfigMap,sourceKey=db_timeout”
    
  5. Mount a resource definition value into the binding Secret
    “service.binding/uri”:
      "path={.status.data.url}"
    
  6. Mount a resource definition value into the binding Secret with a different key
    “service.binding/uri":
      "path={.status.data.connectionURL}”
    
  7. Mount the entries of a collection into the binding Secret selecting the key and value from each entry
    “service.binding/endpoints”:
      "path={.status.bootstrap},elementType=sliceOfMaps,sourceKey=type,sourceValue=url"
    

Role-Based Access Control (RBAC)

Kubernetes clusters often utilize Role-based access control (RBAC) to authorize subjects to perform specific actions on resources. When operating in a cluster with RBAC enabled, the service binding reconciler needs permission to read resources that provisioned a service and write resources that services are projected into. This extension defines a means for third-party CRD authors and cluster operators to expose resources to the service binding reconciler. Cluster operators MAY impose additional access controls beyond RBAC.

For Cluster Operators and CRD Authors

Cluster operators and CRD authors MAY opt-in resources to service binding by defining a ClusterRole with a label matching service.binding/controller=true. For Provisioned Service-able resources the get, list, and watch verbs MUST be granted. For PodSpec-able resources the get, list, watch, update, and patch verbs MUST be granted.

Example Resource

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: awesome-service-bindings
  labels:
    service.binding/controller: "true" # matches the aggregation rule selector
rules:
# for Provisioned Service-able resources only
- apiGroups:
  - awesome.example.com
  resources:
  - awesomeservices
  verbs:
  - get
  - list
  - watch
# for PodSpec-able resources (also compatible with Provisioned Service-able resources)
- apiGroups:
  - awesome.example.com
  resources:
  - awesomeapplications
  verbs:
  - get
  - list
  - watch
  - update
  - patch

For Service Binding Implementors

Service binding reconciler implementations MUST define an aggregated ClusterRole with a label selector matching the label service.binding/controller=true. This ClusterRole MUST be bound (RoleBinding for a single namespace or ClusterRoleBinding if cluster-wide) to the subject the service binding reconciler runs as, typically a ServiceAccount.

Example Resource

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ...
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      service.binding/controller: "true"
rules: [] # The control plane automatically fills in the rules

About

Specification for binding services to k8s applications

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Go 95.3%
  • Makefile 4.7%