Specification for binding services to runtime applications running in Kubernetes.
- service - any software that is exposing functionality. Could be a RESTful application, a database, an event stream, etc.
- application - in this specification we refer to a single runtime-based microservice (e.g. MicroProfile app, or Node Express app) as an application. This is different than an umbrella (SIG) Application which refers to a set of microservices.
- binding - providing the necessary information for an application to connect to a service.
- Secret - refers to a Kubernetes Secret.
- ConfigMap - refers to a Kubernetes ConfigMap.
- Need a consistent way to bind k8s application to services (applications, databases, event streams, etc)
- A standard / spec / RFC will enable adoption from different service providers
- Cloud Foundry has addressed this issue with a buildpack specification. The equivalent is not available for k8s.
Main section of the doc. Has sub-sections that outline the design.
For a service to be bindable it MUST comply with one-of:
- provide a Secret and/or ConfigMap that contains the binding data and reference this Secret and/or ConfigMap using one of the patterns discussed below.
- map its
status
,spec
,data
properties to the corresponding binding data, using one of the patterns discussed below. - include a sample
ServiceBinding
(see the Request service binding section below) in its documentation (e.g. GitHub repository, installation instructions, etc) which contains adataMapping
illustrating how each of itsstatus
properties map to the corresponding binding data. This option allows existing services to be bindable with zero code changes.
In addition to the minimum set above, a bindable service SHOULD provide:
- a ConfigMap (which could be the same as the one holding some of the binding data, if applicable) that describes metadata associated with each of the items referenced in the Secret. The bindable service should also provide a reference to this ConfigMap using one of the patterns discussed below.
The key/value pairs insides this ConfigMap are:
- A set of
metadata.<property>=<value>
- where<property>
maps to one of the defined keys for this service, and<value>
represents the description of the value. For example, this is useful to define what format thepassword
key is in, such as apiKey, basic auth password, token, etc.
The reference's location and format depends on the following scenarios:
-
OLM-enabled Operator: Use the
statusDescriptor
and/orspecDescriptor
parts of the CSV to mark whichstatus
and/orspec
properties reference the binding data:- The reference's
x-descriptors
with a possible combination of:- ConfigMap:
servicebinding:configMap
- Secret:
servicebinding:secret
- Individual binding items:
servicebinding:secret:host
servicebinding:secret:port
servicebinding:secret:uri
servicebinding:secret:<binding_property>
(where<binding_property>
is any property from the binding schema)
- ConfigMap:
- The reference's
-
Non-OLM Operator: - An annotation in the Operator's CRD to mark which
status
properties reference the binding data. The value of this annotation can be specified in either JSONPath or GO templates:- ConfigMap:
- servicebinding/configMap: {.status.bindable.ConfigMap}
- Secret:
- servicebinding/secret: {.status.bindable.Secret}
- Individual binding items:
- servicebinding/secret/host: {.status.address}
- servicebinding/secret/
<binding_property>
: {.status.<status_property>}
(where<binding_property>
is any property from the binding schema, and<status_property>
refers to the path to the correspodingstatus
property)
- ConfigMap:
-
Regular k8s Deployment (Ingress, Route, Service, Secret, ConfigMap etc) - An annotation in the corresponding CR that maps the
status
,spec
ordata
properties to their corresponding binding data. The value of this annotation can be specified in either JSONPath or GO templates:- servicebinding/secret/host: {.status.ingress.host}
- servicebinding/secret/host: {.status.address}
- servicebinding/secret/
<binding_property>
:<property_path>
(where<binding_property>
is any property from the binding schema, and<property_path>
refers to the path to the correspodingstatus
,spec
ordata
property)
The above pattern can be used to expose external services (such as from a VM or external cluster), as long as there is an entity such as a Secret that provides the binding details.
The core set of binding data is:
- type - the type of the service. Examples: openapi, db2, kafka, etc.
- host - host (IP or host name) where the service resides.
- port - the port to access the service.
- protocol - protocol of the service. Examples: http, https, postgresql, mysql, mongodb, amqp, mqtt, etc.
- contextRoot - a context root to be used for this service. Example: the context root for a RESTful service
- username - username to log into the service. Can be omitted if no authorization required, or if equivalent information is provided in the password as a token.
- password - the password or token used to log into the service. Can be omitted if no authorization required, or take another format such as an API key. It is strongly recommended that the corresponding ConfigMap metadata properly describes this key.
- certificate - the certificate used by the client to connect to the service. Can be omitted if no certificate is required, or simply point to another Secret that holds the client certificate.
- uri - for convenience, the full URI of the service in the form of
<protocol>://<host>:<port>[/<contextRoot>]
. - role-needed - the name of the role needed to fetch the Secret containing the binding data. In this scenario a k8s Service Account with the appropriate role must be passed into the binding request (see the RBAC section below).
Extra binding properties can also be defined (with corresponding metadata) in the bindable service's ConfigMap (or Secret). For example, services may have credentials that are the same for any user (global setting) in addition to per-user credentials.
Binding is requested by the consuming application, or an entity on its behalf such as the Runtime Component Operator, via a custom resource that is applied in the same cluster where an implementation of this specification resides.
Since the reference implementation for most of this specification is the Service Binding Operator we will be using the ServiceBinding
CRD, which resides in this folder.
Temporary Note
To ensure a better fit with the specification a few modifications have been proposed to the ServiceBinding
CRD:
- A modification to its API group, to be independent from OpenShift. (ref)
- A simplification of its CRD name to
ServiceBinding
(we are already using this name in the spec). (ref) - Renaming
customEnvVar
todataMapping
. (ref) - Allowing for the
application
selector to be omitted, for the cases where another Operator owns the deployment. (ref) - Addition of fields such as
serviceAccount
andsubscriptionSecret
that support more advanced binding cases. (ref) - Not related to the CRD, but directly related to how this spec approaches item 1 from Pointer to binding data, in terms of referencing a nested property. (ref)
This part of the specification deals with security in three aspects:
- does the user have the authority to create a ServiceBinding CR?
- does the user have the authority to access the binding data for all requested services?
- does the user have the authority to modify the source application with the injected binding data?
Scenario 1 can enforced by requiring a certain role type to create ServiceBinding CR, using k8s native RBAC rules.
Scenario 2 there are a few options, one of them being if the service provider's binding resource (as defined in Pointer to binding data) is protected by RBAC then the service consumer must pass a Service Account in its ServiceBinding
CR to allow implementations of this specification to fetch information from that Secret. If the implementation already has access to all resources in the cluster (as is the case with the Service Binding Operator) it must ensure it uses the provided Service Account instead of its own - blocking the bind if a Service Account was needed (according to the binding data) but not provided.
Example of a partial CR:
services:
- group: postgres.dev
kind: Service
resourceRef: user-db
version: v1beta1
serviceAccount: <my-sa>
Scenario 3 can also be enforced by RBAC in a similar fashion, but passing the service account in the application
section of the RequestBinding
CR.
There are a variety of service providers that require a subscription to be created before accessing the service. Examples:
- an API management framework that provides apiKeys after a plan subscription has been approved
- a database provisioner that spins single-tenant databases upon request
- premium services that deploy a providers located in the same physical node as the caller for low latency
- any other type of subscription service
The only requirement from this specification is that the subscription results in a k8s resources (Secret, etc), containing a partial or complete set of binding data (defined in Service Binding Schema). From the ServiceBinding
CR's perspective, this resource looks and feels like an additional service.
Example of a partial CR:
services:
- group: postgres.dev
kind: Service
resourceRef: global-user-db
version: v1beta1
- group: postgres.dev
kind: Secret
resourceRef: specific-user-db
version: v1beta1
Implementations of this specification must bind the following data into the consuming application container:
<path>/bindings/<service-id>/metadata/<persisted_configMap>
<path>/bindings/<service-id>/request/<ServiceBindingData_CR>
<path>/bindings/<service-id>/secret/<persisted_secret>
Where:
<path>
defaults toplatform
if not specified in theServiceBinding
CR.<service-n-id>
equals themetadata.name
field + theservices[n].resourceRef
from theServiceBinding
CR.<persisted_configMap>
represents a set of files where the filename is a ConfigMap key and the file contents is the corresponding value of that key. This is optional, as the ConfigMap is not mandatory.<ServiceBindingData_CR>
represents the requestedServiceBinding
CR.<persisted_secret>
represents a set of files where the filename is a Secret key and the file contents is the corresponding value of that key.
- How are application expected to consume binding information
- Each framework may take a different approach, so this is about samples & recommendations (best practices)
- Validates the design