npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@sap/sbf

v6.7.6

Published

A Node.js framework for creating CloudFoundry service broker applications

Downloads

14,015

Readme

@sap/sbf.

Documentation can also be found in here

A Node.js framework to create a service broker in SAP Business Technology Platform (SAP BTP)

The Service Broker Framework (SBF) implements the Open Service Broker API. It can be used in the Cloud Foundry environment of SAP Cloud Platform or on-premise in SAP HANA XS advanced model.

Note: SBF Rejects requests for which the X-Broker-API-Version header is not set or its value is outside the supported interval [2.4, 3).

SBF can generate service credentials for the following authentication mechanisms:

  • Basic authentication for technical users (via SBSS)
  • OAuth2 authentication with JSON Web Tokens (JWT) (via XSUAA broker plan)
    • Named user via user_token flow
    • Technical user via client_credentials flow

After adding the necessary configuration, the SBF can be started directly as a service broker. If necessary, it can be extended with custom JavaScript code.

All the information here is valid also for XS advanced, unless explicitly stated otherwise. In the shell commands below replace cf with xs when working on XS advanced.

Table of contents

Usage

Create a simple service broker

For simple use cases, you don't need to write any JavaScript code. You can start this package directly by providing it with the necessary configuration.

The following sections describe the steps to create a simple service broker application by using this framework.

Prerequisites

You need the following:

  • Node.js v18 or later
  • Cloud Foundry CLI
  • Access to a Cloud Foundry installation where you can log in via CLI and push applications

Create a Node.js application

Create a new directory and run the following command in it:

npm init

You are prompted to answer several questions. Upon completion, this command creates a package.json file in the current directory. The presence of this file, tells Cloud Foundry that this is a Node.js application.

Add the Service Broker Framework

Download the @sap/sbf package and add it to your service broker by executing the following command:

npm install @sap/sbf

Add the start command

Edit the package.json file and add the start command in section scripts:

{
  "scripts": {
    "start": "start-broker"
  }
}

Specify a required Node.js version

Add the following property in the package.json file to inform Cloud Foundry that the service broker requires Node.js v18:

  "engines": {
    "node": "^18"
  }

Create the service catalog

The service catalog describes the services offered by this service broker. It is defined in a JSON format as described in Cloud Foundry documentation.

Create a file called catalog.json in the current directory and describe in it the service catalog. For example:

{
  "services": [{
    "name": "my-service",
    "description": "A simple service",
    "bindable": true,
    "plans": [{
      "name": "my-plan",
      "description": "The only plan"
    }]
  }]
}

Execute the following command to generate unique IDs for the services and their plans in the catalog.json file:

npx gen-catalog-ids

Create XSUAA service instance

The service broker can use different services to generate and store credentials needed later on by applications to access your reusable service. In this example we use the XSUAA service as a credentials provider.

Create an XSUAA service instance of plan broker:

cf create-service xsuaa broker xsuaa-broker --wait

Here xsuaa-broker is the service instance name. You can use an arbitrary name here. Make sure to use the same name in the subsequent commands.

Create an instance of the Audit Log service

The service broker is configured by default to audit log every operation. It needs information to connect to the Audit log service.

Create Audit log service with the following command:

cf create-service auditlog oauth2 broker-audit

You can also use the standard plan. It was deprecated, however still supported:

cf create-service auditlog standard broker-audit

Generate a secure broker password

npx hash-broker-password -b

This command generates a random password and hashes it.

Create an application manifest

You can deploy the service broker in Cloud Foundry as a regular application. An easy way to do that is via an application manifest. Create a manifest.yml file in the current directory with the following content:

---
applications:
  - name: my-broker
    memory: 128M
    services:
      - xsuaa-broker
      - broker-audit
    health-check-type: http
    health-check-http-endpoint: /health
    env:
      SBF_BROKER_CREDENTIALS_HASH: >
        {
          "broker-user": "<broker-password-hash>"
        }
      SBF_SERVICE_CONFIG: >
        {
          "my-service": {
            "extend_credentials": {
              "shared": {
                "uri": "https://my-service.example.com"
              }
            }
          }
        }

my-broker is the name of the broker application. You can use an arbitrary name.

Some configurations are not known in advance and they need to be set at the time of deployment via the environment variables.

One such configuration is the credentials configuration used to call the service broker. This configuration is provided via the environment variable SBF_BROKER_CREDENTIALS_HASH. Here you can use arbitrary credentials. Replace <broker-password-hash> with the hashed credentials from the previous step. Just make sure to use matching credentials when you register the broker and don't commit these in source control.

Another configuration done during the deployment is the service URL. You can provide it via the environment variable SBF_SERVICE_CONFIG. See Additional Service Configuration for details.

Push the broker application

Push the broker application to Cloud Foundry by running the following command:

cf push

By default, Cloud Foundry uses the name of the application as the host name. If this name is already taken, you can specify a different host via the host property in the manifest. Alternatively you can use a random host with the following command:

cf push --random-route

Register the service broker

For productive use a service broker is registered globally so you can use it throughout Cloud Foundry, but this requires administrative permissions. During development and testing you can register the service broker only in your space. You do this with the following command:

cf create-service-broker my-broker-name broker-user <plain-broker-password> <broker-url> --space-scoped

Replace here <plain-broker-password> with the plaintext password generated by hash-broker-password script.

You can get the broker URL via the command cf apps.

Here my-broker-name is an arbitrary name used to distinguish this service broker from the rest. It is independent from the broker application name.

Use the service broker

