<html>
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <p>After some more thorough analysis of Glance, I think we finally
      got this figured out.</p>
    <p>The access control to Glance seems to be a two-staged process.</p>
    <p>The first stage is the policy check. This stage determines, which
      API requests are accepted. Nothing special here, the rules defined
      in the "policy.json" are applied.</p>
    <p>The second stage is a hardcoded process that can get confusing.
      At this stage, the requesting user is checked whether they are the
      "glance admin" or not. This is true if either or both of the
      following conditions are met:</p>
    <ul>
      <li>the user matches the rule defined as "context_is_admin" in the
        "policy.json"</li>
      <li>the user has a role that matches the "admin_role" entry in the
        "glance-api.conf"</li>
    </ul>
    As soon as the user is identified to be a "glance admin", they are
    allowed to access and manipulate images at will, not being bound to
    the project context at all. In any other case the project context
    seems to be automatically enforced in the second stage, the check
    for "project_id" or "owner" respectively is unnecessary in the
    policies.<br>
    <br>
    <br>
    This leads to the solution to our problem:<br>
    <br>
    We simply created a new role called "project-admin" and defined
    corresponding rules and restrictions within the "policy.json":<br>
    <blockquote>
      <pre>...
"context_is_admin":  "role:admin",
"admin_restriction": "role:admin or role:project-admin",
...
"delete_image": "rule:admin_restriction",</pre>
    </blockquote>
    It is important to note, that the "role:admin" within
    "admin_restriction" is still absolutely necessary for the global
    admin to pass the 1st stage, even if "context_is_admin" is
    approprietly defined. The "context_is_admin" on the other hand,
    allows the global admin to pass the 2nd stage as the "glance admin"
    but is out of scope for the 1st stage.<br>
    <br>
    The "project-admin" role now allows for project-scoped admins being
    able to delete images, while normal project members can't but still
    retaining the global (and hence glance-) admin's omnipotence.<br>
    <br>
    <br>
    The thing to be learned from this is that the statement "You can use
    the policies to control what users can make the call, but you can't
    use the policies to determine what will be included in the response"
    has one exception. That is the "context_is_admin", since it has
    influence on the second stage that follows the actual policy check
    (together with the "admin_role" config entry).<br>
    <br>
    Mind you, none of this may apply to other OpenStack components at
    all, since this analysis was Glance-specific. There is currently no
    consistent way of identifying the "global admin" across the
    components, so each one of them might implement their unique way of
    handling this. However, it seems there is some movement going on -
    see the following (quite recent) blog post for more details:
    <a class="moz-txt-link-freetext" href="http://adam.younglogic.com/2017/05/fixing-bug-96869/#bug-968696">http://adam.younglogic.com/2017/05/fixing-bug-96869/#bug-968696</a><br>
    <br>
    <br>
    Kind regards,<br>
    <br>
    Markus Hentsch<br>
    Cloud&Heat Technologies<br>
    <div class="moz-cite-prefix"><br>
    </div>
    <blockquote
      cite="mid:dc366959-0ceb-5d5a-7757-0f6f0aa87a56@cloudandheat.com"
      type="cite">
      <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
      <p>Dear Brian,</p>
      <p>thanks for your answer to my previous question!</p>
      <p>I've tried changing the definition of "admin_role" in the
        configuration of the Glance API component but it did not change
        the behavior in the way I was expecting it.<br>
        <br>
        This role is evaluated and sets the "self.is_admin" property,
        see:</p>
      <blockquote><a moz-do-not-send="true"
          class="moz-txt-link-freetext"
href="https://github.com/openstack/glance/blob/0a2074ecef67beb7a3b3531534c9d97b1c3ea828/glance/api/middleware/context.py#L195">https://github.com/openstack/glance/blob/0a2074ecef67beb7a3b3531534c9d97b1c3ea828/glance/api/middleware/context.py#L195</a><br>
      </blockquote>
      <p>However before that check, the policy check possibly alters the
        same "self.is_admin" property in the RequestContext constructor,
        see:</p>
      <blockquote>
        <p><a moz-do-not-send="true" class="moz-txt-link-freetext"
