SERVER-29168 ClientSourceRestriction
This commit is contained in:
parent
7e5ab840e8
commit
2725293934
@ -247,6 +247,26 @@ env.CppUnitTest(
|
||||
]
|
||||
)
|
||||
|
||||
env.Library(
|
||||
target='address_restriction',
|
||||
source='address_restriction.cpp',
|
||||
LIBDEPS=[
|
||||
'authentication_restriction',
|
||||
'$BUILD_DIR/mongo/base',
|
||||
'$BUILD_DIR/mongo/util/net/cidr',
|
||||
],
|
||||
)
|
||||
|
||||
env.CppUnitTest(
|
||||
target='address_restriction_test',
|
||||
source='address_restriction_test.cpp',
|
||||
LIBDEPS=[
|
||||
'address_restriction',
|
||||
'$BUILD_DIR/mongo/base',
|
||||
'$BUILD_DIR/mongo/util/net/cidr',
|
||||
],
|
||||
)
|
||||
|
||||
env.CppUnitTest('sasl_scramsha1_test',
|
||||
'sasl_scramsha1_test.cpp',
|
||||
LIBDEPS=[
|
||||
|
||||
38
src/mongo/db/auth/address_restriction.cpp
Normal file
38
src/mongo/db/auth/address_restriction.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2017 MongoDB Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the GNU Affero General Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/db/auth/address_restriction.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace address_restriction_detail {
|
||||
|
||||
constexpr StringData ClientSource::label;
|
||||
constexpr StringData ClientSource::field;
|
||||
|
||||
} // address_restriction_detail
|
||||
} // mongo
|
||||
153
src/mongo/db/auth/address_restriction.h
Normal file
153
src/mongo/db/auth/address_restriction.h
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Copyright (C) 2017 MongoDB Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the GNU Affero General Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/status_with.h"
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsonmisc.h"
|
||||
#include "mongo/db/auth/restriction.h"
|
||||
#include "mongo/db/auth/restriction_environment.h"
|
||||
#include "mongo/util/net/cidr.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mongo {
|
||||
|
||||
namespace address_restriction_detail {
|
||||
using mongo::operator""_sd;
|
||||
struct ClientSource {
|
||||
static constexpr auto label = "Client source "_sd;
|
||||
static constexpr auto field = "clientSource"_sd;
|
||||
static auto addr(const RestrictionEnvironment& environment) {
|
||||
return environment.getClientSource();
|
||||
}
|
||||
};
|
||||
|
||||
// Represents a restriction based on client or server address
|
||||
template <typename T>
|
||||
class AddressRestriction : public Restriction {
|
||||
public:
|
||||
/**
|
||||
* Construct an AddressRestriction based on a CIDR spec
|
||||
*/
|
||||
explicit AddressRestriction(CIDR cidr) noexcept : _cidr(std::move(cidr)) {}
|
||||
|
||||
/**
|
||||
* Construct an AddressRestriction based on a human readable subnet spec
|
||||
*/
|
||||
explicit AddressRestriction(const std::string& cidr) : _cidr(CIDR(cidr)) {}
|
||||
|
||||
/**
|
||||
* If the given BSONElement represents a valid CIDR range,
|
||||
* constructs and returns the AddressRestriction.
|
||||
* Otherwise returns an error.
|
||||
*/
|
||||
static StatusWith<AddressRestriction<T>> parse(BSONElement from) noexcept {
|
||||
auto cidr = CIDR::parse(from);
|
||||
if (cidr.isOK()) {
|
||||
return AddressRestriction<T>(std::move(cidr.getValue()));
|
||||
}
|
||||
return cidr.getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given string represents a valid CIDR range,
|
||||
* constructs and returns the AddressRestriction.
|
||||
* Otherwise returns an error.
|
||||
*/
|
||||
static StatusWith<AddressRestriction<T>> parse(const std::string& from) noexcept {
|
||||
auto cidr = CIDR::parse(from);
|
||||
if (cidr.isOK()) {
|
||||
return AddressRestriction<T>(std::move(cidr.getValue()));
|
||||
}
|
||||
return cidr.getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Environment's client/server's address
|
||||
* satisfies this restriction set.
|
||||
*/
|
||||
Status validate(const RestrictionEnvironment& environment) const noexcept override {
|
||||
auto const addr = T::addr(environment);
|
||||
if (!addr.isIP()) {
|
||||
std::ostringstream s;
|
||||
s << T::label << " is not an IP address: " << addr.getAddr();
|
||||
return {ErrorCodes::AuthenticationRestrictionUnmet, s.str()};
|
||||
}
|
||||
|
||||
if (!_cidr.contains(CIDR(addr.getAddr()))) {
|
||||
std::ostringstream s;
|
||||
s << T::label << " does not fall within: " << addr.getAddr();
|
||||
return {ErrorCodes::AuthenticationRestrictionUnmet, s.str()};
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to builder as string element with the human-readable CIDR range.
|
||||
*/
|
||||
void appendToBuilder(BSONObjBuilder* builder) const {
|
||||
builder->append(T::field, _cidr.toString());
|
||||
}
|
||||
|
||||
friend bool operator==(const AddressRestriction<T>& lhs, const AddressRestriction<T>& rhs) {
|
||||
return lhs.equalityLens() == rhs.equalityLens();
|
||||
}
|
||||
friend bool operator!=(const AddressRestriction<T>& lhs, const AddressRestriction<T>& rhs) {
|
||||
return !(lhs._cidr == rhs._cidr);
|
||||
}
|
||||
|
||||
private:
|
||||
auto equalityLens() const {
|
||||
return std::tie(_cidr);
|
||||
}
|
||||
|
||||
void serialize(std::ostream& os) const override {
|
||||
os << "{\"" << T::field << "\": \"" << _cidr << "\"}";
|
||||
}
|
||||
|
||||
CIDR _cidr;
|
||||
};
|
||||
} // namespace address_restriction_detail
|
||||
|
||||
using ClientSourceRestriction =
|
||||
address_restriction_detail::AddressRestriction<address_restriction_detail::ClientSource>;
|
||||
|
||||
template <>
|
||||
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<<ClientSourceRestriction>(
|
||||
ClientSourceRestriction value) {
|
||||
BSONObjBuilder b;
|
||||
value.appendToBuilder(&b);
|
||||
_builder->append(_fieldName, b.obj());
|
||||
_fieldName = StringData();
|
||||
return *_builder;
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
167
src/mongo/db/auth/address_restriction_test.cpp
Normal file
167
src/mongo/db/auth/address_restriction_test.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Copyright (C) 2017 MongoDB Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the GNU Affero General Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include "mongo/db/auth/address_restriction.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
#include "mongo/util/net/sock.h"
|
||||
#include "mongo/util/net/sockaddr.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace {
|
||||
|
||||
|
||||
TEST(AddressRestrictionTest, toAndFromString) {
|
||||
const struct {
|
||||
std::string input;
|
||||
std::string output;
|
||||
} strings[] = {
|
||||
{"127.0.0.1/8", "127.0.0.1/8"},
|
||||
{"127.0.0.1", "127.0.0.1/32"},
|
||||
{"::1", "::1/128"},
|
||||
{"169.254.0.0/16", "169.254.0.0/16"},
|
||||
{"fe80::/10", "fe80::/10"},
|
||||
};
|
||||
for (auto&& p : strings) {
|
||||
{
|
||||
const ClientSourceRestriction csr(CIDR(p.input));
|
||||
std::ostringstream actual;
|
||||
actual << csr;
|
||||
std::ostringstream expected;
|
||||
expected << "{\"clientSource\": \"" << p.output << "\"}";
|
||||
ASSERT_EQUALS(actual.str(), expected.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressRestrictionTest, contains) {
|
||||
enableIPv6(true);
|
||||
const struct {
|
||||
std::string range;
|
||||
std::string address;
|
||||
bool valid;
|
||||
} contains[] = {
|
||||
{"127.0.0.1/8", "0.0.0.0", false},
|
||||
{"127.0.0.1/8", "0.0.0.1", false},
|
||||
{"127.0.0.1/8", "126.255.255.255", false},
|
||||
{"127.0.0.1/8", "127.0.0.0", true},
|
||||
{"127.0.0.1/8", "127.0.0.1", true},
|
||||
{"127.0.0.1/8", "127.0.0.127", true},
|
||||
{"127.0.0.1/8", "127.0.0.128", true},
|
||||
{"127.0.0.1/8", "127.0.0.255", true},
|
||||
{"127.0.0.1/8", "127.255.255.255", true},
|
||||
{"127.0.0.1/8", "127.127.128.1", true},
|
||||
{"127.0.0.1/8", "127.128.127.0", true},
|
||||
{"127.0.0.1/8", "128.0.0.1", false},
|
||||
{"127.0.0.1/8", "169.254.13.37", false},
|
||||
{"127.0.0.1/8", "255.0.0.1", false},
|
||||
{"127.0.0.1/8", "255.255.255.254", false},
|
||||
{"127.0.0.1/8", "255.255.255.255", false},
|
||||
|
||||
{"127.0.0.1", "126.255.255.255", false},
|
||||
{"127.0.0.1", "127.0.0.0", false},
|
||||
{"127.0.0.1", "126.0.0.1", false},
|
||||
{"127.0.0.1", "127.0.0.1", true},
|
||||
{"127.0.0.1", "127.0.0.2", false},
|
||||
{"127.0.0.1", "127.0.0.127", false},
|
||||
{"127.0.0.1", "127.0.0.128", false},
|
||||
{"127.0.0.1", "127.0.0.255", false},
|
||||
{"127.0.0.1", "127.1.0.1", false},
|
||||
{"127.0.0.1", "127.128.0.1", false},
|
||||
{"127.0.0.1", "128.0.0.1", false},
|
||||
|
||||
{"127.0.0.2/31", "127.0.0.0", false},
|
||||
{"127.0.0.2/31", "127.0.0.1", false},
|
||||
{"127.0.0.2/31", "127.0.0.2", true},
|
||||
{"127.0.0.2/31", "127.0.0.3", true},
|
||||
{"127.0.0.2/31", "127.0.0.4", false},
|
||||
{"127.0.0.2/31", "127.0.1.1", false},
|
||||
|
||||
{"169.254.80.0/20", "169.254.79.255", false},
|
||||
{"169.254.80.0/20", "169.254.80.0", true},
|
||||
{"169.254.80.0/20", "169.254.95.255", true},
|
||||
{"169.254.80.0/20", "169.254.96.0", false},
|
||||
|
||||
{"169.254.0.160/28", "169.254.0.159", false},
|
||||
{"169.254.0.160/28", "169.254.0.160", true},
|
||||
{"169.254.0.160/28", "169.254.0.175", true},
|
||||
{"169.254.0.160/28", "169.254.0.176", false},
|
||||
|
||||
{"169.254.0.0/16", "8.8.8.8", false},
|
||||
{"169.254.0.0/16", "127.0.0.1", false},
|
||||
{"169.254.0.0/16", "169.254.0.0", true},
|
||||
{"169.254.0.0/16", "169.254.0.1", true},
|
||||
{"169.254.0.0/16", "169.254.31.17", true},
|
||||
{"169.254.0.0/16", "169.254.255.254", true},
|
||||
{"169.254.0.0/16", "169.254.255.255", true},
|
||||
{"169.254.0.0/16", "240.0.0.1", false},
|
||||
{"169.254.0.0/16", "255.255.255.255", false},
|
||||
|
||||
{"::1", "::", false},
|
||||
{"::1", "::0", false},
|
||||
{"::1", "::1", true},
|
||||
{"::1", "::2", false},
|
||||
{"::1", "::1:0", false},
|
||||
{"::1", "::1:0:0", false},
|
||||
{"::1", "1::", false},
|
||||
{"::1", "1::1", false},
|
||||
{"::1", "8000::1", false},
|
||||
|
||||
{"::1/96", "::1", true},
|
||||
{"::1/96", "::1:0:0", false},
|
||||
{"::1/96", "::DEAD:BEEF", true},
|
||||
{"::1/96", "DEAD::BEEF", false},
|
||||
|
||||
{"::2/127", "::0", false},
|
||||
{"::2/127", "::1", false},
|
||||
{"::2/127", "::2", true},
|
||||
{"::2/127", "::3", true},
|
||||
{"::2/127", "::4", false},
|
||||
{"::2/127", "::5", false},
|
||||
|
||||
{"::1/128", "::", false},
|
||||
{"::1/128", "::0", false},
|
||||
{"::1/128", "::1", true},
|
||||
{"::1/128", "::2", false},
|
||||
{"::1/128", "8000::", false},
|
||||
};
|
||||
for (const auto& p : contains) {
|
||||
const SockAddr dummy;
|
||||
const SockAddr addr(p.address, 1024);
|
||||
const RestrictionEnvironment rec(addr, dummy);
|
||||
const RestrictionEnvironment res(dummy, addr);
|
||||
|
||||
const ClientSourceRestriction csr(CIDR(p.range));
|
||||
ASSERT_EQ(csr.validate(rec).isOK(), p.valid);
|
||||
ASSERT_FALSE(csr.validate(res).isOK());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
@ -138,6 +138,10 @@ SockAddr::SockAddr(struct sockaddr_storage& other, socklen_t size)
|
||||
_hostOrIp = toString(true);
|
||||
}
|
||||
|
||||
bool SockAddr::isIP() const {
|
||||
return (getType() == AF_INET) || (getType() == AF_INET6);
|
||||
}
|
||||
|
||||
bool SockAddr::isLocalHost() const {
|
||||
switch (getType()) {
|
||||
case AF_INET:
|
||||
|
||||
@ -89,6 +89,8 @@ struct SockAddr {
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
bool isIP() const;
|
||||
|
||||
/**
|
||||
* @return one of AF_INET, AF_INET6, or AF_UNIX
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user