[prev in list] [next in list] [prev in thread] [next in thread]
List: mesos-commits
Subject: [1/4] git commit: HTTP authentication flag for basic/digest HTTP authentication.
From: benh () apache ! org
Date: 2014-06-29 19:50:24
Message-ID: f9a218086cf9451d80cb947ba939fd44 () git ! apache ! org
[Download RAW message or body]
Repository: mesos
Updated Branches:
refs/heads/master 294337466 -> d4d253df8
HTTP authentication flag for basic/digest HTTP authentication.
Review: https://reviews.apache.org/r/22222
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/2cb3761c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/2cb3761c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/2cb3761c
Branch: refs/heads/master
Commit: 2cb3761c6bfa80b956eaafde9c69eafaeac3deae
Parents: 2943374
Author: Isabel Jimenez <contact@isabeljimenez.com>
Authored: Sun Jun 29 09:30:15 2014 -0700
Committer: Benjamin Hindman <benjamin.hindman@gmail.com>
Committed: Sun Jun 29 09:30:15 2014 -0700
----------------------------------------------------------------------
include/mesos/mesos.proto | 10 ++++
src/Makefile.am | 1 +
src/credentials/credentials.hpp | 73 +++++++++++++++++++---
src/master/flags.hpp | 28 ++++++++-
src/master/master.cpp | 14 +++--
src/master/master.hpp | 2 +
src/sasl/authenticator.hpp | 11 +---
src/slave/flags.hpp | 15 ++++-
src/slave/slave.cpp | 12 ++--
src/tests/credentials_tests.cpp | 113 +++++++++++++++++++++++++++++++++++
src/tests/mesos.cpp | 26 ++++----
11 files changed, 260 insertions(+), 45 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index b41dc7f..ddbf55c 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -676,6 +676,16 @@ message Credential {
/**
+ * Credentials used for authentication
+ *
+ */
+message Credentials {
+ repeated Credential registration = 1;
+ repeated Credential http = 2;
+}
+
+
+/**
* ACLs used for authorization.
*/
message ACL {
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index bc1603a..12d84bf 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -996,6 +996,7 @@ mesos_tests_SOURCES = \
tests/authorization_tests.cpp \
tests/containerizer.cpp \
tests/containerizer_tests.cpp \
+ tests/credentials_tests.cpp \
tests/environment.cpp \
tests/examples_tests.cpp \
tests/exception_tests.cpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/credentials/credentials.hpp
----------------------------------------------------------------------
diff --git a/src/credentials/credentials.hpp b/src/credentials/credentials.hpp
index 98b9088..cb26da9 100644
--- a/src/credentials/credentials.hpp
+++ b/src/credentials/credentials.hpp
@@ -24,13 +24,14 @@
#include <stout/option.hpp>
#include <stout/os.hpp>
+#include <stout/protobuf.hpp>
#include <stout/try.hpp>
namespace mesos {
namespace internal {
namespace credentials {
-inline Result<std::vector<Credential> > read(const std::string& path)
+inline Result<Credentials> read(const std::string& path)
{
LOG(INFO) << "Loading credentials for authentication from '" << path << "'";
@@ -52,24 +53,78 @@ inline Result<std::vector<Credential> > read(const std::string& path)
<< "credentials file is NOT accessible by others.";
}
- std::vector<Credential> credentials;
+ // TODO(ijimenez) deprecate text support only JSON like acls
+ Try<JSON::Object> json = JSON::parse<JSON::Object>(read.get());
+ if (!json.isError()) {
+ Try<Credentials> credentials = ::protobuf::parse<Credentials>(json.get());
+ if (!credentials.isError()) {
+ return credentials.get();
+ }
+ }
+
+ Credentials credentials;
foreach (const std::string& line, strings::tokenize(read.get(), "\n")) {
const std::vector<std::string>& pairs = strings::tokenize(line, " ");
if (pairs.size() != 2) {
- return Error("Invalid credential format at line: " +
- stringify(credentials.size() + 1));
+ return Error("Invalid credential format at line " +
+ credentials.registration().size() + 1);
}
// Add the credential.
- Credential credential;
- credential.set_principal(pairs[0]);
- credential.set_secret(pairs[1]);
- credentials.push_back(credential);
+ Credential *credential = credentials.add_registration();
+ credential->set_principal(pairs[0]);
+ credential->set_secret(pairs[1]);
}
-
return credentials;
}
+
+inline Result<Credential> readCredential(const std::string& path)
+{
+ LOG(INFO) << "Loading credential for authentication from '" << path << "'";
+
+ Try<std::string> read = os::read(path);
+ if (read.isError()) {
+ return Error("Failed to read credential file '" + path +
+ "': " + read.error());
+ } else if (read.get().empty()) {
+ return None();
+ }
+
+ Try<os::Permissions> permissions = os::permissions(path);
+ if (permissions.isError()) {
+ LOG(WARNING) << "Failed to stat credential file '" << path
+ << "': " << permissions.error();
+ } else if (permissions.get().others.rwx) {
+ LOG(WARNING) << "Permissions on credential file '" << path
+ << "' are too open. It is recommended that your "
+ << "credential file is NOT accessible by others.";
+ }
+
+ // TODO(ijimenez) deprecate text support only JSON like acls
+ Try<JSON::Object> json = JSON::parse<JSON::Object>(read.get());
+ if (!json.isError()) {
+ Try<Credential> credential = ::protobuf::parse<Credential>(json.get());
+ if (!credential.isError()) {
+ return credential.get();
+ }
+ }
+
+ Credential credential;
+ const std::vector<std::string>& line = strings::tokenize(read.get(), "\n");
+ if (line.size() != 1) {
+ return Error("Expecting only one credential");
+ }
+ const std::vector<std::string>& pairs = strings::tokenize(line[0], " ");
+ if (pairs.size() != 2) {
+ return Error("Invalid credential format");
+ }
+ // Add the credential.
+ credential.set_principal(pairs[0]);
+ credential.set_secret(pairs[1]);
+ return credential;
+}
+
} // namespace credentials {
} // namespace internal {
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/master/flags.hpp
----------------------------------------------------------------------
diff --git a/src/master/flags.hpp b/src/master/flags.hpp
index cd2a70e..12c1b33 100644
--- a/src/master/flags.hpp
+++ b/src/master/flags.hpp
@@ -209,9 +209,31 @@ public:
add(&Flags::credentials,
"credentials",
- "Path to a file with a list of credentials.\n"
- "Each line contains 'principal' and 'secret' separated by whitespace.\n"
- "Path could be of the form 'file:///path/to/file' or '/path/to/file'.");
+ "Either a path to a text file with a list of credentials,\n"
+ "each line containing 'principal' and 'secret' separated by "
+ "whitespace,\n"
+ "or, a path to a JSON-formatted file containing credentials\n"
+ "for identification/registration and http authentication."
+ "Path could be of the form 'file:///path/to/file' or '/path/to/file'."
+ "\n"
+ "JSON file Example:\n"
+ "{\n"
+ " \"http\": [\n"
+ " {\n"
+ " \"principal\": \"username\",\n"
+ " \"secret\": \"secret\",\n"
+ " }\n"
+ " ],\n"
+ " \"identification\": [\n"
+ " {\n"
+ " \"principal\": \"username\",\n"
+ " \"secret\": \"secret\",\n"
+ " }\n"
+ " ]\n"
+ "}\n"
+ "Text file Example:\n"
+ "username:secret\n"
+ );
add(&Flags::acls,
"acls",
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 21b07c7..a549986 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -337,16 +337,20 @@ void Master::initialize()
const string& path =
strings::remove(flags.credentials.get(), "file://", strings::PREFIX);
- Result<vector<Credential> > credentials = credentials::read(path);
- if (credentials.isError()) {
- EXIT(1) << credentials.error() << " (see --credentials flag)";
- } else if (credentials.isNone()) {
+ Result<Credentials> _credentials = credentials::read(path);
+ if (_credentials.isError()) {
+ EXIT(1) << _credentials.error() << " (see --credentials flag)";
+ } else if (_credentials.isNone()) {
EXIT(1) << "Credentials file must contain at least one credential"
<< " (see --credentials flag)";
}
// Give Authenticator access to credentials.
- sasl::secrets::load(credentials.get());
+ sasl::secrets::load(_credentials.get());
+
+ // Store credentials in master
+ this->credentials = _credentials.get();
+
} else if (flags.authenticate_frameworks || flags.authenticate_slaves) {
EXIT(1) << "Authentication requires a credentials file"
<< " (see --credentials flag)";
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 5fef354..830b538 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -370,6 +370,8 @@ protected:
OfferID newOfferId();
SlaveID newSlaveId();
+ Option<Credentials> credentials;
+
private:
// Inner class used to namespace HTTP route handlers (see
// master/http.cpp for implementations).
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/sasl/authenticator.hpp
----------------------------------------------------------------------
diff --git a/src/sasl/authenticator.hpp b/src/sasl/authenticator.hpp
index 365db5f..aa222d3 100644
--- a/src/sasl/authenticator.hpp
+++ b/src/sasl/authenticator.hpp
@@ -466,17 +466,12 @@ void load(const std::map<std::string, std::string>& secrets)
InMemoryAuxiliaryPropertyPlugin::load(properties);
}
-
-// Load credentials into the in-memory auxiliary propery plugin
-// that is used by the authenticators.
-void load(const std::vector<Credential>& credentials)
+void load(const Credentials& c)
{
std::map<std::string, std::string> secrets;
-
- foreach (const Credential& credential, credentials) {
- secrets[credential.principal()] = credential.secret();
+ foreach(const Credential& registration, c.registration()) {
+ secrets[registration.principal()] = registration.secret();
}
-
load(secrets);
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index c6a970f..1fe7b7d 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -243,9 +243,18 @@ public:
add(&Flags::credential,
"credential",
- "Path to a file containing a single line with\n"
- "the 'principal' and 'secret' separated by whitespace.\n"
- "Path could be of the form 'file:///path/to/file' or '/path/to/file'");
+ "Either a path to a text with a single line\n"
+ "containing 'principal' and 'secret' separated by "
+ "whitespace.\n"
+ "Or a path containing the JSON "
+ "formatted information used for one credential.\n"
+ "Path could be of the form 'file:///path/to/file' or '/path/to/file'."
+ "\n"
+ "Example:\n"
+ "{\n"
+ " \"principal\": \"username\",\n"
+ " \"secret\": \"secret\",\n"
+ "}");
add(&Flags::containerizer_path,
"containerizer_path",
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/slave/slave.cpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.cpp b/src/slave/slave.cpp
index 24eb8fd..f42ab60 100644
--- a/src/slave/slave.cpp
+++ b/src/slave/slave.cpp
@@ -255,16 +255,14 @@ void Slave::initialize()
const string& path =
strings::remove(flags.credential.get(), "file://", strings::PREFIX);
- Result<vector<Credential> > credentials = credentials::read(path);
- if (credentials.isError()) {
- EXIT(1) << credentials.error() << " (see --credential flag)";
- } else if (credentials.isNone()) {
+ Result<Credential> _credential = credentials::readCredential(path);
+ if (_credential.isError()) {
+ EXIT(1) << _credential.error() << " (see --credential flag)";
+ } else if (_credential.isNone()) {
EXIT(1) << "Empty credential file '" << path
<< "' (see --credential flag)";
- } else if (credentials.get().size() != 1) {
- EXIT(1) << "Not expecting multiple credentials (see --credential flag)";
} else {
- credential = credentials.get()[0];
+ credential = _credential.get();
LOG(INFO) << "Slave using credential for: "
<< credential.get().principal();
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/tests/credentials_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/credentials_tests.cpp b/src/tests/credentials_tests.cpp
new file mode 100644
index 0000000..418e02c
--- /dev/null
+++ b/src/tests/credentials_tests.cpp
@@ -0,0 +1,113 @@
+/**
+ * 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 <gmock/gmock.h>
+
+#include <string>
+
+#include <process/gmock.hpp>
+#include <process/pid.hpp>
+
+#include "master/flags.hpp"
+#include "master/master.hpp"
+#include "tests/mesos.hpp"
+#include "tests/utils.hpp"
+
+using std::map;
+using std::string;
+using std::vector;
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::slave;
+using namespace mesos::internal::tests;
+
+using mesos::internal::master::Master;
+using mesos::internal::slave::Slave;
+
+using process::PID;
+
+using testing::_;
+using testing::Eq;
+using testing::Return;
+
+class CredentialsTest : public MesosTest {};
+
+
+// This test verifies that an authenticated slave is
+// granted registration by the master.
+TEST_F(CredentialsTest, authenticatedSlave)
+{
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+ FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+ Try<PID<Slave> > slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ AWAIT_READY(slaveRegisteredMessage);
+ ASSERT_NE("", slaveRegisteredMessage.get().slave_id().value());
+
+ Shutdown();
+}
+
+
+// Test verifing well executed credential authentication
+// using text formatted credentials so as to test
+// backwards compatibility
+TEST_F(CredentialsTest, authenticatedSlaveText)
+{
+ master::Flags flags = CreateMasterFlags();
+
+ const string& path = path::join(os::getcwd(), "credentials");
+
+ Try<int> fd = os::open(
+ path,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+
+ CHECK_SOME(fd);
+
+ const std::string& credentials =
+ DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret();
+ CHECK_SOME(os::write(fd.get(), credentials))
+ << "Failed to write credentials to '" << path << "'";
+ CHECK_SOME(os::close(fd.get()));
+
+ flags.credentials = "file://" + path;
+
+ Try<PID<Master> > master = StartMaster(flags);
+ ASSERT_SOME(master);
+
+ Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+ FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+ slave::Flags slaveFlags = CreateSlaveFlags();
+
+ slaveFlags.credential = "file://" + path;
+
+ Try<PID<Slave> > slave = StartSlave(slaveFlags);
+ ASSERT_SOME(slave);
+
+ AWAIT_READY(slaveRegisteredMessage);
+ ASSERT_NE("", slaveRegisteredMessage.get().slave_id().value());
+
+ Shutdown();
+}
http://git-wip-us.apache.org/repos/asf/mesos/blob/2cb3761c/src/tests/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.cpp b/src/tests/mesos.cpp
index 0bed925..131d18b 100644
--- a/src/tests/mesos.cpp
+++ b/src/tests/mesos.cpp
@@ -91,12 +91,17 @@ master::Flags MesosTest::CreateMasterFlags()
CHECK_SOME(fd);
- const string& credentials =
- DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret();
-
- CHECK_SOME(os::write(fd.get(), credentials))
- << "Failed to write credentials to '" << path << "'";
-
+ // JSON default format for credentials
+ Credentials credentials;
+ Credential *credential = credentials.add_registration();
+ credential->set_principal(DEFAULT_CREDENTIAL.principal());
+ credential->set_secret(DEFAULT_CREDENTIAL.secret());
+ credential = credentials.add_http();
+ credential->set_principal(DEFAULT_CREDENTIAL.principal());
+ credential->set_secret(DEFAULT_CREDENTIAL.secret());
+
+ CHECK_SOME(os::write(fd.get(), stringify(JSON::Protobuf(credentials))))
+ << "Failed to write credentials to '" << path << "'";
CHECK_SOME(os::close(fd.get()));
flags.credentials = "file://" + path;
@@ -137,11 +142,12 @@ slave::Flags MesosTest::CreateSlaveFlags()
CHECK_SOME(fd);
- const string& credential =
- DEFAULT_CREDENTIAL.principal() + " " + DEFAULT_CREDENTIAL.secret();
+ Credential credential;
+ credential.set_principal(DEFAULT_CREDENTIAL.principal());
+ credential.set_secret(DEFAULT_CREDENTIAL.secret());
- CHECK_SOME(os::write(fd.get(), credential))
- << "Failed to write slave credential to '" << path << "'";
+ CHECK_SOME(os::write(fd.get(), stringify(JSON::Protobuf(credential))))
+ << "Failed to write slave credential to '" << path << "'";
CHECK_SOME(os::close(fd.get()));
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic