Skip to main content
Version: v1.1

Permission Model

Introduction

The Permission Model in AosEdge is a fixed, manifest-driven access control system managed by the PermHandler module within IAM. It controls which service instances can access which functional servers and what operations they are allowed to perform.

The model is not OEM-configurable at runtime. Permissions are declared statically in the service manifest (item configuration) at build time. When a service instance launches, the Service Manager registers its declared permissions with IAM and receives a secret token. The service instance uses this secret to authenticate with functional servers, which validate access by querying IAM.

Core Concepts

Functional Servers

A functional server is any system component that provides services to running service instances. Examples include vehicle signal interfaces (VIS), system core services, or custom OEM-provided APIs. Each functional server has a unique identifier (the functional server ID) used to scope permission lookups.

Permissions

Permissions are key-value pairs that define what a service instance is allowed to do within a specific functional server. The key identifies the permission scope (e.g., a resource path or operation name) and the value specifies the access level (e.g., "rw" for read-write, "r" for read-only).

Secrets

A secret is a UUID-v4 token generated by IAM when a service instance is registered. The secret acts as a bearer token — any entity presenting a valid secret can query the permissions associated with it. Secrets are unique per service instance and exist only in memory for the lifetime of the instance.

Data Model

The permission model uses a three-level hierarchy:

InstancePermissions
├── InstanceIdent # Identifies the service instance
│ ├── mItemID # Deployable Item ID (service identifier)
│ ├── mSubjectID # Subject (owner/tenant) ID
│ └── mInstance # Instance index (for multi-instance services)
├── mSecret # UUID-v4 bearer token
└── mFuncServicePerms[] # Permissions per functional server
├── mName # Functional server ID (e.g., "vis", "systemCore")
└── mPermissions[] # Key-value permission pairs
├── mFunction # Permission key (resource/operation)
└── mPermissions # Permission value (access level)

Example

A service instance with access to two functional servers:

Functional ServerPermission KeyPermission Value
visVehicle.Speedr
visVehicle.Cabin.Doorrw
systemCoresystem.rebootx

Permission Lifecycle

1. Declaration in Service Manifest

Permissions are declared in the service's item configuration (the OCI image config layer). The mPermissions field in ItemConfig contains an array of FunctionServicePermissions — one entry per functional server the service needs access to.

This declaration is fixed at service build time and cannot be modified at runtime.

2. Registration at Instance Launch

When the Service Manager's Launcher starts a container instance, it checks whether the item configuration declares any permissions. If permissions are present:

  1. The Launcher calls PermHandler::RegisterInstance via the IAM protected gRPC API (IAMPermissionsService.RegisterInstance)
  2. IAM generates a UUID-v4 secret for the instance
  3. IAM stores the instance identity, secret, and permissions in an in-memory cache
  4. The secret is returned to the Launcher
  5. The Launcher injects the secret into the container's environment as AOS_SECRET

If the same instance is registered again (e.g., after a restart without unregistration), IAM returns the existing secret from cache rather than generating a new one.

3. Access Validation by Functional Servers

When a service instance connects to a functional server:

  1. The service instance presents its AOS_SECRET to the functional server
  2. The functional server calls IAMPublicPermissionsService.GetPermissions with the secret and its own functional server ID
  3. IAM looks up the secret in its cache, finds the matching instance
  4. IAM returns the permissions scoped to the requested functional server ID
  5. The functional server enforces access based on the returned permissions

If the secret is not found or the instance has no permissions for the requested functional server, IAM returns a NOT_FOUND error and the functional server denies access.

4. Unregistration at Instance Stop

When a service instance is stopped:

  1. The Launcher calls PermHandler::UnregisterInstance via the IAM protected gRPC API (IAMPermissionsService.UnregisterInstance)
  2. IAM removes the instance's entry from the in-memory cache
  3. The secret becomes invalid — any subsequent GetPermissions call with that secret will fail

gRPC API

The permission model is exposed through two gRPC services on different security levels:

Protected API (mTLS required)

The IAMPermissionsService is accessible only to authenticated components (SM) over mutual TLS:

RPCPurposeCalled By
RegisterInstanceRegister a service instance and its permissions, receive a secretService Manager (Launcher)
UnregisterInstanceRemove a service instance and invalidate its secretService Manager (Launcher)

RegisterInstance request:

message RegisterInstanceRequest {
InstanceIdent instance = 1;
map<string, Permissions> permissions = 2; // key = functional server ID
}

RegisterInstance response:

message RegisterInstanceResponse {
string secret = 1; // UUID-v4 bearer token
}

Public API (no mTLS required)

The IAMPublicPermissionsService is accessible without mutual TLS authentication, allowing functional servers to validate permissions:

RPCPurposeCalled By
GetPermissionsValidate a secret and retrieve permissions for a functional serverFunctional servers

GetPermissions request:

message PermissionsRequest {
string secret = 1; // The AOS_SECRET presented by the service
string functional_server_id = 2; // The querying functional server's ID
}

GetPermissions response:

message PermissionsResponse {
InstanceIdent instance = 1; // Identity of the service instance
Permissions permissions = 2; // Key-value permission pairs for this server
}

Implementation Details

In-Memory Storage

The PermHandler maintains all permission data in a fixed-size in-memory array (StaticArray<InstancePermissions, cMaxNumInstances>). There is no persistent storage — permissions exist only while the instance is running. This design ensures:

  • Fast lookup performance (linear scan over a bounded array)
  • No stale permissions after system restart
  • Automatic cleanup if IAM restarts (instances must re-register)

Thread Safety

All PermHandler operations are protected by a mutex. Concurrent registration, unregistration, and permission queries are serialized to prevent data races.

Secret Generation

Secrets are generated as UUID-v4 strings using the system's UUID provider. If a generated UUID collides with an existing secret (extremely unlikely), the generator retries until a unique value is produced.

Idempotent Registration

Calling RegisterInstance for an already-registered instance returns the existing secret without creating a duplicate entry. This supports scenarios where the Launcher retries registration after a transient failure.

Security Considerations

  • Bearer token model — the secret grants access to whoever possesses it. Services must not expose their AOS_SECRET to untrusted parties.
  • No persistence — secrets are not written to disk, reducing the risk of credential theft from storage.
  • Scope limitation — a secret only grants access to the permissions declared in the service manifest. A compromised secret cannot escalate privileges beyond what was declared.
  • Lifetime bound — secrets are valid only while the instance is registered. Stopping an instance immediately invalidates its secret.
  • Fixed permissions — the permission set cannot be modified at runtime. There is no API to add or remove permissions after registration.