mesos-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m.@apache.org
Subject [1/2] mesos git commit: Added support for authorization of Hierachical roles.
Date Thu, 27 Apr 2017 08:17:44 GMT
Repository: mesos
Updated Branches:
  refs/heads/master 2cde52884 -> d55148afb


Added support for authorization of Hierachical roles.

Adds mechanisms to support authorization of hierarchical roles,
that is, it allows operators to write ACLs of the form role/%
which will enforce the rule for any nested role, e.g. role/a,
role/b and such.

Review: https://reviews.apache.org/r/57473/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d46d3307
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d46d3307
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d46d3307

Branch: refs/heads/master
Commit: d46d33079805379515de0c300d7e6f4286adeb77
Parents: 2cde528
Author: Alexander Rojas <alexander@mesosphere.io>
Authored: Thu Apr 27 00:02:06 2017 -0700
Committer: Adam B <adam@mesosphere.io>
Committed: Thu Apr 27 00:03:26 2017 -0700

----------------------------------------------------------------------
 src/authorizer/local/authorizer.cpp | 537 ++++++++++++++++++++++---------
 1 file changed, 384 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d46d3307/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp b/src/authorizer/local/authorizer.cpp
index 3eb59d3..9082fb1 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -199,8 +199,6 @@ public:
       aclObject.set_type(mesos::ACL::Entity::ANY);
     } else {
       switch (action_) {
-        // All actions using `object.value` for authorization.
-        case authorization::VIEW_ROLE:
         case authorization::GET_ENDPOINT_WITH_PATH:
           // Check object has the required types set.
           CHECK_NOTNULL(object->value);
@@ -209,25 +207,6 @@ public:
           aclObject.set_type(mesos::ACL::Entity::SOME);
 
           break;
-        case authorization::REGISTER_FRAMEWORK:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->framework_info) {
-            foreach (
-                const string& role,
-                protobuf::framework::getRoles(*object->framework_info)) {
-              aclObject.add_values(role);
-            }
-          } else if (object->value) {
-            // We also update the deprecated `value` field to support custom
-            // authorizers not yet modified to examine `framework_info`.
-            //
-            // TODO(bbannier): Clean up use of `value` here, see MESOS-7091.
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::TEARDOWN_FRAMEWORK:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->framework_info) {
@@ -239,18 +218,6 @@ public:
           }
 
           break;
-        case authorization::CREATE_VOLUME:
-        case authorization::RESERVE_RESOURCES:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->resource) {
-            aclObject.add_values(object->resource->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::DESTROY_VOLUME:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->resource) {
@@ -274,28 +241,6 @@ public:
           }
 
           break;
-        case authorization::GET_QUOTA:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->quota_info) {
-            aclObject.add_values(object->quota_info->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
-        case authorization::UPDATE_WEIGHT:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->weight_info) {
-            aclObject.add_values(object->weight_info->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::RUN_TASK:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->task_info && object->task_info->has_command() &&
@@ -354,14 +299,6 @@ public:
           }
 
           break;
-        case authorization::UPDATE_QUOTA:
-          // Check object has the required types set.
-          CHECK_NOTNULL(object->quota_info);
-
-          aclObject.add_values(object->quota_info->role());
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-
-          break;
         case authorization::VIEW_FRAMEWORK:
           // Check object has the required types set.
           CHECK_NOTNULL(object->framework_info);
@@ -455,11 +392,19 @@ public:
           aclObject.set_type(mesos::ACL::Entity::ANY);
 
           break;
+        case authorization::CREATE_VOLUME:
+        case authorization::GET_QUOTA:
+        case authorization::RESERVE_RESOURCES:
+        case authorization::UPDATE_QUOTA:
+        case authorization::UPDATE_WEIGHT:
+        case authorization::VIEW_ROLE:
+        case authorization::REGISTER_FRAMEWORK:
+          return Error("Authorization for action " + stringify(action_) +
+                       " requires a specialized approver object.");
         case authorization::UNKNOWN:
           LOG(WARNING) << "Authorization for action '" << action_
                        << "' is not defined and therefore not authorized";
           return false;
-          break;
       }
     }
 
