Read and write data on external systems
backends
Backends are the way execution pipelines can read and write data on external systems.
Following backend types are supported:
- JSON File
- LDAP
- PostgreSQL
- MySQL / MariaDB
- SQLite
- RADIUS
- RadSec and UDP transports
- DNS Service Discovery
- HTTP
- Generic REST APIs
- OAuth2 / OpenID Connect providers
- Duo Security
- YubiKey
- RSA Authentication Manager
- NTLM&LSA
The full backend configuration syntax is backend specific, but in general a backend configuration include the backend name, one or more queries and a way to define "bindings" for input arguments and "mappings" that map the query results back to the execution context.
Here is an example of a MySQL backend configuration with a single query:
backends {
mysql "MY_DB" {
server "MYSQL" {
host "example.test";
port 3306;
database "test_db";
username "testuser";
}
query "FIND_USER" {
# SQL statement
statement "SELECT username, password FROM USERIDS WHERE username = ?";
# Query argument binding in order
bindings {
aaa.identity;
}
# Result value mapping
mapping {
# the `username` and `password` variables come from the SQL
# query result columns and are mapped to the execution context
# variables `user.username` and `user.password`
user.username = username;
user.password = password;
}
}
}
}
Calling backends
Backends are called with the backend action
backend {
name "EXAMPLE_FILE_BACKEND";
query "FIND_USER";
}
Some backends may expose different queries. The way of defining backend queries
is backend specific. For example, SQL backends use the query block to define
SQL queries.
If the backend does not support multiple queries, the query name may be omitted.
Using variables as query arguments
Backend bindings reference values from the execution context. In addition to
built-in context variables like aaa.identity, you can use vars.* variables
to pass custom values to backend queries. Set the variable with modify before
calling the backend, and reference it in the bindings block. Mappings can
also set vars.* variables to store query results for use later in the
pipeline.
This is useful when the query arguments do not come directly from the request
but are derived, extracted, or hardcoded earlier in the pipeline. In the
example below, the backend definition uses only vars.* bindings, which
fully decouples it from any specific protocol. The same backend and query
can then be reused across RADIUS, HTTP, and other protocol contexts without
modification.
backends {
sqlite "MY_DB" {
query "UPSERT_USER" {
statement """
INSERT INTO users (username, password) VALUES (?, ?)
ON CONFLICT(username) DO UPDATE SET password = excluded.password
RETURNING id
""";
bindings {
vars.username;
vars.password;
}
mapping {
vars.user_id = id;
}
}
}
}
In the execution pipeline, set the variables before invoking the backend:
@execute {
modify {
vars.username = http.path.2;
vars.password = http.body.password;
}
backend {
name "MY_DB";
query "UPSERT_USER";
}
# The vars.user_id variable is now available for use in the rest of the pipeline
}