Dispatch — A Multi-Tenant FaaS
We are in a world where it’s hard to imagine any enterprise service not being a multi-tenant system. From your IaaS layer to container registries, companies like to easily onboard users and teams, enforce access policies and quotas all while operating as few instances of a service as possible. Why should your FaaS (Function as a Service) offering be any different?
With the latest release of Dispatch — an open source serverless framework, we are closer than ever to help you build a multi-tenant FaaS. In this blog post, we will explore how to create different tenants in Dispatch, add users to it and run functions. We will also discuss some shortcomings and how we are planning to address them in the future. To keep it simple, we will assume Dispatch is configured with OpenFaaS as the FaaS engine and uses Google Identity Platform as the OpenID Connect provider.
But before we talk about multi-tenancy, it’s important to understand about authentication & authorization in Dispatch, as they form the core principles upon which multi-tenancy can be designed.
Authentication
Dispatch does not manage your user accounts
Dispatch does not manage user accounts on its own, in other words, there is no notion of adding accounts with a specific username/password to Dispatch using an API call. This is because:
- There is seldom just an Alice and Bob in a team or only an admin/root user in a corporate system. Usually there is a user directory with LDAP authentication or other Identity Management solutions. It’s cumbersome (and error-prone) for a service to manage large user sets and keep them in sync with directories.
- Decreasing trust of applications that store your enterprise credentials. How familiar is this prompt “Please input your AD username/password” on an internal service portal? Yet we have no idea how our credentials are stored and managed.
- Lastly, single sign-on is here to stay whether you like being hauled into multiple portals via redirects or not.
Hence, Dispatch does not manage user accounts or store credentials but integrates well with OpenID Connect providers like Google Identity Platform, vIDM, Auth0, or Dex that provides session authentication over a well-established standard.
But wait, what about automation or integration accounts running in a non-browser environment? Dispatch has the concept of service accounts for such needs. These are locally managed accounts secured with RSA keys.
Authorization
Policy Driven
Authorization in Dispatch is enforced by policies that describe the access rights of a user/group. Before a user can login to Dispatch, we need to create policies that grant privileges on one or more resources like functions, images etc. Within a single tenant, users can have different privileges based on their functional role. Typically, an administrator will have access to add base-images and images — these are secure container images with the runtime & dependencies needed for executing a function. A Developer, on the contrary, can build functions off of an image provided by an administrator. On the other hand, a service-account used for monitoring resource usage could just have read-only privileges. You get the point!
A Sample policy in Dispatch described in JSON format:
[
{
"createdTime": 1530561893,
"global": true,
"id": "f8d0b220-26b0-4de8-b281-1066156690a6",
"kind": "Policy",
"modifiedTime": 1530561893,
"name": "default-policy",
"rules": [
{
"actions": [
"*"
],
"resources": [
"images,base-images"
],
"subjects": [
"abc@example.com"
]
}
],
"status": "READY"
}
]
Multi-Tenancy
Okay, let’s get to the point of this blog. Tenants in Dispatch are called Organizations. You can create organizations and setup policies under each to onboard users/teams. In this example, we will create organizations representing two fictitious frontend & backend teams of a typical product. We will then add admin policies, user policies and then let the users create and run functions.
Creating a New Organization
After installing dispatch based on the steps detailed here, you are now logged in to Dispatch under the default organization ‘dispatch’ with a user that has god-privileges or in other words a super-admin user. You can use this account to setup other organizations and policies.
Ensure you have the required privileges by listing existing organizations:
$ dispatch iam get organizations NAME | CREATED DATE
----------------------------------------
dispatch | Mon Jul 2 13:04:50 PDT 2018
Now, create two new organizations frontend-team & backend-team with:
$ dispatch iam create organization frontend-team
Created organization: frontend-team$ dispatch iam create organization backend-team
Created organization: backend-team
Adding Admin Policies
We now add two policies granting admin privileges to specific users of the teams. Since we configured Google Identity Platform as the OpenID connect provider, we use the email address associated with the admin users while setting up the policy.
Note the usage of the--organization
flag to denote the tenant under which the policy must be created. This works since we are logged in as the super-admin user that has privileges across all organizations.
$ dispatch iam create policy frontend-admin \
--subject adm.frontend.dispatch@gmail.com \
--action "*" --resource "*" \
--organization frontend-teamCreated policy: frontend-admin$ dispatch iam create policy backend-admin \
--subject adm.backend.dispatch@gmail.com \
--action "*" --resource "*" \
--organization backend-teamCreated policy: backend-admin
Login as the Frontend Admin
Let’s logout as the super-admin user and login as the admin of the frontend team.
$ dispatch logout
You have successfully logged out$ dispatch login --organization frontend-team
Since we configured Google Identity Platform as the OpenID Connect provider, dispatch login
command will open up the default browser and prompt the user to sign-in to their google account. In this case, since we are logging in as the frontend admin, let’s choose the corresponding account.
Upon entering the credentials for the frontend admin account, you will now notice some redirections related to OAuth2 Authorization Code Grant and then the following message on the browser appears:
At this point, your CLI has received a cookie for the session and you are logged-in as the frontend admin to the frontend team organization.
Create base-images & images for use by the team members. As mentioned above, these images are secure container images that provide the runtime and dependencies for your function code.
$ dispatch create seed-imagesCreated BaseImage: nodejs-base
Created BaseImage: python3-base
Created BaseImage: powershell-base
Created BaseImage: java-base
Created Image: nodejs
Created Image: python3
Created Image: powershell
Created Image: java
Add User Policies for The Frontend Team
The following commands adds policies for the developers of the frontend-team restricting their access to creating & executing functions. Since images are fundamental to creating functions, we provide the users with read-access to images.
# Allow CRUD on functions and related resources
$ dispatch iam create policy dev-policy-crud \
--subject dev1.frontend.dispatch@gmail.com \
--subject dev2.frontend.dispatch@gmail.com \
--action "*" \
--resource function,runs,secret,apiCreated policy: dev-policy-crud# Read-Only access on Image resources
$ dispatch iam create policy dev-policy-read \
--subject dev1.frontend.dispatch@gmail.com \
--subject dev2.frontend.dispatch@gmail.com \
--action get \
--resource image,baseimageCreated policy: dev-policy-read
Login as Frontend Developer
$ dispatch logout
You have successfully logged out$ dispatch login --organization frontend-team
Create a Function as the Frontend Developer
$ cat > /tmp/node-hello.js <<EOF
module.exports = function (context, params) {
let name = "Noone";
if (params.name) {
name = params.name;
}
let place = "Nowhere";
if (params.place) {
place = params.place;
}
return {myField: 'Hello, ' + name + ' from ' + place + ' - Yours, nodejs!'}
};
EOF$ dispatch create function --image=nodejs node-hello-js /tmp/node-hello.js
Created function: node-hello-js
Create an API for the nodejs function
Dispatch has an in-built API Gateway (powered by Kong) that supports the creation of API’s that then point to your function.
$ dispatch create api frontend-hello-js node-hello-js --path /hello --cors
Created api: frontend-hello-js
Execute the Function via the API
Once the API is created, you can access it through the Dispatch’s API Gateway. For this blog, the API Gateway has been setup to use the host orgdemo.dispatchframework.io. Hence, all the API paths can be accessed at https://orgdemo.dispatchframework.io/<org_name/<path>.
Thus, the API created above can be accessed at the following URL:
$ curl https://orgdemo.dispatchframework.io/frontend-team/hello{"myField":"Hello, Noone from Nowhere - Yours, nodejs!"}
Repeat steps for the Backend Team
Let’s execute similar steps as before for the backend team. For brevity, let’s not discuss each step in detail.
############################
# Login as Backend Admin
############################$ dispatch login --organization backend-team
# Choose the `Backend Admin`Account in the Google Authentication Prompt# Allow CRUD on functions and related resources
$ dispatch iam create policy dev-policy-crud \
--subject dev1.backend.dispatch@gmail.com \
--subject dev2.backend.dispatch@gmail.com \
--action "*" \
--resource function,runs,secret,api
Created policy: dev-policy-crud# Read-Only access on Image resources
$ dispatch iam create policy dev-policy-read \
--subject dev1.backend.dispatch@gmail.com \
--subject dev2.backend.dispatch@gmail.com \
--action get \
--resource image,baseimage
Created policy: dev-policy-read$ dispatch logout
You have successfully logged out############################
# Login as Backend Developer
############################$ dispatch login --organization backend-team
# Choose the `Backend Developer` Account in the Google Authentication Prompt###################################################
# Create a Python Function as the Backend Developer
###################################################$ cat > /tmp/python_hello.py <<EOF
def handle(ctx, payload):
name = "Noone"
place = "Nowhere"
if payload:
name = payload.get("name", name)
place = payload.get("place", place)
return {"myField": "Hello, %s from %s - Yours, python3!" % (name, place)}
EOF$ dispatch create function --image=python3 python-hello /tmp/python_hello.py#######################################
# Create an API for the python function
#######################################$ dispatch create api backend-hello-python python-hello --path /hello
Created api: backend-hello-python
Execute the Backend Team’s function via the API
$ curl https://orgdemo.dispatchframework.io/backend-team/hello{"myField":"Hello, Noone from Nowhere - Yours, python3!"}
Future Work
We are at the whiteboard jotting designs (and doodling..) to enhance the multi-tenancy experience in many ways. Some of the tasks that are in the pipeline are:
- LDAP groups/OIDC groups claim support
- Support for defining roles in policies
- Automate SSL cert generation per organization for API endpoints
- Support image registries per organization
Conclusion
With the latest version of Dispatch, it’s now possible to build a Multi-Tenant FaaS service in your enterprise and onboard your teams and developers. Isn’t that FaaScinating?