@@ -575,6 +520,207 @@ public:
 };
 
 
+class LocalHierarchicalRoleApprover : public ObjectApprover
+{
+public:
+  LocalHierarchicalRoleApprover(
+      const vector<GenericACL>& acls,
+      const Option<authorization::Subject>& subject,
+      const authorization::Action& action,
+      bool permissive)
+    : acls_(acls), subject_(subject), action_(action), permissive_(permissive)
+  {
+    if (subject_.isSome()) {
+      entitySubject_.set_type(ACL::Entity::SOME);
+      entitySubject_.add_values(subject_->value());
+    } else {
+      entitySubject_.set_type(ACL::Entity::ANY);
+    }
+  }
+
+  virtual Try<bool> approved(const Option<ObjectApprover::Object>& object)
const
+      noexcept override
+  {
+    ACL::Entity entityObject;
+
+    if (object.isNone()) {
+      entityObject.set_type(ACL::Entity::ANY);
+    } else {
+      switch (action_) {
+        case authorization::CREATE_VOLUME:
+        case authorization::RESERVE_RESOURCES: {
+          entityObject.set_type(ACL::Entity::SOME);
+          if (object->resource) {
+            entityObject.add_values(object->resource->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(ACL::Entity::ANY);
+          }
+          break;
+        }
+        case authorization::UPDATE_WEIGHT: {
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+          if (object->weight_info) {
+            entityObject.add_values(object->weight_info->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(mesos::ACL::Entity::ANY);
+          }
+
+          break;
+        }
+        case authorization::VIEW_ROLE: {
+          // Check object has the required types set.
+          CHECK_NOTNULL(object->value);
+
+          entityObject.add_values(*(object->value));
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+
+          break;
+        }
+        case authorization::GET_QUOTA: {
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+          if (object->quota_info) {
+            entityObject.add_values(object->quota_info->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(mesos::ACL::Entity::ANY);
+          }
+
+          break;
+        }
+        case authorization::UPDATE_QUOTA: {
+          // Check object has the required types set.
+          CHECK_NOTNULL(object->quota_info);
+
+          entityObject.add_values(object->quota_info->role());
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+
+          break;
+        }
+        case authorization::REGISTER_FRAMEWORK: {
+          vector<ACL::Entity> objects;
+          if (object->framework_info) {
+            foreach (
+                const string& role,
+                protobuf::framework::getRoles(*(object->framework_info))) {
+              objects.emplace_back();
+              objects.back().set_type(mesos::ACL::Entity::SOME);
+              objects.back().add_values(role);
+            }
+          } else if (object->value) {
+            // We also update the deprecated `value` field to support custom
+            // authorizers not yet modified to examine `framework_info`.
+            //
+            // TODO(bbannier): Clean up use of `value` here, see MESOS-7091.
+            objects.emplace_back();
+            objects.back().set_type(mesos::ACL::Entity::SOME);
+            objects.back().add_values(*(object->value));
+          } else {
+            objects.emplace_back();
+            objects.back().set_type(mesos::ACL::Entity::ANY);
+          }
+
+          // The framework needs to be allowed to register under
+          // all the roles it requests.
+          foreach (const ACL::Entity& entity, objects) {
+            if (!approved(acls_, entitySubject_, entity)) {
+              return false;
+            }
+          }
+
+          return objects.empty() ? permissive_ : true;
+        }
+        case authorization::ACCESS_MESOS_LOG:
+        case authorization::ACCESS_SANDBOX:
+        case authorization::ATTACH_CONTAINER_INPUT:
+        case authorization::ATTACH_CONTAINER_OUTPUT:
+        case authorization::DESTROY_VOLUME:
+        case authorization::GET_ENDPOINT_WITH_PATH:
+        case authorization::KILL_NESTED_CONTAINER:
+        case authorization::LAUNCH_NESTED_CONTAINER:
+        case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
+        case authorization::REMOVE_NESTED_CONTAINER:
+        case authorization::RUN_TASK:
+        case authorization::SET_LOG_LEVEL:
+        case authorization::TEARDOWN_FRAMEWORK:
+        case authorization::UNRESERVE_RESOURCES:
+        case authorization::VIEW_CONTAINER:
+        case authorization::VIEW_EXECUTOR:
+        case authorization::VIEW_FLAGS:
+        case authorization::VIEW_FRAMEWORK:
+        case authorization::VIEW_TASK:
+        case authorization::WAIT_NESTED_CONTAINER:
+        case authorization::UNKNOWN:
+          UNREACHABLE();
+      }
+    }
+
+    CHECK(
+        entityObject.type() == ACL::Entity::ANY ||
+        entityObject.values_size() == 1);
+
+    return approved(acls_, entitySubject_, entityObject);
+  }
+
+private:
+  bool approved(
+      const vector<GenericACL>& acls,
+      const ACL::Entity& subject,
+      const ACL::Entity& object) const
+  {
+    // This entity is used for recursive hierarchies where we already
+    // validated that the object role is a nested hierarchy of the
+    // acl role.
+    ACL::Entity aclAny;
+    aclAny.set_type(ACL::Entity::ANY);
+
+    foreach (const GenericACL& acl, acls) {
+      if (!isRecursiveACL(acl)) {
+        // If `acl` is not recursive, treat it as a normal acl.
+        if (matches(subject, acl.subjects) && matches(object, acl.objects)) {
+          return allows(subject, acl.subjects) && allows(object, acl.objects);
+        }
+      } else if (object.type() == ACL::Entity::SOME &&
+          isNestedHierarchy(acl.objects.values(0), object.values(0))) {
+        // Partial validation was done when verifying that the object is
+        // a nested hierarchy.
+        if (matches(subject, acl.subjects) && matches(object, aclAny)) {
+          return allows(subject, acl.subjects) && allows(object, aclAny);
+        }
+      }
+    }
+
+    return permissive_;
+  }
+
+  static bool isRecursiveACL(const GenericACL& acl)
+  {
+    return acl.objects.values_size() == 1 &&
+           strings::endsWith(acl.objects.values(0), "/%");
+  }
+
+  // Returns true if child is a nested hierarchy of parent, i.e. child has more
+  // levels of nesting and all the levels of parent are a prefix of the levels
+  // of child.
+  static bool isNestedHierarchy(const string& parent, const string& child)
+  {
+    // Requires that parent ends with `/%`.
+    CHECK(strings::endsWith(parent, "/%"));
+    return strings::startsWith(child, parent.substr(0, parent.size() - 1));
+  }
+
+  vector<GenericACL> acls_;
+  Option<authorization::Subject> subject_;
+  authorization::Action action_;
+  bool permissive_;
+  ACL::Entity entitySubject_;
+};
+
+
 class LocalAuthorizerProcess : public ProtobufProcess<LocalAuthorizerProcess>
 {
 public:
@@ -630,9 +776,127 @@ public:
       });
   }
 
+  template <typename SomeACL>
+  static vector<GenericACL> createHierarchicalRoleACLs(
+      SomeACL&& someACL)
+  {
+    vector<GenericACL> acls;
+
+    // This may split an ACL into several. This does not change semantics
+    // since the split rules appear consecutively and if an object
+    // matches any of the split ACLs in any order, it will yield the same
+    // results.
+    foreach (auto&& acl, someACL) {
+      switch (acl.roles().type()) {
+        case ACL::Entity::SOME: {
+          ACL::Entity roles;
+          foreach (const string& value, acl.roles().values()) {
+            if (strings::endsWith(value, "/%")) {
+              // Recursive ACLs only have one value in their object list.
+              GenericACL acl_;
+              acl_.subjects = acl.principals();
+              acl_.objects.add_values(value);
+              acls.push_back(acl_);
+            } else {
+              roles.add_values(value);
+            }
+          }
+
+          if (!roles.values().empty()) {
+            GenericACL acl_;
+            acl_.subjects = acl.principals();
+            acl_.objects = roles;
+            acls.push_back(acl_);
+          }
+          break;
+        }
+        case ACL::Entity::ANY:
+        case ACL::Entity::NONE: {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.roles();
+          acls.push_back(acl_);
+          break;
+        }
+      }
+    }
+
+    return acls;
+  }
+
+  Future<Owned<ObjectApprover>> getHierarchicalRoleApprover(
+      const Option<authorization::Subject>& subject,
+      const authorization::Action& action) const
+  {
+    vector<GenericACL> hierarchicalRoleACLs;
+    switch (action) {
+      case authorization::CREATE_VOLUME: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.create_volumes());
+        break;
+      }
+      case authorization::RESERVE_RESOURCES: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.reserve_resources());
+        break;
+      }
+      case authorization::UPDATE_WEIGHT: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.update_weights());
+        break;
+      }
+      case authorization::VIEW_ROLE: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.view_roles());
+        break;
+      }
+      case authorization::GET_QUOTA: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.get_quotas());
+        break;
+      }
+      case authorization::REGISTER_FRAMEWORK: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.register_frameworks());
+        break;
+      }
+      case authorization::UPDATE_QUOTA: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.update_quotas());
+        break;
+      }
+      case authorization::ACCESS_MESOS_LOG:
+      case authorization::ACCESS_SANDBOX:
+      case authorization::ATTACH_CONTAINER_INPUT:
+      case authorization::ATTACH_CONTAINER_OUTPUT:
+      case authorization::DESTROY_VOLUME:
+      case authorization::GET_ENDPOINT_WITH_PATH:
+      case authorization::KILL_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
+      case authorization::RUN_TASK:
+      case authorization::SET_LOG_LEVEL:
+      case authorization::TEARDOWN_FRAMEWORK:
+      case authorization::UNKNOWN:
+      case authorization::UNRESERVE_RESOURCES:
+      case authorization::VIEW_CONTAINER:
+      case authorization::VIEW_EXECUTOR:
+      case authorization::VIEW_FLAGS:
+      case authorization::VIEW_FRAMEWORK:
+      case authorization::VIEW_TASK:
+      case authorization::WAIT_NESTED_CONTAINER:
+      case authorization::REMOVE_NESTED_CONTAINER:
+        UNREACHABLE();
+    }
+
+    return Owned<ObjectApprover>(
+        new LocalHierarchicalRoleApprover(
+            hierarchicalRoleACLs, subject, action, acls.permissive()));
+  }
+
   Future<Owned<ObjectApprover>> getNestedContainerObjectApprover(
       const Option<authorization::Subject>& subject,
-      const authorization::Action& action)
+      const authorization::Action& action) const
   {
     CHECK(action == authorization::LAUNCH_NESTED_CONTAINER ||
           action == authorization::LAUNCH_NESTED_CONTAINER_SESSION);
@@ -747,24 +1011,56 @@ public:
       return Owned<ObjectApprover>(new RejectingObjectApprover());
     }
 