Now you can use your service broker within the same space. For example, you can now see its services and plans via the cf marketplace command. You can use the new services as regular services in Cloud Foundry.

You can also create a service instance via cf create-service. Then you can bind it to your application via cf bind-service. After that the application gets the URL and the credentials for your service from the environment variable VCAP_SERVICES. You can see them via the cf env command.

You can also create a service key (cf create-service-key) and print its content (cf service-key) to see the credentials to access the service instance.

You can also register the service broker in another space and use it there. To do that append a unique suffix to the broker URL:

cf create-service-broker my-broker-name broker-user broker-password <broker-url>/<unique-suffix> --space-scoped

Use a different suffix for each broker registration.

Extend the service broker

Some service brokers need to perform custom actions during standard broker operations. For example, special actions might be necessary to provision a new service instance. To do that, you can create a custom Node.js application for your service broker. This application can use the service broker framework as a normal Node.js package. Then you can register custom callbacks to be invoked during each broker operation.

Create a Node.js application and add a @sap/sbf dependency as described above.

Create the start script of your broker application, e.g. start.js:

const Broker = require('@sap/sbf');

let broker = new Broker({
  hooks: {
    onProvision: (params, callback) => {
      console.log('Provision service %s with plan %s', params.service_id, params.plan_id);
      // custom provision actions
      callback();
    },
    onDeprovision: (params, callback) => {
      console.log('Deprovision service instance %s', params.instance_id);
      // custom deprovision actions
      callback();
    }
  }
});
let server = broker.start();

Edit the package.json file and set the start command to execute your start script:

  "scripts": {
    "start": "node start.js"
  }

Asynchronous broker operations

By default, service broker operations like provisioning and deprovisioning are synchronous, i.e. HTTP response is returned when the operation is complete. So that long-running operations are also supported, some platforms, like Cloud Foundry, support also asynchronous operations. See Synchronous and Asynchronous Operations in the Open Service Broker API specifications.

Note: currently the XS advanced runtime does not support asynchronous operations.

To perform an asynchronous operation you implement several hooks. First, check if your platform supports asynchronous operations. If it does, start the operation in the background and return async: true in the reply:

function onProvision(params, callback) {
  if (params.accepts_incomplete !== 'true') {
    let error = new Error('Cannot provision service instance synchronously');
    error.statusCode = 422; // Unprocessable Entity
    return callback(error);
  }
  let operationId;
  // start the operation in the background ...
  // and assign operationId ...
  callback(null, {
    async: true, // indicate that operation was started asynchronously
    operation: operationId // later the same operationId will be passed to onLastOperation
  });
}

The same applies also to onUpdate, onDeprovision, onBind and onUnbind hooks.

Next, the platform polls in regular intervals for the status of the operation. Each time it sends the same operation string to identify the operation. This is useful if multiple asynchronous operations are running in parallel, e.g. multiple service instances are created at the same time. To provide the current status of the operation, you implement the onLastOperation hook (or onBindLastOperation in case of async binding operations):

function onLastOperation(params, callback) {
  let operationId = params.operation; // for which operation to return status
  // get operation status ...
  callback(null, {
    state: 'in progress', // or 'succeeded' or 'failed'
    description: 'progress 50%' // Some user-facing message, what is going on
  });
}

Note: With IAS as a credentials provider all requests are proxied to the IAS Broker, which manages the statuses of last operations. If you choose to implement a hook, do it with caution because you could override the original status received from the IAS Broker.

Note: If several instances of the service broker are running, it is very likely that the start of an asynchronous operation and subsequent polling via onLastOperation / onBindLastOperation will be handled by different broker instances. So make sure that all broker instances see the same state.

Service broker as middleware

You can create a custom Node.js application and add the service broker as a middleware. The Broker class has property app which is an express application. Example:

const express = require('express');
const Broker = require('@sap/sbf');

let app = express();
let broker = new Broker();

app.get('/custom-endpoint', (req, res) => {
  res.send('custom-response');
});
app.use('/broker', broker.app);

app.listen(process.env.PORT);

Then to register the broker use url: https://<app-url>/broker.

Health HTTP endpoint

SBF provides an HTTP endpoint (on path /health) whose purpose is to serve as a health check endpoint. It does not require authentication and can process HTTP GET requests only. Currently, it returns a static response with the status 200 and body OK.

By default, Cloud Foundry uses port as a health check type (documentation for health check types). To use the health endpoint provided by SBF:

  • Configure the health check type to http. In manifest.yml, you do this via the property health-check-type.
  • Configure the path of the health endpoint, by default it's /. In manifest.yml, you do this via the property health-check-http-endpoint.

See an example here.

Custom parameters

Custom parameters can be passed to the service broker in several locations. The parameters are passed as a JSON object. It can have arbitrary content and is not interpreted by Cloud Foundry.

Create service with custom parameters

cf create-service <service> <plan> <service-instance> -c parameters.json

The content of parameters.json is passed to the service broker and can be accessed in the onProvision hook via the params.parameters argument.

Note: When using XSUAA for authentication, the parameters JSON should contain xs-security property to avoid confusion with custom parameters. See XSUAA for details.

Update service with custom parameters

cf update-service <service-instance> -c parameters.json

The content of parameters.json is passed to the service broker and can be accessed in onUpdate hook via params.parameters argument.

Bind service with custom parameters

cf bind-service <application> <service-instance> -c parameters.json

The content of parameters.json is passed to the service broker and can be accessed in onBind hook via params.parameters argument.

The same goes for CF service-keys:

cf create-service-key <service-instance> <key-name> -c parameters.json

