CEL in OpenBao
The Common Expression Language (CEL) is used in PKI and JWT to declare validation rules and policies:
The main CEL Expression in a Celprogram is typically one line of code. This can be broken down
into multiple CEL expressions by using CelVariable's. The following sections cover how to build
a CelProgram in any part of OpenBao.
CEL is an advanced topic for operators. All validation is taken on by the CEL program created by the operator; limited to no validation is performed by OpenBao above and beyond what the CEL program contains. It is up to the operator to craft CEL programs which adequately perform validation for the context.
Table of contents
Main expression
-
This is the main expression that determines whether a CEL program has accepted or rejected a request.
-
The main expression in the
CelProgramshould return the engine's specific output object on success, for instance:- PKI →
ValidationOutput - JWT/OIDC →
pb.Auth
- PKI →
-
A custom
stringerror message orboolshould be returned on failure. The error message can be constructed using a CEL expression. For instance:require_cn ? 'request should have a common_name' : (validate_ttl ? 'request has invalid TTL' : "Request Rejected") -
Use a ternary operator when writing a main expression for clarity and to guarantee a value is always produced:
cond ? SuccessObject : error string/bool
Variables in a CEL Program
-
A variable is a named expression that can be referred later in other variables and expressions.
-
They are useful if an expression is too long or has reusable parts.
-
The order of variables matters since they are added into the CEL environment in the order they are defined. A variable can reference only variables declared before it, not ones that come later.
Definition
// Name of the variable.
Name: string
// CEL expression for the variable
Expression: string
Example
{
"name": "small_ttl",
"expression": `has(request.ttl) && duration(request.ttl) < duration("4h")`,
},
CEL Program
- A CEL role is made of 2 parts:
-
A list of
CelVariable's. The CEL variables are declared in the CEL env so that they can be accessed by other variables as well as the main expression. -
A main expression which determines whether the CEL program succeeds or fails.
-
Definition
// List of variables with explicit order (optional)
Variables: []CelVariable
// Required, the main CEL expression
Expression: string
Example
"cel_program": map[string]any{
"variables": []map[string]any{
{
"name": "validate_cn",
"expression": `has(request.common_name) && request.common_name == "example.com"`,
},
{
"name": "small_ttl",
"expression": `has(request.ttl) && duration(request.ttl) < duration("4h")`,
},
{
"name": "cn_value",
"expression": "request.common_name",
},
{
"name": "not_after",
"expression": "now + duration(request.ttl)",
},
{
"name": "cert",
"expression": `CertTemplate{
Subject: PKIX.Name{
CommonName: cn_value,
},
NotBefore: now,
NotAfter: not_after,
}`,
},
{
"name": "output",
"expression": `ValidationOutput{
template: cert,
generate_lease: small_ttl,
no_store: !small_ttl,
}`,
},
{
"name": "err",
"expression": "'Request should have common_name'",
},
},
"expression": "validate_cn ? output : err",
}
The Request Object
-
Every key/value pair in the request body is copied verbatim to the CEL
requestmap. The CEL program (not the endpoint) decides which of these parameters to honour, ignore, or override. -
To avoid breaking existing automation, consider re-using familiar field names such as
common_nameorttl, even though CEL roles are not required to follow the traditional role schema.