Access control
Luna can be used by more people than root on the controller. Each person gets a Luna user and a
place in one or more usergroups — an organisation, a department, a team — and every object
Luna manages carries its own permissions: who owns it, which usergroups it is listed on, and an
access mode read the way ls -l shows one. A person sees only the objects they may read, and may
change or operate only what the mode and their role allow.
Nothing changes until users are created. Root on the controller uses the account in the controller's configuration file, the controller account, which is checked against nothing: every object, every table, every action, exactly as before. That account stays the break-glass account; day-to-day administrators are users with the admin flag.
Users
# luna user add alice --password
# luna user add bob --source pam
# luna user add dave --password --admin yes
# luna user list
A user authenticates at one of the sources in the authentication chain (see Authentication sources):
local: a Luna password, set with--passwordand asked on the terminal. For people without an operating-system account and for service accounts.pam: the controller's operating system, which on a TrinityX controller includes the cluster directory through sssd.ldap: a directory Luna binds to itself.
A person who logs in through pam or ldap does not have to be added first: the user is created
at their first successful login, and their memberships are taken from their directory groups
through the usergroup map. Add them beforehand only to give them memberships
or flags before they log in.
Three flags are set per user, by the controller account or an admin user only:
| Flag | Effect |
|---|---|
--enabled |
a disabled user cannot log in, and an existing token stops working |
--admin |
a cluster-wide administrator: every object and every table, like the controller account, but under their own name in the audit trail |
--delegate |
a service account that may obtain a token on behalf of another user, for a portal that acts for the person logged in to it; it can do nothing else |
luna user rename and luna user remove do what they say. When a user is removed, their
ownership is handed on: an object whose last owner they were passes to an admin of a usergroup
listed on it, or to the controller account when there is none.
Usergroups and roles
# luna usergroup add physics --comment "physics department"
# luna usergroup addmember physics alice --role admin
# luna usergroup addmember physics bob --role operator
# luna usergroup show physics
+-------------------------------+
| Usergroup => physics |
+----------+--------------------+
| name | physics |
| comment | physics department |
| hardware | True |
| members | alice = admin |
| | bob = operator |
+----------+--------------------+
Every membership carries a role. The role caps what a member may do with the objects the usergroup is listed on, and decides who runs the usergroup itself:
| Role | May do on listed objects | Also |
|---|---|---|
admin |
read, change, operate | manages the usergroup's members and their roles; chmod, chgrp and chown on objects the usergroup is listed on; creates groups, osimages and profiles |
manager |
read, change, operate | creates groups, osimages and profiles |
operator |
read, operate | |
reader |
read |
A role only ever caps; it never grants more than the object's mode gives the usergroup. The object's mode is shared by every usergroup listed on it, so the role is how one team gets "read and power on" and another "read only" on the same node.
The usergroup admin role is not the user admin flag. The flag reaches the whole cluster; the role reaches only what its usergroup is listed on, and sees nothing outside it.
Creating users and usergroups is for the controller account and admin users. Once a usergroup
exists and has an admin, that admin adds and removes members (addmember, removemember) and
sets their roles, without further help from the cluster's administrators.
luna usergroup member <name> lists the members, and luna usergroup access <name> and
luna user access <name> list everything a usergroup or a user holds, with the access they have
on each object:
# luna user access alice
+-----------------------------------------------------------+
| What alice holds |
+----------+------------------+-----------------------------+
| kind | object | access |
+----------+------------------+-----------------------------+
| bmcsetup | default-bmcsetup | rwx (read, change, operate) |
| cluster | mycluster | r-- (read) |
| group | compute | rwx (read, change, operate) |
| network | cluster | r-- (read) |
| node | node001 | rwx (read, change, operate) |
| node | node003 | r-- (read) |
| osimage | compute-image | r-x (read, operate) |
+----------+------------------+-----------------------------+
Logging in
A user logs in once from their own account on the controller:
$ luna access login alice
Password:
Logged in as alice; credentials in /home/alice/.luna/luna.ini, token in /home/alice/.luna/token.
$ luna access whoami
+--------------------------------------------------+
| whoami |
+------------+-------------------------------------+
| field | value |
+------------+-------------------------------------+
| user | alice |
| id | 3 |
| source | local |
| admin | no |
| usergroups | chemistry (reader), physics (admin) |
| hardware | physics |
+------------+-------------------------------------+
The login writes the user's own ~/.luna/luna.ini, readable by that user only, and keeps their
token beside it; every later luna command runs as them and fetches a new token when the old one
expires. luna access logout removes both files.
Root does not need to log in: without a username, luna access login refuses for root, because
root already holds the controller account. Root can log in as somebody else by naming them.
Permissions on an object
Every node, group, osimage, profile, BMC setup, Redfish setup, BIOS configuration, firmware
catalogue, network, route, switch, rack, other device, cloud and the cluster itself carries three
fields, shown at the end of every show and as columns in every list:
# luna group show compute
...
| owners | alice |
| usergroups | physics,storage |
| access | rwxr-x--- (owner: read, change, operate · team: read, operate · others: nothing) |
- owners: the users who own the object. Empty means the controller account owns it.
- usergroups: the usergroups the object is listed on.
- access: the mode, owner, team and others, as
ls -lshows it.
The bits mean:
| Bit | On a node or a group | On other objects |
|---|---|---|
r |
show, list, status, inventory, secrets | show it, and refer to it: put a node in a group, give a group an osimage or a BMC setup |
w |
change it, and add or change its secrets | change it, pack or tag an osimage |
x |
power, console, identify, reprovision; push a BIOS configuration or firmware to it | not used |
A person is checked as the object's owner if they are one; otherwise as a member of a usergroup listed on it, capped by their role (the highest of their roles, if more than one of their usergroups is listed); otherwise as others. Admin users and the controller account are not checked.
Referring to an object needs r on it and w on the object that refers: moving a node into a
group needs w on the node and r on the group. Pushing a BIOS configuration or firmware
needs x on the node and w on the configuration or the catalogue.
What a person may not read does not exist for them. It is left out of every list, and asking for it by name answers that it is not available, the same as for an object that does not exist, so one department cannot find out what another has. Where the object can be read but the action is not allowed, the answer says why:
$ luna node show node002
node node002 is not available.
$ luna node change node001 -qc "rack 2"
changing node node001 is not permitted: you may read and operate it (operator role).
An action on a list of nodes or on a whole group, such as a power command, is refused as a whole when any node in it is refused, and the answer names those nodes.
New objects
An object created by a member of a usergroup is owned by its creator and listed on every usergroup in which the creator is an admin or a manager. A node takes the owners, usergroups and access of the group it is created in. After that the object's permissions are its own: moving a node to another group does not change them.
Objects that nobody has given permissions carry the default for their kind:
| Objects | Default access |
|---|---|
| node, group, BMC setup, Redfish setup, profile | rwxrwx--- |
| osimage, BIOS configuration, firmware catalogue | rwxrwxr-- |
| cluster, network, route, cloud, switch, rack, other device | rw-r--r-- |
On an existing cluster all objects are owned by the controller account. To hand part of it to a department, list their usergroup on the objects:
# luna access chgrp group compute physics
# luna access chgrp node node001 physics
The command acts on one object at a time.
Changing owners, usergroups and access
$ luna access chmod node node001 rwxr-x---
node node001: access set to rwxr-x---.
$ luna access addusergroup node node001 chemistry
node node001: usergroup chemistry added to its usergroups.
$ luna access removeusergroup node node001 chemistry
node node001: usergroup chemistry removed from its usergroups.
$ luna access addowner group compute carol
$ luna access removeowner group compute carol
$ luna access chown group compute alice,carol
The mode may be given as ls shows it or in octal (750). chgrp and chown replace the whole
list; addusergroup, removeusergroup, addowner and removeowner add or take away one name
and leave the rest. They follow the same rules as chgrp and chown. The object kind comes
first: node, group, osimage, profile,
bmcsetup, redfishsetup, biosconfig, firmwarecatalog, network, route, switch,
rack, otherdev, cloud, or cluster with the cluster's name.
| Command | Who may use it |
|---|---|
chmod |
the object's owners; admins of a usergroup listed on it |
chgrp, addusergroup, removeusergroup |
the object's owners and admins of a usergroup listed on it; the usergroup added must be one of their own |
chown, addowner, removeowner |
admins of a usergroup listed on the object, to members of that usergroup only |
The controller account and admin users may use all of them on anything. Owners, usergroups and
access cannot be set in an add or change; only these commands change them.
What a department may create and change
Admins and managers of a usergroup create groups, osimages and profiles of their own. Nodes, BMC setups, Redfish setups, BIOS configurations, firmware catalogues and the cluster's infrastructure (networks, routes, switches, racks, other devices, clouds) are created by the controller account and admin users.
On a node or group a department may change, the settings that describe the hardware and the network stay with the cluster's administrators: interfaces and their addresses, MAC addresses, the switch and switch port, the BMC, Redfish and BIOS setup, the provisioning interface, TPM details, the name. Everything else, such as osimage, kernel options, scripts, profiles, disk layout, mounts and install mode, is the department's. A department may clone a group it may read; the clone keeps the original's hardware settings as they were.
A usergroup with the hardware flag (luna usergroup change physics --hardware yes, set by the
controller account or an admin user) has full autonomy over its own part of the cluster. Its
admins and managers may also change those hardware settings on nodes and groups the usergroup
is listed on with w. They may create and remove nodes in those groups, using addresses on
networks they can read, and create their own BMC setups, Redfish setups, BIOS configurations and
firmware catalogues. The flag unlocks nothing owned by the controller account.
To keep a department's nodes off a network, give it no r on that network.
Removing follows creating: removing a node, an interface or one of the hardware setups needs the
same right as creating it; a group, osimage or profile goes with w; infrastructure is removed
by the controller account and admin users only.
Directory groups
People who log in through pam or ldap get their memberships from their directory groups. The
usergroup map says which directory group gives which usergroup, and with which role:
# luna usergroup addmap physics --source pam --external_group physics-staff --role operator
# luna usergroup addmap chemistry --source ldap --external_group cn=chem,ou=Group,dc=cluster --role manager
# luna usergroup map
+---------------------------------------------------------------+
| Usergroup map |
+--------+-------------------------------+-----------+----------+
| source | external group | usergroup | role |
+--------+-------------------------------+-----------+----------+
| pam | physics-staff | physics | operator |
| ldap | cn=chem,ou=Group,dc=cluster | chemistry | manager |
+--------+-------------------------------+-----------+----------+
For pam the external group is the operating-system group name; for ldap it is the group as the
directory's group attribute gives it, usually its DN. Several directory groups may lead to one
usergroup, with different roles. A directory group without a map entry gives nothing.
Memberships from the map are refreshed at every login, so a change in the directory takes effect
the next time the person logs in. luna usergroup removemap removes an entry.
Authentication sources
The sources are asked in the order of CHAIN in the [AUTH] section of the daemon's
configuration file, /trinity/local/luna/daemon/config/luna.ini:
[AUTH]
CHAIN = local, pam
For each login the chain is walked in order, and the first source that knows the name decides. A wrong password at that source is refused; the next source is not asked. A source that cannot be reached is logged and skipped, so local users still get in when the directory is down. The controller account is checked before the chain and is never part of it.
local, pam is the default and reaches everyone who can log in to the controller, including the
cluster directory. ldap binds to a directory directly, for a directory the controller is not
joined to, or to take groups from a directory attribute:
[AUTH]
CHAIN = local, ldap, pam
[AUTH_LDAP]
URI = ldaps://ldap.example.com
BASE = ou=people,dc=example,dc=com
BIND_DN =
BIND_PASSWORD =
USER_ATTRIBUTE = uid
ID_ATTRIBUTE = entryUUID
GROUP_ATTRIBUTE = memberOf
Put ldap before pam when the same directory also reaches the controller through sssd:
otherwise pam knows the directory's users first and applies the controller's own login policy
to them. ID_ATTRIBUTE is the directory's stable identifier for a person; Luna keys users on it,
never on the name, so a name reused for somebody else does not inherit the old account. Use
objectGUID for Active Directory. [AUTH_PAM] SERVICE names the PAM service used, login by
default.
Set the same [AUTH] sections on both controllers of an HA pair, and restart luna2-daemon
after a change. Users, usergroups, memberships and the map are replicated between controllers;
the configuration file is not.
Audit trail
Every call that changes something, every refusal and every login is written to
/var/log/luna/luna2-audit.log, one line each, beside the daemon's own log and rotated with it:
2026-09-24 16:47:34,297 AUDIT user=alice id=3 source=local action="GET /config/node/node002" object="node node002" outcome=refused code=404 controller=controller1 detail="node node002 is not available"
2026-09-24 16:47:36,596 AUDIT user=alice id=3 source=local action="POST /config/node/node001/_chmod" object="node node001" outcome=allowed code=204 controller=controller1
2026-09-24 16:48:22,229 AUDIT user=bob id=4 source=- action="POST /token" object=- outcome=login code=201 controller=controller1
2026-09-24 16:48:22,468 AUDIT user=bob id=4 source=local action="POST /config/node/node001" object="node node001" outcome=refused code=403 controller=controller1 detail="changing node node001 is not permitted: you may read and operate it (operator role)"
Each line names the user, what was asked, the object, and what came of it. Request contents are
never written, so no password or secret ends up in the trail. Every field is key=value, so one
grep finds a person (user=alice), an object (object="node node001") or every refusal
(outcome=refused). The controller account appears under its own name with id=0. To write
the trail elsewhere, set LOGFILE in the [AUDIT] section of the daemon's configuration file.