Note: Binding for reuse service instace when XSUAA is the credentials' provider, allows for specific configuration in order to support certificate credentials, as specified here: Authentication with X.509 client certificates

Credentials providers

By default, this modul searches for a bound service instance which is responsible for generating credentials for the services offered by a service broker. The framework attempts to find a suitable service with the following properties in the following order:

  1. SAP HANA Service Instance (A service with label hana and plan sbss)
  2. PostgreSQL Service Instance (A service with label postgresql and tag sbss)
  3. XSUAA Service Instance (A service with label xsuaa and plan broker)
  4. IAS Service Instance (A service with the label identity and plan application)

If no such service is found in the environment of the broker, an error is returned.

You can disable this behavior by setting the credentials provider service explicitly. When running on K8S you must always set the credentials provider service explicitly.

Depending on the type of the service that provides credentials: SBSS (for SAP HANA and PostgreSQL), XSUAA, or IAS, this module generates credentials and merges them to the credentials object received in the response to bind operation. The same object will appear also in the credentials section for the respective service in the VCAP_SERVICES environment variable in bound applications. You can find some examples below.

SBSS

SBSS (Service Broker Security Support) can generate, store, and verify usernames and passwords in a secure way. It is accessed via the SQL API.

SBSS on SAP HANA

Create SBSS service instance (example):

cf create-service hana sbss hana-sbss

Here hana-sbss is an arbitrary service instance name.

Generated credentials example:

{
  "username": "SBSS_00536748842276225491856140796794258250872406624126918117591330539",
  "password": "Aa_12905484905134285946159829260519429913717511989397057274381675342",
}

Note: Since version 4 of @sap/sbf, broker applications that use SBSS on HANA need to explicitly specify a dependency to the @sap/hdbext package.

SBSS on PostgreSQL

SBSS on PostgreSQL credentials provider requires 2 services to be bound to the service broker application. One is the actual PostrgeSQL service instance and additional user-provided service containing restricted DB user to be used for credentials generation.

Create SBSS service instance (example):

cf cs postgresql-db development pg-sbss -t sbss

Here pg-sbss is an arbitrary service instance name. Notice that the command attaches the tag sbss to the service instance. This tag is mandatory. It can also be set after the service instance has been created like this:

cf update-service pg-sbss -t sbss

Create restricted DB user service (example):

cf create-user-provided-service sbss-configuration -p "{\"tag\":\"sbss-config\",\"restricted-dbuser-name\":\"<dbuser>\",\"restricted-dbuser-password\":\"<dbpassword>\"}"

Here you can use arbitrary values for restricted-dbuser-name and restricted-dbuser-password. You should bind the same service instance to SBSS installer application when deploying SBSS on Postgres. For details see the SBSS documentation.

Note: You should bind both service instances to the service broker application.

XSUAA

Create XSUAA instance of plan broker (example):

cf create-service xsuaa broker <service-instance> -c xs-security.json  --wait
Creating reuse service instances

Later on, you can create instances of the reuse service:

cf create-service my-service my-plan <service-instance> -c parameters.json

Here we assume that service my-service with plan my-plan is defined in the service catalog of the broker.

parameters.json:

{
  "xs-security": {
    "xsappname": "my-app",
    ...
  },
  "customProperty1": 1,
  "customProperty2": "2",
  ...
}

xs-security object in parameters.json has the same structure as [xs-security.json] file. It is sent to XSUAA to create an OAuth client for each instance of the reuse service.

xs-security properties will be overwritten by any additional service configuration defined in the environment variable SBF_SERVICE_CONFIG. See Additional Service Configuration.

Other top level properties are optional (e.g. customProperty1 and customProperty2) and can be used to pass arbitrary parameters. The whole parameters.json file is accessible via params.parameters in onProvision(params, callback) hook.

Note: Custom parameters can be defined as root-level properties. If xs-security is not defined, the SBF generates a default value, where xsappname is set to the service instance id.

Note: During reuse instance create/update, an authorities array can be passed to XSUAA. It is recommended to provide extend_xssecurity from the Additional Service Configuration and set authorities explicitly, to control the scopes exposed for the reuse instance. If no authorities section was provided in Additional service configuration (extend_xssecurity), by default SBF will pass an empty authorities array in create/update requests to XSUAA, andauthorities provided by consumers are ignored. Set the secureUAAScopes option (or environment variable SBF_SECURE_UAA_SCOPES) explicitly to false, in order to pass the authorities array provided by the consumer as is. This behavior and environment variable form an incompatible change from release v6.4.9.

Generated credentials example:

{
  "uaa": {
    "clientid": "sb-my-app!b27|reuse-service!b27",
    "clientsecret": "o7M5UE0S+Q498j9zNmAlAKdYrSo=",
    "identityzone": "cc-sap",
    "tenantid": "cc-sap",
    "tenantmode": "dedicated",
    "uaadomain": "authentication.sap.hana.ondemand.com",
    "url": "https://cc-sap.authentication.sap.hana.ondemand.com",
    "verificationkey": "-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----",
    "xsappname": "my-app!b27|reuse-service!b27"
  }
}

An application can use these credentials to call the UAA to fetch a proper JWT token. Then the application can call the reuse service using this token as authentication.

As it's seen the clientid and the xsappname properties hold some specific information. Here the string "my-app" corresponds to the xsappname from the security.json which was provided when creating the service instance which the service broker offers. The string "reuse-service" corresponds to the xsappname in the security.json which was used to create the XSUAA service instance with plan broker. More information can be found in the examples.

Updating reuse service instances

The instance of the reuse service can be updated as follows:

