Introduction
Authly is a flexible Attribute-based Access Control (ABAC) Identity and Access Management (IAM) solution with minimalist Service Mesh control plane and data plane capabilitites.
The Authly server can be run standalone for its IAM features, but relies on mTLS for service client authentication, and can provision such services with client certificates from a mesh-local Certificate Authority (CA), either manually, through a native Rust client (authly-client
) through its language bindings (TBA), or the minimalist sidecar proxy proxly.
Getting started
Authly's security model depends on a few core principles:
- Authly should not run directly exposed to the internet, as it expects mTLS for all connections. External clients should reach it through a gateway, which is provisioned by Authly.
- Other service clients are provisioned the same way, they are registered with Authly, and the client certificate is used as authentication.
- Authly's embedded database uses encryption-at-rest, and the master encryption key needs to be stored externally (and safely) in order to decrypt its data.
As a result, Authly requires a minimum set of key components to run:
- An Authly-compatible gateway using the
authly-client
Rust library, such as Arx. - Service clients using the
authly-client
Rust library, either directly, through language bindings (TBA) or as a minimal sidecar proxy (TBA) - A secure secrets store, we support OpenBao (implemented), with pending support for AWS Key Management Service, Azure Key Vault Standard, and Google Cloud Key Management.
Our example Kubernetes setup features Authly itself, Arx, OpenBao and an example service. A similar example is available for docker compose.
For Kubernetes installation, use our Helm chart.
Authly configuration documents
Authly's attribute-based identity and access control model is configured through sequentially applied, declarative TOML documents. Any number of these documents can be included from the configuration setting AUTHLY_DOCUMENT_PATH
, and any definition can depend on a previous defintion. To give them some sort of order, they are usually prefixed with a number to ensure they are processed correctly in lexicographical order, e.g.:
0_core.toml
1_services.toml
2_users.toml
Although the documents are intended to be human-readable and -writable, the definitions therein will eventually be accessible through a more user-friendly admin UI.
A full example, step by step
[authly-document]
id = "18d70399-0e89-46b8-81ce-a6a16e5db7cc"
The document requires an authly-document
clause at the top, with a UUID id
value.
[[service-entity]]
eid = "s.3c2f40b3f47a4d9b9129b1e7c15fbc04"
label = "arx"
attributes = ["authly:role:authenticate", "authly:role:get_access_token"]
kubernetes-account = { name = "arx" }
This defines the "arx"
gateway as a kubernetes service-entity. The gateway is responsible for opening up the public aspects of Authly to the world outside the cluster.
Service entity ids are prefixed by s.
.
The built-in attribute triplets "authly:role:authenticate"
and "authly:role:get_access_token"
allows anyone to authenticate and get access tokens through the gateway. An attribute triplet is a colon-separated namespace:label:attribute
string.
The kubernetes-account
is used by Authly to provision the service with an mTLS client certificate, used for (service) authentication.
It only specifies an account name, and not a namespace
. Not specifying the namespace means the same namespace that Authly itself runs within.
[[service-entity]]
eid = "s.ec29ba1d23cb43f89b7c73db6f177a1d"
label = "ultradb"
hosts = ["ultradb"]
kubernetes-account = { name = "ultradb" }
This defines the "ultradb"
service-entity, hosted as a kubernetes service behind the "arx"
. Service-enitity labels are exposed as namespaces in the Authly model.
[[service-entity]]
eid = "s.a1c6134658dd4120823fdc42bb2f42ad"
label = "ultradb_gui"
hosts = ["ultradb-gui"]
This defines the "ultradb_gui"
(client) service-entity, hosted by "ultradb"
.
[[entity-property]]
namespace = "ultradb_gui"
label = "role"
attributes = ["user", "admin"]
This defines the entity-property "role"
for the "ultradb_gui"
client.
Its attributes are "user"
and "admin"
.
In other words, we make the client responsible for the concept of a (persona) entity role, since users access the service through the client.
[[resource-property]]
namespace = "ultradb"
label = "action"
attributes = ["read", "write"]
This defines the resource-property "action"
for the "ultradb"
service.
Its attributes are "read"
and "write"
.
In other words, we make the service is responsible for its own resources, since users access resources (data) via the service's API.
[[policy]]
label = "allow for GUI user"
allow = "Subject.ultradb_gui:role contains ultradb_gui:role:user"
This defines a policy called "allow for GUI user"
.
The policy must either define an allow
or a deny
expression. This policy will resolve to allow
if the expression resolves to true
.
Subject
is the one being access-controlled. ultradb_gui:role
is a namespaced entity property, and ultradb_gui:role:user
is an attribute assigned to an entity (see below).
Referencing the entity-properties above, this should read as "allow if the Subject has the ultradb_gui role user".
[[policy]]
label = "allow for GUI admin"
allow = "Subject.ultradb_gui:role contains ultradb_gui:role:admin"
This defines a policy called "allow for GUI admin"
, similar to the one above.
Referencing the entity-properties above, this should read as "allow if the Subject has the ultradb_gui role admin".
[[policy-binding]]
attributes = ["ultradb:action:read"]
policies = ["allow for GUI user", "allow for GUI admin"]
This defines a policy-binding, from a list of policies to a list of colon-separated attribute triplets (namespace:label:attribute
).
Referencing the resource-properties and policies, this should read as "GUI users and GUI admins are allowed the ultradb action read".
[[policy-binding]]
attributes = ["ultradb:action:write"]
policies = ["allow for GUI admin"]
This defines a policy-binding, from a list of policies to a list of colon-separated attribute triplets (namespace:label:attribute
).
Referencing the resource-properties and policies, this should read as "GUI admins are allowed the ultradb action write".
[[entity]]
eid = "p.96bf83f88cbf455fa356553f7fca1b9e"
label = "Mr. User"
This defines a (persona) entity, "Mr. User"
. For now, they don't have any access credentials.
Persona entity ids are prefixed by p.
.
[[entity]]
eid = "p.81dc1da0fa644142bad35043a9c3b025"
label = "Ms. Admin"
This defines a (persona) entity, "Ms. Admin"
. For now, they don't have any access credentials.
[[entity-attribute-assignment]]
entity = "Mr. User"
attributes = ["ultradb_gui:role:user"]
This defines an entity-attribute-assignment.
Referencing the entities and entity-properties above, this should read as "Mr. User has the ultradb_gui role user".
[[entity-attribute-assignment]]
entity = "Ms. Admin"
attributes = ["ultradb_gui:role:admin"]
This defines an entity-attribute-assignment.
Referencing the entities and entity-properties above, this should read as "Ms. Admin has the ultradb_gui role admin".
To summarize, this document defines three service-entities, and allows anyone to authenticate and resolve an authentication token through the service "arx" (if they had credentials). We define some entity-properties and resource-properties to describe our access control model, and policies are bound to the resource-properties through policy-bindings. Finally, a pair of entities are defined, and are assigned entity attributes through entity-attribute-assignments.
Clauses
[authly-document]
Required. Metadata about the document. Every document must have one of these clauses the top.
Properties:
id
: Required. A UUID value.
Example:
[authly-document]
id = "3ef3430a-6499-497c-b8eb-00516a22f326"
[[entity]]
An entity definition, e.g. a persona or a group.
Properties:
eid
: Required. The entity id. Persona entity ids are prefixed byp.
, while group entity ids are prefixed byg.
. The value is a hex-encoded 128-bit value.label
: A label for the entity visible in the document namespace.attributes
: Attributes bound to the entity. See entity-attribute-assignment.username
: A list of usernames.email
: A list of email addresses.password-hash
: A list of password hashes.
Example:
[[entity]]
eid = "p.0fbcd73e1a884424a1615c3c3fdeebea"
label = "me"
username = "me"
password-hash = [
"$argon2id$v=19$m=19456,t=2,p=1$/lj8Yj6ZTJLiqgpYb4Nn0g$z79FFMXstrkY8KmpC0vQWIDcne0lylBbctUAluIVqLk"
]
[[service-entity]]
A service entity definition.
Services are authenticated through client certificates rather than traditional credentials.
Properties:
eid
: Required. The entity id. Service entity ids are prefixed bys.
.label
: A label for the entity visible in the document namespace.attributes
: Attributes bound to the entity. See entity-attribute-assignment.metadata
: Metadata about this entity. The metadata is not used by authly itself, but can be used by services which have read access to the entity.hosts
: List of service hostnames.kubernetes-account
: An optional Kubernetes account definition.
Example:
[[service-entity]]
eid = "s.3c2f40b3f47a4d9b9129b1e7c15fbc0c"
label = "service"
metadata = { meta = "meta" }
kubernetes-account = { name = "service", namespace = "myspace" }
[[email]]
An email address assignment.
Can also be given as part of an email
property list of an [[entity]]
clause.
Properties:
entity
: The label of the entity that is assigned this address.value
: The address itself.
Example:
[[email]]
entity = "me"
value = "me@mail.com"
[[password-hash]]
A password hash assignment.
Can also be given as part of a password-hash
property list of an [[entity]]
clause.
Properties:
entity
: The label of the entity that is assigned this password.hash
: The password hash itself.
Example:
[[password-hash]]
entity = "you"
hash = "$argon2id$v=19$m=19456,t=2,p=1$/lj8Yj6ZTJLiqgpYb4Nn0g$z79FFMXstrkY8KmpC0vQWIDcne0lylBbctUAluIVqLk"
[[members]]
A members assignment, giving a (group) entity other entities as members.
In the Authly model, any kind of entity may have members.
Properties:
entity
: The label of the entity that members are assigned to.members
: List of entity labels of the members.
Example:
[[members]]
entity = "us"
members = ["me", "you"]
[[domain]]
A domain declaration.
Properties:
label
: Required. A label for an entity visible in the document namespace.metadata
: Metadata about this domain. The metadata is not used by Authly itself, but can be read and used by services.
Example:
[[domain]]
label = "cms"
metadata = { meta = "meta" }
[[service-domain]]
An association of a service and a domain the service can use.
Properties:
service
: Required. A label identifying the implied service-entity.domain
: Required. A label identifying the domain that will be exposed to the service.
Example:
[[service-domain]]
service = "service"
domain = "cms"
[[entity-property]]
A definition of an entity property.
Properties:
namespace
: Required. The label of the namespace this property is defined inside.label
: Required. The property label.attributes
: The list of attributes of the property.
Example:
[[entity-property]]
namespace = "service"
label = "role"
attributes = ["user", "admin"]
[[entity-attribute-assignment]]
An entity attribute binding, which assigns attributes to entities.
Can also be given as part of a attributes
property list on an [[entity]]
or [[service-entity]]
.
Properties:
entity
: Required. An Entity ID or label identifying the entity to assign to.attributes
: Required. The attributes assigned to the entity.
Example:
[[entity-attribute-assignment]]
entity = "me"
attributes = ["service:role:user"]
[[resource-property]]
A definition of a resource property.
Properties:
namespace
: Required. The label of the namespace this property is defined inside.label
: Required. The property label.attributes
: The list of attributes of the property.
Example:
[[resource-property]]
namespace = "service"
label = "action"
attributes = ["read", "create", "delete"]
[[policy]]
A policy definition.
A policy must contain either an allow
or deny
expression.
Access may be granted if any allow-policy evaluates to true
, unless there are applicable deny-policies.
deny-policies are stronger than allow-policies: Access will be denied if any applicable deny-policy evaluates to true
.
Properties:
service
: Required. A label identifying the implied service-entity.domain
: Required. A label identifying the domain that will be exposed to the service.
Example:
[[policy]]
label = "allow for service"
allow = "Subject.authly:entity == service"
[[policy-binding]]
A policy binding.
A policy binding makes policies applicable in the context of the binding's attribute matcher.
Properties:
attributes
: Required. A set of attribute triples that must be matched for the selected policies to apply.policies
: Required. A set of applied policies, by label.
Example:
[[policy-binding]]
attributes = ["service:action:read"]
policies = ["allow for service"]
Authly environment variables
Configuration values are always read from the environment.
These values are closely tied to the platform Authly runs on, and are not runtime-configurable.
AUTHLY_UID
(required; 32-byte hex string; no default)
A unique identifier for this Authly instance. It should be fairly unique, should never change, and is not particularly secret. Global uniqueness is not required, but a form of local uniqueness is required in closed systems running several authly instances. Can be generated with docker run ghcr.io/protojour/authly generate-authly-uid
.
AUTHLY_HOSTNAME
(string; default authly
)
The hostname against which to generate server certificates.
AUTHLY_SERVER_PORT
(integer; default 443
)
The port on which to run the API/web server.
AUTHLY_DOCUMENT_PATH
(list of path strings; default /etc/authly/documents
)
A list of paths to scan for documents during startup.
AUTHLY_ETC_DIR
(path string; default /etc/authly
)
Configuration directory.
AUTHLY_DATA_DIR
(path string; default /var/lib/authly/data
)
Database directory.
AUTHLY_BAO_URL
(url string; no default)
OpenBao URL for master encryption key storage.
AUTHLY_BAO_TOKEN
(string; no default)
OpenBao token support for legacy setups.
AUTHLY_CLUSTER_NODE_ID
(integer; no default)
AUTHLY_CLUSTER_API_NODES
(ip address string; no default)
AUTHLY_CLUSTER_RAFT_NODES
(ip address string; no default)
AUTHLY_CLUSTER_RAFT_SECRET
(string; no default)
AUTHLY_CLUSTER_API_SECRET
(string; no default)
AUTHLY_K8S
(boolean; default false
)
AUTHLY_K8S_STATEFULSET
(string; default authly
)
AUTHLY_K8S_HEADLESS_SVC
(string; default authly-cluster
)
AUTHLY_K8S_REPLICAS
(integer; default 1
)
AUTHLY_K8S_AUTH_HOSTNAME
(string; no default)
AUTHLY_K8S_AUTH_SERVER_PORT
(integer; no default)
AUTHLY_EXPORT_TLS_TO_ETC
(boolean; default false
)
Whether to export certificates and identities to AUTHLY_ETC_DIR
.