Files
scylladb/init.cc
Jesse Haber-Kucharsky ba6a41d397 auth: Switch to sharded service
This change appears quite large, but is logically fairly simple.

Previously, the `auth` module was structured around global state in a
number of ways:

- There existed global instances for the authenticator and the
  authorizer, which were accessed pervasively throughout the system
  through `auth::authenticator::get()` and `auth::authorizer::get()`,
  respectively. These instances needed to be initialized before they
  could be used with `auth::authenticator::setup(sstring type_name)`
  and `auth::authorizer::setup(sstring type_name)`.

- The implementation of the `auth::auth` functions and the authenticator
  and authorizer depended on resources accessed globally through
  `cql3::get_local_query_processor()` and
  `service::get_local_migration_manager()`.

- CQL statements would check for access and manage users through static
  functions in `auth::auth`. These functions would access the global
  authenticator and authorizer instances and depended on the necessary
  systems being started before they were used.

This change eliminates global state from all of these.

The specific changes are:

- Move out `allow_all_authenticator` and `allow_all_authorizer` into
  their own files so that they're constructed like any other
  authenticator or authorizer.

- Delete `auth.hh` and `auth.cc`. Constants and helper functions useful
  for implementing functionality in the `auth` module have moved to
  `common.hh`.

- Remove silent global dependency in
  `auth::authenticated_user::is_super()` on the auth* service in favour
  of a new function `auth::is_super_user()` with an explicit auth*
  service argument.

- Remove global authenticator and authorizer instances, as well as the
  `setup()` functions.

- Expose dependency on the auth* service in
  `auth::authorizer::authorize()` and `auth::authorizer::list()`, which
  is necessary to check for superuser status.

- Add an explicit `service::migration_manager` argument to the
  authenticators and authorizers so they can announce metadata tables.

- The permissions cache now requires an auth* service reference instead
  of just an authorizer since authorizing also requires this.

- The permissions cache configuration can now easily be created from the
  DB configuration.

- Move the static functions in `auth::auth` to the new `auth::service`.
  Where possible, previously static resources like the `delayed_tasks`
  are now members.

- Validating `cql3::user_options` requires an authenticator, which was
  previously accessed globally.

- Instances of the auth* service are accessed through `external`
  instances of `client_state` instead of globally. This includes several
  CQL statements including `alter_user_statement`,
  `create_user_statement`, `drop_user_statement`, `grant_statement`,
  `list_permissions_statement`, `permissions_altering_statement`, and
  `revoke_statement`. For `internal` `client_state`, this is `nullptr`.

- Since the `cql_server` is responsible for instantiating connections
  and each connection gets a new `client_state`, the `cql_server` is
  instantiated with a reference to the auth* service.

- Similarly, the Thrift server is now also instantiated with a reference
  to the auth* service.

- Since the storage service is responsible for instantiating and
  starting the sharded servers, it is instantiated with the sharded
  auth* service which it threads through. All relevant factory functions
  have been updated.

- The storage service is still responsible for starting the auth*
  service it has been provided, and shutting it down.

- The `cql_test_env` is now instantiated with an instance of the auth*
  service, and can be accessed through a member function.

- All unit tests have been updated and pass.

Fixes #2929.
2017-11-15 23:22:42 -05:00

155 lines
5.6 KiB
C++