cf update-service <service-instance> -p <new-plan> -c parameters.json

The plan of the service instance can be optionally changed with the -p option. Updating the plan of an instance to a different one from the catalog is possible only if the service is configured with plan_updateable. Refer to the description of services in the catalog in the OSB specification.

Parameters are provided in the same manner as they are during service instance creation - XSUAA parameters are passed in the xs-security property. Service specific parameters are provided on root level. xs-security properties will be overwritten by any additional service configuration defined in extend_xssecurity from the Additional Service Configuration. The whole parameters.json file is accessible via params.parameters in onUpdate(params, callback) hook.

Note: It is recommended to provide explicitly xsappname in xs-security when creating a service instance instead of letting SBF set it automatically to the service instance id, because xsappname needs to be provided in every update operation on XSUAA clones. In case the service instance id should be provided in the update operation, it can be retrieved for an instance with the following command: cf service <name> --guid.

Note: If xsappname is provided in xs-security when creating a service instance, the same xsappname should be provided in xs-security when updating a service instance. If xsappname is not provided on provision, SBF by default will use the service instance id. The same service instance id will also be used during instance update as value for xsappname.

Note: If xs-security is not provided in parameters.json then no update of the XSUAA options will take place. If only extend_xssecurity from the Additional Service Configuration needs to be taken into account, xs-security can be provided as an empty object.

Note: To enable SBF to use per plan extend_xssecurity from the Additional Service Configuration, the platform requesting the broker should provide a plan id. The OSB specification does not mark any plan_id field as mandatory (neither on root level, nor in previous_values). SBF first checks the plan_id property on root level and if it is not available fallbacks to previous_values.plan_id. If no plan id information is provided, and there is a per_plan configuration in extend_xssecurity from the Additional Service Configuration, SBF will return an error. Cloud Foundry provides both plan_id and previous_values.plan_id. At the moment Service Catalog on Kubernetes does not support service instance update. Refer to the CLI documentation for changes.

Authentication with X.509 client certificates

Authentication with X.509 client certificates can be enabled with the following configuration provided in the xs-security options:

{ 
  "xsappname": "...", 
  "oauth2-configuration": {
    "credential-types": ["x509"]
  } 
}

