Added support for modularized authorizers. Adds and integrates helper classes needed to support an `Authorizer` module. Also adds a flag to the master, allowing the selection of an `Authorizer` module.
Review: https://reviews.apache.org/r/36049 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e6e4acb6 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e6e4acb6 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e6e4acb6 Branch: refs/heads/master Commit: e6e4acb65024b0886e575bd8ab4cf47a3af84e4f Parents: f9a014b Author: Alexander Rojas <[email protected]> Authored: Thu Aug 13 21:49:11 2015 +0200 Committer: Till Toenshoff <[email protected]> Committed: Fri Aug 14 01:43:13 2015 +0200 ---------------------------------------------------------------------- docs/configuration.md | 17 ++++++++ include/mesos/authorizer/authorizer.hpp | 3 ++ include/mesos/module/authorizer.hpp | 63 ++++++++++++++++++++++++++++ src/Makefile.am | 2 + src/authorizer/authorizer.cpp | 48 +++++++++++++++++++++ src/local/local.cpp | 59 +++++++++++++++++++------- src/master/constants.cpp | 1 + src/master/constants.hpp | 3 ++ src/master/flags.cpp | 18 ++++++++ src/master/flags.hpp | 1 + src/master/main.cpp | 55 +++++++++++++++++------- src/module/manager.cpp | 1 + src/tests/cluster.hpp | 3 +- 13 files changed, 243 insertions(+), 31 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/docs/configuration.md ---------------------------------------------------------------------- diff --git a/docs/configuration.md b/docs/configuration.md index 5143811..2b23d48 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -327,6 +327,23 @@ file:///path/to/file (where file contains one of the above)</code></pre> </tr> <tr> <td> + --authorizers=VALUE + </td> + <td> + Authorizer implementation to use when authorizating actions that + required it. Use the default <code>local</code>, or load an alternate + authorizer module using <code>--modules</code>. + <br/> + Note that if the flag <code>--authorizers</code> is provided with a + value different than the default <code>local</code>, the ACLs passed + through the <code>--acls</code> flag will be ignored. + <br/> + Currently there's no support for multiple authorizers.<br/> + (default: <code>local</code>) + </td> + </tr> + <tr> + <td> --cluster=VALUE </td> <td> http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/include/mesos/authorizer/authorizer.hpp ---------------------------------------------------------------------- diff --git a/include/mesos/authorizer/authorizer.hpp b/include/mesos/authorizer/authorizer.hpp index 6ee23c1..d667a52 100644 --- a/include/mesos/authorizer/authorizer.hpp +++ b/include/mesos/authorizer/authorizer.hpp @@ -20,6 +20,7 @@ #define __MESOS_AUTHORIZER_AUTHORIZER_HPP__ #include <ostream> +#include <string> // ONLY USEFUL AFTER RUNNING PROTOC. #include <mesos/authorizer/authorizer.pb.h> @@ -42,6 +43,8 @@ namespace mesos { class Authorizer { public: + static Try<Authorizer*> create(const std::string& name); + virtual ~Authorizer() {} /** http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/include/mesos/module/authorizer.hpp ---------------------------------------------------------------------- diff --git a/include/mesos/module/authorizer.hpp b/include/mesos/module/authorizer.hpp new file mode 100644 index 0000000..7d8fc21 --- /dev/null +++ b/include/mesos/module/authorizer.hpp @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MESOS_MODULE_AUTHORIZER_HPP__ +#define __MESOS_MODULE_AUTHORIZER_HPP__ + +#include <mesos/module.hpp> + +#include <mesos/authorizer/authorizer.hpp> + +namespace mesos { +namespace modules { + +template <> +inline const char* kind<mesos::Authorizer>() +{ + return "Authorizer"; +} + + +template <> +struct Module<mesos::Authorizer> : ModuleBase +{ + Module( + const char* _moduleApiVersion, + const char* _mesosVersion, + const char* _authorName, + const char* _authorEmail, + const char* _description, + bool (*_compatible)(), + mesos::Authorizer* (*_create)(const Parameters& parameters)) + : ModuleBase( + _moduleApiVersion, + _mesosVersion, + mesos::modules::kind<mesos::Authorizer>(), + _authorName, + _authorEmail, + _description, + _compatible), + create(_create) {} + + mesos::Authorizer* (*create)(const Parameters& parameters); +}; + +} // namespace modules { +} // namespace mesos { + +#endif // __MESOS_MODULE_AUTHORIZER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index c481b6f..ae42d52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -402,6 +402,7 @@ libmesos_no_3rdparty_la_SOURCES = \ authentication/cram_md5/authenticatee.cpp \ authentication/cram_md5/authenticator.cpp \ authentication/cram_md5/auxprop.cpp \ + authorizer/authorizer.cpp \ authorizer/local/authorizer.cpp \ common/attributes.cpp \ common/date_utils.cpp \ @@ -549,6 +550,7 @@ module_HEADERS = \ $(top_srcdir)/include/mesos/module/anonymous.hpp \ $(top_srcdir)/include/mesos/module/authenticatee.hpp \ $(top_srcdir)/include/mesos/module/authenticator.hpp \ + $(top_srcdir)/include/mesos/module/authorizer.hpp \ $(top_srcdir)/include/mesos/module/hook.hpp \ $(top_srcdir)/include/mesos/module/isolator.hpp \ $(top_srcdir)/include/mesos/module/module.hpp \ http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/authorizer/authorizer.cpp ---------------------------------------------------------------------- diff --git a/src/authorizer/authorizer.cpp b/src/authorizer/authorizer.cpp new file mode 100644 index 0000000..e1b7057 --- /dev/null +++ b/src/authorizer/authorizer.cpp @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <mesos/authorizer/authorizer.hpp> + +#include <mesos/module/authorizer.hpp> + +#include "authorizer/local/authorizer.hpp" + +#include "master/constants.hpp" + +#include "module/manager.hpp" + +using std::string; + +using mesos::internal::LocalAuthorizer; + +namespace mesos { + +Try<Authorizer*> Authorizer::create(const string& name) +{ + // Create an instance of the default authorizer. If other than the + // default authorizer is requested, search for it in loaded modules. + // NOTE: We do not need an extra not-null check, because both + // ModuleManager and built-in authorizer factory do that already. + if (name == mesos::internal::master::DEFAULT_AUTHORIZER) { + return LocalAuthorizer::create(); + } + + return modules::ModuleManager::create<Authorizer>(name); +} + +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/local/local.cpp ---------------------------------------------------------------------- diff --git a/src/local/local.cpp b/src/local/local.cpp index 0390140..4d98bf2 100644 --- a/src/local/local.cpp +++ b/src/local/local.cpp @@ -28,6 +28,7 @@ #include <mesos/master/allocator.hpp> #include <mesos/module/anonymous.hpp> +#include <mesos/module/authorizer.hpp> #include <mesos/slave/resource_estimator.hpp> @@ -43,8 +44,6 @@ #include <stout/try.hpp> #include <stout/strings.hpp> -#include "authorizer/local/authorizer.hpp" - #include "common/protobuf_utils.hpp" #include "local.hpp" @@ -215,23 +214,51 @@ PID<Master> launch(const Flags& flags, Allocator* _allocator) contender = new StandaloneMasterContender(); detector = new StandaloneMasterDetector(); - if (flags.acls.isSome()) { - Try<Authorizer*> local = LocalAuthorizer::create(); - - if (local.isError()) { - EXIT(EXIT_FAILURE) - << "Failed to instantiate the local authorizer: " - << local.error(); - } + auto authorizerNames = strings::split(flags.authorizers, ","); + if (authorizerNames.empty()) { + EXIT(EXIT_FAILURE) << "No authorizer specified"; + } + if (authorizerNames.size() > 1) { + EXIT(EXIT_FAILURE) << "Multiple authorizers not supported"; + } + std::string authorizerName = authorizerNames[0]; - authorizer = local.get(); + // NOTE: The flag --authorizers overrides the flag --acls, i.e. if + // a non default authorizer is requested, it will be used and + // the contents of --acls will be ignored. + // TODO(arojas): Add support for multiple authorizers. + if (authorizerName != master::DEFAULT_AUTHORIZER || + flags.acls.isSome()) { + Try<Authorizer*> create = Authorizer::create(authorizerName); - Try<Nothing> initialized = authorizer.get()->initialize(flags.acls.get()); + if (create.isError()) { + EXIT(EXIT_FAILURE) << "Could not create '" << authorizerName + << "' authorizer: " << create.error(); + } - if (initialized.isError()) { - EXIT(EXIT_FAILURE) - << "Failed to initialize the local authorizer: " - << initialized.error() << " (see --acls flag)"; + authorizer = create.get(); + + LOG(INFO) << "Using '" << authorizerName << "' authorizer"; + + if (authorizerName == master::DEFAULT_AUTHORIZER) { + Try<Nothing> initialize = + authorizer.get()->initialize(flags.acls.get()); + + if (initialize.isError()) { + // Failing to initialize the authorizer leads to undefined + // behavior, therefore we default to skip authorization + // altogether. + EXIT(EXIT_FAILURE) << "Failed to initialize '" + << authorizerName << "' authorizer: " + << initialize.error(); + + delete authorizer.get(); + authorizer = None(); + } + } else if (flags.acls.isSome()) { + LOG(WARNING) << "Ignoring contents of --acls flag, because '" + << authorizerName << "' authorizer will be used instead" + << " of the default."; } } http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/constants.cpp ---------------------------------------------------------------------- diff --git a/src/master/constants.cpp b/src/master/constants.cpp index 918dd70..2b66b27 100644 --- a/src/master/constants.cpp +++ b/src/master/constants.cpp @@ -47,6 +47,7 @@ const std::string MASTER_INFO_JSON_LABEL = "json.info"; const Duration ZOOKEEPER_SESSION_TIMEOUT = Seconds(10); const std::string DEFAULT_AUTHENTICATOR = "crammd5"; const std::string DEFAULT_ALLOCATOR = "HierarchicalDRF"; +const std::string DEFAULT_AUTHORIZER = "local"; } // namespace master { } // namespace internal { http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/constants.hpp ---------------------------------------------------------------------- diff --git a/src/master/constants.hpp b/src/master/constants.hpp index 7afa1ec..c3fe140 100644 --- a/src/master/constants.hpp +++ b/src/master/constants.hpp @@ -127,6 +127,9 @@ extern const std::string DEFAULT_AUTHENTICATOR; // Name of the default, HierarchicalDRF authenticator. extern const std::string DEFAULT_ALLOCATOR; +// Name of the default, local authorizer. +extern const std::string DEFAULT_AUTHORIZER; + } // namespace master { } // namespace internal { } // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/flags.cpp ---------------------------------------------------------------------- diff --git a/src/master/flags.cpp b/src/master/flags.cpp index 60ac64d..230c1dc 100644 --- a/src/master/flags.cpp +++ b/src/master/flags.cpp @@ -227,6 +227,9 @@ mesos::internal::master::Flags::Flags() "for authorization. Path could be of the form 'file:///path/to/file'\n" "or '/path/to/file'.\n" "\n" + "Note that if the flag --authorizers is provided with a value different\n" + "than '" + DEFAULT_AUTHORIZER + "', the ACLs contents will be ignored.\n" + "\n" "See the ACLs protobuf in mesos.proto for the expected format.\n" "\n" "Example:\n" @@ -407,4 +410,19 @@ mesos::internal::master::Flags::Flags() } return None(); }); + + + add(&Flags::authorizers, + "authorizers", + "Authorizer implementation to use when authorizating actions that\n" + "required it.\n" + "Use the default '" + DEFAULT_AUTHORIZER + "', or\n" + "load an alternate authorizer module using --modules.\n" + "\n" + "Note that if the flag --authorizers is provided with a value different\n" + "than the default '" + DEFAULT_AUTHORIZER + "', the ACLs passed\n" + "through the --acls flag will be ignored.\n" + "\n" + "Currently there's no support for multiple authorizers.", + DEFAULT_AUTHORIZER); } http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/flags.hpp ---------------------------------------------------------------------- diff --git a/src/master/flags.hpp b/src/master/flags.hpp index 25e4631..e4b1df3 100644 --- a/src/master/flags.hpp +++ b/src/master/flags.hpp @@ -78,6 +78,7 @@ public: Option<std::string> hooks; Duration slave_ping_timeout; size_t max_slave_ping_timeouts; + std::string authorizers; #ifdef WITH_NETWORK_ISOLATOR Option<size_t> max_executors_per_slave; http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/master/main.cpp ---------------------------------------------------------------------- diff --git a/src/master/main.cpp b/src/master/main.cpp index 3b4a2c1..e024a2e 100644 --- a/src/master/main.cpp +++ b/src/master/main.cpp @@ -31,6 +31,7 @@ #include <mesos/master/allocator.hpp> #include <mesos/module/anonymous.hpp> +#include <mesos/module/authorizer.hpp> #include <process/limiter.hpp> #include <process/owned.hpp> @@ -48,8 +49,6 @@ #include <stout/strings.hpp> #include <stout/try.hpp> -#include "authorizer/local/authorizer.hpp" - #include "common/build.hpp" #include "common/protobuf_utils.hpp" @@ -367,23 +366,51 @@ int main(int argc, char** argv) detector = detector_.get(); Option<Authorizer*> authorizer = None(); - if (flags.acls.isSome()) { - Try<Authorizer*> local = LocalAuthorizer::create(); - if (local.isError()) { - EXIT(EXIT_FAILURE) - << "Failed to instantiate the local authorizer: " - << local.error(); + auto authorizerNames = strings::split(flags.authorizers, ","); + if (authorizerNames.empty()) { + EXIT(EXIT_FAILURE) << "No authorizer specified"; + } + if (authorizerNames.size() > 1) { + EXIT(EXIT_FAILURE) << "Multiple authorizers not supported"; + } + std::string authorizerName = authorizerNames[0]; + + // NOTE: The flag --authorizers overrides the flag --acls, i.e. if + // a non default authorizer is requested, it will be used and + // the contents of --acls will be ignored. + // TODO(arojas): Add support for multiple authorizers. + if (authorizerName != master::DEFAULT_AUTHORIZER || + flags.acls.isSome()) { + Try<Authorizer*> create = Authorizer::create(authorizerName); + + if (create.isError()) { + EXIT(EXIT_FAILURE) << "Could not create '" << authorizerName + << "' authorizer: " << create.error(); } - authorizer = local.get(); + authorizer = create.get(); - Try<Nothing> initialized = authorizer.get()->initialize(flags.acls.get()); + LOG(INFO) << "Using '" << authorizerName << "' authorizer"; - if (initialized.isError()) { - EXIT(EXIT_FAILURE) - << "Failed to initialize the local authorizer: " - << initialized.error() << " (see --acls flag)"; + if (authorizerName == master::DEFAULT_AUTHORIZER) { + Try<Nothing> initialize = authorizer.get()->initialize(flags.acls.get()); + + if (initialize.isError()) { + // Failing to initialize the authorizer leads to undefined + // behavior, therefore we default to skip authorization + // altogether. + LOG(WARNING) << "Authorization disabled: Failed to initialize '" + << authorizerName << "' authorizer: " + << initialize.error(); + + delete authorizer.get(); + authorizer = None(); + } + } else if (flags.acls.isSome()) { + LOG(WARNING) << "Ignoring contents of --acls flag, because '" + << authorizerName << "' authorizer will be used instead " + << " of the default."; } } http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/module/manager.cpp ---------------------------------------------------------------------- diff --git a/src/module/manager.cpp b/src/module/manager.cpp index 909ca56..862b71f 100644 --- a/src/module/manager.cpp +++ b/src/module/manager.cpp @@ -65,6 +65,7 @@ void ModuleManager::initialize() kindToVersion["Anonymous"] = MESOS_VERSION; kindToVersion["Authenticatee"] = MESOS_VERSION; kindToVersion["Authenticator"] = MESOS_VERSION; + kindToVersion["Authorizer"] = MESOS_VERSION; kindToVersion["Hook"] = MESOS_VERSION; kindToVersion["Isolator"] = MESOS_VERSION; kindToVersion["QoSController"] = MESOS_VERSION; http://git-wip-us.apache.org/repos/asf/mesos/blob/e6e4acb6/src/tests/cluster.hpp ---------------------------------------------------------------------- diff --git a/src/tests/cluster.hpp b/src/tests/cluster.hpp index b59b750..114583d 100644 --- a/src/tests/cluster.hpp +++ b/src/tests/cluster.hpp @@ -63,6 +63,7 @@ #include "log/tool/initialize.hpp" +#include "master/constants.hpp" #include "master/contender.hpp" #include "master/detector.hpp" #include "master/flags.hpp" @@ -351,7 +352,7 @@ inline Try<process::PID<master::Master>> Cluster::Masters::start( if (authorizer.isSome()) { CHECK_NOTNULL(authorizer.get()); } else if (flags.acls.isSome()) { - Try<Authorizer*> local = LocalAuthorizer::create(); + Try<Authorizer*> local = Authorizer::create(master::DEFAULT_AUTHORIZER); if (local.isError()) { EXIT(EXIT_FAILURE)
