XCP-ng
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    ACL V2, we need your feedbacks!

    Scheduled Pinned Locked Moved Xen Orchestra
    2 Posts 2 Posters 818 Views 4 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • MathieuRAM Offline
      MathieuRA Vates 🪐 XO Team
      last edited by MathieuRA

      ACL v2: Fine-grained access control in Xen Orchestra

      With the v2 of the ACL system, Xen Orchestra takes a new step forward in permission management. Where v1 offered basic per-object access control, v2 introduces a full RBAC (Role-Based Access Control) model, with effects, selectors, and an action hierarchy.

      What changes

      The old approach allowed granting access to an object (a VM, an SR…). Simple, but limited: there was no way to say "this user can shutdown only VM with tag: foo".

      Another major limitation of v1: it only covered XAPI objects — VMs, hosts, SRs, networks, so user, groups, backups, schedules, jobs,... was out of scope.

      ACL v2: REST API exclusive

      ACL v2 is available through the REST API only. The JSON-RPC API (used by XO5) stays on ACL v1, and conversely: ACL v1 is not available on the REST API.

      Key concepts

      Roles and privileges

      A role is a named set of privileges. Each privilege defines:

      • a resource type (vm, sr, network, backup-job…)
      • an action (read, start, shutdown:clean, delete…)
      • an effect: allow or deny
      • an optional selector to target specific objects (complex-matcher format)

      Actions are hierarchical. Granting shutdown covers both shutdown:clean and shutdown:hard. But granting shutdown:clean does not cover shutdown as a whole. deny always takes precedence over allow.

      Built-in roles

      Actually, 4 template roles are provided out of the box:

      • Read only — full read-only access to the infrastructure
      • VMs read only — read-only access to VMs only
      • VMs power state manager — manage VM power state (start, stop, reboot, pause…)
      • VMs creator — create VMs from templates

      These roles are immutable and automatically updated on startup — they cannot be assigned directly. To use them, copy the template into a new role and assign that copy to your users or groups. This ensures the built-in templates always stay up to date without affecting your custom configurations.

      Selectors: object-level precision

      A selector restricts a privilege to objects matching certain properties. For example:tags:qa
      This allows add a privilege only on VMs tagged qa.

      What makes this mechanism powerful is its dynamic nature. Selectors are evaluated in real time.
      In case the users subscribed to VMs changes, if the qa tag is added to an existing VM, and the user have a read privilege, he will see that VM appear as a new object — the user will receive an add event, not an update. Conversely, if the tag is removed, he will receive a remove event: the VM disappears from his scope.

      Events are always from the user's perspective, not XOA's. For XOA, it is a simple tag update. For the ACL user, it is an object entering or leaving their scope.

      This enables very practical use cases: a single tag is enough to grant or revoke access to a resource, without touching roles or privileges at all.

      Assigning Roles to Users and Groups

      A role can be attached to a user or a group. A user's effective roles are the union of their direct roles and those of their groups.

      REST API integration

      All endpoints are exposed through the REST API:

      • GET/POST /acl-roles — list and create roles
      • PUT/DELETE /acl-roles/{id}/users/{userId} — attach/detach a role to a user
      • PUT/DELETE /acl-roles/{id}/groups/{groupId} — attach/detach a role to a group
      • GET/POST /acl-privileges — list and create role's privileges
      • POST /acl-roles/{id}/actions/copy — copy a role

      Each REST API endpoint declares the required privileges to access it via the swagger UI. If an endpoint declares none, it is admin-only.

      A concrete example

      Alice is a member of the QA team. She needs to be able to start and stop VMs in her test environment, but must not touch anything in production.

      With ACL v2:

      1. Create a QA Operator role with following privileges:
        • {resource: 'vm', action: 'read', effect: 'allow', selector: 'tags:qa'}
        • {resource: 'vm', action: 'start', effect: 'allow', selector: 'tags:qa'}
        • {resource: 'vm', action: 'stop', effect: 'allow', selector: 'tags:qa'}
      2. Attach this role to Alice (or her group)

      That's it. Alice cannot touch production VMs, and any attempt is blocked with an explicit error.

      Another concrete example

      Bob is allowed to rename VMs, but only while they are running — to prevent renaming VMs that are off and might be part of an automated process.

      With ACL v2:

      1. Create a Running VM Renamer role with following privilege:
        • {resource: 'vm', action: 'read', effect: 'allow', selector: 'power_state:Running'
        • {resource: 'vm', action: 'update:name_label', effect: 'allow', selector: 'power_state:Running'
      2. Attach this role to Bob

      Bob can rename and see any running VM.

      One last example

      Carol can see all VMs in the infrastructure, except those tagged prod.

      With ACL v2:

      1. Create a Non-Prod VM Reader role with two privileges:
        • {resource: 'vm', action: 'read', effect: 'allow'} no selector, grants read access to all VMs
        • {resource: 'vm', action: 'read', effect: 'deny', selector: 'tags:prod' explicitly denies access to production VMs
      2. Attach this role to Carol

      Since deny always takes precedence over allow, Carol can browse the full VM list — except production VMs, which are completely invisible to her.

      List of possible actions (by resource)

      {
        update: {
          name_label:true,
          name_description:true,
          ...
        }
      } 
      

      is translated into -> update:name_label, update:name_description, ...

      Please note that ACL v2 is currently only accessible via the REST API. Support in the XO6 user interface will be available later.

      poddingueP 1 Reply Last reply Reply Quote 4
      • MathieuRAM MathieuRA referenced this topic on
      • MathieuRAM MathieuRA referenced this topic on
      • MathieuRAM MathieuRA referenced this topic on
      • poddingueP Online
        poddingue Vates 🪐 @MathieuRA
        last edited by poddingue

        I'm late to this, but I've been building lately a JetBrains plugin against the REST API and ACL v2 turned out to decide its whole design, so here's some feedback.
        Everything below is just one appliance, one pool (my small homelab), on a plan 4 trial, with an admin control call taken in the same breath as every scoped one. 🤷

        What I experienced is that selectors narrow reads (tags: and id: both, 1 VM against an admin control of 11), they're re-evaluated per request rather than fixed when the privilege is created, deny composes the way your Carol example says (allow-all plus deny on a tag gave 9, which is 11 minus the 2 tagged), and they scope the power verbs too, not just reads.

        The event stream is, to me, scoped as well, which was the thing I most wanted to check, because a scoped read next to an unscoped feed would have been a nasty trap. It's not. 🎉
        With two streams open at once, a change to an out-of-scope VM produced an update on the admin stream and nothing at all on the scoped one, so it's genuinely filtered rather than merely quiet.
        And the bit I'd underline: the same single change is update to the admin and add or remove to the scoped user. The verb is computed per subscriber, not per object, and an admin never sees add or remove at all because nothing ever enters or leaves an admin's scope. 👍
        "From the user's perspective, not XOA's" turns out to be literal rather than a figure of speech, and that's a nicer piece of design than the sentence let me imagine.

        Now the three things that I didn't see in the post above.

        vm-snapshot is a separate privilege resource and vm doesn't imply it. 🤷 Maybe that's obvious, but it was not obvious to me.
        With all six VM privileges granted, GET /vm-snapshots came back empty while admin saw 6, including a snapshot the scoped user had just taken. Anything with a restore or revert screen gets an empty list and no error.

        Inherited snapshot tags look like a snapshot-time copy rather than a link. Tag a VM and it's in scope immediately, but its existing snapshots keep tags: [] and stay invisible. So someone onboarded into a tag scope after their snapshots exist sees the VM and not its history.

        Subscribing delivers no initial dump. Both streams sat on init and keepalives until something changed, so it's a delta feed and a client has to fetch the collection over REST and maintain it from events. Worth a line, since the natural assumption (at least to me, don't make that the rule for everyone) is the other one.

        While I'm here: two smaller ones. Privilege action names aren't REST action names: shutdown:clean grants clean_shutdown, revert-snapshot grants revert_snapshot, and a mistyped action quietly gives you a privilege that grants nothing. 🤔
        And the event: init frame's field is id, not connectionId (why did I think it was connectionId, no idea, I thought it was "natural"), which cost me a while of thinking the stream was dead when I was posting to /events//subscriptions. 🤦
        Yes, I know, I should have read the documentation instead of experimenting in the dark, sending made-up field names in the wild. 🤷

        One last thing: selector is optional, so a privilege created without one reads back as {id, resource, action, effect, roleId} with no hint the field exists.
        I granted allow read on vm, saw all 11 VMs, read the object back, and (falsely) concluded the REST API had no object dimension at all.
        It's all in the previous post and it's in the swagger, and of course, in the official documentation. Once again, I'm an innocent victim because I didn't RTFM. 😊
        I just never saw a privilege that had one. If a privilege echoed selector: null, or if the first example anyone met were a scoped one, I don't think I would have spent much time on that. Once again, my bad, didn't RTFM.

        This was a small, targeted test, at best. I didn't have the intent to test what was brought up in this very thread, I just happened to tinkle with the REST API and ACL V2 for my PoC, so lots of things got untested.
        Please, don't take it from me as settled: only tags: and id: selector forms, nothing on a second pool or a real multi-user deployment, and I have not checked what happens to a live subscription when the privilege itself changes rather than the VM's tags.

        If you read me until there, you're brave, or have too much time on your hands. 😉

        1 Reply Last reply Reply Quote 0

        Hello! It looks like you're interested in this conversation, but you don't have an account yet.

        Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

        With your input, this post could be even better 💗

        Register Login
        • First post
          Last post