href="https://github.com/openstack/glance/blob/0a2074ecef67beb7a3b3531534c9d97b1c3ea828/glance/context.py#L36">https://github.com/openstack/glance/blob/0a2074ecef67beb7a3b3531534c9d97b1c3ea828/glance/context.py#L36</a></p>
      </blockquote>
      <p>The "check_is_admin()" in turn evaluates the policy rule for
        "context_is_admin".<br>
        <br>
        If I interpret this correctly, the value of the "self.is_admin"
        property determines who is what you describe as the "glance
        admin". However, due to the code cited above, the assignment of
        who is the "glance admin" seems to be influenced by <i>both</i>
        the "admin_role" config entry <i>as well as</i> the
        "context_is_admin" policy rule.<br>
        <br>
        This leads to the problem that is affecting my setup. Every
        project member that has the "admin" role within their project,
        seems to be automatically treated as a glance admin
        (self.is_admin). This explains why admins from specific projects
        can still see _private_ images of other projects they are not
        even a member of, just because their role happens to be called
        "admin". In my case this is a serious security issue and I'd
        like to prevent this.<br>
        I want to prevent users from seeing or accessing private images
        of projects they are not assigned to!<br>
        <br>
        Normally, I would solve this by changing<br>
      </p>
      <blockquote>"context_is_admin":  "role:admin"</blockquote>
      <p>to<br>
      </p>
      <blockquote>"context_is_admin":  "is_admin:True"</blockquote>
      <p><br>
        which actually does restrict the set of images that is returned
        from the "image list" call for every user to the desired project
        context. However the "is_admin:True" bit which is intended to
        identify the global admin only, does not work. Even the global
        (project-independent) admin is not able to see all images
        anymore. The "is_admin:True" usually did the trick in other
        components though.</p>
      <p>Is there currently no way in Glance to make private images
        actually private (i.e. only members of the same project can
        see/access them) while retaining access to them for the global
        admin?<br>
      </p>
      <p><br>
      </p>
      <br>
      <div class="moz-cite-prefix"> Kind regards,<br>
        <br>
        Markus Hentsch<br>
        Cloud&Heat Technologies<br>
        <br>
      </div>
      <blockquote
cite="mid:CAFnESPq2MO1hAE3mEuLZRgkzJKJw0=1Z1kDOH+PZ9BMTZ8+Npw@mail.gmail.com"
        type="cite">
        <pre wrap="">Hi Markus,

Quick comment before some details: this is a complicated issue, and
we're working on some Glance docs that will give a coherent statement.
The working draft is here if you want to keep an eye on it:
<a moz-do-not-send="true" class="moz-txt-link-freetext" href="https://etherpad.openstack.org/p/glance-authN-authZ-model">https://etherpad.openstack.org/p/glance-authN-authZ-model</a>

On Tue, May 30, 2017 at 9:44 AM, Markus Hentsch
<a moz-do-not-send="true" class="moz-txt-link-rfc2396E" href="mailto:markus.hentsch@cloudandheat.com"><markus.hentsch@cloudandheat.com></a> wrote:
</pre>
        <blockquote type="cite">
          <pre wrap="">Hello,

I've run into an issue regarding policies with Glance on the Ocata release.
Essentially, I'd like to restrict the actions of listing images and
viewing/editing their details to either members of the same project or the
global admin.
</pre>
        </blockquote>
        <pre wrap="">What images are included in the glance image-list call, and what
images a user can do an image-show on, is determined in code.  Roughly
(because this is affected by the owner_is_tenant setting, which is
True by default), each user can "see":
- all public images
- all community images
- all images owned by the user's tenant (project)
- all images the user is a member of
(whether these images appear in a user's image-list response depend on
some other factors that are outlined in the glance api-ref)

A glance admin can "see" all images.

As far as setting policies, the "get_images" and "get_image" policies,
for example, apply to the API call itself.  You can use the policies
to control what users can make the call, but you can't use the
policies to determine what will be included in the response to someone
who's allowed to make the call.

So if I understand what you what to accomplish correctly, it's already
done by the default policy settings, there is no need to change them
for your use case.

As far as the other weirdnesses you noticed ... we'll try to come up
with a coherent explanation to publish with the glance documentation.

</pre>
        <blockquote type="cite">
          <pre wrap="">Since all policies are empty strings by default, I tried the
following edit:

{
    "context_is_admin":  "role:admin",
    "default": "role:admin",
    "admin_or_owner":  "is_admin:True or project_id:%(project_id)s",

    "add_image": "",
    "delete_image": "",
    "get_image": "admin_or_owner",
    "get_images": "admin_or_owner",
    "modify_image": "admin_or_owner",
    "publicize_image": "role:admin",
    "communitize_image": "",
    ...

The result was that nobody (including the global admin!) could list images
anymore. An "openstack image list" command would always result in a "403
Forbidden" error.
Also, retrieving a single image's info via a user from the same project was
also impossible. An "openstack image show <image_id>" would simply output
"403 Forbidden You are not authorized to compelte get_images action".

>From the policies as quoted above I would have expected the global admin as
well as any project member being able to list and show images without
problems.

Editing 2 lines in above policy definition:

    ...
    "get_images": "",
    "modify_image": "",
    ...

resulted in another weird behavior. With those adjustments, an "openstack
image list" or "openstack image show <image_id>" on the command line
executed as the global admin succeeded. On the dashboard (Horizon) on the
other hand, only listing them was possible. Trying to display their details
resulted in an error.



Digging through the logs and code, I stumbled on an image target object that
is inspected for the policy enforcement, see here:
<a moz-do-not-send="true" class="moz-txt-link-freetext" href="https://github.com/openstack/glance/blob/57c4d7d78f37e840660719b944ebabe91cbf231b/glance/api/policy.py#L109">https://github.com/openstack/glance/blob/57c4d7d78f37e840660719b944ebabe91cbf231b/glance/api/policy.py#L109</a>

Hacking the code to put some more debugging output into the logs, I peeked
into this "ImageTarget(image)" object, which also contains a
".target.context" attribute wrapped into it. Although this "context"
attribute does contain seemingly relevant user data, its contents do
actually differ depending on the logged in user.

My interpretation was that the context of the image target should be static
(representing the owner/project it actually belongs to) and that this is in
turn matched against the dynamic "self.context" dict (representing currently
logged in user) according to the policies defined, something along the lines
of:

self.context (e.g. project_id)   ---[policy check against]--->
ImageTarget(image) (e.g. project_id)

However "ImageTarget(image)" seems to contain context that is not actually
related to the image but differs per logged in user.


Did I misinterpret the policy definitions and/or the code related to it?
How are policies like these actually supposed to be defined in Glance?



Kind regards,

Markus Hentsch
Cloud&Heat Technologies

_______________________________________________
Mailing list: <a moz-do-not-send="true" class="moz-txt-link-freetext" href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a>
Post to     : <a moz-do-not-send="true" class="moz-txt-link-abbreviated" href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a>
Unsubscribe : <a moz-do-not-send="true" class="moz-txt-link-freetext" href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a>


</pre>
        </blockquote>
      </blockquote>
      <br>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <br>
      <pre wrap="">_______________________________________________
Mailing list: <a class="moz-txt-link-freetext" href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a>
Post to     : <a class="moz-txt-link-abbreviated" href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a>
Unsubscribe : <a class="moz-txt-link-freetext" href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a>
</pre>
    </blockquote>
    <br>
    <div class="moz-signature">-- <br>
      <div
style="font-family:tahoma,Arial,sans-serif;font-size:10pt;color:#474543;"><b>Dipl.-Inf.
          Markus Hentsch</b><br>
        <div
style="font-family:tahoma,Arial,sans-serif;font-size:10pt;color:#474543;">DevOps
          Engineer
          <br>
          <br>
          <span
style="color:#231f20;font-family:verdana,tahoma,helvetica;font-size:22pt">CLOUD</span><span
            style="color:#99C221;font-family:times,times new
            roman;font-size:24pt">&</span><span
            style="color:#231f20;font-family:times,times new
            roman;font-size:24pt">HEAT</span><br>
          <br>
          <div
            style="font-family:sans-serif,tahoma,Arial;font-size:9pt;color:#99C221;">
            Treffen Sie uns:<br>
            25.09. - 29.09.2017 | INFORMATIK 2017 | Chemnitz <br>
            28.11. - 29.11.2017 | Data Centre World | Frankfurt <br>
            <br>
          </div>
          <b>CLOUD & HEAT Technologies GmbH</b><br>
          Königsbrücker Str. 96 (Halle 15) | 01099 Dresden<br>
          Tel: +49 351 479 3670 - 100<br>
          Fax: +49 351 479 3670 - 110<br>
          E-Mail: <a href="mailto:markus.hentsch@cloudandheat.com">markus.hentsch@cloudandheat.com</a><br>
          Web: <a href="https://www.cloudandheat.com">https://www.cloudandheat.com</a><br>
          <p>Handelsregister: Amtsgericht Dresden<br>
            Registernummer: HRB 30549<br>
            USt.-Ident.-Nr.: DE281093504<br>
            Geschäftsführer: Nicolas Röhrs</p>
          <p style="font-weight:bold;">
          </p>
        </div>
      </div>
    </div>
  </body>
</html>