TYPED_TEST(AuthorizationTest, AnyPrincipalRunAsAnyUser) { // Any principal can run as any user. ACLs acls; mesos::ACL::RunTask* acl = acls.add_run_tasks(); acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY); acl->mutable_users()->set_type(mesos::ACL::Entity::ANY); // Create an Authorizer with the ACLs. Try<Authorizer*> create = TypeParam::create(); ASSERT_SOME(create); Owned<Authorizer> authorizer(create.get()); Try<Nothing> initialized = authorizer.get()->initialize(acls); ASSERT_SOME(initialized); // Principals "foo" and "bar" can run as "user1" and "user2". mesos::ACL::RunTask request; request.mutable_principals()->add_values("foo"); request.mutable_principals()->add_values("bar"); request.mutable_users()->add_values("user1"); request.mutable_users()->add_values("user2"); AWAIT_EXPECT_EQ(true, authorizer.get()->authorize(request)); }
TYPED_TEST(AuthorizationTest, NoPrincipalRunAsUser) { // No principal can run as "root" user. ACLs acls; { mesos::ACL::RunTask* acl = acls.add_run_tasks(); acl->mutable_principals()->set_type(mesos::ACL::Entity::NONE); acl->mutable_users()->add_values("root"); } // Create an `Authorizer` with the ACLs. Try<Authorizer*> create = TypeParam::create(parameterize(acls)); ASSERT_SOME(create); Owned<Authorizer> authorizer(create.get()); // Principal "foo" cannot run as "root". { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("foo"); request.mutable_object()->set_value("root"); AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request)); } }
void execute(const string& script) { // Create a temporary directory for the test. Try<string> directory = environment->mkdtemp(); CHECK_SOME(directory) << "Failed to create temporary directory"; if (flags.verbose) { std::cerr << "Using temporary directory '" << directory.get() << "'" << std::endl; } // Determine the path for the script. Result<string> path = os::realpath(path::join(flags.source_dir, "src", "tests", script)); if (!path.isSome()) { FAIL() << "Failed to locate script: " << (path.isError() ? path.error() : "No such file or directory"); } // Fork a process to change directory and run the test. pid_t pid; if ((pid = fork()) == -1) { FAIL() << "Failed to fork to launch script"; } if (pid > 0) { // In parent process. int status; while (wait(&status) != pid || WIFSTOPPED(status)); CHECK(WIFEXITED(status) || WIFSIGNALED(status)); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { FAIL() << script << " " << WSTRINGIFY(status); } } else { // In child process. DO NOT USE GLOG! // Start by cd'ing into the temporary directory. Try<Nothing> chdir = os::chdir(directory.get()); if (chdir.isError()) { std::cerr << "Failed to chdir to '" << directory.get() << "': " << chdir.error() << std::endl; abort(); } // Redirect output to /dev/null unless the test is verbose. if (!flags.verbose) { if (freopen("/dev/null", "w", stdout) == NULL || freopen("/dev/null", "w", stderr) == NULL) { std::cerr << "Failed to redirect stdout/stderr to /dev/null:" << os::strerror(errno) << std::endl; abort(); } } // Set up the environment for executing the script. os::setenv("MESOS_SOURCE_DIR", flags.source_dir); os::setenv("MESOS_BUILD_DIR", flags.build_dir); os::setenv("MESOS_WEBUI_DIR", path::join(flags.source_dir, "src", "webui")); os::setenv("MESOS_LAUNCHER_DIR", path::join(flags.build_dir, "src")); // Enable replicated log based registry. os::setenv("MESOS_REGISTRY", "replicated_log"); // Enable authentication. os::setenv("MESOS_AUTHENTICATE", "true"); // Create test credentials. const string& credentials = DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret(); const string& credentialsPath = path::join(directory.get(), "credentials"); CHECK_SOME(os::write(credentialsPath, credentials)) << "Failed to write credentials to '" << credentialsPath << "'"; os::setenv("MESOS_CREDENTIALS", "file://" + credentialsPath); // We set test credentials here for example frameworks to use. os::setenv("DEFAULT_PRINCIPAL", DEFAULT_CREDENTIAL.principal()); os::setenv("DEFAULT_SECRET", DEFAULT_CREDENTIAL.secret()); // TODO(bmahler): Update the example frameworks to use flags and // remove the special DEFAULT_* environment variables above. os::setenv("MESOS_PRINCIPAL", DEFAULT_CREDENTIAL.principal()); os::setenv("MESOS_SECRET", DEFAULT_CREDENTIAL.secret()); // Create test ACLs. ACLs acls; acls.set_permissive(false); mesos::ACL::RunTask* run = acls.add_run_tasks(); run->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal()); Result<string> user = os::user(); CHECK_SOME(user) << "Failed to get current user name"; run->mutable_users()->add_values(user.get()); mesos::ACL::RegisterFramework* register_ = acls.add_register_frameworks(); register_->mutable_principals()->add_values(DEFAULT_CREDENTIAL.principal()); register_->mutable_roles()->add_values("*"); const string& aclsPath = path::join(directory.get(), "acls"); CHECK_SOME(os::write(aclsPath, stringify(JSON::protobuf(acls)))) << "Failed to write ACLs to '" << aclsPath << "'"; os::setenv("MESOS_ACLS", "file://" + aclsPath); // Now execute the script. execl(path.get().c_str(), path.get().c_str(), (char*) NULL); std::cerr << "Failed to execute '" << script << "': " << os::strerror(errno) << std::endl; abort(); } }
// This test verifies that an authorized task launch is successful. TEST_F(MasterAuthorizationTest, AuthorizedTask) { // Setup ACLs so that the framework can launch tasks as "foo". ACLs acls; mesos::ACL::RunTasks* acl = acls.add_run_tasks(); acl->mutable_principals()->add_values(DEFAULT_FRAMEWORK_INFO.principal()); acl->mutable_users()->add_values("foo"); master::Flags flags = CreateMasterFlags(); flags.acls = acls; Try<PID<Master> > master = StartMaster(flags); ASSERT_SOME(master); // Create an authorized executor. ExecutorInfo executor; // Bug in gcc 4.1.*, must assign on next line. executor = CREATE_EXECUTOR_INFO("test-executor", "exit 1"); executor.mutable_command()->set_user("foo"); MockExecutor exec(executor.executor_id()); Try<PID<Slave> > slave = StartSlave(&exec); ASSERT_SOME(slave); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL); EXPECT_CALL(sched, registered(&driver, _, _)) .Times(1); Future<vector<Offer> > offers; EXPECT_CALL(sched, resourceOffers(&driver, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver.start(); AWAIT_READY(offers); EXPECT_NE(0u, offers.get().size()); // Create an authorized task. TaskInfo task; task.set_name("test"); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id()); task.mutable_resources()->MergeFrom(offers.get()[0].resources()); task.mutable_executor()->MergeFrom(executor); vector<TaskInfo> tasks; tasks.push_back(task); EXPECT_CALL(exec, registered(_, _, _, _)) .Times(1); EXPECT_CALL(exec, launchTask(_, _)) .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING)); Future<TaskStatus> status; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&status)); driver.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY(status); EXPECT_EQ(TASK_RUNNING, status.get().state()); EXPECT_CALL(exec, shutdown(_)) .Times(AtMost(1)); driver.stop(); driver.join(); Shutdown(); // Must shutdown before 'containerizer' gets deallocated. }
// This test verifies that a task is launched on the agent if the task // user is authorized based on `run_tasks` ACL configured on the agent // to only allow whitelisted users to run tasks on the agent. TYPED_TEST(SlaveAuthorizerTest, AuthorizeRunTaskOnAgent) { // Get the current user. Result<string> user = os::user(); ASSERT_SOME(user) << "Failed to get the current user name" << (user.isError() ? ": " + user.error() : ""); Try<Owned<cluster::Master>> master = this->StartMaster(); ASSERT_SOME(master); // Start a slave with `bar` and the current user being the only authorized // users to launch tasks on the agent. ACLs acls; acls.set_permissive(false); // Restrictive. mesos::ACL::RunTask* acl = acls.add_run_tasks(); acl->mutable_principals()->set_type(ACL::Entity::ANY); acl->mutable_users()->add_values("bar"); acl->mutable_users()->add_values(user.get()); slave::Flags slaveFlags = this->CreateSlaveFlags(); slaveFlags.acls = acls; Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = this->StartSlave( detector.get(), slaveFlags); ASSERT_SOME(slave); // Create a framework with user `foo`. FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO; frameworkInfo.set_user("foo"); MockScheduler sched; MesosSchedulerDriver driver( &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); Future<vector<Offer>> offers; EXPECT_CALL(sched, resourceOffers(&driver, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver.start(); // Framework is registered since the master admits frameworks of any user. AWAIT_READY(frameworkId); AWAIT_READY(offers); ASSERT_FALSE(offers->empty()); Offer offer = offers.get()[0]; // Launch the first task with no user, so it defaults to the // framework user `foo`. TaskInfo task1 = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:32").get(), "sleep 1000"); // Launch the second task as the current user. TaskInfo task2 = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:32").get(), "sleep 1000"); task2.mutable_command()->set_user(user.get()); // The first task should fail since the task user `foo` is not an // authorized user that can launch a task. However, the second task // should succeed. Future<TaskStatus> status0; Future<TaskStatus> status1; Future<TaskStatus> status2; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&status0)) .WillOnce(FutureArg<1>(&status1)) .WillOnce(FutureArg<1>(&status2)); driver.acceptOffers( {offer.id()}, {LAUNCH({task1, task2})}); // Wait for TASK_ERROR for 1st task, and TASK_STARTING followed by // TASK_RUNNING for 2nd task. AWAIT_READY(status0); AWAIT_READY(status1); AWAIT_READY(status2); // Validate both the statuses. Note that the order of receiving the // status updates for the 2 tasks is not deterministic, but we know // that task2's TASK_RUNNING arrives after TASK_STARTING. hashmap<TaskID, TaskStatus> statuses; statuses[status0->task_id()] = status0.get(); statuses[status1->task_id()] = status1.get(); statuses[status2->task_id()] = status2.get(); ASSERT_TRUE(statuses.contains(task1.task_id())); EXPECT_EQ(TASK_ERROR, statuses.at(task1.task_id()).state()); EXPECT_EQ(TaskStatus::SOURCE_SLAVE, statuses.at(task1.task_id()).source()); EXPECT_EQ(TaskStatus::REASON_TASK_UNAUTHORIZED, statuses.at(task1.task_id()).reason()); ASSERT_TRUE(statuses.contains(task2.task_id())); EXPECT_EQ(TASK_RUNNING, statuses.at(task2.task_id()).state()); driver.stop(); driver.join(); }
TYPED_TEST(AuthorizationTest, OnlySomePrincipalsRunAsSomeUsers) { // Only some principals can run as some users. ACLs acls; { // ACL for some principals to run as some users. mesos::ACL::RunTask* acl = acls.add_run_tasks(); acl->mutable_principals()->add_values("foo"); acl->mutable_principals()->add_values("bar"); acl->mutable_users()->add_values("user1"); acl->mutable_users()->add_values("user2"); } { // ACL for no one else to run as some users. mesos::ACL::RunTask* acl = acls.add_run_tasks(); acl->mutable_principals()->set_type(mesos::ACL::Entity::NONE); acl->mutable_users()->add_values("user1"); acl->mutable_users()->add_values("user2"); } // Create an `Authorizer` with the ACLs. Try<Authorizer*> create = TypeParam::create(parameterize(acls)); ASSERT_SOME(create); Owned<Authorizer> authorizer(create.get()); // Principals "foo" and "bar" can run as "user1" and "user2". { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("foo"); request.mutable_object()->set_value("user1"); AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request)); } { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("foo"); request.mutable_object()->set_value("user2"); AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request)); } { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("bar"); request.mutable_object()->set_value("user1"); AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request)); } { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("bar"); request.mutable_object()->set_value("user2"); AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request)); } // Principal "baz" cannot run as "user1". { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("baz"); request.mutable_object()->set_value("user1"); AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request)); } // Principal "baz" cannot run as "user2". { authorization::Request request; request.set_action(authorization::RUN_TASK_WITH_USER); request.mutable_subject()->set_value("baz"); request.mutable_object()->set_value("user1"); AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request)); } }