//------------------------------------------------------------------------------ // @file: com_proto_access.cc // @author: Fabio Luchetti - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2018 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your token) any later version. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "console/commands/ICmdHelper.hh" #include "common/StringTokenizer.hh" #include "console/ConsoleMain.hh" extern int com_access(char*); void com_access_help(); //------------------------------------------------------------------------------ //! Class AccessHelper //------------------------------------------------------------------------------ class AccessHelper : public ICmdHelper { public: //---------------------------------------------------------------------------- //! Constructor //! //! @param opts global options //---------------------------------------------------------------------------- AccessHelper(const GlobalOptions& opts): ICmdHelper(opts) {} //---------------------------------------------------------------------------- //! Destructor //---------------------------------------------------------------------------- ~AccessHelper() override = default; //---------------------------------------------------------------------------- //! Parse command line input //! //! @param arg input //! //! @return true if successful, otherwise false //---------------------------------------------------------------------------- bool ParseCommand(const char* arg) override; }; //------------------------------------------------------------------------------ // Parse command line input //------------------------------------------------------------------------------ bool AccessHelper::ParseCommand(const char* arg) { eos::console::AccessProto* access = mReq.mutable_access(); eos::common::StringTokenizer tokenizer(arg); tokenizer.GetLine(); std::string token; if (!tokenizer.NextToken(token)) { return false; } if (token == "ls") { eos::console::AccessProto_LsProto* ls = access->mutable_ls(); while (tokenizer.NextToken(token)) { if (token == "-m") { ls->set_monitoring(true); } else if (token == "-n") { ls->set_id2name(true); } else { return false; } } } else if (token == "rm") { eos::console::AccessProto_RmProto* rm = access->mutable_rm(); if (!tokenizer.NextToken(token)) { return false; } if (token == "redirect") { rm->set_rule(eos::console::AccessProto_RmProto::REDIRECT); if (tokenizer.NextToken(token)) { if (token == "r" || token == "w" || token == "ENOENT" || token == "ENONET" || token == "ENETUNREACH") { rm->set_key(token); } else { return false; } } } else if (token == "stall") { rm->set_rule(eos::console::AccessProto_RmProto::STALL); if (tokenizer.NextToken(token)) { if (token == "r" || token == "w" || token == "ENOENT" || token == "ENONET" || token == "ENETUNREACH") { rm->set_key(token); } else { return false; } } } else if (token == "limit") { rm->set_rule(eos::console::AccessProto_RmProto::LIMIT); if (!tokenizer.NextToken(token)) { return false; } if ((!token.find("threads:")) && ( !(token.find("rate:user:") || token.find("rate:group:")) && token.find(':', 11))) { return false; } rm->set_key(token); } else { return false; } } else if (token == "set") { eos::console::AccessProto_SetProto* set = access->mutable_set(); if (!tokenizer.NextToken(token)) { return false; } if (token == "redirect") { set->set_rule(eos::console::AccessProto_SetProto::REDIRECT); if (!tokenizer.NextToken(token)) { return false; } set->set_target(token); if (tokenizer.NextToken(token)) { if (token == "r" || token == "w" || token == "ENOENT" || token == "ENONET" || token == "ENETUNREACH") { set->set_key(token); } else { return false; } } } else if (token == "stall") { set->set_rule(eos::console::AccessProto_SetProto::STALL); if (!tokenizer.NextToken(token)) { return false; } set->set_target(token); if (tokenizer.NextToken(token)) { if (token == "r" || token == "w" || token == "ENOENT" || token == "ENONET" || token == "ENETUNREACH") { set->set_key(token); } else { return false; } } } else if (token == "limit") { set->set_rule(eos::console::AccessProto_SetProto::LIMIT); if (!tokenizer.NextToken(token)) { return false; } set->set_target(token); if (!tokenizer.NextToken(token)) { return false; } if ((!token.find("threads:")) && ( !(token.find("rate:user:") || token.find("rate:group:")) && token.find(':', 11))) { return false; } fprintf(stderr, "settings %s\n", token.c_str()); set->set_key(token); } else { return false; } } else if (token == "ban") { eos::console::AccessProto_BanProto* ban = access->mutable_ban(); if (!tokenizer.NextToken(token)) { return false; } if (token == "user") { ban->set_idtype(eos::console::AccessProto_BanProto::USER); } else if (token == "group") { ban->set_idtype(eos::console::AccessProto_BanProto::GROUP); } else if (token == "host") { ban->set_idtype(eos::console::AccessProto_BanProto::HOST); } else if (token == "domain") { ban->set_idtype(eos::console::AccessProto_BanProto::DOMAINNAME); } else if (token == "token") { ban->set_idtype(eos::console::AccessProto_BanProto::TOKEN); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } ban->set_id(token); } else if (token == "unban") { eos::console::AccessProto_UnbanProto* unban = access->mutable_unban(); if (!tokenizer.NextToken(token)) { return false; } if (token == "user") { unban->set_idtype(eos::console::AccessProto_UnbanProto::USER); } else if (token == "group") { unban->set_idtype(eos::console::AccessProto_UnbanProto::GROUP); } else if (token == "host") { unban->set_idtype(eos::console::AccessProto_UnbanProto::HOST); } else if (token == "domain") { unban->set_idtype(eos::console::AccessProto_UnbanProto::DOMAINNAME); } else if (token == "token") { unban->set_idtype(eos::console::AccessProto_UnbanProto::TOKEN); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } unban->set_id(token); } else if (token == "allow") { eos::console::AccessProto_AllowProto* allow = access->mutable_allow(); if (!tokenizer.NextToken(token)) { return false; } if (token == "user") { allow->set_idtype(eos::console::AccessProto_AllowProto::USER); } else if (token == "group") { allow->set_idtype(eos::console::AccessProto_AllowProto::GROUP); } else if (token == "host") { allow->set_idtype(eos::console::AccessProto_AllowProto::HOST); } else if (token == "domain") { allow->set_idtype(eos::console::AccessProto_AllowProto::DOMAINNAME); } else if (token == "token") { allow->set_idtype(eos::console::AccessProto_AllowProto::TOKEN); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } allow->set_id(token); } else if (token == "unallow") { eos::console::AccessProto_UnallowProto* unallow = access->mutable_unallow(); if (!tokenizer.NextToken(token)) { return false; } if (token == "user") { unallow->set_idtype(eos::console::AccessProto_UnallowProto::USER); } else if (token == "group") { unallow->set_idtype(eos::console::AccessProto_UnallowProto::GROUP); } else if (token == "host") { unallow->set_idtype(eos::console::AccessProto_UnallowProto::HOST); } else if (token == "domain") { unallow->set_idtype(eos::console::AccessProto_UnallowProto::DOMAINNAME); } else if (token == "token") { unallow->set_idtype(eos::console::AccessProto_UnallowProto::TOKEN); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } unallow->set_id(token); } else if (token == "stallhosts") { eos::console::AccessProto_StallHostsProto* stall = access->mutable_stallhosts(); if (!tokenizer.NextToken(token)) { return false; } if (token == "add") { stall->set_op(eos::console::AccessProto_StallHostsProto::ADD); } else if (token == "remove") { stall->set_op(eos::console::AccessProto_StallHostsProto::REMOVE); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } if (token == "stall") { stall->set_type(eos::console::AccessProto_StallHostsProto::STALL); } else if (token == "nostall") { stall->set_type(eos::console::AccessProto_StallHostsProto::NOSTALL); } else { return false; } if (!tokenizer.NextToken(token)) { return false; } stall->set_hostpattern(token); } else { // no proper subcommand return false; } return true; } //------------------------------------------------------------------------------ // Config command entry point //------------------------------------------------------------------------------ int com_protoaccess(char* arg) { if (wants_help(arg)) { com_access_help(); global_retc = EINVAL; return EINVAL; } AccessHelper access(gGlobalOpts); if (!access.ParseCommand(arg)) { com_access_help(); global_retc = EINVAL; return EINVAL; } global_retc = access.Execute(); return global_retc; } //------------------------------------------------------------------------------ // Print help message //------------------------------------------------------------------------------ void com_access_help() { std::ostringstream oss; oss << " usage:\n" << "access ban|unban|allow|unallow|set|rm|stallhosts|ls [OPTIONS]\n" << "'[eos] access ..' provides the access interface of EOS to allow/disallow hosts/domains and/or users\n" << std::endl << "Subcommands:\n" << "access ban user|group|host|domain|token : ban user, group, host, domain or token with identifier \n" << "\t : can be a user name, user id, group name, group id, hostname or IP or domainname or token voucher id\n" << std::endl << "access unban user|group|host|domain|token : unban user, group, host, domain or token with identifier \n" << "\t : can be a user name, user id, group name, group id, hostname or IP or domainname or token voucher id\n" << std::endl << "access allow user|group|host|domain|token : allows this user, group, host, domain or token access\n" << "\t : can be a user name, user id, group name, group id, hostname or IP or domainname or token voucher id\n" << std::endl << "access unallow user|group|host|domain|token : unallows this user,group, host, domain or token access\n" << "\t : can be a user name, user id, group name, group id, hostname or IP or domainname or token voucher id\n" << std::endl << "\t HINT: if you add any 'allow' the instance allows only the listed identity. A banned identifier will still overrule an allowed identifier!\n" << std::endl << "access set redirect [r|w|ENOENT|ENONET|ENETUNREACH] : allows to set a global redirection to \n" << "\t : hostname to which all requests get redirected\n" << "\t [r|w] : optional set a redirect for read/write requests seperatly\n" << "\t [ENOENT] : optional set a redirect if a file is not existing\n" << "\t [ENONET] : optional set a redirect if a file is offline\n" << "\t [ENETUNREACH] : optional set a redirect if @todo \n" << "\t can be structured like :] where holds each request for a given time before redirecting\n" << std::endl << "access set stall [r|w|ENOENT|ENONET|ENETUNREACH] : allows to set a global stall time\n" << "\t : time in seconds after which clients should rebounce\n" << "\t [r|w] : optional set stall time for read/write requests seperatly\n" << "\t [ENOENT] : optional set stall time if a file is not existing\n" << "\t [ENONET] : optional set stall time if a file is offline\n" << "\t [ENETUNREACH] : optional set a stall time if @todo \n" << std::endl << "access set limit rate:{user,group}:{name}:\n" << "\t rate:{user:group}:{name}: : stall the defined user group for 5s if the exceeds a frequency of in a 5s interval\n" << "\t - the instantaneous rate can exceed this value by 33%%\n" << "\t rate:user:*: : apply to all users based on user counter\n" << "\t rate:group:*:: apply to all groups based on group counter\n" << "\t set to 0 (zero) to continuously stall the user or group\n" << std::endl << "access set limit threads:{*,max,}\n" << "\t threads:max : set the maximum number of threads running in parallel\n" << "\t threads:* : set the default thread pool limit for each user\n" << "\t threads: : set a specific thread pool limit for user \n" << std::endl << "access set limit rate:user:{name}:FindFiles :\n\tset find query limit to for user {name}\n" << std::endl << "access set limit rate:user:{name}:FindDirs:\n\tset find query limit to for user {name}\n" << std::endl << "access set limit rate:group:{name}:FindFiles :\n\tset find query limit to for group {name}\n" << std::endl << "access set limit rate:group:{name}:FindDirs :\n\tset find query limit to for group {name}\n" << std::endl << "access set limit rate:user:*:FindFiles :\n\tset default find query limit to for everybody\n" << std::endl << "access set limit rate:user:*:FindDirs :\n\tset default find query limit to for everybody\n" << std::endl << "\t HINT : rule strength => user-limit >> group-limit >> wildcard-limit\n" << std::endl << "access rm redirect [r|w|ENOENT|ENONET|ENETUNREACH] : removes global redirection\n" << std::endl << "access rm stall [r|w|ENOENT|ENONET|ENETUNREACH] : removes global stall time\n" << std::endl << "access rm limit rate:{user,group}:{name}: : remove rate limitation\n" << std::endl << "access rm limit threads:{max,*,} : remove thread pool limit\n" << std::endl << "access stallhosts add|remove stall|nostall \n" << std::endl << "access ls [-m] [-n] : print banned,unbanned user,group, hosts\n" << "\t -m : output in monitoring format with =\n" << "\t -n : don't translate uid/gids to names\n" << std::endl << "Examples:\n" << " access ban host foo : Ban host foo\n" << " access ban domain bar : Ban domain bar\n" << " access allow domain nobody@bar : Allows user nobody from domain bar\n" << " access allow domain - : use domain allow as whitelist - e.g. nobody@bar will additionally allow the nobody user from domain bar!\n" << " access allow domain bar : Allow only domain bar\n" << " access set redirect foo : Redirect all requests to host foo\n" << " access set redirect foo:1094:1000 : Redirect all requests to host foo:1094 and hold each reqeust for 1000ms\n" << " access rm redirect : Remove redirection to previously defined host foo\n" << " access set stall 60 : Stall all clients by 60 seconds\n" << " access ls : Print all defined access rules\n" << " access set limit 100 rate:user:*:OpenRead : Limit the open for read rate to a frequency of 100 Hz for all users\n" << " access set limit 0 rate:user:ab:OpenRead : Limit the open for read rate for the ab user to 0 Hz, to continuously stall it\n" << " access set limit 2000 rate:group:zp:Stat : Limit the stat rate for the zp group to 2kHz\n" << " access set limit 500 threads:* : Limit the thread pool usage to 500 threads per user\n" << " access rm limit rate:user:*:OpenRead : Removes the defined limit\n" << " access rm limit threads:* : Removes the default per user thread pool limit\n" << " access stallhosts add stall foo*.bar : Add foo*.bar to the list of hosts which are stalled by limit rules (white list)\n" << " access stallhosts remove stall foo*.bar : Remove foo*.bar from the list of hosts which are stalled by limit rules (white list)\n" << " access stallhosts add nostall foo*.bar : Add foo*.bar to the list of hosts which are never stalled by limit rules (black list)\n" << " access stallhosts remove nostall foo*.bar : Remove foo*.bar from the list of hosts which are never stalled by limit rules (black list)\n"; std::cerr << oss.str() << std::endl; }