credential-types is an array of allowed credential types, where allowed values are binding-secret, x509. If x509 is not the only type in the array AND not the first item (e.g "credential-types": ["binding-secret", x509"]), in order to enable X.509 authentication, you MUST specify it explicitly during bind (see below).

X.509 client certificate authentication can be separately configured on 3 levels:

  1. XSUAA instance of plan broker. This enables X.509 authentication between the SBF based broker application and XSUAA.

    • If x509 is not the default credential-type that was configured during the XSUAA instance creation, it must be explicitly requested during bind:
        cf bind-service <broker-application> <xsuaa-instance> -c parameters.json

    Where the relevant parameters.json format is:

       {
          "credential-type": "x509", 
          "x509": { 
              ...
          } 
      }
  2. Reuse service instance. The required configuration can be provided either via SBF's additional service configuration (property extend_xssecurity) or via the parameters during reuse service instance creation (parameters.json, property xs-security).

  3. Reuse service instance binding. The required configuration should be provided via the parameters during reuse service instance binding creation:

    • If x509 is not the default credential-type that was configured during the reuse instance creation, it must be explicitly requested during bind:
     cf bind-service <application> <service-instance> -c parameters.json

    Where the relevant configuration must be wrapped with xsuaa. The exact format in which the configuration should be provided is:

    {
      "xsuaa": { 
           "credential-type": "x509", 
           "x509": { 
               ...
           } 
       }
    }

    The same goes for CF service-keys:

    cf create-service-key <service-instance> <key-name> -c parameters.json

    Note: currently only credential-type and x509 (and X.509 for Externally managed certificates) fields are supported under xsuaa.

Certificate types:

The certificate-key pairs that will be used can either be automatically generated by XSUAA (XSUAA managed certificates) or explicitly provided custom ones (externally managed certificates).

XSUAA managed certificates

The configuration above is sufficient to use XSUAA managed certificates. The credentials of the XSUAA broker instance or/and the credentials of the reuse service instance will contain certificate and key properties that can be used for X.509 client certificate authentication.

Externally managed certificates

In this case, the certificate should be provided in the parameters during bind of the XSUAA instnace.

1. XSUAA broker instance

The format in which the certificate should be provided is:

{
  "credential-type": "x509",
  "x509": {
    "certificate": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
    "ensure-uniqueness": false,
    "certificate-pinning": true
  }
}

Then pass the following bind parameters:

{
   "X.509": "-----BEGIN CERTIFICATE-----\nMIID1j ..."
}

The resulting credentials will contain a certificate property. The key should be provided to the broker application, so that it can be used in combination with the certificate from the binding. This is done through the SBF_CLIENT_CERTIFICATE_KEY environment variable or through the clientCertificateKey option. Format should be:

SBF_CLIENT_CERTIFICATE_KEY: -----BEGIN RSA PRIVATE KEY-----
MIIEpA ...

In order to set SBF_CLIENT_CERTIFICATE_KEY properly, linebreaks needs to be preserved as \n. The following 2 options demonstrates how to configure this key in the broker's manifest.yaml:

  • Use the pipe charachter (|):
SBF_CLIENT_CERTIFICATE_KEY: |
        -----BEGIN RSA PRIVATE KEY-----
        MIIEpAIBAAKCABCD+59YBTqEOvQaYWvc/hg+ixznh59qVBXxJKLH0jw85RSlGp
        ...
        asBsdfNfWvdf1FPf6QV+VWaRdQ6zneimsbkxjIg/cdfpW2HmVyTSnqw==
        -----END RSA PRIVATE KEY-----
  • Keep the \n and wrap with quotation marks ("..."):
SBF_CLIENT_CERTIFICATE_KEY: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBA ... VyTSnqw==\n-----END RSA PRIVATE KEY-----\n"

When enabled, SBF uses X.509 certificates authentication with precedence over clientid and clientsecret.

2. Reuse service instance

The relevant configuration must be wrapped with xsuaa, that is:

{
  "xsuaa": {
      "credential-type": "x509",
      "x509": {
        "certificate": "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----",
        "ensure-uniqueness": false,
        "certificate-pinning": true
      }
  }
}

Note: currently only credential-type, x509 and X.509 fields are supported under xsuaa.

The key should be provided (in the environment, for example) to the consumer application so that it can be used in combination with the certificate from the binding.

IAS

IAS (Identity and Authentication Service) is enabling service owners to maintain a service broker within IAS. Therefore, SBF with IAS as credentials provider acts as a proxy to the IAS Broker, while still allowing you to extend functionalities by using custom hooks (an ability IAS Broker doesn't support).

Prerequisites

You can find the complete IAS Broker documentation here, along with prerequisites and procedures.

Creating an IAS instance

Create an IAS instance of the plan application (example):

cf create-service identity application <service-instance> -c catalog.json

Here, the catalog.json is passed to the IAS instance, as this instance represents a IAS Broker. Therefore, a file that contains the catalog should not be provided to the application (see Service Catalog).

Binding an IAS instance to the broker application

Bind an IAS instance (example):

cf bind-service <broker-app> <service-instance> -c '{"credential-type": "X509_GENERATED"}'

Here we use the IAS ability to generate X.509 client certificates for us. Refer to the documentation for alternatives.

Note: communication between the SBF-based broker application and the IAS broker is based on TLS authentication (X.509 client certificates). Make sure you create the binding properly by passing the parameters correctly. In addition, arbitrary parameters are not supported in app manifests in cf CLI v6.x (see here), therefore the binding cannot be declared in the manifest.yml file.

Unsupported Features

The following Additional Service Configuration are not yet supported with IAS as credentials provider:

  1. extend_credentials
  2. The following properties from the Business Service Support section:
    • sap.cloud.service
    • sap.cloud.service.alias
    • saasregistryenabled

You can still choose to extend the response with these parameters within the onBind hook.

Unique service broker

Sometimes for testing it is useful to register the same broker multiple times as if it were multiple different brokers.

The service broker framework can append a suffix to each service name, ID and plan ID to make them unique. There are 3 options to provide this suffix (taken in the following order):

  1. append the suffix in the broker URL
cf create-service-broker broker-name user password https://broker.domain/suffix --space-scoped
  1. broker's constructor option catalogSuffix (requires separate broker deployment)
  2. environment variable SBF_CATALOG_SUFFIX (requires separate broker deployment)

If this suffix is specified, it is used only in the communication with service broker clients like Cloud Controller. Internally this suffix is removed, so all hook functions will get IDs without the suffix, just like in the service catalog. Still in the marketplace the services will be visible with the suffix. So you will have to use the suffix in commands like cf create-service.

The URL suffix should not contain URL special characters (/, ?, etc.).

Assume you have pushed a service broker application and its URL is https://my-broker.domain.com/. Then you can register this broker using suffix abc in the URL:

cf create-service-broker my-broker-abc user password https://my-broker.domain.com/abc --space-scoped

This will append the suffix "abc" to each service name, ID and plan ID in the catalog in this space.

You can also register the same broker with another suffix:

cf create-service-broker my-broker-xyz user password https://my-broker.domain.com/xyz --space-scoped

This will append the suffix "xyz" to each service name, ID and plan ID in the catalog in that space.

Secure outgoing connections

By default all outgoing connections from the service broker must be encrypted or the broker will fail to start (this is not the case with SBSS connections). This behavior can be changed using the secureOutgoingConnections option or the environment variable SBF_SECURE_OUTGOING_CONNECTIONS. If one of them is set to false, unencrypted connections will be allowed.

Note: This option does not apply for connections made by custom code (for example: hooks).

Stateless

A service broker can and should be scaled to run with several instances to achieve high availability and if necessary handle more load. For this reason the service broker framework is designed to be stateless. So it maintains no state between broker requests and provides no communication among broker instances. If custom hooks introduce some state, they should take care to synchronize it across multiple broker instances.

Special care should be taken for asynchronous operations which have inherent state. It is very likely that the start of an asynchronous operation and subsequent polling via onLastOperation will be handled by different broker instances.

Memory usage

A non customized SBF application (without custom code) consumes around 52MB of memory in idle state. During light load (less then 10 concurrent requests) the memory consumption is around 110MB (depending on the requests type). During heavy load (100+ concurrent requests) the memory could reach 160MB. Recommendations:

  • For light load scenarios, deploy with memory limit at least 128MB.
  • For heavy load scenarios, deploy with memory limit at least 256MB.

Also, consider scaling out the broker application with multiple instances. This will increase both the throughput and the fault tolerance (against app crashes).

If custom code is used (e.g. hooks or middleware), its memory usage should be taken into account when calculating the overall memory consumption.

In addition you should limit the Node.js heap size accordingly. The general recommendation is to set it to 75% of the application memory limit. For example (in manifest.yml):

  memory: 128M
  env:
    NODE_OPTIONS: --max-old-space-size=96

User Interface

The service broker framework provides no user interface. It implements only the standard Service Broker API.

Still if your services provide a web-based management user interface, you can expose it via the reply.dashboard_url property in onProvision hook.

Security

To ensure ISO/SOC compliance, certain security requirements should be fulfilled:

  • Minimum password length of 15 characters (fulfilled by passwords generated by SBSS, XSUAA and deploy service)
  • Regular password rotation

Password rotation

You can use this procedure to update the broker password without downtime.

  1. Deploy the broker with the new password as a new application
  2. Update the broker registration with the new password and URL
cf update-service-broker <broker-name> <broker-username> <broker-password> <broker-url>
  1. Delete the old broker application

HTTPS

Since different credentials are transferred between the service broker and its client (Cloud Controller), the communication should be encrypted. If the broker is deployed as an application in Cloud Foundry, no special configuration is required as the platform provides HTTPS support. If the broker is deployed outside the platform then HTTPS should be enabled. This section describes how to do that.

Create a custom start script, for example broker.js:

const https = require('https');
const fs = require('fs');
const Broker = require('@sap/sbf');

const broker = new Broker();

// this is an example, you should provide your own key and certificate
const options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem')
};

https.createServer(options, broker.app).listen(process.env.PORT);

For details how to configure HTTPS see https.createServer.

Use your start script in the start command in package.json

  "scripts": {
    "start": "node broker.js"
  }

Authentication

SBF supports Basic and mTLS authentication. Below you can find information about how to configure each type of authentication.

Basic Authentication

You can configure SBF to use basic authentication. You need to configure username and password. You set the credentials either in plain text or hashed format.

Service Broker Credentials

Credentials in plain text format used by the Cloud Controller and other clients to call the service broker. It is an object where each key is a user name and the value is the respective password. It may contain multiple credentials but at least one is required.

Example:

{
  "user1": "password1",
  "user2": "password2"
}

These credentials can be provided via the option brokerCredentials or the environment variable SBF_BROKER_CREDENTIALS.

Note: Service broker credentials must be provided either in plain text or hashed format.

Service Broker Hashed Credentials

Credentials in hashed format used by the Cloud Controller and other clients to call the service broker. It is an object where each key is a user name and the value is the respective password in format sha256:<salt>:<hash-digest-of-salt+password>. Here <salt> and <hash-digest-of-salt+password> are base64-encoded strings. It may contain multiple credentials but at least one is required.

Example:

{
  "user1": "sha256:gVJILqx/97j4aWVQas5RbSUFpWzu7OpaHOt0O29CJOc=:4klnhxFY2YYwzHO7unYu7jc+HuikQLhF7Ebk8tjOJ9c=",
  "user2": "sha256:0NRIb4Gzx1zFRTTs6qpElujmHuUE1TAIg3NbES219f0=:Gv1NMeIzxlbmOCLvY3q4DMbiDXamqF3xRfFivUdligo="
}

These credentials can be provided via the option brokerCredentialsHash or the environment variable SBF_BROKER_CREDENTIALS_HASH.

Note: Service broker credentials must be provided either in plain text or hashed format.

Note: To generate such hashed credentials, you can use the hash-broker-password script.

mTLS Authentication

Out-of-the-Box mTLS

Note: For those of you who use out-of-the-box mTLS, we recommend using unique credentials, i.e. Basic authentication, alongside out-of-the-box mTLS, to ensure accurate identification in relation to your brokers. This can assist in identifying potential misconfiguration incidents.

You can configure SBF broker to verify the Service Manager-issued client certificate each time the Service Manager communicates with the broker.

For that, you need to create your broker with the secureIncomingConnections option set to true, or set the environment variable SBF_SECURE_INCOMING_CONNECTIONS to true.

You also have to specify the Service Manager certificate's subject so that its identity can be verified. If you don't specify the Service Manager certificate's subject the out-of-box validation is not performed.

Note: In such case, the only validation performed is the one that your provided with the implementation of the verifyClientCertificate hook.

If the hook is also not implemented, the validation fails.

To set the Service Manager certificate's subject, create the broker with the serviceManagerCertificateSubject parameter or set the SBF_SERVICE_MANAGER_CERTIFICATE_SUBJECT environment variable. You can retrieve the Service Manager certificate's subject at https://service-manager.cfapps.{landscape-domain}/v1/info from the service_manager_certificate_subject field, where {landscape-domain} is the landscape in which you registered your broker.

For example, calling https://service-manager.cfapps.stagingaws.hanavlab.ondemand.com/v1/info, returns

{
  "token_issuer_url": "https://svcmgr.authentication.stagingaws.hanavlab.ondemand.com",
  "token_basic_auth": false,
  "service_manager_tenant_id": "svcmgr-cf-us10-staging",
  "service_manager_subaccount_id": "svcmgr-cf-us10-staging",
  "service_manager_certificate_subject":"/C=DE/O=SAP SE/OU=SAP Cloud Platform Clients/OU=Staging/OU=svcmgr-cf-us10-staging/L=service-manager/CN=service-manager"
}

You can enhance the out-of-box client certificate verification by implementing the verifyClientCertificate hook.

With this hook, you can perform your own validations.

Customized mTLS

Alternatively, you can register your broker with your own certificate.

Once the service broker has been registered, it will send the client certificate you configured each time it communicates with SBF.

To enable this verification, make sure to create your broker with the secureIncomingConnections option set to true, or set the SBF_SECURE_INCOMING_CONNECTIONS environment variable to true. You have to implement verifyClientCertificate hook to verify the received certificate.

Audit logging

@sap/sbf writes to audit log for every operation except for catalog (an instance of the Audit log service should be bound to the broker). The user is taken from the first of the following that contains a valid value:

  • X-Broker-API-Originating-Identity header (if provided), property user_id (in case running on Cloud Foundry).
  • X-Broker-API-Originating-Identity header (if provided), property uid (in case running on Kubernetes).
  • X-Broker-API-Originating-Identity header (if provided), property username (in case running on Kubernetes).
  • The authenticated user that calls the service broker.

It is recommended to use the same user in hooks that also write audit messages. What the X-Broker-API-Originating-Identity header contains (if present) can be found in the originating_identity property of the params object passed to the hook. If originating_identity is not available, then the user_id property can be used. It contains the authenticated user that called the broker.

Auditlog Viewer

In Cloud Foundry environment the Auditlog Viewer is the tool used for retrieving audit logs. The tool shows logs for concrete subaccount. In order for the SBF application to write auditlog messages properly it needs tenant ID.

Running on XSA

In XSA environment the tenant ID can be provided but it is not mandatory, since the audit logs are viewed in the SAP HANA Studio and not in Auditlog viewer (it is deprecated).

Providing the tenant ID

The value of the tenant ID can be taken from the SAP CP Cockpit. It is listed as ID in the Subaccount details for the subaccount that will deploy the SBF application. Then it can be provided to the SBF application:

  • manually by setting tenantId in SBF options.
  • manually by setting SBF_TENANT_ID environment variable.
  • automatically if XSUAA is used as credentials provider. It is part of the service instance binding.

The priority of the above settings is as follows: tenantId > SBF_TENANT_ID > XSUAA service instance binding: credentials.tenantid

Please be aware that if you are running in Cloud Foundry and Audit logging is enabled you must provide tenant ID for the broker application. Otherwise it will fail to start with error: Audit logging is enabled and "tenantId" option or "SBF_TENANT_ID" environment variable must be provided for successful writing of audit log messages..

Reference

Class: ServiceBroker

Service broker class.

new ServiceBroker([options])

Creates a new ServiceBroker instance.

  • options Object Optional parameter containing the following properties:
    • brokerCredentials Object The credentials for calling the service broker, if using the plain text format.
    • brokerCredentialsHash Object The credentials for calling the service broker, if using the hashed format.
    • catalog String|Object This property holds the service catalog. If it is a String, it should be a path to a JSON file which contains the catalog information.
    • serviceConfig Object Provides additional deploy-time configuration to extend the service catalog.
    • hooks Object Contains callback functions that can extend or customize service broker operations.
    • autoCredentials Boolean Enable automatic credentials generation.
    • credentialsProviderService String The name of the credentials provider service instance.
    • sbssRestrictedUserService String The name of the service containing SBSS restricted user credentials.
    • catalogSuffix String Suffix which will be appended to each service name, ID and plan ID in the service catalog to make them unique across Cloud Foundry.
    • enableAuditLog Boolean Enable/Disable audit logging. Defaults to true.
    • tenantId String Tenant ID of the broker application. It is used for audit logging. Mandatory if the broker is running on CF and audit logging is enabled.
    • secureOutgoingConnections Boolean If false, unencrypted outgoing connections will be allowed. Default value is true.
    • secureIncomingConnections Boolean If set to true, secure connection is established and the custom hook verifyClientCertificate is called . For the automatic verification of the Service Manager certificate's subject, you also have to configure the serviceManagerCertificateSubject. Default value is false.
    • serviceManagerCertificateSubject String If secureIncomingConnections is set to true and serviceManagerCertificateSubject is configured to the Service Manager certificate's subject in the broker's landscape, the Service Manager client certificate is verified in each public landscape.
    • clientCertificateKey String the private key corresponding to the client certificate used for authentication with XSUAA.
    • secureUAAScopes Boolean Relevant for XSUAA as a credentials provider. When set to true and no authorities section was provided in Additional service configuration (extend_xssecurity), SBF will pass an empty authorities array in create/update requests to XSUAA, regardless of the authorities provided by the consumer. Default: true. Note: This behavior and environment variable form an incompatible change from release v6.4.9.
    • k8sSecretsPath String the path to the mounted volume containing service secrets when running on K8S. Default is '/etc/secrets/sapcp/'.

ServiceBroker.start()

Starts the service broker.

You can attach handlers for 'error' and 'listening' events like this:

let broker = new ServiceBroker(options);
let server = broker.start();
server.on('error', err => console.error(err));
server.on('listening', () => console.log('Listening'));

ServiceBroker.app

Express application which you can use as a middleware. See Service broker as middleware.

ServiceBroker.callXsuaa(req, options, callback)

Utility function to easily make HTTP calls to XSUAA. Authentication to XSUAA is performed internally.

  • req Object Details can be found here.
  • options Object Parameter containing the following properties:
    • baseUrlProperty String Name of a property from the XSUAA credentials to be used as base URL. Defaults to url.
    • path String XSUAA REST API endpoint. This path will be appended to the XSUAA base URL internally.
    • The options described in the documentation of the axios request package.
  • callback function(error, res, body)

Example:

const Broker = require('@sap/sbf');

let broker = new Broker({
  hooks: {
    onProvision: (params, callback) => {
      const encodedInstanceId = encodeURIComponent(params['instance_id']);
      let options = {
        path: `/sap/rest/broker/clones/${encodedInstanceId}`,
        method: 'GET'
      };
      broker.callXsuaa(params.req, options, (err, res, body) => {
        if (err) { return callback(err); }

        if (res.status !== 200) {
          return callback(new Error(`Status code: ${res.status}. Body: ${body}`));
        }

        try {
          const cloneInfo = JSON.parse(body);
          console.log('XSUAA clone info:', cloneInfo);
          callback();
        } catch (err) {
          callback(new Error(`Failed to parse UAA response with status code ${res.status} and body ${body}`));
        }
      });
    }
  }
});
let server = broker.start();

(static) ServiceBroker.createCredentialsProvider(credentials)

Creates a suitable credentials provider instance according to the passed credentials. Disabling automatic credentials generation and manually creating a credentials provider allows using multiple providers in the same broker and brings more flexibility when extending the broker with custom code.

Note: This approach requires implementing the relevant hooks (onProvision, onBind, onUnbind and onDeprovision) and calling the respective method on the created provider.

  • credentials Object Credentials for a UAA or an SBSS service.

Note: If using SBSS on PostgreSQL, the restricted user properties should be provided in the restrictedUser property of credentials. Example:

const credentials = xsenv.cfServiceCredentials('postgre-service-name');
credentials.restrictedUser = xsenv.cfServiceCredentials('restricted-user-service-name');
const provider = Broker.createCredentialsProvider(credentials);

A credentials provider has the following methods:

  • provision(req, callback) Performs operations associated with service provisioning.

    • req Object Details can be found here.
    • callback function(error, operationData) An error is received in the callback in case of operations' failure. operationData (optional) Object Contains operation data provided by the credentials provider. In case XSUAA is used as a credentials provider operationData contains a property xsuaa which is an Object described here.
  • update(req, callback) Performs operations associated with service updating.

    • req Object Details can be found here.
    • callback function(error, operationData) An error is received in the callback in case of operations' failure. operationData (optional) Object Contains operation data provided by the credentials provider. In case XSUAA is used as a credentials provider operationData contains a property xsuaa which is an Object described here.
  • bind(req, callback) Performs operations associated with service binding.

    • req Object Details can be found here.
    • callback function(error, credentials) An error is received in the callback in case of operations' failure. credentials is an object containing the generated credentials.
  • unbind(req, callback) Performs operations associated with service unbinding.

    • req Object Details can be found here.
    • callback function(error) An error is received in the callback in case of operations' failure.
  • deprovision(req, callback) Performs operations associated with service deprovisioning.

    • req Object Details can be found here.
    • callback function(error) An error is received in the callback in case of operations' failure.

In addition, a UAA credentials provider has the following method:

  • callXsuaa(req, options, callback) See this section for more information.

Example:

const Broker = require('@sap/sbf');

// Applications can have a single provider:
// const provider = Broker.createCredentialsProvider({ /* ... */ })

// or multiple providers, shown below

let broker = new Broker({
  autoCredentials: false,
  hooks: {
    onProvision: (params, callback) => {
      // validate request's params
      const provider = Broker.createCredentialsProvider({ /* ... */ });
      provider.provision(params.req, callback);
    },
    onBind: (params, callback) => {
      const provider = Broker.createCredentialsProvider({ /* ... */ });
      provider.bind(params.req, (err, credentials) => {
        if (err) { return callback(err); }
        credentials.url = '...';
        callback(null, { credentials });
      });
    },
    onUnbind: (params, callback) => {
      const provider = Broker.createCredentialsProvider({ /* ... */ });
      provider.unbind(params.req, callback);
    },
    onDeprovision: (params, callback) => {
      const provider = Broker.createCredentialsProvider({ /* ... */ });
      provider.deprovision(params.req, callback);
    }
  }
});
let server = broker.start();

Note: Provision and bind are operations associated with creating a resource in the service used for credentials generation. If an error has occurred in a hook after the respective provider method has been called (i.e. provider.provision and provider.bind), it is recommended that hooks also call the opposite method (i.e. provider.deprovision and provider.unbind) to clean up the already created resources.

Service Broker Credentials

Credentials in plain text format used by the Cloud Controller and other clients to call the service broker. It is an object where each key is a user name and the value is the respective password. It may contain multiple credentials but at least one is required.

Example:

{
  "user1": "password1",
  "user2": "password2"
}

These credentials can be provided via the option brokerCredentials or the environment variable SBF_BROKER_CREDENTIALS.

Note: Service broker credentials must be provided either in plain text or hashed format. The service broker will not work if credentials in neither or both formats are present.

Service Broker Hashed Credentials

Credentials in hashed format used by the Cloud Controller and other clients to call the service broker. It is an object where each key is a user name and the value is the respective password in format sha256:<salt>:<hash-digest-of-salt+password>. Here <salt> and <hash-digest-of-salt+password> are base64-encoded strings. It may contain multiple credentials but at least one is required.

Example:

{
  "user1": "sha256:gVJILqx/97j4aWVQas5RbSUFpWzu7OpaHOt0O29CJOc=:4klnhxFY2YYwzHO7unYu7jc+HuikQLhF7Ebk8tjOJ9c=",
  "user2": "sha256:0NRIb4Gzx1zFRTTs6qpElujmHuUE1TAIg3NbES219f0=:Gv1NMeIzxlbmOCLvY3q4DMbiDXamqF3xRfFivUdligo="
}

These credentials can be provided via the option brokerCredentialsHash or the environment variable SBF_BROKER_CREDENTIALS_HASH.

Note: Service broker credentials must be provided either in plain text or hashed format. The service broker will not work if credentials in neither or both formats are present.

Note: To generate such hashed credentials, you can use the hash-broker-password script.

Service Catalog

This is a JSON object describing all services and plans offered by this service broker. Its structure is described in [Catalog Manage