Configuring OpenUnison Applications
The OpenUnison you deploy with your kubernetes implementation can be customized for several kinds of identity services. For instance, if you're running a workflow in GitLab or GitHub, you can create a service in OpenUnison that allows you to get a kubectl configuration from the workflow using the token from your workflow! There's an incredible amount of power in OpenUnison's capabilities to tightly integrate a strong identity into your environment. Whether you're trying to integrate user facing applications or need additional services. OpenUnison has the capability. When looking at building identity services on OpenUnison, the first place to look is OpenUnison's Application
object.
Application Configuration
Applications
Application
objects are the core for building identity services and integrations with OpenUnison. There are three kinds of Application
objects:
- Reverse Proxy - OpenUnison forwards all requests to this
Application
to a downstream service. For instance, if you're deploying Prometheus and AlertManager into your cluster, you'll use this method. - Identity Provider - An
Application
can be configured to be either an OpenID Connect or SAML2 identity provider. In this mode you have the ability to customize how a user's identity is presented to the relying party. For instance, if you were to deploy an integration with ArgoCD with a customized groups mapping you would use this method. - Local Service - You can implement custom web services in an
Application
that aren't proxied back to another webapp or API. This is really useful for implementing Security Token Services (STS) like we described above.
An Application
is built on a set of url
s. Each url
specifies:
- Hosts - Each URL can be associated with multiple host names, or all host names with
*
- Authentication Chain - How to authenticate this URL
- Filters - A list of plugins that can manipulate both request and response headers. This is useful for injecting identity information or adding data to responses. OpenUnison is bundled with several pre-built filters.
- Authorization Rules - A list of rules that defines who is able to access this URL
- Results - A set of authorization results, such as setting a cookie or adding a header
- Cookie Configuration - If a web application requires a session, this is how you configure how cookies are encrypted. This allows you to isolate applications with separate keys, even if the cookie space covers the same scope.
If you're Application
is meant to be an identity provider (IdP), the URL will have a special configuration as well.
For detailed configuration information for each configuration option, see the Application CRD documentation.
With a general idea of how to configure an application in OpenUnison, the next concept we'll work on is how authentication is built in OpenUnison.
Authentication
Every URL in an Application
has exactly one authChain
. Authentication chains are a sequence of authentication mechanisms that have a level associated with it. This gives you the ability to string together different kinds of authentication and provides for some limited branching. For instance, you can create a chain that accepts an OIDC token, then just-in-time provisions an account into a database. By assigning an authentication level to each chain, you can force re-authentication. As an example, you have an application that only needs a single factor, except for the admin system. Assigning an authentication chain with a higher level for the admin system will force a re-authentication.
The following mechanisms are pre-configure with OpenUnison:
Mechanism | Description |
---|---|
login-form | Login with a username and password |
jit | Just-in-time provisioning |
genoidctoken | Generates an OIDC token from an oidc identity provider and puts it in the user's session |
login-service | Allows a user to choose from multiple authentication chains |
oidc | Authenticate using a remote OIDC identity provider |
saml2 | Authenticate using a remote SAML2 identity provider |
oauth2jwt | Authenticate with a JWT |
oauth2k8s | Authenticate with a Kubernetes ServiceAccount and validate it using a TokenReview |
map | Maps attributes in an authenticated identity |
github | Authenticates to GitHub |
az | Allows for authorization rules to be executed during the authentication process |
js | Write custom authentication mechanisms in JavaScript |
include | Includes another authentication chain in the current chain |
Additional Authentication Mechanisms can be configured by creating the appropriate Kubernetes AuthenticationMechanism CRD.
Once authentication has been configured for an Application
, the next question to answer is how to authorize each request.
Authorization
OpenUnison's built in authorization system is common across multiple processes. Each authorization is made up of a series of authorization rules. Authorization succeeds if ANY of the rules pass. There are four authorization options for any given rule:
Authorization Mode | Description | Constraint Example |
---|---|---|
group | The DN of a group the suer must be a member of in OpenUnison's virtual directory | cn=group-name,ou-orgs,dc=domain,dc=com |
dynamicGroup | The DN of an LDAP dynamic group the suer must be a member of in OpenUnison's virtual directory | cn=ldap-dynamic-group,ou=groups,dc=domain,dc=com |
dn | The DN of an area of the virtual directory where all users under that DN are authorized | ou=users,dc=domain,dc=com |
custom | The name of a CustomAuthorization. Can be followed by a ! with parameters. For instance SomeCustomAZ!param1!param2 will call the custom authorization SomeCustomAZ with the parameters param1 and param2 | SomeCustomAZ!param1!param2 |
filter | An LDAP filter that will be applied to a logged in user | (objectClass=*) |
Since authorization rules can incur a significant performance impact, each Application
has an option called azTimeoutMillis
that caches authorization decisions for a user for a very limited time. Usually a few seconds.
OpenUnison comes with several Custom Authorizations that you can leverage in your configurations. You can also write your own using JavaScript.
Once your authorizations are configured, the last choice is to decide if there will be any actions performed as a result of an authentication or an authorization decision.
Result Groups
Every URL can trigger an event as the result of one of four possible events:
Event | Description |
---|---|
auSuccess | Successful Authentication, happens once per session |
auFail | Failed authentication, happens when an authentication chain fails |
azSuccess | When a URL access is authorized, triggers on each request |
azFail | When a URL access is denied, triggers on each request |
Each event can be configured with a ResultGroup, which can in turn be configured with multiple results. Results can be a cookie, a request header, or a redirect. The value of a result can come from a static value, and attribute value, or from a custom result
Workflows and Automation
OpenUnison can run a series of tasks, or a workflow, to provision access or infrastructure. There are multiple use cases for using workflows in OpenUnison:
- Just-In-Time Provisioning - When providing SSO to an application that stores its own identity data, OpenUnison can synchronize this data from your identity provider as the user logs in.
- Granting Access to Resources - If a namespace or cluster has been provisioned, and you wish to grant someone access.
- Bootstrapping Infrastructure - A user needs a new namespace to be created, OpenUnison can create it, and the additional objects across databases, git repositories, and controllers to make namespace useable.
When using OpenUnison, there are two kinds of workflows:
- Synchronous - Each task of the workflow is run in the same thread and immediately, if there's a failure then the entire workflow fails.
- Asynchronous - Each task of the workflow is places in a message queue and executed asynchronously. If a task fails, then it is send to a dead letter queue for later processing.
When using OpenUnison as a login portal, only synchronous workflows can be run. When using the Namespace as a Service portal, either synchronous or asynchronous workflows may be run. This document will provide the form of workflows and instructions on how to build them.
Workflow YAML
Workflows in OpenUnison are stored as Workflow
objects in your Kubernetes API server. A basic workflow will look like:
The above yaml stores the metadata for the workflow as well as the individual tasks. Tasks are embedded yaml, and are NOT part of the Workflow
CRD. To make it easier to debug workflows, OpenUnison has an admission controller that will report back errors when trying to submit a workflow. Below are the components of the workflow configuration:
Line Number | Description |
---|---|
30 | A detailed description of the workflow, may include request parameters by using $parameter$ |
31 | When using Namespace as a Service, should this workflow be listed in the UI and requested by users? |
32 | A shorter label that will be displayed in the NaaS portal. May include request parameters by using $parameter$ |
33 | The Org that the workflow is a member of. If inList is true , then the logged in user must be authorized per the Org to see and request this workflow from the NaaS portal |
34 - 37 | dynamicConfiguration allows for a single workflow to be created to handle multiple subjects. For instance, this is how the NaaS portal provides a workflow for access to each namespace |
39 - 42 | The workflow tasks for this workflow as an embedded YAML array. If there is a problem with the embedded YAML, the admission controller will report back the error |
With the basic form of the Workflow
defined, the next step is define what tasks are available for workflows.
Workflow Tasks
OpenUnison defines a handful of workflow tasks, with the rest being filled in by "custom tasks", which are written in Java to support more complex logic. You don't need to write tasks in Java though, as OpenUnison supports JavaScript for tasks too.
provision
This task is used to push user data to a provisioning target in an idempotent way. This task should be safe to run with the same inputs multiple times with only differences updated. This task does NOT support sub tasks.
Line Number | Description |
---|---|
1 | The task type |
2 | If false , the provision task will add attributes and groups in a non-destructive way. For instance if the sn attribute is not included in the provision task, it will not be removed. The same is true of groups. It true , the provision task will make the target object match the current workflow representation. Groups that aren't in the workflow are removed |
3 | The OpenUnison Target to run the task against |
4 | If true , the task attempts to set the user's password, usually false |
5 | If true , the task will only attempt to synchronize attributes that are in the workflow's user object. Usually false . It's better to explicitly set attributes via the attributes option. |
6 - 7 | List of attributes the task should work with. |
ifNotUserExists
This task will execute sub tasks if-and-only-if there is not a user in the internal virtual directory that matches the value of the attribute specified in the current user's context.
Line Number | Description |
---|---|
1 | The task type |
2 | The OpenUnison Target to run the task against |
3 | The name of the user attribute to use for lookup |
4 - 8 | The list of tasks to run if the user does not exist |
9 - 13 | The list of tasks to run if the user does exist |
addAttribute
Adds an attribute to the user’s object or to the workflow’s request object.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the attribute, may include request parameters by using $parameter$ |
3 | The value to add or remove from the attribute, may include request parameters by using $parameter$ |
4 | If true , the attribute value is removed. If false , the attribute value is added |
5 | If true , the attribute is added to the workflow's request object. If false , the attribute is added to the workflow's user object |
addGroup
Adds the named group to the user. NOTE - The group name is NOT a distinguished name, its a name that will be mapped to a group in a provisioning target. This usually means mapping to the cn attribute.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the group, may include request parameters by using $parameter$ |
3 | If true , the group is removed from the user object. If false , the group is added |
approval
The approval step provides for a user to act on a request. and provide either their consent or to disapprove of the request. Each approval can have a set of authorization rules to determine who can make the approval. In addition to authorizations escalation rules can be defined that let the authorization rules change. Finally, if all escalations are exhausted a failure policy can be defined to allow for a request to be re-assigned or rejected.
Line Number | Description |
---|---|
1 | The task type |
2 | An email template to send to approvers, may include request parameters by using $parameter$ |
3 | The name of the attribute on the workflow's user that has their email attribute |
4 | Subject of the email to send to the user if the request is denided, may include request parameters by using $parameter$ |
5 - 7 | Body of the email to send to the user if the request is denied, may include request parameters by using $parameter$ |
8 | The label for the approval, seen in the Orchestra portal. may include request parameters by using $parameter$ |
9 - 13 | Defines who is able to approve requests. May include request parameters by using $parameter$ . An authorization rule is made of a scope (one of dn ,group ,filter ,custom ) and a constraint , which defines what the rule is. The combination of dn and o=Tremolo tells OpenUnison to authorize any user in the internal LDAP virtual directory. If you wanted to only allow users in the group cn=argocd-users,ou=Groups,DC=domain,DC=com you would use filter as the scope and (groups=cn=argocd-users,ou=Groups,DC=domain,DC=com) as the constraint because OpenUnison internally represents groups in the Kubernetes integration as attributes instead of seperate objects. Multipls rules may be listed. |
14 - 26 | An optional policy can be set to escalate requests that aren't acted on in a specific amount of time. |
15 - 21 | Lists each escalation rule in order |
16 - 21 | Specific escalation rule |
16 | The amount of time before the escalation rule triggers |
17 | Class that is called when verifying an escalation should execute, must implement com.tremolosecurity.proxy.az.VerifyEscalation class |
18 | The unit of time (sec,min,hour,day,month,year) |
19 - 21 | Defines who is able to approve requests. May include request parameters by using $parameter$ . An authorization rule is made of a scope (one of dn ,group ,filter ,custom ) and a constraint , which defines what the rule is. The combination of dn and o=Tremolo tells OpenUnison to authorize any user in the internal LDAP virtual directory. If you wanted to only allow users in the group cn=argocd-users,ou=Groups,DC=domain,DC=com you would use filter as the scope and (groups=cn=argocd-users,ou=Groups,DC=domain,DC=com) as the constraint because OpenUnison internally represents groups in the Kubernetes integration as attributes instead of seperate objects. Multipls rules may be listed. |
22 - 26 | Defines what happens if the last escalation is not acted on in its escalationAftertime , this usually is used to assign to an automatic failure user so the request can be closed. |
23 | Either "leave" to do nothing, or "assign" to assign the request to the user(s) defined in the azRules |
24 - 26 | Defines who is able to approve requests. May include request parameters by using $parameter$ . An authorization rule is made of a scope (one of dn ,group ,filter ,custom ) and a constraint , which defines what the rule is. The combination of dn and o=Tremolo tells OpenUnison to authorize any user in the internal LDAP virtual directory. If you wanted to only allow users in the group cn=argocd-users,ou=Groups,DC=domain,DC=com you would use filter as the scope and (groups=cn=argocd-users,ou=Groups,DC=domain,DC=com) as the constraint because OpenUnison internally represents groups in the Kubernetes integration as attributes instead of seperate objects. Multipls rules may be listed. |
27 | Tasks to execute if the approval succeeds |
32 | Tasks to execute if the request is denied |
customTask
This task provides a mechanism by which custom logic may be added to a workflow. See individual custom tasks for parameters that support parameters.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the class that implements the com.tremolosecurity.provisioning.util.CustomTask interface |
3 | Parameters for the task |
There are numerous OpenUnison custom tasks. Look for them based on their application. If you want to build your own custom task, the easiest way is using JavaScript.
delete
The delete task will delete a user in an individual provisioning target.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the provisioning target to delete the user from |
ifAttrExists
This task will run if a user object in the workflow has a particuler attribute, regardless of the value.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the attribute to look for, may include request parameters by using $parameter$ |
3 - 7 | Tasks to run if the attribute exists |
8 - 12 | Tasks to run if the attribute does not exist |
mapping
The mapping task will create a copy of the user object based on the mapping rules. Any changes made inside of the mapping to the user object will NOT affect the user object in the workflow outside of the mapping. Unlike other tasks that have sub-tasks, mappings can only have "onSuccess" sub tasks. Anything in "onFailure" will be ignored.
Line Number | Description |
---|---|
1 | The task type |
2 | If true, the mapped user object will ONLY contain attributes explicitly named in the mapping. If false, then any attribute not mentioned will be added to the user object with their present value. |
3 - 6 | The mapping |
4 | The name of the attribute in the new user object, supports parameters. If TREMOLO_USER_ID is used, the mapped user's ID will be set |
5 | The value to be used for the new attribute, supports parameters. If TREMOLO_USER_ID is used, the value is pulled from the user object's identity |
6 | Describes what the targetAttributeSource is. Possible values are:static - Takes the value from targetAttributeSource as isuser - Takes the existing value from the attribute named in targetAttributeSourcecomposite - Use a composite of multiple attributes and static values by containing attribute names in ${attributeName} custom - targetAttributeSource is the name of a class that implements com.tremolosecurity.provisioning.mapping.CustomMapping |
7 - 11 | The tasks to run within the mapping context |
notifyUser
This task provides a way to send an email to the requester of the workflow. This can be used to notify the user of a successful execution, request more information, etc. Emails are sent from the server specified in the approvals section of the configuration. If the requester is different then the subject of the workflow, then the requester is notified UNLESS the unison.sendToSubject property is stored in the workflow’s request object. The value does not matter.
Line Number | Description |
---|---|
1 | The task type |
2 | The subject of the email notification, supports parameters |
3 | The name of the attribute that stores the user's email address |
4 | The message to send. Attributes may be added between ${} , supports parameters |
resync
When executing a just-in-time provisioning workflow, for instance when using identity federation, once the user's object is created in downstream targets the user?s object in Unison will need to be ?refreshed?. This task updates the internal OpenUnison object.
Line Number | Description |
---|---|
1 | The task type |
2 | If true, the user object will maintain any attributes that were not loaded from the internal virtual directory. |
3 | If true, OpenUnison will reload the user's object from a different search base then the one that was originally used to find the user |
4 | If changeRoot is checked, the root of the virtual directory to use to find the user; supports parameters |
callWorkflow
This task allows for another workflow to be called. This allows for the creation of modular workflows. For instance a modular workflow can be created that requires 2 approvals before provisioning to a resource. This workflow can be included in a self-service request from the portal and a helpdesk application with the same results without having to duplicate the workflow.
Line Number | Description |
---|---|
1 | The task type |
2 | The name of the workflow to call |
Deployment and Customization
Once your Application
object is deployed, you can immediately start testing. OpenUnison is an operator, so it will automatically load the objects you create and process them. You may want to consider building your objects into a Helm chart. This makes it easy to deploy for both a gitops scenario or using the ouctl utility.
In addition to any values configured into your objects as parameters, you can also include some custom variables by using #[variable_name]
in your configuration. For instance, #[OU_HOST]
will include the value for OpenUnison's host (network.openunison_host
in your values.yaml). Any of the entries in the orchestra
OpenUnison
object's non_secret_data
can be included this way. You can get a list of non secret entries with the command:
k get openunison orchestra -n openunison -o json | jq -r '.spec.non_secret_data'
[
{
"name": "K8S_URL",
"value": "https://192.168.2.130:6443"
},
{
"name": "SESSION_INACTIVITY_TIMEOUT_SECONDS",
"value": "900"
},
{
"name": "K8S_DASHBOARD_NAMESPACE",
"value": "kubernetes-dashboard"
},
{
"name": "K8S_DASHBOARD_SERVICE",
"value": "kubernetes-dashboard"
},
{
"name": "K8S_CLUSTER_NAME",
"value": "openunison-cp"
},
{
"name": "OPENUNISON_PROVISIONING_ENABLED",
"value": "false"
},
{
"name": "K8S_IMPERSONATION",
"value": "false"
},
{
"name": "PROMETHEUS_SERVICE_ACCOUNT",
"value": "system:serviceaccount:monitoring:prometheus-k8s"
},
{
"name": "OU_SVC_NAME",
"value": "openunison-orchestra.openunison.svc"
},
{
"name": "K8S_TOKEN_TYPE",
"value": "legacy"
},
{
"name": "K8S_DB_SSO",
"value": "oidc"
},
{
"name": "PROMETHEUS_SERVICE_ACCOUNT",
"value": "system:serviceaccount:monitoring:prometheus-k8s"
},
{
"name": "SHOW_PORTAL_ORGS",
"value": "false"
},
{
"name": "K8S_OPENUNISON_NS",
"value": "openunison"
}
]