/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#include "init.hh"
#include "message/messaging_service.hh"
#include "gms/failure_detector.hh"
#include "gms/gossiper.hh"
#include "service/storage_service.hh"
#include "to_string.hh"
#include "gms/inet_address.hh"
logging::logger startlog("init");
//
// NOTE: there functions are (temporarily)
// duplicated in cql_test_env.cc
// until proper shutdown is done.
void init_storage_service(distributed<database>& db, sharded<auth::service>& auth_service) {
service::init_storage_service(db, auth_service).get();
// #293 - do not stop anything
//engine().at_exit([] { return service::deinit_storage_service(); });
}
void init_ms_fd_gossiper(sstring listen_address_in
, uint16_t storage_port
, uint16_t ssl_storage_port
, bool tcp_nodelay_inter_dc
, sstring ms_encrypt_what
, sstring ms_trust_store
, sstring ms_cert
, sstring ms_key
, sstring ms_tls_prio
, bool ms_client_auth
, sstring ms_compress
, db::seed_provider_type seed_provider
, sstring cluster_name
, double phi
, bool sltba)
{
const auto listen = gms::inet_address::lookup(listen_address_in).get0();
using encrypt_what = netw::messaging_service::encrypt_what;
using compress_what = netw::messaging_service::compress_what;
using tcp_nodelay_what = netw::messaging_service::tcp_nodelay_what;
using namespace seastar::tls;
encrypt_what ew = encrypt_what::none;
if (ms_encrypt_what == "all") {
ew = encrypt_what::all;
} else if (ms_encrypt_what == "dc") {
ew = encrypt_what::dc;
} else if (ms_encrypt_what == "rack") {
ew = encrypt_what::rack;
}
compress_what cw = compress_what::none;
if (ms_compress == "all") {
cw = compress_what::all;
} else if (ms_compress == "dc") {
cw = compress_what::dc;
}
tcp_nodelay_what tndw = tcp_nodelay_what::all;
if (!tcp_nodelay_inter_dc) {
tndw = tcp_nodelay_what::local;
}
future<> f = make_ready_future<>();
std::shared_ptr<credentials_builder> creds;
if (ew != encrypt_what::none) {
creds = std::make_shared<credentials_builder>();
creds->set_dh_level(dh_params::level::MEDIUM);
creds->set_x509_key_file(ms_cert, ms_key, x509_crt_format::PEM).get();
if (ms_trust_store.empty()) {
creds->set_system_trust().get();
} else {
creds->set_x509_trust_file(ms_trust_store, x509_crt_format::PEM).get();
}
if (!ms_tls_prio.empty()) {
creds->set_priority_string(ms_tls_prio);
}
if (ms_client_auth) {
creds->set_client_auth(seastar::tls::client_auth::REQUIRE);
}
}
// Init messaging_service
// Delay listening messaging_service until gossip message handlers are registered
bool listen_now = false;
netw::get_messaging_service().start(listen, storage_port, ew, cw, tndw, ssl_storage_port, creds, sltba, listen_now).get();
// #293 - do not stop anything
//engine().at_exit([] { return netw::get_messaging_service().stop(); });
// Init failure_detector
gms::get_failure_detector().start(std::move(phi)).get();
// #293 - do not stop anything
//engine().at_exit([]{ return gms::get_failure_detector().stop(); });
// Init gossiper
std::set<gms::inet_address> seeds;
if (seed_provider.parameters.count("seeds") > 0) {
size_t begin = 0;
size_t next = 0;
sstring seeds_str = seed_provider.parameters.find("seeds")->second;
while (begin < seeds_str.length() && begin != (next=seeds_str.find(",",begin))) {
auto seed = boost::trim_copy(seeds_str.substr(begin,next-begin));
try {
seeds.emplace(gms::inet_address::lookup(seed).get0());
} catch (...) {
startlog.error("Bad configuration: invalid value in 'seeds': '{}': {}", seed, std::current_exception());
throw bad_configuration_error();
}
begin = next+1;
}
}
if (seeds.empty()) {
seeds.emplace(gms::inet_address("127.0.0.1"));
}
auto broadcast_address = utils::fb_utilities::get_broadcast_address();
if (broadcast_address != listen && seeds.count(listen)) {
print("Use broadcast_address instead of listen_address for seeds list: seeds=%s, listen_address=%s, broadcast_address=%s\n",
to_string(seeds), listen_address_in, broadcast_address);
throw std::runtime_error("Use broadcast_address for seeds list");
}
gms::get_gossiper().start().get();
auto& gossiper = gms::get_local_gossiper();
gossiper.set_seeds(seeds);
// #293 - do not stop anything
//engine().at_exit([]{ return gms::get_gossiper().stop(); });
gms::get_gossiper().invoke_on_all([cluster_name](gms::gossiper& g) {
g.set_cluster_name(cluster_name);
});
}