-    if (action == authorization::LAUNCH_NESTED_CONTAINER ||
-        action == authorization::LAUNCH_NESTED_CONTAINER_SESSION) {
-      return getNestedContainerObjectApprover(subject, action);
-    }
-
-    Result<vector<GenericACL>> genericACLs = createGenericACLs(action, acls);
-    if (genericACLs.isError()) {
-      return Failure(genericACLs.error());
-    }
+    switch (action) {
+      case authorization::LAUNCH_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER_SESSION: {
+        return getNestedContainerObjectApprover(subject, action);
+      }
+      case authorization::CREATE_VOLUME:
+      case authorization::RESERVE_RESOURCES:
+      case authorization::UPDATE_WEIGHT:
+      case authorization::VIEW_ROLE:
+      case authorization::GET_QUOTA:
+      case authorization::REGISTER_FRAMEWORK:
+      case authorization::UPDATE_QUOTA: {
+        return getHierarchicalRoleApprover(subject, action);
+      }
+      case authorization::ACCESS_MESOS_LOG:
+      case authorization::ACCESS_SANDBOX:
+      case authorization::ATTACH_CONTAINER_INPUT:
+      case authorization::ATTACH_CONTAINER_OUTPUT:
+      case authorization::DESTROY_VOLUME:
+      case authorization::GET_ENDPOINT_WITH_PATH:
+      case authorization::KILL_NESTED_CONTAINER:
+      case authorization::RUN_TASK:
+      case authorization::SET_LOG_LEVEL:
+      case authorization::TEARDOWN_FRAMEWORK:
+      case authorization::UNRESERVE_RESOURCES:
+      case authorization::VIEW_CONTAINER:
+      case authorization::VIEW_EXECUTOR:
+      case authorization::VIEW_FLAGS:
+      case authorization::VIEW_FRAMEWORK:
+      case authorization::VIEW_TASK:
+      case authorization::WAIT_NESTED_CONTAINER:
+      case authorization::REMOVE_NESTED_CONTAINER:
+      case authorization::UNKNOWN: {
+        Result<vector<GenericACL>> genericACLs =
+          createGenericACLs(action, acls);
+        if (genericACLs.isError()) {
+          return Failure(genericACLs.error());
+        }
+        if (genericACLs.isNone()) {
+          // If we could not create acls, we deny all objects.
+          return Owned<ObjectApprover>(new RejectingObjectApprover());
+        }
 
-    if (genericACLs.isNone()) {
-      // If we could not create acls, we deny all objects.
-      return Owned<ObjectApprover>(new RejectingObjectApprover());
+        return Owned<ObjectApprover>(
+            new LocalAuthorizerObjectApprover(
+                genericACLs.get(), subject, action, acls.permissive()));
+      }
     }
 
-    return Owned<ObjectApprover>(
-        new LocalAuthorizerObjectApprover(
-            genericACLs.get(), subject, action, acls.permissive()));
+    UNREACHABLE();
   }
 
 private:
@@ -775,17 +1071,6 @@ private:
     vector<GenericACL> acls_;
 
     switch (action) {
-      case authorization::REGISTER_FRAMEWORK:
-        foreach (
-            const ACL::RegisterFramework& acl, acls.register_frameworks()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::TEARDOWN_FRAMEWORK:
         foreach (
             const ACL::TeardownFramework& acl, acls.teardown_frameworks()) {
@@ -807,16 +1092,6 @@ private:
         }
 
         return acls_;
-      case authorization::RESERVE_RESOURCES:
-        foreach (const ACL::ReserveResources& acl, acls.reserve_resources()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::UNRESERVE_RESOURCES:
         foreach (
             const ACL::UnreserveResources& acl, acls.unreserve_resources()) {
@@ -828,16 +1103,6 @@ private:
         }
 
         return acls_;
-      case authorization::CREATE_VOLUME:
-        foreach (const ACL::CreateVolume& acl, acls.create_volumes()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::DESTROY_VOLUME:
         foreach (const ACL::DestroyVolume& acl, acls.destroy_volumes()) {
           GenericACL acl_;
@@ -848,47 +1113,6 @@ private:
         }
 
         return acls_;
-      case authorization::GET_QUOTA:
-        foreach (const ACL::GetQuota& acl, acls.get_quotas()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      case authorization::UPDATE_QUOTA: {
-        foreach (const ACL::UpdateQuota& acl, acls.update_quotas()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      }
-      case authorization::VIEW_ROLE:
-        foreach (const ACL::ViewRole& acl, acls.view_roles()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      case authorization::UPDATE_WEIGHT:
-        foreach (const ACL::UpdateWeight& acl, acls.update_weights()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::GET_ENDPOINT_WITH_PATH:
         foreach (const ACL::GetEndpoint& acl, acls.get_endpoints()) {
           GenericACL acl_;
@@ -1034,9 +1258,16 @@ private:
         }
 
         return acls_;
+      case authorization::REGISTER_FRAMEWORK:
+      case authorization::CREATE_VOLUME:
+      case authorization::RESERVE_RESOURCES:
+      case authorization::UPDATE_WEIGHT:
+      case authorization::VIEW_ROLE:
+      case authorization::GET_QUOTA:
+      case authorization::UPDATE_QUOTA:
       case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
       case authorization::LAUNCH_NESTED_CONTAINER:
-        return Error("Extracting ACLs for launching nested containers requires "
+        return Error("Extracting ACLs for " + stringify(action) + " requires "
                      "a specialized function");
       case authorization::UNKNOWN:
         // Cannot generate acls for an unknown action.


Mime
View raw message